from dataclasses import replace from pathlib import Path import pytest from fastapi.testclient import TestClient from hub_core.runtime.app import create_app from hub_core.runtime.config import RuntimeSettings from hub_core.security.config import SecuritySettings def settings(): return SecuritySettings(issuer='https://issuer.example', audience='hub-core', root_subject='root-sub', policy_url='https://policy.example', policy_caller='system:serviceaccount:hub-core:hub-core', policy_token_file=Path('/run/policy-token'), policy_keys_file=Path('/run/keys.json'), audit_url='https://audit.example', audit_token_file=Path('/run/audit-token')) def test_partial_configuration_does_not_silently_disable_enforcement(monkeypatch): monkeypatch.setenv('HUB_CORE_SECURITY_ISSUER','https://issuer.example') with pytest.raises(ValueError, match='incomplete'): SecuritySettings.from_env() def test_missing_facts_and_development_mode_cannot_compose(): with pytest.raises(ValueError, match='authoritative owner facts'): create_app(settings=RuntimeSettings(access_mode='enforce'), security_settings=settings()) with pytest.raises(ValueError, match='enforcement mode'): create_app(settings=RuntimeSettings(), security_settings=settings(), access_facts=object()) @pytest.mark.parametrize('changes', [ {'issuer':'http://issuer.example'}, {'audit_url':'https://localhost'}, {'policy_token_file':Path('relative')}, {'root_subject':''}, {'audit_token_file':Path('/run/policy-token')}, ]) def test_invalid_trust_configuration(changes): with pytest.raises(ValueError): replace(settings(), **changes) def test_composed_clients_are_closed_by_runtime_lifespan(): app = create_app(settings=RuntimeSettings(access_mode='enforce'), security_settings=settings(), access_facts=object()) controller = app.state.access_controller client = controller.identity.client assert client is controller.audit.client is controller.policy.client with TestClient(app) as api: assert api.get('/healthz').status_code == 200 assert not client.is_closed assert client.is_closed def test_environment_composition_requires_explicit_owner_adapter(monkeypatch): configured = settings() for field in configured.__dataclass_fields__: monkeypatch.setenv('HUB_CORE_SECURITY_'+field.upper(),str(getattr(configured,field))) closed = create_app(settings=RuntimeSettings(access_mode='enforce')) with TestClient(closed) as client: assert client.get('/docs',headers={'Authorization':'Bearer untrusted'}).status_code == 503 composed = create_app(settings=RuntimeSettings(access_mode='enforce'),access_facts=object()) with TestClient(composed) as client: assert client.get('/healthz').status_code == 200 assert composed.state.access_controller is not None def test_direct_controller_cannot_be_silently_disabled(): with pytest.raises(ValueError, match='enforcement mode'): create_app(settings=RuntimeSettings(), access_controller=object())