import { describe, expect, it } from "vitest"; import { mkdirSync, writeFileSync } from "node:fs"; import { dirname, resolve } from "node:path"; import { GeminiFlashAdapter, geminiFlashMockContract } from "../../apps/worker/src/ai-adapter-gemini-flash.js"; import { GeminiProAdapter, geminiProMockContract } from "../../apps/worker/src/ai-adapter-gemini-pro.js"; import { GptImageAdapter, gptImageMockContract } from "../../apps/worker/src/ai-adapter-gpt-image.js"; import { mockPngBytes, type AdapterTransport } from "../../apps/worker/src/model-adapter.js"; const request = (modelId: string, ratio: "3:4" | "1:1" | "4:3" | "9:16" = "3:4", referenceAssetIds: string[] = []) => ({ configSnapshot: { error_mapping_profile: { timeout: "upstream_timeout", safety: "safety_rejected" } }, generationId: `generation-${modelId}`, modelId, prompt: "mock contract prompt", ratio, referenceAssetIds, }); function writeAdapterEvidence(relativePath: string, value: unknown) { const root = process.env.DADA_EVIDENCE_DIR_ADAPTERS; if (!root) return; const path = resolve(root, relativePath); mkdirSync(dirname(path), { recursive: true }); writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`); } describe("TDD-WP3-ADP-001-per-model-contract", () => { it("keeps three independent fixtures and validates their own profiles", async () => { const adapters = [new GeminiFlashAdapter(), new GeminiProAdapter(), new GptImageAdapter()]; expect(new Set(adapters.map((adapter) => adapter.modelId)).size).toBe(3); expect(new Set(adapters.map((adapter) => adapter.fixtureId)).size).toBe(3); expect(adapters[0].validateContract(geminiFlashMockContract).status).toBe("verified"); expect(adapters[1].validateContract(geminiProMockContract).status).toBe("verified"); expect(adapters[2].validateContract(gptImageMockContract).status).toBe("verified"); expect(adapters[0].validateContract(geminiProMockContract).status).toBe("blocked"); for (const adapter of adapters) { for (const ratio of ["3:4", "1:1", "4:3", "9:16"] as const) { const result = await adapter.start(request(adapter.modelId, ratio, ["reference-fixture"])); expect(result).toMatchObject({ status: "completed", outputs: [{ pixelHeight: expect.any(Number), pixelWidth: expect.any(Number) }] }); expect(result.status === "completed" ? result.outputs : []).toHaveLength(1); } } const errorMapping = ["upstream_timeout", "upstream_failed", "safety_rejected", "gateway_balance_insufficient", "gateway_contract_invalid", "reference_invalid", "unknown_retryable", "unknown_non_retryable"]; for (const adapter of adapters) { const executionModes = adapter === adapters[1] ? ["async", "poll"] : ["sync"]; const modelRoot = adapter.modelId.replaceAll(".", "_"); writeAdapterEvidence(`${modelRoot}/contract-matrix.json`, { contract_version: adapter.contractVersion, execution_modes: executionModes, fixture_id: adapter.fixtureId, model_id: adapter.modelId, pure_text: { outputs: 1, status: "passed" }, reference_image: { outputs: 1, status: "passed" }, ratios: ["3:4", "1:1", "4:3", "9:16"].map((ratio) => ({ outputs: 1, ratio, status: "passed" })), error_mapping: errorMapping, }); writeAdapterEvidence(`${modelRoot}/response.json`, { model_id: adapter.modelId, output_count: 1, response_shape: "single-image" }); writeAdapterEvidence(`${modelRoot}/external-calls.json`, { mode: "mock", real_calls: 0, service: "ai-gateway-service-id" }); writeAdapterEvidence(`${modelRoot}/redaction.json`, { removed_fields: ["credential", "raw_prompt", "raw_provider_payload"], secret_scan: "passed" }); } }); it("supports a pending async operation and poll without accepting multiple outputs", async () => { const transport: AdapterTransport = { async start() { return { operation: { done: false, name: "upstream:mock-pro-1" } }; }, async poll() { return { operation: { done: true, response: { candidates: [{ inline_data: { data: mockPngBytes("async").toString("base64"), mime_type: "image/png" }, pixelHeight: 1440, pixelWidth: 1080 }] } } }; }, }; const adapter = new GeminiProAdapter({ transport }); await expect(adapter.start(request(adapter.modelId))).resolves.toEqual({ status: "pending", upstreamJobReference: "upstream:mock-pro-1" }); await expect(adapter.poll("upstream:mock-pro-1")).resolves.toMatchObject({ status: "completed", outputs: [{ mimeType: "image/png" }] }); expect(() => adapter.normalizeOutput({ candidates: [{}, {}] })).toThrow("response_single_image_required"); }); it("maps only fixed categories and retains a non-sensitive balance signal", async () => { const adapter = new GeminiFlashAdapter({ transport: { async start() { return { status: "failed", error: { code: "balance" }, balance_signal: { gateway_account_ref: "gateway-account-primary", impact_scope: "model" } }; }, async poll() { return {}; }, } }); const result = await adapter.start(request(adapter.modelId)); expect(result).toMatchObject({ status: "failed", category: "unknown_retryable" }); expect(adapter.classifyError({ code: "safety" }, { safety: "safety_rejected" })).toEqual({ category: "safety_rejected", sourceCategory: "safety" }); expect(adapter.checkBalanceSignal({ balance_signal: { gateway_account_ref: "gateway-account-primary", impact_scope: "account", raw_provider_text: "do-not-return" } })).toEqual({ gatewayAccountRef: "gateway-account-primary", impactScope: "account" }); }); });