Files
Nexus/tests/test_step1to5_features.py
2026-07-08 22:31:31 +08:00

167 lines
5.8 KiB
Python

"""Nexus — Step 1-5: Data Layer + Auth + Sync + Models Tests
D10: Data migration tests
A5: JWT auth flow tests
S6: Sync engine tests
"""
import pytest
from unittest.mock import AsyncMock, MagicMock
class TestDomainModels:
"""D10: Model integrity tests"""
def test_platform_model_fields(self):
"""Platform model has all required fields"""
from server.domain.models import Platform
p = Platform(name="Linux", category="host", type="linux")
assert p.name == "Linux"
assert p.category == "host"
assert p.type == "linux"
# Column default only applies on DB INSERT, not Python construction
def test_node_model_fields(self):
"""Node model has tree structure fields"""
from server.domain.models import Node
n = Node(name="Web Servers", parent_id=None, sort_order=1)
assert n.name == "Web Servers"
assert n.parent_id is None
assert n.sort_order == 1
def test_server_model_new_fields(self):
"""Server model has new extended fields"""
from server.domain.models import Server
s = Server(name="test", domain="1.2.3.4")
assert s.platform_id is None
assert s.node_id is None
assert s.protocols is None
assert s.extra_attrs is None
# Column default only applies on DB INSERT, not Python construction
def test_admin_model_jwt_fields(self):
"""Admin model has JWT fields"""
from server.domain.models import Admin
a = Admin(username="admin", password_hash="xxx")
assert (a.token_version or 0) == 0
def test_ssh_session_model(self):
"""SshSession model has lifecycle fields"""
from server.domain.models import SshSession
s = SshSession(server_id=1, admin_id=1)
# Column default only applies on DB INSERT, not Python construction
assert s.closed_at is None
def test_command_log_model(self):
"""CommandLog model has required fields"""
from server.domain.models import CommandLog
c = CommandLog(server_id=1, command="ls -la")
assert c.command == "ls -la"
assert c.session_id is None # Can be standalone
class TestAuthService:
"""A5: Auth service unit tests"""
def test_jwt_token_creation(self):
"""JWT access token contains correct claims"""
from server.application.services.auth_service import AuthService
from server.domain.models import Admin
service = AuthService(
admin_repo=MagicMock(),
attempt_repo=MagicMock(),
audit_repo=MagicMock(),
)
admin = Admin(id=42, username="testuser", password_hash="xxx")
token = service._create_access_token(admin)
import jwt
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
assert payload["sub"] == "42"
assert payload["username"] == "testuser"
assert "exp" in payload
assert "iat" in payload
def test_jwt_token_verification(self):
"""JWT token can be verified correctly"""
from server.application.services.auth_service import AuthService
from server.domain.models import Admin
service = AuthService(
admin_repo=MagicMock(),
attempt_repo=MagicMock(),
audit_repo=MagicMock(),
)
admin = Admin(id=42, username="testuser", password_hash="xxx")
token = service._create_access_token(admin)
payload = service._decode_access_token(token)
assert payload is not None
assert payload["sub"] == "42"
def test_invalid_token_returns_none(self):
"""Invalid JWT token returns None"""
from server.application.services.auth_service import AuthService
service = AuthService(
admin_repo=MagicMock(),
attempt_repo=MagicMock(),
audit_repo=MagicMock(),
)
result = service._decode_access_token("invalid.token.here")
assert result is None
def test_refresh_token_is_random(self):
"""Refresh tokens are random and unique"""
from server.application.services.auth_service import AuthService
service = AuthService(
admin_repo=MagicMock(),
attempt_repo=MagicMock(),
audit_repo=MagicMock(),
)
from server.domain.models import Admin
admin = Admin(id=1, username="admin", password_hash="xxx", token_version=0)
t1 = service._create_refresh_token(admin)
t2 = service._create_refresh_token(admin)
assert t1 != t2
assert len(t1) > 20
class TestTOTPVerification:
"""A5: TOTP verification tests"""
def test_totp_invalid_code_rejected(self):
"""Invalid TOTP code is rejected"""
from server.application.services.auth_service import AuthService
service = AuthService(
admin_repo=MagicMock(),
attempt_repo=MagicMock(),
audit_repo=MagicMock(),
)
# Generate a real secret and test with wrong code
import base64, secrets
secret = base64.b32encode(secrets.token_bytes(20)).decode()
assert service._verify_totp("000000", secret) is False
class TestSyncEngineV2:
"""S6: Sync engine unit tests"""
def test_max_concurrent_default(self):
"""Default concurrency is 10"""
from server.application.services.sync_engine_v2 import MAX_CONCURRENT
assert MAX_CONCURRENT == 10
def test_engine_creation(self):
"""SyncEngineV2 can be instantiated with mock repos"""
from server.application.services.sync_engine_v2 import SyncEngineV2
engine = SyncEngineV2(
server_repo=MagicMock(),
sync_log_repo=MagicMock(),
audit_repo=MagicMock(),
retry_repo=MagicMock(),
)
assert engine is not None
from server.config import settings