import type { OrganizationFunctionActionContext, OrganizationFunctionActionContribution, PlatformWebModule } from "../src/types"; import { hasUiCapability, moduleInstalled, moduleIntegrationEnabled, routeContributionsForModules, uiCapability } from "../src/platform/moduleLogic"; import { isViewSurfaceVisible, viewSurfaceCatalogueForModules, visibleRoutesForProjection } from "../src/platform/views"; import { scopeGrants } from "../src/utils/permissions"; function assert(condition: unknown, message: string): void { if (!condition) throw new Error(message); } const access: PlatformWebModule = { id: "access", label: "Access", version: "test" }; const filesCapability = { kind: "fileExplorer" }; const mailCapability = { kind: "mailProfiles" }; const files: PlatformWebModule = { id: "files", label: "Files", version: "test", routes: [{ path: "/files", order: 20, render: () => null }], uiCapabilities: { "files.fileExplorer": filesCapability } }; const mail: PlatformWebModule = { id: "mail", label: "Mail", version: "test", routes: [{ path: "/mail", order: 30, render: () => null }], uiCapabilities: { "mail.profiles": mailCapability } }; const campaigns: PlatformWebModule = { id: "campaigns", label: "Campaigns", version: "test", dependencies: ["access"], optionalDependencies: ["files", "mail"], routes: [{ path: "/campaigns", order: 10, render: () => null }] }; const cases: Array<{ name: string; modules: PlatformWebModule[]; files: boolean; mail: boolean }> = [ { name: "core-only", modules: [access], files: false, mail: false }, { name: "files-only", modules: [access, files], files: true, mail: false }, { name: "mail-only", modules: [access, mail], files: false, mail: true }, { name: "campaign-only", modules: [access, campaigns], files: false, mail: false }, { name: "campaign-files", modules: [access, campaigns, files], files: true, mail: false }, { name: "campaign-mail", modules: [access, campaigns, mail], files: false, mail: true }, { name: "full-product", modules: [access, campaigns, files, mail], files: true, mail: true } ]; for (const testCase of cases) { const campaignInstalled = moduleInstalled("campaigns", testCase.modules); assert(moduleInstalled("access", testCase.modules), `${testCase.name}: access should be installed`); assert(moduleInstalled("files", testCase.modules) === testCase.files, `${testCase.name}: files installed state`); assert(moduleInstalled("mail", testCase.modules) === testCase.mail, `${testCase.name}: mail installed state`); assert(hasUiCapability("files.fileExplorer", testCase.modules) === testCase.files, `${testCase.name}: files UI capability`); assert(hasUiCapability("mail.profiles", testCase.modules) === testCase.mail, `${testCase.name}: mail UI capability`); assert(moduleIntegrationEnabled("campaigns", "files", testCase.modules) === (campaignInstalled && testCase.files), `${testCase.name}: campaign files integration`); assert(moduleIntegrationEnabled("campaigns", "mail", testCase.modules) === (campaignInstalled && testCase.mail), `${testCase.name}: campaign mail integration`); } assert(uiCapability("files.fileExplorer", [access, files]) === filesCapability, "files capability should return the module-provided object"); assert(uiCapability("mail.profiles", [access, mail]) === mailCapability, "mail capability should return the module-provided object"); const routes = routeContributionsForModules([access, files, mail, campaigns]).map((route) => route.path); assert(routes.join(",") === "/campaigns,/files,/mail", "routes should aggregate and sort by contribution order"); assert(scopeGrants("tenant:*", "calendar:event:read"), "tenant wildcard should grant tenant-level module scopes"); assert(!scopeGrants("tenant:*", "system:settings:read"), "tenant wildcard should not grant system scopes"); let selectedFunctionId = ""; const functionAction: OrganizationFunctionActionContribution = { id: "test.view-assignments", label: "View assignments", icon: "assignments-icon", order: 40, anyOf: ["idm:organization_assignment:read"], onClick: (context) => { selectedFunctionId = context.function.id; } }; functionAction.onClick({ settings: { apiBaseUrl: "", apiKey: "", accessToken: "" }, auth: {} as OrganizationFunctionActionContext["auth"], function: { id: "function-1", tenant_id: "tenant-1", organization_unit_id: "unit-1", slug: "test-function", name: "Test function" } }); assert(selectedFunctionId === "function-1", "organization function actions receive their row context"); assert(!("render" in functionAction), "organization function actions expose metadata instead of arbitrary rendered controls"); const viewAwareFiles: PlatformWebModule = { ...files, navItems: [{ to: "/files", label: "Files", order: 20 }], viewSurfaces: [ { id: "files.settings.connectors", moduleId: "files", kind: "section", label: "File connectors" } ] }; const viewCatalogue = viewSurfaceCatalogueForModules([viewAwareFiles]); assert( viewCatalogue.some((surface) => surface.id === "files.module"), "view catalogue should derive a module root" ); assert( viewCatalogue.some((surface) => surface.id === "files.nav.files"), "view catalogue should derive navigation surfaces" ); assert( viewCatalogue.some((surface) => surface.id === "files.route.files"), "view catalogue should derive route surfaces" ); const serverNormalizedCatalogue = viewSurfaceCatalogueForModules([ { ...viewAwareFiles, viewSurfaces: [ { id: "files.module", moduleId: "files", kind: "module", label: "Files", parentId: null }, ...(viewAwareFiles.viewSurfaces ?? []) ] } ]); assert( serverNormalizedCatalogue.find((surface) => surface.id === "files.module") ?.parentId == null, "server-normalized module roots must not become their own parent" ); const filesView = { activeViewId: "view-files", activeRevisionId: "revision-files", activeViewName: "Files only", visibleSurfaceIds: [ "files.module", "files.nav.files", "files.route.files" ], locked: false, availableViews: [], provenance: [], diagnostics: [] }; assert( isViewSurfaceVisible(filesView, "files.route.files", viewCatalogue), "selected child surfaces should remain visible when their ancestor is selected" ); assert( !isViewSurfaceVisible(filesView, "files.settings.connectors", viewCatalogue), "unselected sections should be hidden" ); assert( visibleRoutesForProjection([viewAwareFiles], filesView).length === 1, "selected routes should remain in the effective route list" ); const missingParentView = { ...filesView, visibleSurfaceIds: ["files.route.files"] }; assert( !isViewSurfaceVisible( missingParentView, "files.route.files", viewCatalogue ), "a selected child should stay hidden when its module ancestor is not selected" ); assert( isViewSurfaceVisible(filesView, "future.module.action", viewCatalogue), "unknown surfaces should fail open for forward compatibility and recovery" );