@@ -0,0 +1,163 @@
|
||||
// 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)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user