Skip to content

Commit d67ba31

Browse files
Copilotajthinking
andcommitted
Add comprehensive integration tests for Diagram validation
Co-authored-by: ajthinking <[email protected]>
1 parent 3bdb70b commit d67ba31

File tree

1 file changed

+216
-0
lines changed

1 file changed

+216
-0
lines changed
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
import { describe, it, expect } from 'vitest';
2+
import { RunMessageSchema, SerializedDiagramSchema } from '../validation/schemas';
3+
import { Diagram } from '../Diagram';
4+
5+
describe('Integration: Diagram validation', () => {
6+
it('validates a Diagram class instance can be serialized and validated', () => {
7+
// Create a real Diagram instance (hydrated with methods)
8+
const diagram = new Diagram({
9+
nodes: [
10+
{
11+
id: 'input-1',
12+
type: 'Input',
13+
inputs: [],
14+
outputs: [
15+
{ id: 'output-port-1', name: 'output', schema: {} }
16+
],
17+
params: [],
18+
position: { x: 100, y: 100 }
19+
},
20+
{
21+
id: 'output-1',
22+
type: 'Output',
23+
inputs: [
24+
{ id: 'input-port-1', name: 'input', schema: {} }
25+
],
26+
outputs: [],
27+
params: [],
28+
position: { x: 300, y: 100 }
29+
}
30+
],
31+
links: [
32+
{
33+
id: 'link-1',
34+
sourcePortId: 'output-port-1',
35+
targetPortId: 'input-port-1'
36+
}
37+
],
38+
params: [],
39+
viewport: { x: 0, y: 0, zoom: 1 }
40+
});
41+
42+
// Verify the diagram has methods (is hydrated)
43+
expect(typeof diagram.clone).toBe('function');
44+
expect(typeof diagram.add).toBe('function');
45+
expect(typeof diagram.connect).toBe('function');
46+
47+
// Serialize the diagram to plain data (non-hydrated)
48+
const serializedDiagram = {
49+
nodes: diagram.nodes,
50+
links: diagram.links,
51+
params: diagram.params,
52+
viewport: diagram.viewport
53+
};
54+
55+
// Validate the serialized diagram
56+
expect(() => SerializedDiagramSchema.parse(serializedDiagram)).not.toThrow();
57+
58+
// Create a valid RunMessage using the serialized diagram
59+
const runMessage = {
60+
msgId: 'test-run-msg',
61+
type: 'run' as const,
62+
diagram: serializedDiagram,
63+
executionId: 'test-execution-id'
64+
};
65+
66+
// Validate the complete RunMessage
67+
expect(() => RunMessageSchema.parse(runMessage)).not.toThrow();
68+
69+
const validated = RunMessageSchema.parse(runMessage);
70+
71+
// Verify the validated data structure
72+
expect(validated.msgId).toBe('test-run-msg');
73+
expect(validated.type).toBe('run');
74+
expect(validated.executionId).toBe('test-execution-id');
75+
expect(validated.diagram.nodes).toHaveLength(2);
76+
expect(validated.diagram.links).toHaveLength(1);
77+
expect(validated.diagram.viewport).toEqual({ x: 0, y: 0, zoom: 1 });
78+
});
79+
80+
it('demonstrates the difference between hydrated and serialized diagrams', () => {
81+
// Hydrated diagram (has methods)
82+
const hydratedDiagram = new Diagram({
83+
nodes: [],
84+
links: [],
85+
params: [],
86+
viewport: { x: 0, y: 0, zoom: 1 }
87+
});
88+
89+
// Actually, the schema is flexible enough to validate Diagram instances
90+
// since it only checks for required properties, not extra ones
91+
expect(() => SerializedDiagramSchema.parse(hydratedDiagram)).not.toThrow();
92+
93+
// The key benefit is having a dedicated SerializedDiagram type for non-hydrated data
94+
const serializedDiagram = {
95+
nodes: hydratedDiagram.nodes,
96+
links: hydratedDiagram.links,
97+
params: hydratedDiagram.params,
98+
viewport: hydratedDiagram.viewport
99+
};
100+
101+
expect(() => SerializedDiagramSchema.parse(serializedDiagram)).not.toThrow();
102+
103+
// The type system ensures we work with plain data in RunMessage
104+
const runMessage = {
105+
msgId: 'test',
106+
type: 'run' as const,
107+
diagram: serializedDiagram, // This is SerializedDiagram type, not Diagram class
108+
executionId: 'test'
109+
};
110+
111+
expect(() => RunMessageSchema.parse(runMessage)).not.toThrow();
112+
});
113+
114+
it('validates complex diagram with params', () => {
115+
const complexDiagram = {
116+
nodes: [
117+
{
118+
id: 'node-with-params',
119+
type: 'CustomNode',
120+
inputs: [
121+
{ id: 'in-1', name: 'input', schema: { type: 'object' } }
122+
],
123+
outputs: [
124+
{ id: 'out-1', name: 'output', schema: { type: 'array' } }
125+
],
126+
params: [
127+
{
128+
name: 'testParam',
129+
label: 'Test Parameter',
130+
help: 'This is a test parameter',
131+
type: 'StringableParam' as const,
132+
multiline: false,
133+
canInterpolate: true,
134+
interpolate: true,
135+
input: {
136+
rawValue: 'default value',
137+
Cast: 'string'
138+
}
139+
}
140+
],
141+
position: { x: 200, y: 150 }
142+
}
143+
],
144+
links: [],
145+
params: [
146+
{
147+
name: 'globalParam',
148+
label: 'Global Parameter',
149+
help: 'A global parameter',
150+
type: 'StringableParam' as const,
151+
multiline: true,
152+
canInterpolate: false,
153+
input: {
154+
rawValue: 'global value'
155+
}
156+
}
157+
],
158+
viewport: { x: -50, y: -25, zoom: 1.5 }
159+
};
160+
161+
expect(() => SerializedDiagramSchema.parse(complexDiagram)).not.toThrow();
162+
163+
const runMessage = {
164+
msgId: 'complex-run-msg',
165+
type: 'run' as const,
166+
diagram: complexDiagram,
167+
executionId: 'complex-execution'
168+
};
169+
170+
expect(() => RunMessageSchema.parse(runMessage)).not.toThrow();
171+
});
172+
173+
it('rejects invalid diagram structures', () => {
174+
const invalidDiagrams = [
175+
// Missing required viewport
176+
{
177+
nodes: [],
178+
links: [],
179+
params: []
180+
},
181+
// Invalid node structure
182+
{
183+
nodes: [
184+
{
185+
id: 'invalid-node',
186+
// missing type
187+
inputs: [],
188+
outputs: [],
189+
params: []
190+
}
191+
],
192+
links: [],
193+
params: [],
194+
viewport: { x: 0, y: 0, zoom: 1 }
195+
},
196+
// Invalid link structure
197+
{
198+
nodes: [],
199+
links: [
200+
{
201+
id: 'invalid-link',
202+
sourcePortId: 'port-1'
203+
// missing targetPortId
204+
}
205+
],
206+
params: [],
207+
viewport: { x: 0, y: 0, zoom: 1 }
208+
}
209+
];
210+
211+
invalidDiagrams.forEach((invalidDiagram, index) => {
212+
expect(() => SerializedDiagramSchema.parse(invalidDiagram),
213+
`Invalid diagram ${index} should fail validation`).toThrow();
214+
});
215+
});
216+
});

0 commit comments

Comments
 (0)