93 lines
3.3 KiB
TypeScript
93 lines
3.3 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { PipelineGraph, parseDocument, serializeDocument } from '../src/model/graph';
|
|
|
|
function sampleGraph(): PipelineGraph {
|
|
const g = new PipelineGraph();
|
|
g.addNode({ id: 'src', typeId: 'waterSource', x: 0, y: 0, angle: 0 });
|
|
g.addNode({ id: 'pump', typeId: 'pump', x: 200, y: 0, angle: 0 });
|
|
g.addNode({ id: 'home', typeId: 'consumer', x: 400, y: 0, angle: 0 });
|
|
g.addEdge({
|
|
id: 'e1',
|
|
sourceNodeId: 'src',
|
|
sourcePortId: 'out',
|
|
targetNodeId: 'pump',
|
|
targetPortId: 'in',
|
|
});
|
|
g.addEdge({
|
|
id: 'e2',
|
|
sourceNodeId: 'pump',
|
|
sourcePortId: 'out',
|
|
targetNodeId: 'home',
|
|
targetPortId: 'waterIn',
|
|
});
|
|
return g;
|
|
}
|
|
|
|
describe('PipelineGraph', () => {
|
|
it('adds nodes with type defaults merged into props', () => {
|
|
const g = sampleGraph();
|
|
const src = g.nodes.get('src')!;
|
|
expect(src.props.supply).toBe(50); // waterSource default
|
|
expect(src.props.title).toBe('Water source');
|
|
});
|
|
|
|
it('rejects unknown node types, ports and duplicate ids', () => {
|
|
const g = sampleGraph();
|
|
expect(() => g.addNode({ id: 'x', typeId: 'nope', x: 0, y: 0, angle: 0 })).toThrow(/Unknown node type/);
|
|
expect(() => g.addNode({ id: 'src', typeId: 'pump', x: 0, y: 0, angle: 0 })).toThrow(/Duplicate/);
|
|
expect(() =>
|
|
g.addEdge({ id: 'e3', sourceNodeId: 'src', sourcePortId: 'nope', targetNodeId: 'pump', targetPortId: 'in' }),
|
|
).toThrow(/Unknown source port/);
|
|
});
|
|
|
|
it('rejects edges between incompatible ports', () => {
|
|
const g = sampleGraph();
|
|
g.addNode({ id: 'ps', typeId: 'powerSupply', x: 0, y: 200, angle: 0 });
|
|
expect(() =>
|
|
g.addEdge({ id: 'bad', sourceNodeId: 'ps', sourcePortId: 'out', targetNodeId: 'home', targetPortId: 'waterIn' }),
|
|
).toThrow(/Incompatible relations/);
|
|
// power → consumer power inlet is fine
|
|
expect(() =>
|
|
g.addEdge({ id: 'ok', sourceNodeId: 'ps', sourcePortId: 'out', targetNodeId: 'home', targetPortId: 'powerIn' }),
|
|
).not.toThrow();
|
|
});
|
|
|
|
it('derives edge relation from the source port', () => {
|
|
const g = sampleGraph();
|
|
expect(g.edges.get('e1')!.relation).toBe('water');
|
|
});
|
|
|
|
it('removing a node cascades to connected edges', () => {
|
|
const g = sampleGraph();
|
|
g.removeNode('pump');
|
|
expect(g.nodes.has('pump')).toBe(false);
|
|
expect(g.edges.size).toBe(0);
|
|
});
|
|
|
|
it('edgesOf returns incident edges', () => {
|
|
const g = sampleGraph();
|
|
expect(g.edgesOf('pump').map((e) => e.id).sort()).toEqual(['e1', 'e2']);
|
|
expect(g.edgesOf('home').map((e) => e.id)).toEqual(['e2']);
|
|
});
|
|
|
|
it('survives a serialization round-trip', () => {
|
|
const g = sampleGraph();
|
|
g.edges.get('e1')!.vertices = [{ x: 100, y: 50 }];
|
|
g.nodes.get('pump')!.props.head = 45;
|
|
|
|
const json = serializeDocument(g.toDocument());
|
|
const restored = PipelineGraph.fromDocument(parseDocument(json));
|
|
|
|
expect(restored.nodes.size).toBe(3);
|
|
expect(restored.edges.size).toBe(2);
|
|
expect(restored.nodes.get('pump')!.props.head).toBe(45);
|
|
expect(restored.edges.get('e1')!.vertices).toEqual([{ x: 100, y: 50 }]);
|
|
expect(restored.edges.get('e2')!.relation).toBe('water');
|
|
});
|
|
|
|
it('parseDocument rejects malformed payloads', () => {
|
|
expect(() => parseDocument('{"foo": 1}')).toThrow(/Invalid diagram document/);
|
|
expect(() => parseDocument('not json')).toThrow();
|
|
});
|
|
});
|