"""Quick validation tests for P1+P2 features""" import sys sys.path.insert(0, ".") import datetime from datetime import timezone def test_jwt_updated_at(): """P2-6: JWT token includes updated_at claim""" from server.domain.models import Admin from server.application.services.auth_service import AuthService admin = Admin( id=1, username="testadmin", password_hash="fakehash", updated_at=datetime.datetime.now(timezone.utc), ) service = AuthService(None, None, None) token = service._create_access_token(admin) import jwt from server.config import settings payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"]) assert "updated" in payload, "Missing 'updated' claim in JWT" admin_ts = int(admin.updated_at.timestamp()) token_ts = payload["updated"] assert abs(admin_ts - token_ts) < 2, f"updated_at mismatch: {admin_ts} vs {token_ts}" print(f" JWT updated_at claim: OK (ts={token_ts})") def test_jwt_password_invalidation(): """P2-6: Token invalidated when admin.updated_at changes""" from server.domain.models import Admin from server.application.services.auth_service import AuthService import datetime from datetime import timezone admin = Admin( id=1, username="testadmin", password_hash="fakehash", updated_at=datetime.datetime.now(timezone.utc), ) service = AuthService(None, None, None) token = service._create_access_token(admin) import jwt from server.config import settings payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"]) token_updated = payload["updated"] # Simulate password change (updated_at moves forward) import time time.sleep(1) admin.updated_at = datetime.datetime.now(timezone.utc) admin_ts = int(admin.updated_at.timestamp()) # Token should be invalid if admin updated after token issued assert admin_ts > token_updated, "Password change should make token stale" print(f" JWT password invalidation: OK (token_updated={token_updated}, admin_updated={admin_ts})") def test_alert_dedup(): """P2-1: Alert deduplication cooldown""" from server.api.websocket import _should_push_telegram # First call should allow assert _should_push_telegram("alert:1:cpu") is True, "First alert should be allowed" # Immediate second call should be suppressed assert _should_push_telegram("alert:1:cpu") is False, "Duplicate alert should be suppressed" # Different server/metric should be allowed assert _should_push_telegram("alert:2:mem") is True, "Different alert should be allowed" # Same server, different metric should be allowed assert _should_push_telegram("alert:1:mem") is True, "Different metric should be allowed" print(" Alert dedup cooldown: OK") def test_server_repo_paginated(): """P2-3: Server repository paginated query signature""" import inspect from server.infrastructure.database.server_repo import ServerRepositoryImpl # Check method exists and has correct signature assert hasattr(ServerRepositoryImpl, "get_paginated"), "Missing get_paginated method" sig = inspect.signature(ServerRepositoryImpl.get_paginated) params = list(sig.parameters.keys()) assert "category" in params, "Missing category parameter" assert "offset" in params, "Missing offset parameter" assert "limit" in params, "Missing limit parameter" print(f" Server repo get_paginated: OK (params={params})") def test_config_dedup_command(): """P1-7: Config dedup sed command uses escaped \\s""" # Just verify the string doesn't have invalid escape import re key = "net.ipv4.ip_forward" escaped_key = re.escape(key) cmd = f"sed -i '/^{escaped_key}\\s*=/d' /etc/sysctl.d/99-nexus.conf 2>/dev/null || true" # Should contain \\s* not \s* assert "\\s*" in cmd, f"Dedup command should use \\s* not \\s" print(f" Config dedup command: OK") def test_server_to_dict_no_password(): """P2-7/P2-8: _server_to_dict never returns password/ssh_key_private""" from server.api.servers import _server_to_dict from server.domain.models import Server server = Server( id=1, name="test", domain="10.0.0.1", password="secret123", ssh_key_private="-----BEGIN KEY-----", agent_api_key="nxs-verylongkeystring1234567890", ) d = _server_to_dict(server) assert "password" not in d, "password should not be in API response" assert "ssh_key_private" not in d, "ssh_key_private should not be in API response" assert d.get("password_set") is True, "password_set should be True" assert d.get("ssh_key_private_set") is True, "ssh_key_private_set should be True" assert d.get("agent_api_key") == "nxs-very...", "agent_api_key should be masked (first 8 chars + ...)" print(" Server dict no password: OK") def test_admin_model_updated_at(): """P2-6: Admin model has updated_at field""" from server.domain.models import Admin assert hasattr(Admin, "updated_at"), "Admin model missing updated_at column" print(" Admin.updated_at field: OK") if __name__ == "__main__": print("=" * 60) print("Nexus P1+P2 Validation Tests") print("=" * 60) tests = [ ("P2-6 JWT updated_at claim", test_jwt_updated_at), ("P2-6 JWT password invalidation", test_jwt_password_invalidation), ("P2-1 Alert dedup cooldown", test_alert_dedup), ("P2-3 Server repo pagination", test_server_repo_paginated), ("P1-7 Config dedup command", test_config_dedup_command), ("P2-7/P2-8 Server dict no password", test_server_to_dict_no_password), ("P2-6 Admin.updated_at field", test_admin_model_updated_at), ] passed = 0 failed = 0 for name, fn in tests: try: print(f"\n[{name}]") fn() passed += 1 except Exception as e: print(f" FAIL: {e}") failed += 1 print("\n" + "=" * 60) print(f"Results: {passed} passed, {failed} failed, {passed + failed} total") print("=" * 60)