"""Tenant isolation for configuration values (TENANCY.md ยง10).""" import pytest from infrasynth.configs.models import ConfigValue from infrasynth.configs.registry import ConfigRegistry, ConfigType from infrasynth.configs.services import ConfigService from infrasynth.tenancy.context import tenant_context from infrasynth.tenancy.models import Tenant @pytest.fixture(autouse=True) def clean_registry(clean_config_registry): yield @pytest.fixture(autouse=True) def envelope_errors(settings): settings.REST_FRAMEWORK = { **settings.REST_FRAMEWORK, "EXCEPTION_HANDLER": "infrasynth.api.exceptions.envelope_exception_handler", } @pytest.fixture def service(): return ConfigService() @pytest.fixture def other_tenant(): return Tenant.objects.create(slug="other-config-ws", name="Other Workspace") class TestServiceIsolation: def test_tenant_a_cannot_read_tenant_b(self, service, tenant, other_tenant): ConfigRegistry.register("k", type=ConfigType.STRING, default="default") service.set("k", "b-secret", tenant=other_tenant) assert service.get("k", tenant=tenant) == "default" def test_absent_override_falls_back_to_global_not_other_tenant(self, service, tenant, other_tenant): ConfigRegistry.register("k", type=ConfigType.STRING, default="default") service.set_global("k", "global") service.set("k", "b-secret", tenant=other_tenant) assert service.get("k", tenant=tenant) == "global" def test_reset_only_affects_own_tenant(self, service, tenant, other_tenant): ConfigRegistry.register("k", type=ConfigType.STRING, default="default") service.set("k", "b-value", tenant=other_tenant) assert service.reset("k", tenant=tenant) is False assert service.get("k", tenant=other_tenant) == "b-value" class TestManagerIsolation: def test_scoped_manager_hides_other_tenant(self, tenant, other_tenant): ConfigValue.all_objects.create(tenant=other_tenant, key="other.only", value="x") with tenant_context(tenant): assert ConfigValue.objects.count() == 0 assert ConfigValue.objects.filter(key="other.only").delete()[0] == 0 with tenant_context(other_tenant): assert ConfigValue.objects.filter(key="other.only").exists() class TestApiIsolation: def test_other_tenant_key_is_404_not_403(self, authenticated_client, tenant, other_tenant): ConfigValue.all_objects.create(tenant=other_tenant, key="other.only", value="x") response = authenticated_client.get("/api/v1/configs/other.only/") assert response.status_code == 404 def test_list_never_exposes_other_tenant_values(self, authenticated_client, tenant, other_tenant): ConfigRegistry.register("shared.key", type=ConfigType.STRING, default="default") ConfigValue.all_objects.create(tenant=other_tenant, key="shared.key", value="b-secret") response = authenticated_client.get("/api/v1/configs/") assert response.status_code == 200 values = {entry["key"]: entry["value"] for entry in response.json()["values"]} assert values["shared.key"] == "default"