Decouple scopes from tenancy schema
This commit is contained in:
@@ -73,6 +73,7 @@ from govoplan_core.core.registry import PlatformRegistry
|
||||
from govoplan_core.db.base import Base
|
||||
from govoplan_core.server.app import create_app
|
||||
from govoplan_core.server.config import GovoplanServerConfig
|
||||
from govoplan_core.tenancy.scope import create_scope_tables
|
||||
from govoplan_access.backend.db.models import (
|
||||
Account,
|
||||
Function,
|
||||
@@ -604,6 +605,7 @@ class AccessContractTests(unittest.TestCase):
|
||||
root = Path(tempfile.mkdtemp(prefix="govoplan-access-directory-"))
|
||||
try:
|
||||
with temporary_database(f"sqlite:///{root / 'directory.db'}") as database:
|
||||
create_scope_tables(database.engine)
|
||||
Base.metadata.create_all(bind=database.engine)
|
||||
with database.session() as session:
|
||||
tenant = Tenant(id="tenant-directory", slug="directory", name="Directory Tenant", settings={})
|
||||
@@ -716,6 +718,7 @@ class AccessContractTests(unittest.TestCase):
|
||||
root = Path(tempfile.mkdtemp(prefix="govoplan-access-semantic-api-"))
|
||||
try:
|
||||
with temporary_database(f"sqlite:///{root / 'semantic.db'}") as database:
|
||||
create_scope_tables(database.engine)
|
||||
Base.metadata.create_all(bind=database.engine)
|
||||
tenant_id = "tenant-semantic"
|
||||
account_id = "account-semantic"
|
||||
@@ -906,6 +909,7 @@ class AccessContractTests(unittest.TestCase):
|
||||
with temporary_database(f"sqlite:///{root / 'test.db'}") as database:
|
||||
app = create_app(config)
|
||||
|
||||
create_scope_tables(database.engine)
|
||||
Base.metadata.create_all(bind=database.engine)
|
||||
with database.session() as session:
|
||||
tenant = Tenant(id=tenant_id, slug="capability", name="Capability Tenant", settings={})
|
||||
|
||||
@@ -136,7 +136,7 @@ class DatabaseMigrationTests(unittest.TestCase):
|
||||
job_indexes = {index["name"] for index in inspector.get_indexes("campaign_jobs")}
|
||||
attempt_columns = {column["name"] for column in inspector.get_columns("send_attempts")}
|
||||
tables = set(inspector.get_table_names())
|
||||
tenant_columns = {column["name"] for column in inspector.get_columns("tenancy_tenants")}
|
||||
tenant_columns = {column["name"] for column in inspector.get_columns("core_scopes")}
|
||||
user_columns = {column["name"] for column in inspector.get_columns("access_users")}
|
||||
session_columns = {column["name"] for column in inspector.get_columns("access_auth_sessions")}
|
||||
group_columns = {column["name"] for column in inspector.get_columns("access_groups")}
|
||||
@@ -186,6 +186,8 @@ class DatabaseMigrationTests(unittest.TestCase):
|
||||
self.assertIn("claim_token", attempt_columns)
|
||||
self.assertIn("access_accounts", tables)
|
||||
self.assertIn("access_system_role_assignments", tables)
|
||||
self.assertIn("core_scopes", tables)
|
||||
self.assertNotIn("tenancy_tenants", tables)
|
||||
self.assertIn("core_system_settings", tables)
|
||||
self.assertIn("admin_governance_templates", tables)
|
||||
self.assertIn("admin_governance_template_assignments", tables)
|
||||
@@ -234,6 +236,34 @@ class DatabaseMigrationTests(unittest.TestCase):
|
||||
finally:
|
||||
engine.dispose()
|
||||
|
||||
def test_module_migrations_apply_after_core_scope_table_rename(self) -> None:
|
||||
with tempfile.TemporaryDirectory(prefix="msm-late-module-migration-test-") as directory:
|
||||
database = Path(directory) / "late-modules.db"
|
||||
url = f"sqlite:///{database}"
|
||||
command.upgrade(alembic_config(database_url=url, enabled_modules=()), "heads")
|
||||
|
||||
engine = create_engine(url)
|
||||
try:
|
||||
with engine.connect() as connection:
|
||||
tables = set(inspect(connection).get_table_names())
|
||||
self.assertIn("core_scopes", tables)
|
||||
self.assertNotIn("tenancy_tenants", tables)
|
||||
finally:
|
||||
engine.dispose()
|
||||
|
||||
migrate_database(database_url=url)
|
||||
|
||||
engine = create_engine(url)
|
||||
try:
|
||||
with engine.connect() as connection:
|
||||
inspector = inspect(connection)
|
||||
mail_policy_fks = inspector.get_foreign_keys("mail_profile_policies")
|
||||
referred_tables = {fk["referred_table"] for fk in mail_policy_fks}
|
||||
self.assertIn("core_scopes", referred_tables)
|
||||
self.assertNotIn("tenancy_tenants", referred_tables)
|
||||
finally:
|
||||
engine.dispose()
|
||||
|
||||
def test_repairs_current_schema_stamped_at_file_folders(self) -> None:
|
||||
with tempfile.TemporaryDirectory(prefix="msm-current-schema-marker-test-") as directory:
|
||||
database = Path(directory) / "current-schema-marker.db"
|
||||
@@ -355,6 +385,7 @@ class DatabaseMigrationTests(unittest.TestCase):
|
||||
engine = create_engine(url)
|
||||
try:
|
||||
with engine.connect() as connection:
|
||||
tables = set(inspect(connection).get_table_names())
|
||||
account = connection.execute(text(
|
||||
"SELECT id, normalized_email, password_hash FROM access_accounts"
|
||||
)).mappings().one()
|
||||
@@ -389,8 +420,14 @@ class DatabaseMigrationTests(unittest.TestCase):
|
||||
"SELECT slug, is_builtin FROM access_roles WHERE tenant_id IS NULL AND slug IN ('system_owner', 'system_admin', 'system_auditor')"
|
||||
)).mappings().all()
|
||||
}
|
||||
scope_slug = connection.execute(text(
|
||||
"SELECT slug FROM core_scopes WHERE id = 'tenant-1'"
|
||||
)).scalar_one()
|
||||
self.assertIn("core_scopes", tables)
|
||||
self.assertNotIn("tenancy_tenants", tables)
|
||||
self.assertEqual(account["normalized_email"], "owner@example.local")
|
||||
self.assertEqual(account["password_hash"], "legacy-password-hash")
|
||||
self.assertEqual(scope_slug, "legacy")
|
||||
self.assertEqual(membership["account_id"], account["id"])
|
||||
self.assertEqual(migrated_session["account_id"], account["id"])
|
||||
self.assertEqual(owner_assignment, "owner")
|
||||
|
||||
@@ -26,9 +26,15 @@ from govoplan_identity.backend.db.models import Identity, IdentityAccountLink
|
||||
from govoplan_organizations.backend.db.models import (
|
||||
OrganizationFunction,
|
||||
OrganizationFunctionAssignment,
|
||||
OrganizationFunctionType,
|
||||
OrganizationRelation,
|
||||
OrganizationRelationType,
|
||||
OrganizationStructure,
|
||||
OrganizationUnit,
|
||||
OrganizationUnitType,
|
||||
)
|
||||
from govoplan_tenancy.backend.db.models import Tenant
|
||||
from govoplan_core.tenancy.scope import create_scope_tables
|
||||
from tests.db_isolation import temporary_database
|
||||
|
||||
|
||||
@@ -70,8 +76,8 @@ class _FakeOrganizationDirectory:
|
||||
assignment = OrganizationFunctionAssignmentRef(
|
||||
id="assignment-1",
|
||||
tenant_id="tenant-1",
|
||||
account_id="account-1",
|
||||
identity_id="identity-1",
|
||||
account_id="account-1",
|
||||
function_id=function.id,
|
||||
organization_unit_id=unit.id,
|
||||
applies_to_subunits=True,
|
||||
@@ -100,6 +106,13 @@ class _FakeOrganizationDirectory:
|
||||
return ()
|
||||
return (self.assignment,)
|
||||
|
||||
def function_assignments_for_identity(self, identity_id: str, *, tenant_id: str | None = None):
|
||||
if identity_id != self.assignment.identity_id:
|
||||
return ()
|
||||
if tenant_id is not None and tenant_id != self.assignment.tenant_id:
|
||||
return ()
|
||||
return (self.assignment,)
|
||||
|
||||
|
||||
class IdentityOrganizationContractTests(unittest.TestCase):
|
||||
def test_protocol_shapes_match_reference_implementations(self) -> None:
|
||||
@@ -129,6 +142,7 @@ class IdentityOrganizationContractTests(unittest.TestCase):
|
||||
root = Path(tempfile.mkdtemp(prefix="govoplan-directory-contracts-"))
|
||||
try:
|
||||
with temporary_database(f"sqlite:///{root / 'directory.db'}") as database:
|
||||
create_scope_tables(database.engine)
|
||||
Base.metadata.create_all(bind=database.engine)
|
||||
with database.session() as session:
|
||||
tenant = Tenant(id="tenant-directory", slug="directory", name="Directory Tenant", settings={})
|
||||
@@ -138,15 +152,60 @@ class IdentityOrganizationContractTests(unittest.TestCase):
|
||||
account_id="account-directory",
|
||||
is_primary=True,
|
||||
)
|
||||
unit_type = OrganizationUnitType(
|
||||
id="unit-type-directory",
|
||||
tenant_id=tenant.id,
|
||||
slug="office",
|
||||
name="Office",
|
||||
)
|
||||
structure = OrganizationStructure(
|
||||
id="structure-directory",
|
||||
tenant_id=tenant.id,
|
||||
slug="employer",
|
||||
name="Employer hierarchy",
|
||||
)
|
||||
relation_type = OrganizationRelationType(
|
||||
id="relation-type-directory",
|
||||
tenant_id=tenant.id,
|
||||
structure_id=structure.id,
|
||||
slug="supervises",
|
||||
name="Supervises",
|
||||
source_unit_type_id=unit_type.id,
|
||||
target_unit_type_id=unit_type.id,
|
||||
)
|
||||
unit = OrganizationUnit(
|
||||
id="ou-directory",
|
||||
tenant_id=tenant.id,
|
||||
unit_type_id=unit_type.id,
|
||||
slug="registry",
|
||||
name="Registry",
|
||||
)
|
||||
child_unit = OrganizationUnit(
|
||||
id="ou-directory-child",
|
||||
tenant_id=tenant.id,
|
||||
unit_type_id=unit_type.id,
|
||||
slug="registry-frontdesk",
|
||||
name="Registry Frontdesk",
|
||||
)
|
||||
relation = OrganizationRelation(
|
||||
id="relation-directory",
|
||||
tenant_id=tenant.id,
|
||||
structure_id=structure.id,
|
||||
relation_type_id=relation_type.id,
|
||||
source_unit_id=unit.id,
|
||||
target_unit_id=child_unit.id,
|
||||
)
|
||||
function_type = OrganizationFunctionType(
|
||||
id="function-type-directory",
|
||||
tenant_id=tenant.id,
|
||||
slug="clerk",
|
||||
name="Clerk",
|
||||
organization_unit_type_id=unit_type.id,
|
||||
)
|
||||
function = OrganizationFunction(
|
||||
id="function-directory",
|
||||
tenant_id=tenant.id,
|
||||
function_type_id=function_type.id,
|
||||
organization_unit_id=unit.id,
|
||||
slug="registry-clerk",
|
||||
name="Registry Clerk",
|
||||
@@ -154,26 +213,44 @@ class IdentityOrganizationContractTests(unittest.TestCase):
|
||||
assignment = OrganizationFunctionAssignment(
|
||||
id="assignment-directory",
|
||||
tenant_id=tenant.id,
|
||||
account_id="account-directory",
|
||||
identity_id=identity.id,
|
||||
function_id=function.id,
|
||||
organization_unit_id=unit.id,
|
||||
applies_to_subunits=True,
|
||||
)
|
||||
session.add_all([tenant, identity, link, unit, function, assignment])
|
||||
session.add_all([
|
||||
tenant,
|
||||
identity,
|
||||
link,
|
||||
unit_type,
|
||||
structure,
|
||||
relation_type,
|
||||
unit,
|
||||
child_unit,
|
||||
relation,
|
||||
function_type,
|
||||
function,
|
||||
assignment,
|
||||
])
|
||||
session.commit()
|
||||
|
||||
from govoplan_identity.backend.directory import SqlIdentityDirectory
|
||||
from govoplan_organizations.backend.directory import SqlOrganizationDirectory
|
||||
|
||||
identity_directory = SqlIdentityDirectory()
|
||||
organization_directory = SqlOrganizationDirectory()
|
||||
organization_directory = SqlOrganizationDirectory(identity_directory=identity_directory)
|
||||
|
||||
self.assertEqual("Directory Person", identity_directory.get_identity("identity-directory").display_name) # type: ignore[union-attr]
|
||||
self.assertEqual("identity-directory", identity_directory.identity_for_account("account-directory").id) # type: ignore[union-attr]
|
||||
self.assertEqual(["account-directory"], [item.account_id for item in identity_directory.accounts_for_identity("identity-directory")])
|
||||
self.assertEqual("Registry", organization_directory.get_organization_unit("ou-directory").name) # type: ignore[union-attr]
|
||||
self.assertEqual("unit-type-directory", organization_directory.get_organization_unit("ou-directory").unit_type_id) # type: ignore[union-attr]
|
||||
self.assertEqual("Registry Clerk", organization_directory.get_function("function-directory").name) # type: ignore[union-attr]
|
||||
self.assertEqual("function-type-directory", organization_directory.get_function("function-directory").function_type_id) # type: ignore[union-attr]
|
||||
self.assertEqual(
|
||||
["assignment-directory"],
|
||||
[item.id for item in organization_directory.function_assignments_for_identity("identity-directory", tenant_id="tenant-directory")],
|
||||
)
|
||||
self.assertEqual(
|
||||
["assignment-directory"],
|
||||
[item.id for item in organization_directory.function_assignments_for_account("account-directory", tenant_id="tenant-directory")],
|
||||
|
||||
@@ -219,11 +219,15 @@ class ModuleSystemTests(unittest.TestCase):
|
||||
def test_discovers_installed_core_and_product_module_manifests(self) -> None:
|
||||
manifests = available_module_manifests()
|
||||
self.assertTrue({"access", "admin", "tenancy", "policy", "audit", "dashboard", "files", "mail", "campaigns", "docs", "ops"}.issubset(manifests))
|
||||
self.assertEqual(manifests["campaigns"].dependencies, ("access",))
|
||||
self.assertEqual(manifests["campaigns"].dependencies, ())
|
||||
self.assertTrue(manifests["campaigns"].required_capabilities)
|
||||
self.assertEqual(manifests["campaigns"].optional_dependencies, ("files", "mail"))
|
||||
self.assertEqual(manifests["dashboard"].dependencies, ("access",))
|
||||
self.assertEqual(manifests["docs"].dependencies, ("access",))
|
||||
self.assertEqual(manifests["ops"].dependencies, ("access",))
|
||||
self.assertEqual(manifests["dashboard"].dependencies, ())
|
||||
self.assertTrue(manifests["dashboard"].required_capabilities)
|
||||
self.assertEqual(manifests["docs"].dependencies, ())
|
||||
self.assertTrue(manifests["docs"].required_capabilities)
|
||||
self.assertEqual(manifests["ops"].dependencies, ())
|
||||
self.assertTrue(manifests["ops"].required_capabilities)
|
||||
|
||||
def test_access_manifest_contributes_admin_frontend_metadata(self) -> None:
|
||||
manifests = available_module_manifests()
|
||||
@@ -266,11 +270,12 @@ class ModuleSystemTests(unittest.TestCase):
|
||||
from govoplan_access.backend.db.models import Account, ApiKey, AuthSession, User
|
||||
from govoplan_core.admin.models import SystemSettings
|
||||
from govoplan_core.db.base import Base
|
||||
from govoplan_core.tenancy.scope import scope_registry
|
||||
from govoplan_tenancy.backend.db.models import Tenant
|
||||
|
||||
self.assertIs(AccessBase, Base)
|
||||
self.assertEqual("access_accounts", Account.__tablename__)
|
||||
self.assertEqual("tenancy_tenants", Tenant.__tablename__)
|
||||
self.assertEqual("core_scopes", Tenant.__tablename__)
|
||||
self.assertEqual("access_users", User.__tablename__)
|
||||
self.assertEqual("access_api_keys", ApiKey.__tablename__)
|
||||
self.assertEqual("access_auth_sessions", AuthSession.__tablename__)
|
||||
@@ -278,7 +283,8 @@ class ModuleSystemTests(unittest.TestCase):
|
||||
self.assertEqual("admin_governance_templates", GovernanceTemplate.__tablename__)
|
||||
self.assertEqual("core_system_settings", SystemSettings.__tablename__)
|
||||
self.assertIn("access_accounts", Base.metadata.tables)
|
||||
self.assertIn("tenancy_tenants", Base.metadata.tables)
|
||||
self.assertIn("core_scopes", scope_registry.metadata.tables)
|
||||
self.assertNotIn("core_scopes", Base.metadata.tables)
|
||||
self.assertNotIn("accounts", Base.metadata.tables)
|
||||
self.assertNotIn("auth_sessions", Base.metadata.tables)
|
||||
|
||||
@@ -408,15 +414,15 @@ class ModuleSystemTests(unittest.TestCase):
|
||||
|
||||
def test_enabled_module_permutations_register_expected_routes(self) -> None:
|
||||
cases = (
|
||||
("core_only", (), {"tenancy", "access"}, set()),
|
||||
("files_only", ("files",), {"tenancy", "access", "files"}, {"/api/v1/files"}),
|
||||
("mail_only", ("mail",), {"tenancy", "access", "mail"}, {"/api/v1/mail"}),
|
||||
("campaign_without_files_or_mail", ("campaigns",), {"tenancy", "access", "campaigns"}, {"/api/v1/campaigns"}),
|
||||
("campaign_without_files", ("campaigns", "mail"), {"tenancy", "access", "campaigns", "mail"}, {"/api/v1/campaigns", "/api/v1/mail"}),
|
||||
("campaign_with_files_but_no_mail", ("campaigns", "files"), {"tenancy", "access", "campaigns", "files"}, {"/api/v1/campaigns", "/api/v1/files"}),
|
||||
("dashboard_only", ("dashboard",), {"tenancy", "access", "dashboard"}, set()),
|
||||
("docs_and_ops", ("docs", "ops"), {"tenancy", "access", "docs", "ops"}, {"/api/v1/docs", "/api/v1/ops"}),
|
||||
("full_product", ("dashboard", "campaigns", "files", "mail", "docs", "ops"), {"tenancy", "access", "dashboard", "campaigns", "files", "mail", "docs", "ops"}, {"/api/v1/campaigns", "/api/v1/files", "/api/v1/mail", "/api/v1/docs", "/api/v1/ops"}),
|
||||
("core_only", (), {"access"}, set()),
|
||||
("files_only", ("files",), {"access", "files"}, {"/api/v1/files"}),
|
||||
("mail_only", ("mail",), {"access", "mail"}, {"/api/v1/mail"}),
|
||||
("campaign_without_files_or_mail", ("campaigns",), {"access", "campaigns"}, {"/api/v1/campaigns"}),
|
||||
("campaign_without_files", ("campaigns", "mail"), {"access", "campaigns", "mail"}, {"/api/v1/campaigns", "/api/v1/mail"}),
|
||||
("campaign_with_files_but_no_mail", ("campaigns", "files"), {"access", "campaigns", "files"}, {"/api/v1/campaigns", "/api/v1/files"}),
|
||||
("dashboard_only", ("dashboard",), {"access", "dashboard"}, set()),
|
||||
("docs_and_ops", ("docs", "ops"), {"access", "docs", "ops"}, {"/api/v1/docs", "/api/v1/ops"}),
|
||||
("full_product", ("dashboard", "campaigns", "files", "mail", "docs", "ops"), {"access", "dashboard", "campaigns", "files", "mail", "docs", "ops"}, {"/api/v1/campaigns", "/api/v1/files", "/api/v1/mail", "/api/v1/docs", "/api/v1/ops"}),
|
||||
)
|
||||
for name, enabled_modules, expected_modules, expected_prefixes in cases:
|
||||
with self.subTest(name=name):
|
||||
@@ -524,12 +530,32 @@ finally:
|
||||
self.assertEqual(files_enabled, registry.integration_enabled("campaigns", "files"))
|
||||
self.assertEqual(mail_enabled, registry.integration_enabled("campaigns", "mail"))
|
||||
|
||||
def test_registry_rejects_missing_required_capability(self) -> None:
|
||||
registry = PlatformRegistry()
|
||||
registry.register(ModuleManifest(
|
||||
id="needs_auth",
|
||||
name="Needs Auth",
|
||||
version="test",
|
||||
required_capabilities=("auth.principalResolver",),
|
||||
))
|
||||
|
||||
with self.assertRaisesRegex(RegistryError, "requires unavailable capability"):
|
||||
registry.validate()
|
||||
|
||||
def test_access_and_organizations_do_not_hard_depend_on_tenancy(self) -> None:
|
||||
manifests = available_module_manifests()
|
||||
|
||||
self.assertNotIn("tenancy", manifests["access"].dependencies)
|
||||
self.assertIn("tenancy", manifests["access"].optional_dependencies)
|
||||
self.assertNotIn("tenancy", manifests["organizations"].dependencies)
|
||||
self.assertIn("tenancy", manifests["organizations"].optional_dependencies)
|
||||
|
||||
def test_module_management_plan_adds_protected_modules_and_required_dependencies(self) -> None:
|
||||
manifests = available_module_manifests()
|
||||
|
||||
plan = plan_desired_enabled_modules(("campaigns",), manifests)
|
||||
|
||||
self.assertEqual(("tenancy", "access", "admin", "campaigns"), plan.enabled_modules)
|
||||
self.assertEqual(("access", "admin", "campaigns"), plan.enabled_modules)
|
||||
self.assertEqual((), plan.added_dependencies)
|
||||
|
||||
def test_module_install_plan_is_saved_alongside_desired_state(self) -> None:
|
||||
@@ -2032,7 +2058,7 @@ finally:
|
||||
|
||||
self.assertEqual(response.status_code, 200, response.text)
|
||||
payload_modules = {item["id"] for item in response.json()["modules"]}
|
||||
self.assertEqual({"tenancy", "access", "files"}, payload_modules)
|
||||
self.assertEqual({"access", "files"}, payload_modules)
|
||||
|
||||
def test_create_app_ignores_saved_desired_modules_that_are_not_installed(self) -> None:
|
||||
root = Path(tempfile.mkdtemp(prefix="govoplan-module-state-stale-", dir=_TEST_ROOT))
|
||||
@@ -2065,7 +2091,7 @@ finally:
|
||||
|
||||
self.assertEqual(response.status_code, 200, response.text)
|
||||
payload_modules = {item["id"] for item in response.json()["modules"]}
|
||||
self.assertEqual({"tenancy", "access", "files"}, payload_modules)
|
||||
self.assertEqual({"access", "files"}, payload_modules)
|
||||
|
||||
def test_create_app_preserves_admin_when_configured_and_saved_state_is_older(self) -> None:
|
||||
root = Path(tempfile.mkdtemp(prefix="govoplan-module-state-admin-", dir=_TEST_ROOT))
|
||||
@@ -2098,7 +2124,7 @@ finally:
|
||||
|
||||
self.assertEqual(response.status_code, 200, response.text)
|
||||
payload_modules = {item["id"] for item in response.json()["modules"]}
|
||||
self.assertEqual({"tenancy", "access", "admin", "files"}, payload_modules)
|
||||
self.assertEqual({"access", "admin", "files"}, payload_modules)
|
||||
|
||||
def test_module_lifecycle_enables_and_disables_routes_without_restart(self) -> None:
|
||||
app, _settings_obj = self._app_for_modules(())
|
||||
@@ -2108,14 +2134,14 @@ finally:
|
||||
with TestClient(app) as client:
|
||||
response = client.get("/api/v1/platform/modules")
|
||||
self.assertEqual(response.status_code, 200, response.text)
|
||||
self.assertEqual({"tenancy", "access"}, {item["id"] for item in response.json()["modules"]})
|
||||
self.assertEqual({"access"}, {item["id"] for item in response.json()["modules"]})
|
||||
self.assertFalse("/api/v1/files" in _route_paths(app))
|
||||
|
||||
result = lifecycle.apply_enabled_modules(("files",), migrate=False)
|
||||
self.assertEqual(("files",), result.activated_modules)
|
||||
response = client.get("/api/v1/platform/modules")
|
||||
self.assertEqual(response.status_code, 200, response.text)
|
||||
self.assertEqual({"tenancy", "access", "files"}, {item["id"] for item in response.json()["modules"]})
|
||||
self.assertEqual({"access", "files"}, {item["id"] for item in response.json()["modules"]})
|
||||
self.assertTrue("/api/v1/files" in _route_paths(app))
|
||||
self.assertEqual(401, client.get("/api/v1/files").status_code)
|
||||
|
||||
@@ -2123,7 +2149,7 @@ finally:
|
||||
self.assertEqual(("files",), result.deactivated_modules)
|
||||
response = client.get("/api/v1/platform/modules")
|
||||
self.assertEqual(response.status_code, 200, response.text)
|
||||
self.assertEqual({"tenancy", "access"}, {item["id"] for item in response.json()["modules"]})
|
||||
self.assertEqual({"access"}, {item["id"] for item in response.json()["modules"]})
|
||||
disabled_response = client.get("/api/v1/files")
|
||||
self.assertEqual(404, disabled_response.status_code)
|
||||
self.assertEqual("Module is disabled: files", disabled_response.json()["detail"])
|
||||
@@ -2131,12 +2157,12 @@ finally:
|
||||
|
||||
def test_module_permutations_start_when_absent_modules_are_physically_unavailable(self) -> None:
|
||||
cases = (
|
||||
((), ("govoplan_files", "govoplan_mail", "govoplan_campaign")),
|
||||
(("files",), ("govoplan_mail", "govoplan_campaign")),
|
||||
(("mail",), ("govoplan_files", "govoplan_campaign")),
|
||||
(("campaigns",), ("govoplan_files", "govoplan_mail")),
|
||||
(("campaigns", "files"), ("govoplan_mail",)),
|
||||
(("campaigns", "mail"), ("govoplan_files",)),
|
||||
((), ("govoplan_tenancy", "govoplan_files", "govoplan_mail", "govoplan_campaign")),
|
||||
(("files",), ("govoplan_tenancy", "govoplan_mail", "govoplan_campaign")),
|
||||
(("mail",), ("govoplan_tenancy", "govoplan_files", "govoplan_campaign")),
|
||||
(("campaigns",), ("govoplan_tenancy", "govoplan_files", "govoplan_mail")),
|
||||
(("campaigns", "files"), ("govoplan_tenancy", "govoplan_mail")),
|
||||
(("campaigns", "mail"), ("govoplan_tenancy", "govoplan_files")),
|
||||
(("campaigns", "files", "mail"), ()),
|
||||
)
|
||||
for enabled_modules, blocked_modules in cases:
|
||||
|
||||
Reference in New Issue
Block a user