194 lines
7.5 KiB
TypeScript
194 lines
7.5 KiB
TypeScript
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
|
// Actual app caller and native readiness protocol over a synthetic transport; no live service proof.
|
|
|
|
import { loadAppEnv, platformClients } from "@app/config"
|
|
import { type Actor, buildRegistry, Container, createCaller } from "@conusai-kit/core"
|
|
import { createMemoryDataSource, DATA_SOURCE, entitiesOf, SYSTEM_DATA } from "@conusai-kit/data"
|
|
import { SUPPORT_PLATFORM, SUPPORT_PROCESSING_STATUS, supportModule } from "@conusai-kit/support"
|
|
import { createTenancyHooks, orgMemberships, TENANCY, tenancyModule, users } from "@conusai-kit/tenancy"
|
|
import { afterEach, expect, it, vi } from "vitest"
|
|
import { bindWebPorts, nativeHelpSelectors } from "../apps/web/src/server/ports.js"
|
|
import { businessServiceComposition } from "../config/src/business-services.js"
|
|
|
|
const selection = vi.hoisted(() => ({ documents: false }))
|
|
vi.mock("@app/modules", async (original) => ({
|
|
...(await original<Record<string, unknown>>()),
|
|
moduleSelected: (id: string) =>
|
|
id === "documents" ? selection.documents : ["agent", "guides", "support"].includes(id),
|
|
}))
|
|
afterEach(() => {
|
|
selection.documents = false
|
|
vi.restoreAllMocks()
|
|
})
|
|
|
|
const descriptor = `sha256:${"a".repeat(64)}`
|
|
const source = "b".repeat(40)
|
|
const deployment = {
|
|
CONUSAI_API_URL: "https://platform.example.test/api",
|
|
CONUSAI_API_TOKEN: "synthetic-token",
|
|
CONUSAI_ORG_ID: "1",
|
|
CONUSAI_PROJECT_ID: "9",
|
|
CONUSAI_ENVIRONMENT_ID: "3",
|
|
CONUSAI_ORG_SLUG: "org",
|
|
CONUSAI_WORKSPACE_SLUG: "workspace",
|
|
CONUSAI_PROJECT_SLUG: "project",
|
|
CONUSAI_PLATFORM_RELEASE_DESCRIPTOR: descriptor,
|
|
CONUSAI_PLATFORM_SOURCE_COMMIT: source,
|
|
}
|
|
const observation = () => ({
|
|
component: {
|
|
id: "support",
|
|
observed_at: new Date().toISOString(),
|
|
facts: {
|
|
installed: "true",
|
|
enabled: "true",
|
|
entitled: "true",
|
|
configured: "true",
|
|
ready: "true",
|
|
},
|
|
},
|
|
authorized: "true",
|
|
environment_bound: "true",
|
|
compatible: "true",
|
|
dependencies_ready: "true",
|
|
policy_epoch: 2,
|
|
descriptor_digest: descriptor,
|
|
source_commit: source,
|
|
organization_id: 1,
|
|
project_id: 9,
|
|
environment_id: 3,
|
|
app_scope: "org",
|
|
authorization_checked_at: new Date().toISOString(),
|
|
})
|
|
|
|
function composition() {
|
|
const modules = [tenancyModule, supportModule]
|
|
const registry = buildRegistry(modules)
|
|
const data = createMemoryDataSource({ entities: entitiesOf(registry, "postgres") })
|
|
const base = new Container()
|
|
base.value(DATA_SOURCE, data)
|
|
base.value(SYSTEM_DATA, data)
|
|
const env = loadAppEnv("web", {
|
|
...deployment,
|
|
NODE_ENV: "test",
|
|
POSTGRES_URL: "postgresql://app_runtime:fixture@db/app",
|
|
POSTGRES_READER_URL: "postgresql://app_reader:fixture@db/app",
|
|
})
|
|
bindWebPorts(base, platformClients(env, registry), registry)
|
|
const root = base.child()
|
|
root.value(SUPPORT_PLATFORM, { mode: "platform", channel: "spk_actual", client: {} } as never)
|
|
const hooks = businessServiceComposition({ deployment }, modules, "postgres", nativeHelpSelectors)
|
|
const caller = (actor: Actor) => {
|
|
const tenancy = createTenancyHooks({ data, registry })
|
|
const container = root.child()
|
|
container.value(TENANCY, tenancy)
|
|
return createCaller({ registry, container, tenancy, ...hooks }, { actor })
|
|
}
|
|
return { data, root, caller }
|
|
}
|
|
|
|
it("returns empty without touching Documents when that module is not selected", async () => {
|
|
const app = composition()
|
|
const scope = {
|
|
level: "project" as const,
|
|
orgId: "local-org",
|
|
workspaceId: "local-workspace",
|
|
projectId: "local-project",
|
|
actor: { userId: "member" },
|
|
}
|
|
const read = vi.spyOn(app.data, "forScope")
|
|
expect(await app.root.resolve(SUPPORT_PROCESSING_STATUS).read(scope)).toEqual({
|
|
state: "empty",
|
|
processing: 0,
|
|
extracted: 0,
|
|
failed: 0,
|
|
})
|
|
expect(read).not.toHaveBeenCalled()
|
|
})
|
|
|
|
// kit:documents:begin
|
|
it("reads the requested project's processing counts when Documents is selected", async () => {
|
|
selection.documents = true
|
|
const app = composition()
|
|
const scope = {
|
|
level: "project" as const,
|
|
orgId: "local-org",
|
|
workspaceId: "local-workspace",
|
|
projectId: "local-project",
|
|
actor: { userId: "member" },
|
|
}
|
|
const list = vi.fn(async () => ({ items: [{ status: "processing" }, { status: "extracted" }, { status: "failed" }] }))
|
|
const read = vi.spyOn(app.data, "forScope").mockReturnValue({ of: () => ({ list }) } as never)
|
|
expect(await app.root.resolve(SUPPORT_PROCESSING_STATUS).read(scope)).toEqual({
|
|
state: "failed",
|
|
processing: 1,
|
|
extracted: 1,
|
|
failed: 1,
|
|
})
|
|
expect(read).toHaveBeenCalledWith(scope)
|
|
})
|
|
|
|
// kit:documents:end
|
|
|
|
it("admits the containing org service after actual project authorization and preserves native denials", async () => {
|
|
const app = composition()
|
|
const user = await app.data.global(users).insert({
|
|
issuer: "https://identity.example.test",
|
|
subject: "owner",
|
|
email: "owner@example.test",
|
|
displayName: "Owner",
|
|
locale: "en",
|
|
})
|
|
const actor = { userId: user.id }
|
|
const caller = app.caller(actor)
|
|
const org = (await caller.callByName("tenancy.org.create", { slug: "alpha", name: "Alpha" })) as { orgId: string }
|
|
await caller.callByName("tenancy.project.create", {
|
|
org: "alpha",
|
|
workspace: "general",
|
|
slug: "main",
|
|
name: "Main",
|
|
moduleKey: "support",
|
|
})
|
|
const input = { org: "alpha", workspace: "general", project: "main" }
|
|
let current = observation()
|
|
let status = 200
|
|
const fetch = vi
|
|
.spyOn(globalThis, "fetch")
|
|
.mockImplementation(async () => (status === 200 ? Response.json(current) : new Response(null, { status })))
|
|
const dispatch = (query = input) => app.caller(actor).callByName("support.projectProcessingStatus.get", query)
|
|
await expect(dispatch({ ...input, project: "missing" })).rejects.toMatchObject({ status: 404 })
|
|
expect(fetch).not.toHaveBeenCalled()
|
|
await expect(dispatch()).resolves.toEqual({ state: "empty", processing: 0, extracted: 0, failed: 0 })
|
|
const first = fetch.mock.calls[0]
|
|
if (!first) throw new Error("Expected native readiness request")
|
|
const [url, request] = first
|
|
expect(String(url)).toBe(
|
|
"https://platform.example.test/api/v1/orgs/org/workspaces/workspace/projects/project/environments/3/platform-services/support/readiness",
|
|
)
|
|
expect(request?.redirect).toBe("error")
|
|
expect(request?.headers).toMatchObject({ authorization: "Bearer synthetic-token" })
|
|
expect(JSON.parse(String(request?.body))).toEqual({
|
|
api_range: "=1.0.0-preview.20261103",
|
|
scopes: ["support:widget-sessions"],
|
|
app_scope: "org",
|
|
channel: "spk_actual",
|
|
})
|
|
for (const changed of [{ project_id: 10 }, { app_scope: "project" }, { environment_id: 4 }]) {
|
|
current = { ...observation(), ...changed }
|
|
await expect(dispatch()).rejects.toMatchObject({ code: "service_permission_missing" })
|
|
}
|
|
current = { ...observation(), source_commit: "c".repeat(40) }
|
|
await expect(dispatch()).rejects.toMatchObject({ code: "service_incompatible" })
|
|
status = 403
|
|
await expect(dispatch()).rejects.toMatchObject({ code: "service_permission_missing" })
|
|
const beforeRevocation = fetch.mock.calls.length
|
|
await app.data.unitOfWork.run({ level: "org", orgId: org.orgId, actor }, async (tx) => {
|
|
const membership = (await tx.of(orgMemberships).list({ where: [{ field: "userId", op: "eq", value: user.id }] }))
|
|
.items[0]
|
|
if (!membership) throw new Error("Expected owner membership")
|
|
await tx.of(orgMemberships).delete(membership.id)
|
|
})
|
|
await expect(dispatch()).rejects.toMatchObject({ status: 404 })
|
|
expect(fetch).toHaveBeenCalledTimes(beforeRevocation)
|
|
})
|