1 | if typeof process isnt 'undefined' and process.execPath and process.execPath.indexOf('node') isnt -1
|
2 | chai = require 'chai' unless chai
|
3 | parser = require '../lib/index'
|
4 | parser.validateSchema = true
|
5 | else
|
6 | parser = require 'fbp'
|
7 |
|
8 | describe 'JSON to FBP parser', ->
|
9 | it 'should provide a parse method', ->
|
10 | chai.expect(parser.parse).to.be.a 'function'
|
11 | chai.expect(parser.serialize).to.be.a 'function'
|
12 |
|
13 | describe 'roundtrip', ->
|
14 | describe 'with simple FBP string', ->
|
15 | fbpData = """
|
16 | '8003' -> LISTEN WebServer(HTTP/Server) REQUEST -> IN Profiler(HTTP/Profiler) OUT -> IN Authentication(HTTP/BasicAuth)
|
17 | Authentication() OUT -> IN GreetUser(HelloController) OUT[0] -> IN[0] WriteResponse(HTTP/WriteResponse) OUT -> IN Send(HTTP/SendResponse)
|
18 | 'hello.jade' -> SOURCE ReadTemplate(ReadFile) OUT -> TEMPLATE Render(Template)
|
19 | GreetUser() DATA -> OPTIONS Render() OUT -> STRING WriteResponse()
|
20 | """
|
21 | roundTrippedFbpData = ""
|
22 | graphString = ""
|
23 | graphData = null
|
24 | graphData2 = null
|
25 |
|
26 | it 'should produce a graph JSON object', ->
|
27 |
|
28 | graphData = parser.parse fbpData, caseSensitive:true
|
29 | chai.expect(graphData).to.be.an 'object'
|
30 | chai.expect(graphData.caseSensitive).to.equal true
|
31 |
|
32 |
|
33 | roundTrippedFbpData = parser.serialize graphData
|
34 |
|
35 |
|
36 | graphData2 = parser.parse roundTrippedFbpData, caseSensitive:true
|
37 |
|
38 |
|
39 | fbp2 = parser.serialize graphData2
|
40 |
|
41 | chai.expect(roundTrippedFbpData).to.equal fbp2
|
42 |
|
43 | describe 'the generated graph', ->
|
44 | it 'should contain eight nodes', ->
|
45 | chai.expect(graphData2.processes).to.be.an 'object'
|
46 | chai.expect(graphData2.processes).to.have.keys [
|
47 | 'WebServer'
|
48 | 'Profiler'
|
49 | 'Authentication'
|
50 | 'GreetUser'
|
51 | 'WriteResponse'
|
52 | 'Send'
|
53 | 'ReadTemplate'
|
54 | 'Render'
|
55 | ]
|
56 | it 'should contain ten edges and IIPs', ->
|
57 | chai.expect(graphData2.connections).to.be.an 'array'
|
58 | chai.expect(graphData2.connections.length).to.equal 10
|
59 | it 'should contain no exports', ->
|
60 | chai.expect(graphData2.exports).to.be.an 'undefined'
|
61 |
|
62 | describe 'with flowhub json graph (from ingress c-base table)', ->
|
63 | jsonData = """
|
64 | {
|
65 | "properties": {
|
66 | "name": "ConfigPaths",
|
67 | "environment": {
|
68 | "type": "noflo-nodejs"
|
69 | },
|
70 | "description": "Read Ingress Table configuration files",
|
71 | "icon": "file"
|
72 | },
|
73 | "inports": {
|
74 | "envvar": {
|
75 | "process": "core/ReadEnv_2mde7",
|
76 | "port": "key",
|
77 | "metadata": {
|
78 | "x": -612,
|
79 | "y": 432,
|
80 | "width": 72,
|
81 | "height": 72
|
82 | }
|
83 | },
|
84 | "serverfile": {
|
85 | "process": "core/Repeat_ktvob",
|
86 | "port": "in",
|
87 | "metadata": {
|
88 | "x": -612,
|
89 | "y": 324,
|
90 | "width": 72,
|
91 | "height": 72
|
92 | }
|
93 | },
|
94 | "portalfile": {
|
95 | "process": "core/Repeat_r65df",
|
96 | "port": "in",
|
97 | "metadata": {
|
98 | "x": -612,
|
99 | "y": 540,
|
100 | "width": 72,
|
101 | "height": 72
|
102 | }
|
103 | }
|
104 | },
|
105 | "outports": {
|
106 | "error": {
|
107 | "process": "core/ReadEnv_2mde7",
|
108 | "port": "error",
|
109 | "metadata": {
|
110 | "x": -324,
|
111 | "y": 684,
|
112 | "width": 72,
|
113 | "height": 72
|
114 | }
|
115 | },
|
116 | "serverfile": {
|
117 | "process": "strings/CompileString_pjcg2",
|
118 | "port": "out",
|
119 | "metadata": {
|
120 | "x": 0,
|
121 | "y": 432,
|
122 | "width": 72,
|
123 | "height": 72
|
124 | }
|
125 | },
|
126 | "portalfile": {
|
127 | "process": "strings/CompileString_667tt",
|
128 | "port": "out",
|
129 | "metadata": {
|
130 | "x": 0,
|
131 | "y": 540,
|
132 | "width": 72,
|
133 | "height": 72
|
134 | }
|
135 | }
|
136 | },
|
137 | "groups": [],
|
138 | "processes": {
|
139 | "core/ReadEnv_2mde7": {
|
140 | "component": "core/ReadEnv",
|
141 | "metadata": {
|
142 | "label": "core/ReadEnv",
|
143 | "x": -468,
|
144 | "y": 432,
|
145 | "width": 72,
|
146 | "height": 72
|
147 | }
|
148 | },
|
149 | "core/Repeat_ktvob": {
|
150 | "component": "core/Repeat",
|
151 | "metadata": {
|
152 | "label": "core/Repeat",
|
153 | "x": -324,
|
154 | "y": 324,
|
155 | "width": 72,
|
156 | "height": 72
|
157 | }
|
158 | },
|
159 | "packets/DoNotDisconnect_uev3m": {
|
160 | "component": "packets/DoNotDisconnect",
|
161 | "metadata": {
|
162 | "label": "packets/DoNotDisconnect",
|
163 | "x": -324,
|
164 | "y": 432,
|
165 | "width": 72,
|
166 | "height": 72
|
167 | }
|
168 | },
|
169 | "strings/CompileString_pjcg2": {
|
170 | "component": "strings/CompileString",
|
171 | "metadata": {
|
172 | "label": "strings/CompileString",
|
173 | "x": -144,
|
174 | "y": 432,
|
175 | "width": 72,
|
176 | "height": 72
|
177 | }
|
178 | },
|
179 | "strings/CompileString_667tt": {
|
180 | "component": "strings/CompileString",
|
181 | "metadata": {
|
182 | "label": "strings/CompileString",
|
183 | "x": -144,
|
184 | "y": 540,
|
185 | "width": 72,
|
186 | "height": 72
|
187 | }
|
188 | },
|
189 | "core/Repeat_r65df": {
|
190 | "component": "core/Repeat",
|
191 | "metadata": {
|
192 | "label": "core/Repeat",
|
193 | "x": -324,
|
194 | "y": 540,
|
195 | "width": 72,
|
196 | "height": 72
|
197 | }
|
198 | }
|
199 | },
|
200 | "connections": [
|
201 | {
|
202 | "src": {
|
203 | "process": "core/Repeat_r65df",
|
204 | "port": "out"
|
205 | },
|
206 | "tgt": {
|
207 | "process": "strings/CompileString_667tt",
|
208 | "port": "in"
|
209 | }
|
210 | },
|
211 | {
|
212 | "src": {
|
213 | "process": "core/Repeat_ktvob",
|
214 | "port": "out"
|
215 | },
|
216 | "tgt": {
|
217 | "process": "strings/CompileString_pjcg2",
|
218 | "port": "in"
|
219 | }
|
220 | },
|
221 | {
|
222 | "src": {
|
223 | "process": "core/ReadEnv_2mde7",
|
224 | "port": "out"
|
225 | },
|
226 | "tgt": {
|
227 | "process": "packets/DoNotDisconnect_uev3m",
|
228 | "port": "in"
|
229 | },
|
230 | "metadata": {
|
231 | "route": null
|
232 | }
|
233 | },
|
234 | {
|
235 | "src": {
|
236 | "process": "packets/DoNotDisconnect_uev3m",
|
237 | "port": "out"
|
238 | },
|
239 | "tgt": {
|
240 | "process": "strings/CompileString_pjcg2",
|
241 | "port": "in"
|
242 | }
|
243 | },
|
244 | {
|
245 | "src": {
|
246 | "process": "packets/DoNotDisconnect_uev3m",
|
247 | "port": "out"
|
248 | },
|
249 | "tgt": {
|
250 | "process": "strings/CompileString_667tt",
|
251 | "port": "in"
|
252 | }
|
253 | },
|
254 | {
|
255 | "data": "/",
|
256 | "tgt": {
|
257 | "process": "strings/CompileString_pjcg2",
|
258 | "port": "delimiter"
|
259 | }
|
260 | },
|
261 | {
|
262 | "data": "/",
|
263 | "tgt": {
|
264 | "process": "strings/CompileString_667tt",
|
265 | "port": "delimiter"
|
266 | }
|
267 | }
|
268 | ]
|
269 | }
|
270 | """
|
271 | roundTrippedFbpData = ""
|
272 | graphString = ""
|
273 | graphData = null
|
274 | graphData2 = null
|
275 |
|
276 | it 'should produce a graph JSON object', ->
|
277 | fbpData = parser.serialize jsonData
|
278 | jsonFromFbp = parser.parse fbpData, caseSensitive:true
|
279 | it 'should have retained properties', ->
|
280 | fbpData = parser.serialize jsonData
|
281 | jsonFromFbp = parser.parse fbpData, caseSensitive:true
|
282 | chai.expect(jsonFromFbp.properties).to.eql JSON.parse(jsonData).properties
|
283 |
|
284 | describe 'roundtrip with FBP string with inports and outports', ->
|
285 | fbpData = """
|
286 | INPORT=Read.IN:FILENAME
|
287 | INPORT=Display.OPTIONS:OPTIONS
|
288 | OUTPORT=Display.OUT:OUT
|
289 | Read(ReadFile) OUT -> IN Display(Output)
|
290 | """
|
291 | graphData = null
|
292 | graphData2 = null
|
293 | it 'should produce a graph JSON object', ->
|
294 |
|
295 | graphData = parser.parse fbpData, caseSensitive:true
|
296 | chai.expect(graphData).to.be.an 'object'
|
297 |
|
298 |
|
299 | jsonGraph = parser.serialize graphData
|
300 |
|
301 |
|
302 | graphData2 = parser.parse jsonGraph, caseSensitive:true
|
303 |
|
304 |
|
305 | fbpData2 = parser.serialize graphData2
|
306 |
|
307 |
|
308 | fbpData = fbpData.replace /(\n)+/g, ""
|
309 | fbpData2 = fbpData2.replace /(\n)+/g, ""
|
310 |
|
311 |
|
312 | chai.expect(fbpData).to.equal fbpData2
|
313 |
|
314 | describe 'the generated graph', ->
|
315 | it 'should contain two nodes', ->
|
316 | chai.expect(graphData2.processes).to.eql
|
317 | Read:
|
318 | component: 'ReadFile'
|
319 | Display:
|
320 | component: 'Output'
|
321 | it 'should contain no legacy exports', ->
|
322 | chai.expect(graphData2.exports).to.be.an 'undefined'
|
323 | it 'should contain a single connection', ->
|
324 | chai.expect(graphData2.connections).to.be.an 'array'
|
325 | chai.expect(graphData2.connections.length).to.equal 1
|
326 | chai.expect(graphData2.connections[0]).to.eql
|
327 | src:
|
328 | process: 'Read'
|
329 | port: 'OUT'
|
330 | tgt:
|
331 | process: 'Display'
|
332 | port: 'IN'
|
333 | it 'should contain two inports', ->
|
334 | chai.expect(graphData2.inports).to.be.an 'object'
|
335 | chai.expect(graphData2.inports.FILENAME).to.eql
|
336 | process: 'Read'
|
337 | port: 'IN'
|
338 | chai.expect(graphData2.inports.OPTIONS).to.eql
|
339 | process: 'Display'
|
340 | port: 'OPTIONS'
|
341 | it 'should contain an outport', ->
|
342 | chai.expect(graphData2.outports).to.be.an 'object'
|
343 | chai.expect(graphData2.outports.OUT).to.eql
|
344 | process: 'Display'
|
345 | port: 'OUT'
|
346 |
|
347 | describe 'annotations', ->
|
348 | fbpData = """
|
349 | # @runtime foo
|
350 | # @name ReadSomefile
|
351 |
|
352 | "somefile" -> SOURCE Read(ReadFile)
|
353 |
|
354 | """
|
355 | graphData =
|
356 | caseSensitive: false
|
357 | properties:
|
358 | name: 'ReadSomefile'
|
359 | environment:
|
360 | type: 'foo'
|
361 | inports: {}
|
362 | outports: {}
|
363 | groups: []
|
364 | processes:
|
365 | Read:
|
366 | component: 'ReadFile'
|
367 | connections: [
|
368 | data: 'somefile'
|
369 | tgt:
|
370 | process: 'Read'
|
371 | port: 'source'
|
372 | ]
|
373 | it 'should produce expected FBP string', ->
|
374 | serialized = parser.serialize graphData
|
375 | chai.expect(serialized).to.equal fbpData
|
376 | it 'should produce expected FBP graph', ->
|
377 | serialized = parser.parse fbpData
|
378 | chai.expect(serialized).to.eql graphData
|
379 |
|
380 | describe 'annotations in case sensitive graph', ->
|
381 | fbpData = """
|
382 | # @runtime foo
|
383 | # @name ReadSomefile
|
384 | INPORT=Read.Encoding:FileEncoding
|
385 | OUTPORT=Read.Out:Result
|
386 |
|
387 | "somefile" -> SourceCode Read(ReadFile)
|
388 | Read Errors -> TryAgain Read
|
389 |
|
390 | """
|
391 | graphData =
|
392 | caseSensitive: true
|
393 | properties:
|
394 | name: 'ReadSomefile'
|
395 | environment:
|
396 | type: 'foo'
|
397 | inports:
|
398 | FileEncoding:
|
399 | process: 'Read'
|
400 | port: 'Encoding'
|
401 | outports:
|
402 | Result:
|
403 | process: 'Read'
|
404 | port: 'Out'
|
405 | groups: []
|
406 | processes:
|
407 | Read:
|
408 | component: 'ReadFile'
|
409 | connections: [
|
410 | data: 'somefile'
|
411 | tgt:
|
412 | process: 'Read'
|
413 | port: 'SourceCode'
|
414 | ,
|
415 | src:
|
416 | process: 'Read'
|
417 | port: 'Errors'
|
418 | tgt:
|
419 | process: 'Read'
|
420 | port: 'TryAgain'
|
421 | ]
|
422 | it 'should produce expected FBP string', ->
|
423 | serialized = parser.serialize graphData
|
424 | chai.expect(serialized).to.equal fbpData
|
425 | it 'should produce expected FBP graph', ->
|
426 | serialized = parser.parse fbpData,
|
427 | caseSensitive: true
|
428 | chai.expect(serialized).to.eql graphData
|