164 lines
6.2 KiB
TypeScript
164 lines
6.2 KiB
TypeScript
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
|
// Generated SDK + synthetic HTTP proves wire contracts, never live service acceptance.
|
|
import { actorRef, projectRef, requestBusinessBinding } from "@conusai-kit/business"
|
|
import { businessServiceHooks } from "@conusai-kit/business/server"
|
|
import { Container, type ServiceAdmissionContext } from "@conusai-kit/core"
|
|
import { afterEach, describe, expect, it, vi } from "vitest"
|
|
import { sdkActivitiesService } from "../adapters/service.js"
|
|
import { activityId, commentReceipt, receipt } from "../contract/index.js"
|
|
import { activitiesModule } from "../module.js"
|
|
|
|
const bindingId = "01900000-0000-7000-8000-000000000001"
|
|
const projectId = "01900000-0000-7000-8000-000000000002"
|
|
const actorId = "01900000-0000-7000-8000-000000000003"
|
|
const taskId = "act_01900000-0000-7000-8000-000000000004"
|
|
const commentId = "cmt_01900000-0000-7000-8000-000000000005"
|
|
const revision = "9007199254740993"
|
|
const target = projectRef(bindingId, projectId)
|
|
const member = actorRef(bindingId, actorId)
|
|
const task = {
|
|
id: taskId,
|
|
kind: "task",
|
|
status: "open",
|
|
revision: "RAW_REVISION",
|
|
subject: "Synthetic task",
|
|
body: "Details",
|
|
targets: [target],
|
|
participants: [member],
|
|
assignee: member,
|
|
due_at: "2026-12-01T00:00:00Z",
|
|
ext: { fixture: "preserve" },
|
|
pinned: true,
|
|
created_at: "2026-11-03T00:00:00Z",
|
|
updated_at: "2026-11-03T00:00:00Z",
|
|
erased: false,
|
|
deleted: false,
|
|
}
|
|
function json(value: unknown) {
|
|
return new Response(JSON.stringify(value).replaceAll('"RAW_REVISION"', revision), {
|
|
headers: { "content-type": "application/json" },
|
|
})
|
|
}
|
|
async function fixture(reply: (request: Request) => Promise<Response> | Response) {
|
|
const requests: Request[] = []
|
|
vi.stubGlobal("fetch", async (input: Request | string | URL) => {
|
|
const request = new Request(input)
|
|
requests.push(request.clone())
|
|
return reply(request)
|
|
})
|
|
const container = new Container()
|
|
const hooks = businessServiceHooks({
|
|
env: { apiUrl: "https://platform.example.test/api", token: "synthetic-token", orgSlug: "fixture" },
|
|
platformOrganizationId: 7,
|
|
platformProjectId: 9,
|
|
platformEnvironmentId: 11,
|
|
profile: "postgres",
|
|
controlPlane: {
|
|
lookup: async (appTenantId) => ({
|
|
partition_major: 1,
|
|
binding_id: bindingId,
|
|
organization_id: 7,
|
|
project_id: 9,
|
|
environment_id: 11,
|
|
environment_uuid: projectId,
|
|
app_tenant_id: appTenantId,
|
|
}),
|
|
observe: async () => {
|
|
throw new Error("This fixture does not certify readiness")
|
|
},
|
|
},
|
|
})
|
|
const context: ServiceAdmissionContext = {
|
|
container,
|
|
module: "activities",
|
|
procedure: "activities.task.list",
|
|
kind: "read",
|
|
scope: { level: "org", orgId: bindingId, actor: { userId: actorId } },
|
|
}
|
|
await hooks.prepareServices?.(container, context, activitiesModule.serviceRequirements ?? [])
|
|
return { service: sdkActivitiesService(requestBusinessBinding(container)), requests }
|
|
}
|
|
afterEach(() => vi.unstubAllGlobals())
|
|
describe("Activities generated wire adapter (synthetic HTTP)", () => {
|
|
it("accepts real resource prefixes and preserves int64 revision digits", async () => {
|
|
const { service, requests } = await fixture(() => json(task))
|
|
const result = await service.get(taskId)
|
|
expect(result.revision).toBe(revision)
|
|
expect(activityId.parse(result.taskId)).toBe(taskId)
|
|
expect(() => activityId.parse(taskId.slice(4))).toThrow()
|
|
expect(requests[0]?.headers.get("Conusai-App-Binding")).toBe(bindingId)
|
|
expect(requests[0]?.headers.get("Conusai-App-Actor")).toBe(actorId)
|
|
expect(requests[0]?.redirect).toBe("error")
|
|
})
|
|
it("forwards a service cursor unchanged in both directions", async () => {
|
|
const cursor = "eyJvcmRlciI6WzEsInN5bnRoZXRpYyJdfQ"
|
|
const { service, requests } = await fixture(() => json({ data: [task], next_after: cursor }))
|
|
expect((await service.list({ target, after: cursor })).after).toBe(cursor)
|
|
expect(new URL(requests[0]?.url ?? "").searchParams.get("after")).toBe(cursor)
|
|
expect(new URL(requests[0]?.url ?? "").searchParams.get("target")).toBe(target)
|
|
})
|
|
it("preserves untouched fields on edit and sends revision digits exactly", async () => {
|
|
let body = ""
|
|
const { service } = await fixture(async (request) => {
|
|
if (request.method === "GET") return json(task)
|
|
body = await request.text()
|
|
return json({ id: taskId, revision: "RAW_REVISION" })
|
|
})
|
|
const current = await service.get(taskId)
|
|
const result = await service.update({
|
|
task: current,
|
|
target,
|
|
subject: "Changed",
|
|
body: "Edited",
|
|
expectedVersion: revision,
|
|
key: "synthetic-update",
|
|
})
|
|
expect(receipt.parse(result).revision).toBe(revision)
|
|
expect(body).toContain(`"revision":${revision}`)
|
|
expect(JSON.parse(body).fields).toMatchObject({
|
|
assignee: member,
|
|
due_at: task.due_at,
|
|
ext: task.ext,
|
|
participants: [member],
|
|
targets: [target],
|
|
pinned: true,
|
|
subject: "Changed",
|
|
body: "Edited",
|
|
})
|
|
})
|
|
it("returns the actual comment receipt instead of inventing a comment body", async () => {
|
|
const { service } = await fixture(() => json({ id: commentId, revision: "RAW_REVISION" }))
|
|
const result = await service.comment({
|
|
taskId,
|
|
body: "Synthetic comment",
|
|
expectedVersion: revision,
|
|
key: "synthetic-comment",
|
|
})
|
|
expect(commentReceipt.parse(result)).toEqual({ commentId, revision })
|
|
expect(result).not.toHaveProperty("body")
|
|
})
|
|
it("refuses a changed project target before the adapter writes", async () => {
|
|
const other = projectRef(bindingId, "01900000-0000-7000-8000-000000000099")
|
|
const { service, requests } = await fixture(() => json({ ...task, targets: [other] }))
|
|
await expect(
|
|
service.update({
|
|
task: {
|
|
taskId,
|
|
subject: "Old",
|
|
body: "",
|
|
revision,
|
|
status: "open",
|
|
targets: [target],
|
|
createdAt: task.created_at,
|
|
},
|
|
target,
|
|
subject: "Changed",
|
|
body: "Edited",
|
|
expectedVersion: revision,
|
|
key: "synthetic-foreign",
|
|
}),
|
|
).rejects.toMatchObject({ code: "not_found" })
|
|
expect(requests.every((request) => request.method === "GET")).toBe(true)
|
|
})
|
|
})
|