godcrm/backend/services/atoms/__tests__/widget-atom-recursion-guard.test.js
GOD CRM Release f89e074dd1
Some checks failed
CI / Lint / Typecheck / Test / Build (push) Has been cancelled
CI / PostgreSQL Integration Tests (push) Has been cancelled
GOD CRM — public scrubbed snapshot
Governed substrate for autonomous agents: scoped identity (passports),
audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
2026-08-10 04:01:45 +03:00

162 lines
5.7 KiB
JavaScript

// ADR-0005 §C-12 · widget-atom recursion guard tests
//
// Pure-unit (no DB): both `loadWidget` and `loadDocumentRegistryId` are
// injected. Mirrors the DI pattern used by widget-atom-resolver tests.
import { describe, it, expect } from 'vitest';
import { validateWidgetAtomRecursion } from '../widget-atom-recursion-guard.js';
const ATOMS_V2 = 3574;
function makeWidgetLoader(widgetsById) {
return async (id) => widgetsById[id] ?? null;
}
function makeRegistryLoader(documentsById) {
return async (id) => documentsById[id] ?? null;
}
describe('validateWidgetAtomRecursion', () => {
it('passes through when tableId is not atoms_v2', async () => {
const out = await validateWidgetAtomRecursion({
tableId: 9999,
data: { widget_ref: 1, document_id: 100 },
atomsV2TableId: ATOMS_V2,
loadWidget: makeWidgetLoader({}),
loadDocumentRegistryId: makeRegistryLoader({}),
});
expect(out.ok).toBe(true);
});
it('passes through when atom is not a widget atom (no widget_ref)', async () => {
const out = await validateWidgetAtomRecursion({
tableId: ATOMS_V2,
data: { type: 'paragraph', document_id: 100 },
atomsV2TableId: ATOMS_V2,
loadWidget: makeWidgetLoader({}),
loadDocumentRegistryId: makeRegistryLoader({}),
});
expect(out.ok).toBe(true);
});
it('passes through when atom has no document_id', async () => {
const out = await validateWidgetAtomRecursion({
tableId: ATOMS_V2,
data: { widget_ref: 218 },
atomsV2TableId: ATOMS_V2,
loadWidget: makeWidgetLoader({
218: { id: 218, preset_name: 'documents', config: { registry_table_id: 5000 } },
}),
loadDocumentRegistryId: makeRegistryLoader({}),
});
expect(out.ok).toBe(true);
});
it('passes through when widget is not a documents preset', async () => {
const out = await validateWidgetAtomRecursion({
tableId: ATOMS_V2,
data: { widget_ref: 9, document_id: 100 },
atomsV2TableId: ATOMS_V2,
loadWidget: makeWidgetLoader({
9: { id: 9, preset_name: 'tickets', config: { registry_table_id: 5000 } },
}),
loadDocumentRegistryId: makeRegistryLoader({ 100: 5000 }),
});
expect(out.ok).toBe(true);
});
it('passes through when widget registry differs from host registry', async () => {
const out = await validateWidgetAtomRecursion({
tableId: ATOMS_V2,
data: { widget_ref: 218, document_id: 100 },
atomsV2TableId: ATOMS_V2,
loadWidget: makeWidgetLoader({
218: { id: 218, preset_name: 'documents', config: { registry_table_id: 6000 } },
}),
loadDocumentRegistryId: makeRegistryLoader({ 100: 5000 }),
});
expect(out.ok).toBe(true);
});
it('blocks recursion: widget.registry_table_id == host registry', async () => {
const out = await validateWidgetAtomRecursion({
tableId: ATOMS_V2,
data: { widget_ref: 218, document_id: 100 },
atomsV2TableId: ATOMS_V2,
loadWidget: makeWidgetLoader({
218: { id: 218, preset_name: 'documents', config: { registry_table_id: 5000 } },
}),
loadDocumentRegistryId: makeRegistryLoader({ 100: 5000 }),
});
expect(out.ok).toBe(false);
expect(out.code).toBe('RECURSIVE_DOCUMENT_EMBEDDING');
expect(out.status).toBe(400);
expect(out.widget_id).toBe(218);
expect(out.document_id).toBe(100);
});
it('blocks recursion via legacy `documents_table_id` config key', async () => {
const out = await validateWidgetAtomRecursion({
tableId: ATOMS_V2,
data: { widget_ref: 7, document_id: 100 },
atomsV2TableId: ATOMS_V2,
loadWidget: makeWidgetLoader({
7: { id: 7, preset_name: 'documents_legacy', config: { documents_table_id: 5000 } },
}),
loadDocumentRegistryId: makeRegistryLoader({ 100: 5000 }),
});
expect(out.ok).toBe(false);
expect(out.code).toBe('RECURSIVE_DOCUMENT_EMBEDDING');
});
it('blocks recursion via legacy `table_id` config key', async () => {
const out = await validateWidgetAtomRecursion({
tableId: ATOMS_V2,
data: { widget_ref: 7, document_id: 100 },
atomsV2TableId: ATOMS_V2,
loadWidget: makeWidgetLoader({
7: { id: 7, preset_name: 'documents', config: { table_id: 5000 } },
}),
loadDocumentRegistryId: makeRegistryLoader({ 100: 5000 }),
});
expect(out.ok).toBe(false);
});
it('parses widget.config when stored as a JSON string', async () => {
const out = await validateWidgetAtomRecursion({
tableId: ATOMS_V2,
data: { widget_ref: 7, document_id: 100 },
atomsV2TableId: ATOMS_V2,
loadWidget: makeWidgetLoader({
7: { id: 7, preset_name: 'documents', config: '{"registry_table_id":5000}' },
}),
loadDocumentRegistryId: makeRegistryLoader({ 100: 5000 }),
});
expect(out.ok).toBe(false);
expect(out.code).toBe('RECURSIVE_DOCUMENT_EMBEDDING');
});
it('tolerates legacy `doc_id` field on the atom', async () => {
const out = await validateWidgetAtomRecursion({
tableId: ATOMS_V2,
data: { widget_ref: 218, doc_id: 100 },
atomsV2TableId: ATOMS_V2,
loadWidget: makeWidgetLoader({
218: { id: 218, preset_name: 'documents', config: { registry_table_id: 5000 } },
}),
loadDocumentRegistryId: makeRegistryLoader({ 100: 5000 }),
});
expect(out.ok).toBe(false);
});
it('passes through when widget cannot be loaded (broken ref)', async () => {
const out = await validateWidgetAtomRecursion({
tableId: ATOMS_V2,
data: { widget_ref: 999, document_id: 100 },
atomsV2TableId: ATOMS_V2,
loadWidget: makeWidgetLoader({}),
loadDocumentRegistryId: makeRegistryLoader({ 100: 5000 }),
});
expect(out.ok).toBe(true);
});
});