Assessor_2.0/tests/compliance/qdrant-regulatory-index.tes...

123 lines
6.7 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest';
import type { RegulatoryChunk } from '../../src/compliance/regulatory-rag';
import {
createQdrantRegulatoryIndexFromEnvironment,
QdrantClientError,
QdrantRegulatoryIndex,
REGULATORY_QDRANT_COLLECTION
} from '../../server/compliance/qdrant-regulatory-index';
const chunk: RegulatoryChunk = {
schemaVersion: 1,
corpusId: 'iso-27001', corpusVersion: '2022-de-1', documentId: 'a-5-1-de',
framework: 'ISO/IEC 27001', frameworkVersion: '2022', controlId: 'A.5.1',
chunkId: 'a-5-1-de:0:abcdef', chunkIndex: 0, language: 'de', title: 'Policies',
text: 'Synthetic regulatory text.', textSha256: 'a'.repeat(64)
};
function json(value: unknown, status = 200): Response {
return new Response(JSON.stringify(value), { status, headers: { 'Content-Type': 'application/json' } });
}
describe('Qdrant regulatory index', () => {
it('creates the versioned dense/sparse collection and payload indexes', async () => {
const fetcher = vi.fn(async (urlValue: string | URL | Request, init?: RequestInit) => {
const url = String(urlValue);
if (init?.method === 'GET' && url.endsWith(`/collections/${REGULATORY_QDRANT_COLLECTION}`)) {
const getCount = fetcher.mock.calls.filter(call => call[1]?.method === 'GET').length;
if (getCount === 1) return json({}, 404);
return json({ result: { config: { params: {
vectors: { dense: { size: 2, distance: 'Cosine' } }, sparse_vectors: { sparse: {} }
} }, payload_schema: {} } });
}
return json({ result: true, status: 'ok' });
});
const index = new QdrantRegulatoryIndex({
baseUrl: new URL('http://127.0.0.1:6333'), apiKey: 'secret', dimensions: 2,
embeddingModel: 'multilingual-model', timeoutMs: 1_000, fetcher
});
await index.ensureCollection();
const bodies = fetcher.mock.calls.map(call => call[1]?.body && JSON.parse(String(call[1]?.body))).filter(Boolean);
expect(bodies[0]).toMatchObject({ vectors: { dense: { size: 2, distance: 'Cosine' } }, sparse_vectors: { sparse: {} } });
expect(bodies.filter(body => body.field_name)).toHaveLength(12);
expect(fetcher.mock.calls.every(call => new Headers(call[1]?.headers).get('api-key') === 'secret')).toBe(true);
});
it('replaces document points and performs filtered dense retrieval', async () => {
const requests: Array<{ url: string; body?: any }> = [];
const fetcher = vi.fn(async (urlValue: string | URL | Request, init?: RequestInit) => {
const body = init?.body ? JSON.parse(String(init.body)) : undefined;
requests.push({ url: String(urlValue), body });
if (String(urlValue).endsWith('/points/query')) return json({ result: { points: [{ score: 0.91, payload: {
schema_version: 1, corpus_id: chunk.corpusId, corpus_version: chunk.corpusVersion,
document_id: chunk.documentId, framework: chunk.framework, framework_version: chunk.frameworkVersion,
control_id: chunk.controlId, chunk_id: chunk.chunkId, chunk_index: chunk.chunkIndex,
language: chunk.language, title: chunk.title, text: chunk.text, text_sha256: chunk.textSha256
} }] } });
return json({ result: true, status: 'ok' });
});
const index = new QdrantRegulatoryIndex({
baseUrl: new URL('http://127.0.0.1:6333'), apiKey: 'secret', dimensions: 2,
embeddingModel: 'multilingual-model', timeoutMs: 1_000, fetcher
});
await index.replaceDocuments([{ chunk, embedding: [0.2, 0.8] }]);
await expect(index.searchDense([0.4, 0.6], 5, { framework: 'ISO/IEC 27001', language: 'DE' }))
.resolves.toEqual([{ score: 0.91, chunk }]);
expect(requests[0].body.filter.must).toEqual([
{ key: 'corpus_id', match: { value: 'iso-27001' } },
{ key: 'corpus_version', match: { value: '2022-de-1' } },
{ key: 'document_id', match: { value: 'a-5-1-de' } }
]);
expect(requests[1].body.points[0]).toMatchObject({ vector: { dense: [0.2, 0.8] }, payload: {
framework: 'ISO/IEC 27001', control_id: 'A.5.1', language: 'de', embedding_model: 'multilingual-model'
} });
expect(requests[2].body.filter.must).toEqual(expect.arrayContaining([
{ key: 'embedding_model', match: { value: 'multilingual-model' } },
{ key: 'framework', match: { value: 'ISO/IEC 27001' } },
{ key: 'language', match: { value: 'de' } }
]));
});
it('fails closed for an incompatible collection and unsafe remote HTTP', async () => {
const index = new QdrantRegulatoryIndex({
baseUrl: new URL('http://127.0.0.1:6333'), apiKey: 'secret', dimensions: 2,
embeddingModel: 'model', timeoutMs: 1_000,
fetcher: async () => json({ result: { config: { params: {
vectors: { dense: { size: 3, distance: 'Cosine' } }, sparse_vectors: { sparse: {} }
} }, payload_schema: {} } })
});
await expect(index.ensureCollection()).rejects.toMatchObject({ code: 'qdrant_collection_incompatible' } satisfies Partial<QdrantClientError>);
expect(() => createQdrantRegulatoryIndexFromEnvironment(2, 'model', {
ASSESSOR_QDRANT_URL: 'http://qdrant.example', ASSESSOR_QDRANT_API_KEY: 'secret'
})).toThrow(/must use HTTPS/);
});
it('deletes every other version of the corpus while keeping the active version', async () => {
const fetcher = vi.fn(async (_url: string | URL | Request, _init?: RequestInit) => json({ result: {} }));
const index = new QdrantRegulatoryIndex({ baseUrl: new URL('http://127.0.0.1:6333'), apiKey: 'secret',
dimensions: 2, embeddingModel: 'multilingual-model', timeoutMs: 1_000, fetcher });
await index.deleteOtherCorpusVersions('iso-27001', '2022-de-2');
expect(String(fetcher.mock.calls[0][0])).toContain('/points/delete?wait=true');
expect(JSON.parse(String(fetcher.mock.calls[0][1]?.body))).toEqual({ filter: {
must: [{ key: 'corpus_id', match: { value: 'iso-27001' } }],
must_not: [{ key: 'corpus_version', match: { value: '2022-de-2' } }]
} });
});
it('counts only points in the requested corpus version and embedding model', async () => {
const fetcher = vi.fn(async (_url: string | URL | Request, _init?: RequestInit) => json({ result: { count: 2 } }));
const index = new QdrantRegulatoryIndex({ baseUrl: new URL('http://127.0.0.1:6333'), apiKey: 'secret',
dimensions: 2, embeddingModel: 'multilingual-model', timeoutMs: 1_000, fetcher });
await expect(index.count({ corpusId: 'iso-27001', corpusVersion: '2022-de-1' })).resolves.toBe(2);
expect(JSON.parse(String(fetcher.mock.calls[0][1]?.body))).toMatchObject({ exact: true, filter: { must: [
{ key: 'embedding_model', match: { value: 'multilingual-model' } },
{ key: 'corpus_id', match: { value: 'iso-27001' } },
{ key: 'corpus_version', match: { value: '2022-de-1' } }
] } });
});
});