d4910aba8c
📊 Benchmark the Runtime / benchmark (pull_request) Successful in 1m2s
🚀 Lint & Test & Deploy / quality (pull_request) Successful in 2m30s
🚀 Lint & Test & Deploy / test-unit (pull_request) Successful in 32s
🚀 Lint & Test & Deploy / test-e2e (pull_request) Failing after 33s
🚀 Lint & Test & Deploy / deploy (pull_request) Has been skipped
165 lines
5.1 KiB
TypeScript
165 lines
5.1 KiB
TypeScript
import type { Graph } from '@nodarium/types';
|
|
import { describe, expect, it } from 'vitest';
|
|
import { expandGroups } from './runtime-executor';
|
|
|
|
// Helpers to build minimal serialized nodes/edges
|
|
function node(id: number, type: string, props?: Record<string, number>) {
|
|
return {
|
|
id,
|
|
type: type as Graph['nodes'][0]['type'],
|
|
position: [0, 0] as [number, number],
|
|
...(props ? { props } : {})
|
|
};
|
|
}
|
|
|
|
function edge(
|
|
from: number,
|
|
fromSocket: number,
|
|
to: number,
|
|
toSocket: string
|
|
): [number, number, number, string] {
|
|
return [from, fromSocket, to, toSocket];
|
|
}
|
|
|
|
describe('expandGroups', () => {
|
|
it('returns graph unchanged when there are no groups', () => {
|
|
const graph: Graph = {
|
|
id: 1,
|
|
nodes: [node(0, 'test/node/output'), node(1, 'test/node/input')],
|
|
edges: [edge(0, 0, 1, 'value')],
|
|
groups: []
|
|
};
|
|
|
|
const result = expandGroups(graph);
|
|
|
|
expect(result.nodes.length).toBe(2);
|
|
expect(result.edges.length).toBe(1);
|
|
expect(result).toBe(graph); // same reference — no copy needed
|
|
});
|
|
|
|
it('expands a simple group: A → [B] → C rewires to A → B → C', () => {
|
|
// IDs: A=1, B=2, C=3, groupNode=4, group.id=5, inputBoundary=6, outputBoundary=7
|
|
const groupId = 5;
|
|
const groupNodeId = 4;
|
|
const remappedB = (groupNodeId + 1) * 1_000_000 + 2; // 5_000_002
|
|
|
|
const graph: Graph = {
|
|
id: 1,
|
|
nodes: [
|
|
node(1, 'test/node/output'),
|
|
node(groupNodeId, '__internal/group/instance', { groupId }),
|
|
node(3, 'test/node/input')
|
|
],
|
|
edges: [
|
|
edge(1, 0, groupNodeId, 'input_0'), // A → group
|
|
edge(groupNodeId, 0, 3, 'value') // group → C
|
|
],
|
|
groups: [{
|
|
id: groupId,
|
|
nodes: [
|
|
node(6, '__internal/group/input'),
|
|
node(2, 'test/node/output'),
|
|
node(7, '__internal/group/output')
|
|
],
|
|
edges: [
|
|
edge(6, 0, 2, 'input'), // inputBoundary → B
|
|
edge(2, 0, 7, 'Out') // B → outputBoundary
|
|
],
|
|
inputs: { input_0: { type: 'float' } },
|
|
outputs: [{ type: 'float', label: 'Output 0' }]
|
|
}]
|
|
};
|
|
|
|
const result = expandGroups(graph);
|
|
|
|
const ids = result.nodes.map(n => n.id);
|
|
expect(ids).not.toContain(groupNodeId);
|
|
expect(ids).toContain(remappedB);
|
|
expect(ids).toContain(1); // A
|
|
expect(ids).toContain(3); // C
|
|
expect(result.nodes.length).toBe(3); // A, B(remapped), C
|
|
|
|
expect(result.edges).toContainEqual(edge(1, 0, remappedB, 'input')); // A → B
|
|
expect(result.edges).toContainEqual(edge(remappedB, 0, 3, 'value')); // B → C
|
|
expect(result.edges.length).toBe(2);
|
|
});
|
|
|
|
it('expands a group with two internal nodes (B→D) and preserves the internal edge', () => {
|
|
// A → [B → D] → C
|
|
const groupId = 10;
|
|
const groupNodeId = 5;
|
|
const remappedB = (groupNodeId + 1) * 1_000_000 + 1; // 6_000_001
|
|
const remappedD = (groupNodeId + 1) * 1_000_000 + 2; // 6_000_002
|
|
|
|
const graph: Graph = {
|
|
id: 1,
|
|
nodes: [
|
|
node(0, 'test/node/output'),
|
|
node(groupNodeId, '__internal/group/instance', { groupId }),
|
|
node(9, 'test/node/input')
|
|
],
|
|
edges: [
|
|
edge(0, 0, groupNodeId, 'input_0'),
|
|
edge(groupNodeId, 0, 9, 'value')
|
|
],
|
|
groups: [{
|
|
id: groupId,
|
|
nodes: [
|
|
node(3, '__internal/group/input'),
|
|
node(1, 'test/node/output'), // B
|
|
node(2, 'test/node/output'), // D
|
|
node(4, '__internal/group/output')
|
|
],
|
|
edges: [
|
|
edge(3, 0, 1, 'input'), // inputBoundary → B
|
|
edge(1, 0, 2, 'input'), // B → D (internal)
|
|
edge(2, 0, 4, 'Out') // D → outputBoundary
|
|
],
|
|
inputs: { input_0: { type: 'float' } },
|
|
outputs: [{ type: 'float' }]
|
|
}]
|
|
};
|
|
|
|
const result = expandGroups(graph);
|
|
|
|
expect(result.nodes.map(n => n.id)).not.toContain(groupNodeId);
|
|
expect(result.nodes.map(n => n.id)).toContain(remappedB);
|
|
expect(result.nodes.map(n => n.id)).toContain(remappedD);
|
|
|
|
expect(result.edges).toContainEqual(edge(0, 0, remappedB, 'input')); // A → B
|
|
expect(result.edges).toContainEqual(edge(remappedB, 0, remappedD, 'input')); // B → D (internal)
|
|
expect(result.edges).toContainEqual(edge(remappedD, 0, 9, 'value')); // D → C
|
|
expect(result.edges.length).toBe(3);
|
|
});
|
|
|
|
it('expands a group with no external connections (isolated)', () => {
|
|
const groupId = 20;
|
|
const groupNodeId = 1;
|
|
const remappedB = (groupNodeId + 1) * 1_000_000 + 2; // 2_000_002
|
|
|
|
const graph: Graph = {
|
|
id: 1,
|
|
nodes: [node(groupNodeId, '__internal/group/instance', { groupId })],
|
|
edges: [],
|
|
groups: [{
|
|
id: groupId,
|
|
nodes: [
|
|
node(3, '__internal/group/input'),
|
|
node(2, 'test/node/output'),
|
|
node(4, '__internal/group/output')
|
|
],
|
|
edges: [
|
|
edge(3, 0, 2, 'input'),
|
|
edge(2, 0, 4, 'Out')
|
|
]
|
|
}]
|
|
};
|
|
|
|
const result = expandGroups(graph);
|
|
|
|
expect(result.nodes.map(n => n.id)).not.toContain(groupNodeId);
|
|
expect(result.nodes.map(n => n.id)).toContain(remappedB);
|
|
expect(result.edges.length).toBe(0);
|
|
});
|
|
});
|