Files
PlantAI-rag-new/tests/frontend/unit/b3/graphLayout.test.js
jerryandClaude Fable 5 0875bb036c P0/M1: 四部分重构——frontend→ui、backend→sdk 改名 + 引用修复 + 骨架
- git mv 保留历史:ui/(Vue3+Vite)、sdk/(BFF 中间层)
- 路径语义修正:db_path/figures_cache 改相对 sdk/(BFF cwd),
  历史库 sdk/backend/data 迁移至 sdk/data/(会话/图片缓存无损)
- 引用修复 ~15 处:config.py、deploy/*.sh、vitest/playwright 配置、
  单测导入路径、docs、README、gitignore、.env.example
- 骨架:agent/{qa,methodology,legacy-rag,vendor}、knowledge/manifest.yaml、
  tooling/{compile,kb,crypto,assemble,audit,tools,scripts} + README
- 门禁验证:sdk pytest 125 通过、ui vitest 158 通过、
  生产 BFF 脚本重启四上游全绿 + 静态托管 + 会话库无损

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 12:56:47 +08:00

87 lines
3.1 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// graphLayout.test.js —— B3 单测:手写力导向布局(graphLayout.js)
import { describe, it, expect } from 'vitest';
import { forceLayout, buildPalette, hashString } from '../../../../ui/src/components/bench/graphLayout.js';
describe('forceLayout 手写力导向(FR-KG-001)', () => {
const small = {
nodes: [
{ id: 'a', label: 'A', type: 'gene' },
{ id: 'b', label: 'B', type: 'tissue' },
{ id: 'c', label: 'C', type: 'process' },
],
edges: [
{ source: 'a', target: 'b', relation: 'expressed_in' },
{ source: 'a', target: 'c', relation: 'involved_in' },
],
};
it('输出节点带有限位置(无 NaN/Infinity),且包围盒适配画布', () => {
const { nodes } = forceLayout(small, { width: 800, height: 600 });
expect(nodes).toHaveLength(3);
for (const n of nodes) {
expect(Number.isFinite(n.x)).toBe(true);
expect(Number.isFinite(n.y)).toBe(true);
expect(n.x).toBeGreaterThanOrEqual(0);
expect(n.x).toBeLessThanOrEqual(800);
expect(n.y).toBeGreaterThanOrEqual(0);
expect(n.y).toBeLessThanOrEqual(600);
}
});
it('边端点引用不存在的节点 → 被过滤,不抛错', () => {
const { nodes, edges } = forceLayout({
nodes: [{ id: 'a' }, { id: 'b' }],
edges: [{ source: 'a', target: 'ghost' }, { source: 'a', target: 'b' }],
});
expect(nodes).toHaveLength(2);
expect(edges).toHaveLength(1);
});
it('空图 → 空结果', () => {
const { nodes } = forceLayout({ nodes: [], edges: [] });
expect(nodes).toHaveLength(0);
});
it('单节点不崩溃', () => {
const { nodes } = forceLayout({ nodes: [{ id: 'x', type: 'gene' }], edges: [] });
expect(nodes).toHaveLength(1);
expect(Number.isFinite(nodes[0].x)).toBe(true);
});
it('大图(60 节点)也能稳定布局(供降级前渲染兜底)', () => {
const nodes = [];
const edges = [];
for (let i = 0; i < 60; i += 1) {
nodes.push({ id: `n${i}`, type: `t${i % 5}` });
if (i > 0) edges.push({ source: `n${i}`, target: `n${i - 1}` });
}
const out = forceLayout({ nodes, edges }, { width: 800, height: 600, iterations: 120 });
expect(out.nodes).toHaveLength(60);
for (const n of out.nodes) {
expect(Number.isFinite(n.x)).toBe(true);
expect(Number.isFinite(n.y)).toBe(true);
}
});
it('相同输入两次运行结果一致(确定性布局)', () => {
const a = forceLayout(small, { width: 800, height: 600 });
const b = forceLayout(small, { width: 800, height: 600 });
for (let i = 0; i < a.nodes.length; i += 1) {
expect(a.nodes[i].x).toBeCloseTo(b.nodes[i].x, 6);
expect(a.nodes[i].y).toBeCloseTo(b.nodes[i].y, 6);
}
});
});
describe('buildPalette 稳定映射', () => {
it('hashString 稳定', () => {
expect(hashString('gene')).toBe(hashString('gene'));
expect(hashString('gene')).not.toBe(hashString('tissue'));
});
it('调色板大小写敏感(Gene 与 gene 视为不同 type,与后端一致)', () => {
const p = buildPalette(['Gene', 'gene']);
expect(p.get('Gene')).not.toBe(p.get('gene'));
});
});