test: align qdrant ollama verification fixtures
This commit is contained in:
@@ -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 () => {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user