397777089e
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
71 lines
3.4 KiB
JavaScript
71 lines
3.4 KiB
JavaScript
import { describe, it, expect } from 'vitest';
|
|
import {
|
|
buildDecisionGraph, validateSections, dependentsOf, impactedBy, buildValidatedGraph,
|
|
} from './decision-graph.mjs';
|
|
|
|
const SECTIONS = [
|
|
{ id: 'отступ', dependsOn: [] },
|
|
{ id: 'вёрстка', dependsOn: ['отступ'] },
|
|
{ id: 'мобильный', dependsOn: ['вёрстка', 'отступ'] },
|
|
];
|
|
|
|
describe('buildDecisionGraph (узлы=секции, рёбра=зависит-от)', () => {
|
|
it('узлы = id секций', () => {
|
|
expect(buildDecisionGraph(SECTIONS).nodes).toEqual(['отступ', 'вёрстка', 'мобильный']);
|
|
});
|
|
it('ребро from зависит от to', () => {
|
|
expect(buildDecisionGraph(SECTIONS).edges).toContainEqual({ from: 'вёрстка', to: 'отступ' });
|
|
});
|
|
});
|
|
|
|
describe('validateSections', () => {
|
|
it('валидные секции → ok', () => { expect(validateSections(SECTIONS).ok).toBe(true); });
|
|
it('дубль id → error', () => {
|
|
expect(validateSections([{ id: 'a', dependsOn: [] }, { id: 'a', dependsOn: [] }]).ok).toBe(false);
|
|
});
|
|
it('зависимость от несуществующей секции → error', () => {
|
|
const r = validateSections([{ id: 'a', dependsOn: ['нет-такой'] }]);
|
|
expect(r.ok).toBe(false); expect(r.errors.some((e) => /несуществ/i.test(e))).toBe(true);
|
|
});
|
|
it('пустой id → error', () => {
|
|
expect(validateSections([{ id: '', dependsOn: [] }]).ok).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('dependentsOf (прямые зависимые)', () => {
|
|
it('кто зависит от отступа', () => {
|
|
expect(dependentsOf(SECTIONS, 'отступ').sort()).toEqual(['вёрстка', 'мобильный']);
|
|
});
|
|
it('от кого никто не зависит → []', () => {
|
|
expect(dependentsOf(SECTIONS, 'мобильный')).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('impactedBy (транзитивное наведение внимания; cycle-safe; без самого changedId)', () => {
|
|
it('сменил отступ → перепроверить вёрстку и мобильный', () => {
|
|
expect(impactedBy(SECTIONS, 'отступ').sort()).toEqual(['вёрстка', 'мобильный']);
|
|
});
|
|
it('сменил вёрстку → мобильный', () => {
|
|
expect(impactedBy(SECTIONS, 'вёрстка')).toEqual(['мобильный']);
|
|
});
|
|
it('цикл не зацикливает', () => {
|
|
const cyc = [{ id: 'a', dependsOn: ['b'] }, { id: 'b', dependsOn: ['a'] }];
|
|
expect(impactedBy(cyc, 'a')).toEqual(['b']);
|
|
});
|
|
it('сам changedId не в результате', () => {
|
|
expect(impactedBy(SECTIONS, 'отступ')).not.toContain('отступ');
|
|
});
|
|
});
|
|
|
|
describe('buildValidatedGraph (validate→build; бросает на невалидном — аудит footgun)', () => {
|
|
it('валидные секции → тот же граф, что buildDecisionGraph', () => {
|
|
expect(buildValidatedGraph(SECTIONS)).toEqual(buildDecisionGraph(SECTIONS));
|
|
});
|
|
it('висящая зависимость → бросает (не строит молча рёбра на несуществующий узел)', () => {
|
|
expect(() => buildValidatedGraph([{ id: 'a', dependsOn: ['нет'] }])).toThrow(/несуществ|невалид/i);
|
|
});
|
|
it('дубль id → бросает', () => {
|
|
expect(() => buildValidatedGraph([{ id: 'a', dependsOn: [] }, { id: 'a', dependsOn: [] }])).toThrow();
|
|
});
|
|
});
|