fix: close internal semantic review gaps
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
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<any>) {
|
||||
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<string, unknown> = {}) {
|
||||
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",
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user