This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Knowledge graph route projection — regression for the QA-polish P0
|
||||
* (2026-06-24). The route used to return raw SQLite rows
|
||||
* ({id, entity_type, name} / {id, source_id, target_id, relation_type}),
|
||||
* but the FE contract is KGNode {id,label,type} / KGEdge {source,target,
|
||||
* relationship}. The mismatch made every node render as type "Unknown" and
|
||||
* every edge fail the FE's `visibleNodeIds.has(e.source)` filter (undefined),
|
||||
* yielding "0 / 312 edges". The prior FE test only passed empty arrays, so
|
||||
* this shipped. This asserts the projected contract on real rows.
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import Fastify from 'fastify';
|
||||
import { MindDB } from '@waggle/core';
|
||||
import { knowledgeRoutes } from '../../src/local/routes/knowledge.js';
|
||||
|
||||
function createTestServer(db: MindDB) {
|
||||
const server = Fastify({ logger: false });
|
||||
server.decorate('multiMind', { personal: db });
|
||||
server.decorate('agentState', {
|
||||
getWorkspaceMindDb: () => undefined,
|
||||
listWorkspaces: () => [],
|
||||
});
|
||||
server.register(knowledgeRoutes);
|
||||
return server;
|
||||
}
|
||||
|
||||
describe('Knowledge graph route projection (regression: raw-column passthrough)', () => {
|
||||
let db: MindDB;
|
||||
let server: ReturnType<typeof Fastify>;
|
||||
|
||||
beforeEach(() => {
|
||||
db = new MindDB(':memory:');
|
||||
const raw = db.getDatabase();
|
||||
const e1 = raw
|
||||
.prepare('INSERT INTO knowledge_entities (entity_type, name) VALUES (?, ?)')
|
||||
.run('person', 'Marko');
|
||||
const e2 = raw
|
||||
.prepare('INSERT INTO knowledge_entities (entity_type, name) VALUES (?, ?)')
|
||||
.run('organization', 'Egzakta');
|
||||
raw
|
||||
.prepare('INSERT INTO knowledge_relations (source_id, target_id, relation_type) VALUES (?, ?, ?)')
|
||||
.run(e1.lastInsertRowid, e2.lastInsertRowid, 'works_at');
|
||||
server = createTestServer(db);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await server.close();
|
||||
db.close();
|
||||
});
|
||||
|
||||
it('projects raw rows to the FE KGNode/KGEdge contract', async () => {
|
||||
const res = await server.inject({ method: 'GET', url: '/api/memory/graph?scope=personal' });
|
||||
expect(res.statusCode).toBe(200);
|
||||
const body = res.json() as {
|
||||
nodes: Array<{ id: string; label: string; type: string }>;
|
||||
edges: Array<{ source: string; target: string; relationship: string }>;
|
||||
};
|
||||
|
||||
expect(body).toHaveProperty('nodes');
|
||||
expect(body).toHaveProperty('edges');
|
||||
expect(body.nodes).toHaveLength(2);
|
||||
expect(body.edges).toHaveLength(1);
|
||||
|
||||
// Node contract: real label + real type (NOT the "unknown" fallback), id is string.
|
||||
const marko = body.nodes.find((n) => n.label === 'Marko');
|
||||
expect(marko).toBeDefined();
|
||||
expect(typeof marko!.id).toBe('string');
|
||||
expect(marko!.type).toBe('person');
|
||||
expect(marko as Record<string, unknown>).not.toHaveProperty('entity_type'); // raw column not leaked
|
||||
|
||||
// Edge contract: source/target are string ids that resolve to nodes (the bug
|
||||
// was undefined source/target → FE drew 0 edges), relationship is set.
|
||||
const edge = body.edges[0];
|
||||
expect(typeof edge.source).toBe('string');
|
||||
expect(typeof edge.target).toBe('string');
|
||||
expect(edge.relationship).toBe('works_at');
|
||||
const ids = new Set(body.nodes.map((n) => n.id));
|
||||
expect(ids.has(edge.source)).toBe(true);
|
||||
expect(ids.has(edge.target)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns empty nodes/edges (not entities/relations) for an empty mind', async () => {
|
||||
const empty = new MindDB(':memory:');
|
||||
const s = createTestServer(empty);
|
||||
const res = await s.inject({ method: 'GET', url: '/api/memory/graph?scope=personal' });
|
||||
expect(res.json()).toEqual({ nodes: [], edges: [] });
|
||||
await s.close();
|
||||
empty.close();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user