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