import assert from "node:assert/strict"; import { test } from "vitest"; import type { ApplicationFeatureInputs } from "../../../src/application/ports/in/application-api.ts"; import { createInstalledFeatureInputs } from "../../../src/features/installed-feature-adapters.ts"; import { FIXTURE_IDS } from "../../../src/features/tech-log/adapters/mock/fixtures.ts"; import { createCsrfTokenProvider } from "../../../src/features/tech-log/adapters/http/studio-session-csrf.ts"; import type { TechLogFeatureInput } from "../../../src/features/tech-log/application/tech-log-feature-input.ts"; import type { WorkingCopy } from "../../../src/features/tech-log/contracts/studio/contract.ts"; type Equal = (() => Value extends Left ? 1 : 2) extends () => Value extends Right ? 1 : 2 ? true : false; type Expect = Value; type InstalledInputs = ReturnType; type _InstalledIdsAreExact = Expect< Equal >; type _TechLogRegistryValueIsExact = Expect< Equal >; void (0 as unknown as _InstalledIdsAreExact); void (0 as unknown as _TechLogRegistryValueIsExact); function inputOf(document: WorkingCopy) { const { id, version, updatedAt, ...input } = document; void id; void version; void updatedAt; return input; } function installedInputs( studioSource: "MOCK" | "HTTP" = "MOCK", publicSource: "MOCK" | "HTTP" = "MOCK", ): InstalledInputs { return createInstalledFeatureInputs({ studioSource, publicSource, contractOperations: { async execute() { throw new Error("reference executor is not used by composition tests"); }, }, apiBaseUrl: "http://composition.test/", requestTimeoutMs: 10_000, csrf: createCsrfTokenProvider({ async execute() { throw new Error("CSRF provider is not used by composition tests"); }, }), }); } test("installs TechLog beside the retained reference feature through application-facing inputs", async () => { const installed = installedInputs(); assert.deepEqual(Object.keys(installed), ["reference-feature", "tech-log"]); assert.deepEqual(Object.keys(installed["tech-log"]).sort(), [ "createManagementGateway", "createStudioAssetGateway", "createStudioGateway", "publicContent", ]); assert.equal(Object.isFrozen(installed), true); assert.equal(Object.isFrozen(installed["tech-log"]), true); assert.equal(Object.isFrozen(installed["tech-log"].publicContent), true); assert.equal( (await installed["tech-log"].publicContent.getRelease("0.1.0"))?.title, "TechLog Public·Studio 경계를 확정했습니다", ); }); test("selects the mock gateway by default and the HTTP gateway when switched", async () => { const mockGateway = installedInputs("MOCK")["tech-log"].createStudioGateway(); const httpGateway = installedInputs("HTTP")["tech-log"].createStudioGateway(); // The mock reads an in-memory fixture immediately. HTTP defers to the // (here, throwing) contract executor, so it rejects instead. await assert.doesNotReject(mockGateway.getDashboard()); await assert.rejects(httpGateway.getDashboard()); }); test("each createStudioGateway call owns an isolated mutable Studio session", async () => { const installed = installedInputs(); const first = installed["tech-log"].createStudioGateway(); const second = installed["tech-log"].createStudioGateway(); assert.notEqual(first, second); const firstBefore = await first.getDocument(FIXTURE_IDS.fetchJoinCase); const secondBefore = await second.getDocument(FIXTURE_IDS.fetchJoinCase); assert.equal(firstBefore.document.title, secondBefore.document.title); const saved = await first.saveDocument( firstBefore.document.id, { expectedVersion: firstBefore.document.version, document: { ...inputOf(firstBefore.document), title: "첫 번째 세션에서만 수정", }, }, { idempotencyKey: "session-isolation" }, ); assert.equal(saved.document.title, "첫 번째 세션에서만 수정"); assert.equal( (await second.getDocument(FIXTURE_IDS.fetchJoinCase)).document.title, secondBefore.document.title, ); assert.equal( ( await installed["tech-log"].publicContent.getRecord( "CASE", "collection-fetch-join-pagination", ) )?.title, "컬렉션 Fetch Join과 페이징은 왜 충돌하는가", ); }); test("each Studio session gets its own asset gateway instance", () => { const installed = installedInputs("HTTP"); assert.notEqual( installed["tech-log"].createStudioAssetGateway(), installed["tech-log"].createStudioAssetGateway(), ); });