mirror of
https://github.com/miro0o/miniWorldMap.git
synced 2026-07-22 07:46:00 +00:00
96 lines
4.3 KiB
TypeScript
96 lines
4.3 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
||
import { buildGraph } from '../src/data/buildGraph';
|
||
|
||
const files = [
|
||
{ path: '01学习/笔记A.md', basename: '笔记A' },
|
||
{ path: '01学习/子目录/笔记B.md', basename: '笔记B' },
|
||
{ path: '02工作/笔记C.md', basename: '笔记C' },
|
||
{ path: '根笔记.md', basename: '根笔记' },
|
||
];
|
||
|
||
describe('buildGraph', () => {
|
||
it('节点:路径为 id,顶层文件夹归组,根目录为空串', () => {
|
||
const g = buildGraph(files, {}, {}, { includeUnresolved: false, includeOrphans: true });
|
||
expect(g.nodes).toHaveLength(4);
|
||
expect(g.nodes[0]).toMatchObject({ id: '01学习/笔记A.md', folderTop: '01学习', unresolved: false });
|
||
expect(g.nodes[1]?.folderTop).toBe('01学习');
|
||
expect(g.nodes[3]?.folderTop).toBe('');
|
||
});
|
||
|
||
it('边:按索引,degree=出+入;非 md 集合内目标(附件/不存在)被丢弃', () => {
|
||
const resolved = {
|
||
'01学习/笔记A.md': { '01学习/子目录/笔记B.md': 3, '附件/图.png': 1 },
|
||
'02工作/笔记C.md': { '01学习/笔记A.md': 1 },
|
||
'幽灵来源.md': { '01学习/笔记A.md': 1 },
|
||
};
|
||
const g = buildGraph(files, resolved, {}, { includeUnresolved: false, includeOrphans: true });
|
||
expect(g.links).toEqual([
|
||
{ source: 0, target: 1 },
|
||
{ source: 2, target: 0 },
|
||
]);
|
||
expect(g.nodes[0]?.degree).toBe(2);
|
||
expect(g.nodes[1]?.degree).toBe(1);
|
||
expect(g.nodes[2]?.degree).toBe(1);
|
||
expect(g.nodes[3]?.degree).toBe(0);
|
||
});
|
||
|
||
it('resolvedLinks 同对多次出现(值=次数)只产生一条边', () => {
|
||
const g = buildGraph(files, { '01学习/笔记A.md': { '02工作/笔记C.md': 99 } }, {}, { includeUnresolved: false, includeOrphans: true });
|
||
expect(g.links).toHaveLength(1);
|
||
});
|
||
|
||
it('未解析:开关开启时生成幽灵节点并跨来源去重', () => {
|
||
const unresolved = {
|
||
'01学习/笔记A.md': { 概念词典: 2 },
|
||
'02工作/笔记C.md': { 概念词典: 1, 另一个幽灵: 1 },
|
||
};
|
||
const off = buildGraph(files, {}, unresolved, { includeUnresolved: false, includeOrphans: true });
|
||
expect(off.nodes).toHaveLength(4);
|
||
expect(off.links).toHaveLength(0);
|
||
|
||
const on = buildGraph(files, {}, unresolved, { includeUnresolved: true, includeOrphans: true });
|
||
const ghosts = on.nodes.filter((n) => n.unresolved);
|
||
expect(ghosts).toHaveLength(2);
|
||
expect(ghosts[0]).toMatchObject({ id: 'unresolved:概念词典', folderTop: '__unresolved__' });
|
||
expect(on.links).toHaveLength(3);
|
||
expect(on.nodes.find((n) => n.id === 'unresolved:概念词典')?.degree).toBe(2);
|
||
});
|
||
|
||
it('空 vault 不炸', () => {
|
||
const g = buildGraph([], {}, {}, { includeUnresolved: true, includeOrphans: true });
|
||
expect(g.nodes).toHaveLength(0);
|
||
expect(g.links).toHaveLength(0);
|
||
});
|
||
});
|
||
|
||
describe('孤儿过滤', () => {
|
||
it('includeOrphans=false 时去掉零度节点并重排边索引', () => {
|
||
const resolved = { '01学习/笔记A.md': { '02工作/笔记C.md': 1 } };
|
||
const g = buildGraph(files, resolved, {}, { includeUnresolved: false, includeOrphans: false });
|
||
expect(g.nodes.map((n) => n.name)).toEqual(['笔记A', '笔记C']);
|
||
expect(g.links).toEqual([{ source: 0, target: 1 }]);
|
||
});
|
||
|
||
it('fileSize 从 FileRecord.size 透传', () => {
|
||
const sized = [{ path: 'a.md', basename: 'a', size: 12345 }];
|
||
const g = buildGraph(sized, {}, {}, { includeUnresolved: false, includeOrphans: true });
|
||
expect(g.nodes[0]?.fileSize).toBe(12345);
|
||
});
|
||
});
|
||
|
||
describe('质量档位帽(M4)', () => {
|
||
it('nodeCap 按度数取 top N 并重排索引;linkCap 按 min(端点度数) 截断', () => {
|
||
const resolved = {
|
||
'01学习/笔记A.md': { '01学习/子目录/笔记B.md': 1, '02工作/笔记C.md': 1, '根笔记.md': 1 },
|
||
'01学习/子目录/笔记B.md': { '02工作/笔记C.md': 1 },
|
||
};
|
||
// 度数:A=3, B=2, C=2, 根=1
|
||
const capped = buildGraph(files, resolved, {}, { includeUnresolved: false, includeOrphans: true, nodeCap: 3 });
|
||
expect(capped.nodes.map((n) => n.name)).toEqual(['笔记A', '笔记B', '笔记C']);
|
||
expect(capped.links).toHaveLength(3); // A-根 被丢弃
|
||
const linkCapped = buildGraph(files, resolved, {}, { includeUnresolved: false, includeOrphans: true, linkCap: 2 });
|
||
expect(linkCapped.links).toHaveLength(2);
|
||
// 保留的是 min 度数最高的边:A-B(min2)、A-C(min2),丢 B-C? min(B,C)=2 同分按原序——丢的是 A-根(min1)
|
||
expect(linkCapped.links.every((l) => l.source !== 3 && l.target !== 3)).toBe(true);
|
||
});
|
||
});
|