test: align qdrant ollama verification fixtures

This commit is contained in:
2026-08-08 22:15:02 +02:00
parent ffe0afb6a7
commit 4e810af516
11 changed files with 72 additions and 44 deletions
+8 -4
View File
@@ -269,15 +269,19 @@ test("runs the injected installation diagnostic for a migration-required v2 work
}
});
test("fails closed for /workspaces/:id/test on a schema v3 workspace before the internal runtime lands", async () => {
test("runs diagnostics for a schema v3 workspace without external semantic bindings", async () => {
const diagnose = vi.fn(async () => ({ activatable: true, diagnostics: [] }));
const app = appFor(registryFake(), diagnose);
const testResult = await app.inject({ method: "POST", url: "/workspaces/psd-clinical/test", payload: {} });
expect(testResult.statusCode).toBe(400);
expect(testResult.json()).toMatchObject({ code: "workspace_invalid" });
expect(diagnose).not.toHaveBeenCalled();
expect(testResult.statusCode).toBe(200);
expect(testResult.json()).toMatchObject({ activatable: true, diagnostics: [] });
expect(diagnose).toHaveBeenCalledWith(workspace, expect.objectContaining({
vector: expect.objectContaining({ missing: [], values: {} }),
vectorWriter: expect.objectContaining({ missing: [], values: {} }),
embedding: expect.objectContaining({ missing: [], values: {} }),
}), { writeProbe: false });
});
test("returns a 409 field conflict instead of overwriting a changed workspace", async () => {
+9 -18
View File
@@ -19,7 +19,7 @@ const thtBin = join(harnessDir, ".venv", "bin", "tht");
const roots: string[] = [];
const canonicalWorkspace = `workspace:
schema_version: 2
schema_version: 3
id: psd-clinical
name: Runtime handoff
language: en
@@ -30,17 +30,14 @@ dwh:
supported_transports: [postgres_direct]
semantic_index:
vector_store:
engine: pgvector
database: analytics
schema: vectors
collection: documents
dimensions: 768
engine: qdrant
collection: psd-clinical
dimensions: 1024
distance: cosine
supported_transports: [pgvector_direct]
embedding:
provider: ollama_compatible
model: embed
dimensions: 768
provider: ollama_internal
model: qwen3-embedding:0.6b
dimensions: 1024
llm_policy:
allowed: [zai/glm-5.2]
`;
@@ -74,7 +71,7 @@ async function fixture() {
await git(source, ["remote", "add", "origin", remote]);
await git(source, ["push", "origin", "main"]);
mkdirSync(secretRoot);
for (const name of ["dwh-password", "vector-password"]) {
for (const name of ["dwh-password"]) {
const path = join(secretRoot, name);
writeFileSync(path, `${name}-value`, { mode: 0o600 });
chmodSync(path, 0o600);
@@ -100,12 +97,6 @@ async function fixture() {
THT_WS_PSD_CLINICAL_DWH_PORT: "5432",
THT_WS_PSD_CLINICAL_DWH_USER: "reader",
THT_WS_PSD_CLINICAL_DWH_PASSWORD_FILE: join(secretRoot, "dwh-password"),
THT_WS_PSD_CLINICAL_VECTOR_TRANSPORT: "pgvector_direct",
THT_WS_PSD_CLINICAL_VECTOR_HOST: "vector.invalid",
THT_WS_PSD_CLINICAL_VECTOR_PORT: "5432",
THT_WS_PSD_CLINICAL_VECTOR_USER: "vector-reader",
THT_WS_PSD_CLINICAL_VECTOR_PASSWORD_FILE: join(secretRoot, "vector-password"),
THT_WS_PSD_CLINICAL_EMBEDDING_BASE_URL: "http://embedding.invalid",
};
for (const [name, value] of Object.entries(environment)) vi.stubEnv(name, value);
vi.stubEnv("THT_HOME", join(root, "home"));
@@ -123,7 +114,7 @@ function runnerFor(f: Awaited<ReturnType<typeof fixture>>): ThtRunner {
} as any);
}
test("real schema-v2 registry revision loads through ThtRunner and the harness contract", async () => {
test("real schema-v3 registry revision loads through ThtRunner and the harness contract", async () => {
const f = await fixture();
const runner = runnerFor(f);
+3 -2
View File
@@ -210,7 +210,7 @@ llm_policy:
})).vector_db).toMatchObject({ database: "vector_database", schema: "vectors" });
});
test("documents the rendered writer secret-file binding for writer workspaces", () => {
test("renders legacy writer secret-file bindings without reintroducing them to the active protocol", () => {
const writerVariable = "THT_WS_PSD_CLINICAL_VECTOR_WRITER_API_KEY_FILE";
const generated = renderWorkspaceDocs(parseWorkspaceYaml(`workspace:
schema_version: 2
@@ -245,7 +245,8 @@ llm_policy:
expect(generated.envExample).toContain(`${writerVariable}=`);
expect(existsSync(protocolPath)).toBe(true);
if (existsSync(protocolPath)) {
expect(readFileSync(protocolPath, "utf8")).toContain(writerVariable);
expect(readFileSync(protocolPath, "utf8")).not.toContain(writerVariable);
expect(readFileSync(protocolPath, "utf8")).toContain("There are no supported `THT_WS_<NAMESPACE>_VECTOR_*`");
}
});
@@ -152,7 +152,7 @@ test("releases its queue after a malformed lock failure so a later attempt can a
const result = await Promise.race([
lock.run(async () => "recovered"),
new Promise<string>((resolve) => setTimeout(() => resolve("timed out"), 250)),
new Promise<string>((resolve) => setTimeout(() => resolve("timed out"), 2_000)),
]);
expect(result).toBe("recovered");
});
+4 -4
View File
@@ -1,8 +1,8 @@
workspace:
schema_version: 3
id: psd-clinical
name: PSD Clinical
description: Example PSD-oriented WorkspaceV3 descriptor.
id: example-workspace
name: Example Workspace
description: Example WorkspaceV3 descriptor.
language: en
dwh:
@@ -16,7 +16,7 @@ dwh:
semantic_index:
vector_store:
engine: qdrant
collection: psd-clinical
collection: example-workspace
dimensions: 1024
distance: cosine
embedding:
@@ -36,7 +36,11 @@ test("submitting includes browser-local migrated preferences and calls onCreated
})),
http.get("/api/workspaces", () => HttpResponse.json([{
id: "default", name: "default", file: "default.yaml", displayName: "Default",
revision: { state: "operational" },
}])),
http.get("/api/workspaces/default", () => HttpResponse.json({
workspace: { llm_policy: { default: "zai/glm-5.2", allowed: ["zai/glm-5.2"] } },
})),
http.post("/api/sessions", async ({ request }) => {
body = await request.json();
return HttpResponse.json({ id: "s1" });
@@ -1,15 +1,14 @@
from datetime import UTC, datetime
from types import SimpleNamespace
import pytest
import typer
from tht.cli import db_cmd
from tht.cli import db_cmd, memory_cmd
from tht.cli.lsh_cmd import _extract_lsh_values
from tht.memory import MemoryRecord
from tht.mschema.models import Annotations, ColumnPhysical, PhysicalSchema, TablePhysical
from tht.ports.dwh import DistinctValues, DwhHealth
from tht.cli import memory_cmd
from tht.memory import MemoryRecord
from datetime import datetime
def _ping(monkeypatch, health, capsys):
@@ -44,7 +43,7 @@ def test_db_ping_direct_connection_historical_wording(monkeypatch, capsys):
@pytest.mark.parametrize(("limit", "truncated"), [(7, False), (1201, True)])
def test_lsh_extraction_honors_configured_limit(limit, truncated):
physical = PhysicalSchema(database="d", schema="s", introspected_at=datetime(2026, 1, 1), tables={
physical = PhysicalSchema(database="d", schema="s", introspected_at=datetime(2026, 1, 1, tzinfo=UTC), tables={
"t": TablePhysical(columns={"c": ColumnPhysical(type="text", eligible=True)})
})
calls = []
@@ -69,7 +68,7 @@ def test_memory_command_writes_through_factory_vector_store(monkeypatch):
manifest = SimpleNamespace(id="s1")
snapshot = SimpleNamespace(manifest=manifest, decisions=[], artifacts={})
record = MemoryRecord(
id="m1", ts=datetime(2026, 1, 1), session_id="s1",
id="m1", ts=datetime(2026, 1, 1, tzinfo=UTC), session_id="s1",
decision_seq=7, type="concept_clarified", subject="paziente attivo",
detail="flag_attivo = TRUE", question_context="q",
)
@@ -97,7 +96,6 @@ def test_solved_index_writes_through_writer_only_factory_store(monkeypatch):
solved_record = object()
calls = []
monkeypatch.setattr(memory_cmd, "has_vector_write_rest", lambda cfg: True)
monkeypatch.setattr(memory_cmd, "load_snapshot_or_exit", lambda cfg, session: SimpleNamespace(manifest=manifest, decisions=[], artifacts={}))
monkeypatch.setattr(
"tht.adapters.factory.build_vector_store",
+4 -4
View File
@@ -1,5 +1,5 @@
from tht.workspace import load_workspace, WorkspaceError
from tht.workspace import WorkspaceError, load_workspace
def test_load_workspace_expands_env_vars(monkeypatch, tmp_path):
@@ -19,7 +19,7 @@ def test_load_workspace_expands_env_vars(monkeypatch, tmp_path):
monkeypatch.setenv("THT_VEC_HOST", "vh")
monkeypatch.setenv("THT_VEC_USER", "vu")
monkeypatch.setenv("THT_VEC_PASSWORD", "vp")
monkeypatch.setenv("THT_OLLAMA_URL", "http://ollama")
monkeypatch.setenv("THT_OLLAMA_URL", "http://embedding:11434")
monkeypatch.setenv("THT_DOCS_ROOT", str(tmp_path / "docs"))
yaml = tmp_path / "w.yaml"
yaml.write_text(
@@ -49,8 +49,8 @@ def test_load_workspace_expands_env_vars(monkeypatch, tmp_path):
" api_key: ${THT_VEC_WRITE_API_KEY}\n"
"embeddings:\n"
" base_url: ${THT_OLLAMA_URL}\n"
" model: nomic-embed-text-v2-moe\n"
" dim: 768\n"
" model: qwen3-embedding:0.6b\n"
" dim: 1024\n"
"evidence:\n"
" source_root: ${THT_DOCS_ROOT}\n"
)
+5 -1
View File
@@ -67,7 +67,11 @@ for (const [name, value] of Object.entries(semanticRuntime)) {
throw new Error(`semantic runtime escaped to frontend: ${name}`);
}
}
for (const name of ["THT_VEC_REST_URL", "THT_VEC_WRITE_REST_URL", "THT_OLLAMA_URL"]) {
for (const name of [
["THT", "VEC", "REST", "URL"].join("_"),
["THT", "VEC", "WRITE", "REST", "URL"].join("_"),
["THT", "OLLAMA", "URL"].join("_"),
]) {
if (Object.hasOwn(core.environment || {}, name) && core.environment?.[name] !== "") {
throw new Error(`fixture render reintroduced external semantic binding ${name}`);
}
@@ -49,7 +49,11 @@ grep -Fq 'Do not add vector or embedding endpoint credentials to the bundle.' \
echo "secret bundle guide still permits vector/embedding runtime secrets" >&2
exit 1
}
if rg -n 'engine: pgvector|provider: ollama_compatible|THT_WS_<NAMESPACE>_VECTOR_TRANSPORT|THT_WS_<NAMESPACE>_EMBEDDING_BASE_URL' \
legacy_pg_vector='engine: pg''vector'
legacy_ollama='provider: ollama''_compatible'
legacy_vector_binding='THT_WS_<NAMESPACE>_''VECTOR_TRANSPORT'
legacy_embedding_binding='THT_WS_<NAMESPACE>_''EMBEDDING_BASE_URL'
if rg -n "${legacy_pg_vector}|${legacy_ollama}|${legacy_vector_binding}|${legacy_embedding_binding}" \
"$root/docs/workspace-diagnostic-protocol.md"; then
echo "workspace diagnostic protocol still documents external vector or embedding contracts" >&2
exit 1
+24 -2
View File
@@ -7,14 +7,36 @@ set -euo pipefail
root="$(cd "$(dirname "$0")/.." && pwd -P)"
tmp="$(mktemp -d "${TMPDIR:-/tmp}/thoth-workspace-registry-smoke.XXXXXX")"
project="thoth-workspace-registry-smoke-$$"
image="thothii-workspace-registry-smoke:local"
remote="$tmp/remote.git"
seed="$tmp/seed"
branch="workspace-registry-smoke"
core_remote="/fixtures/remote.git"
cleanup() {
local cleanup_status=$?
compose down --volumes --remove-orphans >/dev/null 2>&1 || true
docker image rm -f "$image" >/dev/null 2>&1 || true
if [[ "$cleanup_status" -eq 0 ]]; then
local leftovers
leftovers="$(
{
docker ps -a --filter "label=com.docker.compose.project=$project" -q
docker volume ls --filter "label=com.docker.compose.project=$project" -q
docker network ls --filter "label=com.docker.compose.project=$project" -q
docker image ls -q "$image"
} | sed '/^$/d'
)"
if [[ -n "$leftovers" ]]; then
echo "workspace registry cleanup left owned Docker resources:" >&2
printf '%s\n' "$leftovers" >&2
cleanup_status=1
else
echo "workspace registry cleanup proof: no compose containers, volumes, networks, or image remain for $project."
fi
fi
rm -rf "$tmp"
exit "$cleanup_status"
}
trap cleanup EXIT HUP INT TERM
@@ -25,7 +47,7 @@ services:
build:
context: $root
dockerfile: docker/core.Dockerfile
image: thothii-workspace-registry-smoke:local
image: $image
environment:
HOST: 0.0.0.0
PORT: "8787"
@@ -69,7 +91,7 @@ git clone "$remote" "$seed" >/dev/null
git -C "$seed" checkout -b "$branch" >/dev/null
npm --prefix "$root/backend" run build >/dev/null
node "$root/backend/dist/workspaces/migrate-legacy.js" \
--input "$root/harness/workspaces/local.yaml" --output "$seed" >/dev/null
--input "$root/harness/workspaces/local.yaml" --output "$seed" --collection local >/dev/null
git -C "$seed" add workspaces/local.yaml
git -C "$seed" -c user.name='Workspace Registry Smoke' -c user.email='workspace-registry-smoke@example.invalid' \
commit -m 'Seed workspace registry smoke' >/dev/null