Files
brain/tools/graph-radar.test.mjs

75 lines
3.8 KiB
JavaScript

// tools/graph-radar.test.mjs
import { describe, it, expect } from 'vitest';
import { buildGraphRadar } from './graph-radar.mjs';
const graph = {
nodes: [
{ id: 'x', label: 'X', source_file: 'tools/x.mjs' },
{ id: 'y', label: 'Y', source_file: 'tools/y.mjs' },
{ id: 'z', label: 'Z', source_file: 'tools/z.mjs' },
],
links: [
{ source: 'x', target: 'y', relation: 'references' },
{ source: 'z', target: 'x', relation: 'references' },
],
};
describe('buildGraphRadar (§5.7 граф-радар)', () => {
it('тронут x → соседи y и z в чек-листе (addressed:false)', () => {
const r = buildGraphRadar({ touchedFiles: ['tools/x.mjs'], graph });
const ids = r.neighbors.map((n) => n.node).sort();
expect(ids).toEqual(['y', 'z']);
expect(r.neighbors.every((n) => n.addressed === false)).toBe(true);
expect(r.neighbors.find((n) => n.node === 'y').viaTouched).toBe('x');
});
it('сосед, который САМ тронут, не попадает в чек-лист (уже адресован)', () => {
const r = buildGraphRadar({ touchedFiles: ['tools/x.mjs', 'tools/y.mjs'], graph });
expect(r.neighbors.map((n) => n.node)).toEqual(['z']); // y тронут → не флагуем
});
it('нет тронутых узлов в графе → пустой чек-лист', () => {
expect(buildGraphRadar({ touchedFiles: ['tools/nope.mjs'], graph }).neighbors).toEqual([]);
});
it('битый граф/входы → пусто, не крашит', () => {
expect(buildGraphRadar({ touchedFiles: null, graph: null }).neighbors).toEqual([]);
expect(buildGraphRadar({ touchedFiles: ['a'], graph: { nodes: 'x', links: 1 } }).neighbors).toEqual([]);
});
// F-F1: слепота радара видна — подано файлов > 0, найдено узлов 0
it('F-F1: summary.touchedFiles показывает поданное; слепота = touchedFiles>0 ∧ touchedNodes=0', () => {
const blind = buildGraphRadar({ touchedFiles: ['tools/nope.mjs'], graph });
expect(blind.summary.touchedFiles).toBe(1);
expect(blind.summary.touchedNodes).toBe(0);
const ok = buildGraphRadar({ touchedFiles: ['tools/x.mjs'], graph });
expect(ok.summary.touchedFiles).toBe(1);
expect(ok.summary.touchedNodes).toBe(1);
});
// F-F2: нормализация путей — backslash и ./-префикс не дают тихий промах
it('F-F2: windows-backslash и ./-префикс в touchedFiles находят узел', () => {
const r1 = buildGraphRadar({ touchedFiles: ['tools\\x.mjs'], graph });
expect(r1.summary.touchedNodes).toBe(1);
expect(r1.neighbors.map((n) => n.node).sort()).toEqual(['y', 'z']);
const r2 = buildGraphRadar({ touchedFiles: ['./tools/x.mjs'], graph });
expect(r2.summary.touchedNodes).toBe(1);
});
it('F-F2: backslash в source_file графа тоже нормализуется', () => {
const g = {
nodes: [
{ id: 'a', label: 'A', source_file: 'tools\\a.mjs' },
{ id: 'b', label: 'B', source_file: 'tools/b.mjs' },
],
links: [{ source: 'a', target: 'b' }],
};
const r = buildGraphRadar({ touchedFiles: ['tools/a.mjs'], graph: g });
expect(r.neighbors.map((n) => n.node)).toEqual(['b']);
});
// F-F4: выходные элементы заморожены — машинное addressed:true бросает (L2-пол)
it('F-F4: элементы neighbors заморожены, мутация addressed бросает TypeError', () => {
const r = buildGraphRadar({ touchedFiles: ['tools/x.mjs'], graph });
expect(r.neighbors.length).toBeGreaterThan(0);
expect(r.neighbors.every((n) => Object.isFrozen(n))).toBe(true);
expect(() => { r.neighbors[0].addressed = true; }).toThrow(TypeError);
});
});