test.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. #!/usr/bin/env python3
  2. import os
  3. import sys
  4. import json
  5. import tempfile
  6. import shutil
  7. from pathlib import Path
  8. # Add the current directory to Python path to import our modules
  9. sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
  10. from config import Config, KeyConfig
  11. def test_multikey_config():
  12. """Test multi-key configuration loading"""
  13. print("Testing multi-key configuration...")
  14. # Create temporary config file
  15. config_data = {
  16. "server": {
  17. "host": "127.0.0.1",
  18. "port": 1127
  19. },
  20. "routes": {
  21. "health_route": "/health-check-test"
  22. },
  23. "files": {
  24. "dummy_file": "/tmp/dummy.txt"
  25. },
  26. "keys": {
  27. "backup_key": {
  28. "route": "/emergency-key-backup-a7f9d2e1b4c7f3e6d9a2b5c8e1f4d7a0b3c6e9f2a5d8b1c4e7f0a3d6b9c2e5f8a1d4b7c0e3f6d9a2b5c8e1f4a7b0d3",
  29. "file": "/tmp/backup-key.txt",
  30. "backends": ["test_backend1", "test_backend2"],
  31. "message": "Backup key accessed"
  32. },
  33. "master_key": {
  34. "route": "/emergency-key-master-x3k8m9n2p5q7r1t4u6v9w2y5z8b1c4d7e0f3g6h9j2k5l8m1n4o7p0q3r6s9t2u5v8w1x4y7z0a3b6c9d2e5f8",
  35. "file": "/tmp/master-key.txt",
  36. "backends": ["test_backend1", "test_backend3"],
  37. "message": "Master key accessed"
  38. }
  39. },
  40. "notifications": {
  41. "health_backends": ["health_backend"],
  42. "config_path": "/tmp/ntfy.yml",
  43. "health_message": "Health check",
  44. "log_level": "WARNING",
  45. "send_all_logs": true
  46. }
  47. }
  48. # Write config to temporary file
  49. with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
  50. json.dump(config_data, f)
  51. config_file = f.name
  52. try:
  53. # Load config
  54. config = Config(config_file)
  55. # Test basic properties
  56. assert config.server_host == "127.0.0.1"
  57. assert config.server_port == 1127
  58. assert config.health_route == "/health-check-test"
  59. # Test keys
  60. keys = config.keys
  61. assert len(keys) == 2
  62. assert "backup_key" in keys
  63. assert "master_key" in keys
  64. # Test backup key
  65. backup_key = keys["backup_key"]
  66. assert backup_key.route == "/emergency-key-backup-a7f9d2e1b4c7f3e6d9a2b5c8e1f4d7a0b3c6e9f2a5d8b1c4e7f0a3d6b9c2e5f8a1d4b7c0e3f6d9a2b5c8e1f4a7b0d3"
  67. assert backup_key.file_path == "/tmp/backup-key.txt"
  68. assert backup_key.backends == ["test_backend1", "test_backend2"]
  69. assert backup_key.message == "Backup key accessed"
  70. # Test master key
  71. master_key = keys["master_key"]
  72. assert master_key.route == "/emergency-key-master-x3k8m9n2p5q7r1t4u6v9w2y5z8b1c4d7e0f3g6h9j2k5l8m1n4o7p0q3r6s9t2u5v8w1x4y7z0a3b6c9d2e5f8"
  73. assert master_key.file_path == "/tmp/master-key.txt"
  74. assert master_key.backends == ["test_backend1", "test_backend3"]
  75. assert master_key.message == "Master key accessed"
  76. # Test key lookup methods
  77. found_key = config.get_key_by_route("/emergency-key-backup-a7f9d2e1b4c7f3e6d9a2b5c8e1f4d7a0b3c6e9f2a5d8b1c4e7f0a3d6b9c2e5f8a1d4b7c0e3f6d9a2b5c8e1f4a7b0d3")
  78. assert found_key is not None
  79. assert found_key.key_id == "backup_key"
  80. found_key = config.get_key_by_id("master_key")
  81. assert found_key is not None
  82. assert found_key.route == "/emergency-key-master-x3k8m9n2p5q7r1t4u6v9w2y5z8b1c4d7e0f3g6h9j2k5l8m1n4o7p0q3r6s9t2u5v8w1x4y7z0a3b6c9d2e5f8"
  83. print("✅ Multi-key configuration test passed")
  84. finally:
  85. os.unlink(config_file)
  86. def test_invalid_config():
  87. """Test invalid configuration handling"""
  88. print("Testing invalid configuration handling...")
  89. # Test config without keys section
  90. config_data = {
  91. "server": {
  92. "host": "127.0.0.1",
  93. "port": 1127
  94. },
  95. "routes": {
  96. "health_route": "/health-check-test"
  97. },
  98. "files": {
  99. "dummy_file": "/tmp/dummy.txt"
  100. },
  101. "notifications": {
  102. "health_backends": ["health_backend"],
  103. "config_path": "/tmp/ntfy.yml",
  104. "health_message": "Health check",
  105. "log_level": "WARNING",
  106. "send_all_logs": true
  107. }
  108. }
  109. # Write config to temporary file
  110. with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
  111. json.dump(config_data, f)
  112. config_file = f.name
  113. try:
  114. # Should raise exception for missing keys
  115. try:
  116. config = Config(config_file)
  117. assert False, "Should have raised exception for missing keys section"
  118. except Exception as e:
  119. assert "No keys configured" in str(e)
  120. print("✅ Invalid configuration test passed")
  121. finally:
  122. os.unlink(config_file)
  123. def test_key_config_validation():
  124. """Test key configuration validation"""
  125. print("Testing key configuration validation...")
  126. # Test missing route
  127. try:
  128. KeyConfig("test", {"file": "/tmp/test.txt"}, {})
  129. assert False, "Should have raised exception for missing route"
  130. except Exception as e:
  131. assert "Route not configured" in str(e)
  132. # Test missing file
  133. try:
  134. KeyConfig("test", {"route": "/test"}, {})
  135. assert False, "Should have raised exception for missing file"
  136. except Exception as e:
  137. assert "File path not configured" in str(e)
  138. # Test missing backends
  139. try:
  140. KeyConfig("test", {"route": "/test", "file": "/tmp/test.txt"}, {})
  141. assert False, "Should have raised exception for missing backends"
  142. except Exception as e:
  143. assert "No notification backends configured" in str(e)
  144. # Test valid config
  145. key_config = KeyConfig(
  146. "test",
  147. {
  148. "route": "/test",
  149. "file": "/tmp/test.txt",
  150. "backends": ["backend1"],
  151. "message": "Test message"
  152. },
  153. {}
  154. )
  155. assert key_config.key_id == "test"
  156. assert key_config.route == "/test"
  157. assert key_config.file_path == "/tmp/test.txt"
  158. assert key_config.backends == ["backend1"]
  159. assert key_config.message == "Test message"
  160. print("✅ Key configuration validation test passed")
  161. def test_app_integration():
  162. """Test Flask app integration with multiple keys"""
  163. print("Testing Flask app integration...")
  164. # Create test files
  165. temp_dir = tempfile.mkdtemp()
  166. try:
  167. # Create test key files
  168. backup_key_file = os.path.join(temp_dir, "backup-key.txt")
  169. master_key_file = os.path.join(temp_dir, "master-key.txt")
  170. dummy_file = os.path.join(temp_dir, "dummy.txt")
  171. with open(backup_key_file, 'w') as f:
  172. f.write("backup_key_content_123")
  173. with open(master_key_file, 'w') as f:
  174. f.write("master_key_content_456")
  175. with open(dummy_file, 'w') as f:
  176. f.write("system_healthy")
  177. # Create config
  178. config_data = {
  179. "server": {
  180. "host": "127.0.0.1",
  181. "port": 1127
  182. },
  183. "routes": {
  184. "health_route": "/health-check-test"
  185. },
  186. "files": {
  187. "dummy_file": dummy_file
  188. },
  189. "keys": {
  190. "backup_key": {
  191. "route": "/emergency-key-backup-a7f9d2e1b4c7f3e6d9a2b5c8e1f4d7a0b3c6e9f2a5d8b1c4e7f0a3d6b9c2e5f8a1d4b7c0e3f6d9a2b5c8e1f4a7b0d3",
  192. "file": backup_key_file,
  193. "backends": ["test_backend1"],
  194. "message": "Backup key accessed"
  195. },
  196. "master_key": {
  197. "route": "/emergency-key-master-x3k8m9n2p5q7r1t4u6v9w2y5z8b1c4d7e0f3g6h9j2k5l8m1n4o7p0q3r6s9t2u5v8w1x4y7z0a3b6c9d2e5f8",
  198. "file": master_key_file,
  199. "backends": ["test_backend2"],
  200. "message": "Master key accessed"
  201. }
  202. },
  203. "notifications": {
  204. "health_backends": ["health_backend"],
  205. "config_path": "/tmp/ntfy.yml",
  206. "health_message": "Health check",
  207. "log_level": "WARNING",
  208. "send_all_logs": false
  209. }
  210. }
  211. # Write config
  212. with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
  213. json.dump(config_data, f)
  214. config_file = f.name
  215. try:
  216. # Set environment variable
  217. os.environ['EMERGENCY_CONFIG'] = config_file
  218. # Import main module (this tests configuration loading)
  219. import main
  220. # Test config loading
  221. main.config = Config(config_file)
  222. # Test file reading
  223. success, content = main.read_file_safely(backup_key_file)
  224. assert success == True
  225. assert content == "backup_key_content_123"
  226. success, content = main.read_file_safely(master_key_file)
  227. assert success == True
  228. assert content == "master_key_content_456"
  229. # Test key handler creation
  230. backup_key_config = main.config.get_key_by_id("backup_key")
  231. handler = main.create_key_handler(backup_key_config)
  232. assert handler is not None
  233. print("✅ Flask app integration test passed")
  234. finally:
  235. os.unlink(config_file)
  236. if 'EMERGENCY_CONFIG' in os.environ:
  237. del os.environ['EMERGENCY_CONFIG']
  238. finally:
  239. shutil.rmtree(temp_dir)
  240. def main():
  241. """Run all tests"""
  242. print("Running multi-key functionality tests...\n")
  243. try:
  244. test_multikey_config()
  245. test_invalid_config()
  246. test_key_config_validation()
  247. test_app_integration()
  248. print("\n🎉 All tests passed! Multi-key functionality is working correctly.")
  249. return True
  250. except AssertionError as e:
  251. print(f"\n❌ Test failed: {e}")
  252. return False
  253. except Exception as e:
  254. print(f"\n❌ Unexpected error: {e}")
  255. import traceback
  256. traceback.print_exc()
  257. return False
  258. if __name__ == "__main__":
  259. success = main()
  260. sys.exit(0 if success else 1)