import { expect, test } from "vitest"; import * as workspaceSchema from "../src/workspaces/schema.js"; import { parseWorkspaceYaml, serializeWorkspaceYaml, validateCanonicalWorkspace } from "../src/workspaces/schema.js"; export const validYaml = `workspace: schema_version: 2 id: psd-clinical name: Policlinico San Donato description: Clinical data warehouse workspace language: it dwh: engine: postgres database: postgres schema: datawarehouse port: 5432 timeout_ms: 5000 supported_transports: - postgres_direct - rest_api - ssh_tunnel semantic_index: vector_store: engine: pgvector database: postgres schema: vectors collection: clinical_documents dimensions: 768 distance: cosine port: 5432 timeout_ms: 5000 supported_transports: - pgvector_direct - rest_api - ssh_tunnel embedding: provider: ollama_compatible model: nomic-embed-text-v2-moe dimensions: 768 timeout_ms: 5000 llm_policy: default: zai/glm-5.2 allowed: - zai/glm-5.2 - openai/gpt-5 `; test("rejects a workspace whose embedding dimensions differ from its collection", () => { expect(() => parseWorkspaceYaml(validYaml.replace("dimensions: 768", "dimensions: 1536"))) .toThrow(/dimensions/i); }); test("rejects an LLM default outside its allowlist", () => { expect(() => parseWorkspaceYaml(validYaml.replace("- zai/glm-5.2", "- openai/gpt-5"))) .toThrow(/allowlist/i); }); test("rejects unknown keys and invalid immutable IDs", () => { expect(() => parseWorkspaceYaml(validYaml.replace(" language: it", " language: it\n label: PSD"))) .toThrow(/unrecognized key/i); expect(() => parseWorkspaceYaml(validYaml.replace("id: psd-clinical", "id: PSD"))) .toThrow(/id/i); }); test("accepts optional connection ports and timeouts but rejects unsafe values", () => { expect(parseWorkspaceYaml(validYaml).dwh.port).toBe(5432); expect(() => parseWorkspaceYaml(validYaml.replace("port: 5432", "port: 0"))) .toThrow(/port/i); expect(() => parseWorkspaceYaml(validYaml.replace("port: 5432", "port: 65536"))) .toThrow(/port/i); expect(() => parseWorkspaceYaml(validYaml.replace("timeout_ms: 5000", "timeout_ms: 0"))) .toThrow(/timeout/i); }); test("requires explicit vector database and schema identities with strict diagnostic declarations", () => { const diagnosticWorkspace = validYaml.replace( " engine: pgvector\n database: postgres", " engine: pgvector\n database: vector_database", ).replace( "llm_policy:\n", "diagnostics:\n" + " dwh_rest:\n" + " method: POST\n" + " path: /rpc/ping\n" + " auth: bearer\n" + " response:\n" + " database: database\n" + " schema: schema\n" + " vector_rest:\n" + " metadata:\n" + " method: GET\n" + " path: /metadata\n" + " auth: bearer\n" + " response:\n" + " collection: collection\n" + " dimensions: dimensions\n" + " distance: distance\n" + " reversible_probe:\n" + " method: POST\n" + " path: /rpc/diagnostic_vector_probe\n" + " auth: bearer\n" + " response: { operation: operation }\n" + " embedding:\n" + " method: GET\n" + " path: /models\n" + " auth: none\n" + " response:\n" + " model: model\n" + " dimensions: dimensions\n" + "llm_policy:\n", ); expect(parseWorkspaceYaml(diagnosticWorkspace).semantic_index.vector_store).toMatchObject({ database: "vector_database", schema: "vectors", }); expect(() => parseWorkspaceYaml(diagnosticWorkspace.replace("database: vector_database", 'database: " "'))) .toThrow(/database/i); expect(() => parseWorkspaceYaml(diagnosticWorkspace.replace("schema: vectors", 'schema: " "'))) .toThrow(/schema/i); expect(() => parseWorkspaceYaml(diagnosticWorkspace.replace("method: POST", "method: PATCH"))) .toThrow(/method/i); expect(() => parseWorkspaceYaml(diagnosticWorkspace.replace(" distance: distance", " distance: distance\n extra: ignored"))) .toThrow(/unrecognized key/i); for (const unsafePath of [ "//diagnostic.invalid/rpc", "'/\\\\diagnostic'", "'/rpc\\\\diagnostic'", "'/rpc/%5Cdiagnostic'", "'/rpc/\u0001'", ]) { expect(() => parseWorkspaceYaml(diagnosticWorkspace.replace("path: /rpc/ping", `path: ${unsafePath}`))) .toThrow(/origin-relative|path/i); } expect(() => parseWorkspaceYaml(diagnosticWorkspace.replace("auth: bearer", "auth: basic"))) .toThrow(/auth/i); expect(() => parseWorkspaceYaml(diagnosticWorkspace.replace(" schema: schema", " schema: schema\n status: status"))) .toThrow(/unrecognized key/i); expect(() => parseWorkspaceYaml(diagnosticWorkspace.replace(" schema: schema", " schema: bad field"))) .toThrow(/response field/i); }); test("requires a reversible writer probe to declare the response operation it verifies", () => { const writerProbe = validYaml.replace("llm_policy:\n", `diagnostics: vector_rest: metadata: method: GET path: /metadata auth: bearer response: { collection: collection, dimensions: dimensions, distance: distance } reversible_probe: method: POST path: /diagnostic-probe auth: bearer llm_policy: `); expect(() => parseWorkspaceYaml(writerProbe)).toThrow(/response|operation/i); }); test("keeps v1 descriptors readable but requires explicit migration before v2 operations", () => { const v1WithoutVectorIdentity = validYaml.replace("schema_version: 2", "schema_version: 1").replace( " database: postgres\n schema: vectors\n", "", ); expect(() => parseWorkspaceYaml(v1WithoutVectorIdentity)).not.toThrow(); expect(() => parseWorkspaceYaml(validYaml)).not.toThrow(); const v1 = parseWorkspaceYaml(v1WithoutVectorIdentity); const v2 = parseWorkspaceYaml(validYaml); expect(v1.workspace.schema_version).toBe(1); expect(() => validateCanonicalWorkspace(v1)).toThrow(/migrat/i); expect(v2.workspace.schema_version).toBe(2); const migrate = (workspaceSchema as { migrateWorkspaceV1ToV2?: unknown }).migrateWorkspaceV1ToV2; expect(migrate).toBeTypeOf("function"); const migrated = (migrate as (workspace: typeof v1, identity: { database: string; schema: string }) => unknown)(v1, { database: "vector_database", schema: "vectors", }); expect(validateCanonicalWorkspace(migrated)).toMatchObject({ workspace: { schema_version: 2 }, semantic_index: { vector_store: { database: "vector_database", schema: "vectors" } }, }); }); test("constructs diagnostic URLs only when the resolved URL remains on the service origin", () => { const resolveDiagnosticUrl = (workspaceSchema as { resolveDiagnosticUrl?: unknown }).resolveDiagnosticUrl; expect(resolveDiagnosticUrl).toBeTypeOf("function"); expect((resolveDiagnosticUrl as (baseUrl: string, path: string) => URL)("https://service.example/base", "/rpc/ping")) .toMatchObject({ href: "https://service.example/rpc/ping" }); expect(() => (resolveDiagnosticUrl as (baseUrl: string, path: string) => URL)( "https://service.example/base", "//diagnostic.invalid/rpc", )).toThrow(/origin/i); }); test("rejects REST diagnostic declarations without their matching connector transport", () => { const diagnostics = `diagnostics: dwh_rest: method: POST path: /rpc/ping auth: bearer response: database: database schema: schema vector_rest: metadata: method: GET path: /metadata auth: bearer response: collection: collection dimensions: dimensions distance: distance llm_policy: `; const declared = validYaml.replace("llm_policy:\n", diagnostics); expect(() => parseWorkspaceYaml(declared.replace(" - rest_api\n", ""))).toThrow(/dwh_rest/i); expect(() => parseWorkspaceYaml(declared.replace(" - rest_api\n", " - rest_api\n", 1).replace( " - rest_api\n", "", ))).toThrow(/vector_rest/i); }); test("serializes canonical YAML that parses back to the same workspace", () => { const workspace = parseWorkspaceYaml(validYaml); const serialized = serializeWorkspaceYaml(workspace); expect(serializeWorkspaceYaml(parseWorkspaceYaml(serialized))).toBe(serialized); expect(parseWorkspaceYaml(serialized)).toEqual(workspace); });