import { expect, test, vi } from "vitest"; import { ThtRunner } from "../src/tht/tht-runner.js"; import type { CanonicalWorkspace } from "../src/workspaces/schema.js"; const keywordIndexes = [ "content_hash", "document_id", "kind", "record_key", "record_kind", "vector_generation", "workspace_id", "workspace_revision", ]; const workspace: CanonicalWorkspace = { workspace: { schema_version: 3, id: "psd", name: "PSD", language: "it" }, dwh: { engine: "postgres", database: "warehouse", schema: "public", supported_transports: ["postgres_direct"], }, semantic_index: { vector_store: { engine: "qdrant", collection: "psd", dimensions: 1024, distance: "cosine" }, embedding: { provider: "ollama_internal", model: "qwen3-embedding:0.6b", dimensions: 1024, }, }, llm_policy: { allowed: ["zai/glm-5.2"] }, }; function runner(request: (...args: any[]) => Promise) { return new ThtRunner({ thtBin: "tht", harnessDir: "/harness", configPath: "config/tht.yaml", semanticRuntime: { internalQdrantUrl: "http://qdrant:6333", internalEmbeddingUrl: "http://embedding:11434", internalEmbeddingModel: "qwen3-embedding:0.6b", internalEmbeddingDimensions: 1024, }, qdrantRequest: request, }); } function response(status: number, body: unknown) { return { ok: status >= 200 && status < 300, status, json: async () => body, }; } function collection(overrides: Record = {}) { return { result: { config: { params: { vectors: { size: 1024, distance: "Cosine" } } }, payload_schema: Object.fromEntries(keywordIndexes.map((field) => [field, { data_type: "keyword" }])), ...overrides, }, }; } test("Qdrant readiness uses only the internal URL and accepts the exact collection contract", async () => { const request = vi.fn(async () => response(200, collection())); await expect(runner(request).qdrantEnsure(workspace, 3)).resolves.toEqual({ ok: true }); expect(request).toHaveBeenCalledOnce(); expect(request.mock.calls[0][0]).toBe("http://qdrant:6333/collections/psd"); expect(request.mock.calls[0][1]).toMatchObject({ method: "GET", signal: expect.any(AbortSignal) }); }); test("Qdrant readiness classifies a missing collection as semantic incompatibility", async () => { const request = vi.fn(async () => response(404, { status: "error", detail: "secret" })); await expect(runner(request).qdrantEnsure(workspace, 3)).resolves.toEqual({ ok: false, code: "semantic_index_incompatible", }); }); test.each([ ["dimensions", collection({ config: { params: { vectors: { size: 768, distance: "Cosine" } } }, })], ["distance", collection({ config: { params: { vectors: { size: 1024, distance: "Dot" } } }, })], ["payload indexes", collection({ payload_schema: { workspace_id: { data_type: "keyword" } } })], ])("Qdrant readiness rejects incompatible %s", async (_label, body) => { const request = vi.fn(async () => response(200, body)); await expect(runner(request).qdrantEnsure(workspace, 3)).resolves.toEqual({ ok: false, code: "semantic_index_incompatible", }); }); test("Qdrant readiness sanitizes unreachable internal service failures", async () => { const request = vi.fn(async () => { throw new Error("connect http://qdrant:6333/private"); }); await expect(runner(request).qdrantEnsure(workspace, 3)).resolves.toEqual({ ok: false, code: "workspace_not_activatable", }); });