@alt-javascript/camel-lite-core 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +161 -0
- package/package.json +47 -0
- package/src/AggregationStrategies.js +40 -0
- package/src/CamelContext.js +131 -0
- package/src/ConsumerTemplate.js +77 -0
- package/src/Exchange.js +70 -0
- package/src/ExpressionBuilder.js +122 -0
- package/src/Message.js +38 -0
- package/src/Pipeline.js +96 -0
- package/src/ProcessorNormalizer.js +14 -0
- package/src/ProducerTemplate.js +98 -0
- package/src/RouteBuilder.js +21 -0
- package/src/RouteDefinition.js +526 -0
- package/src/RouteLoader.js +390 -0
- package/src/component.js +33 -0
- package/src/errors/CamelError.js +9 -0
- package/src/errors/CamelFilterStopException.js +15 -0
- package/src/errors/CycleDetectedError.js +12 -0
- package/src/errors/SedaQueueFullError.js +12 -0
- package/src/index.js +17 -0
- package/test/ConsumerTemplate.test.js +146 -0
- package/test/ProducerTemplate.test.js +132 -0
- package/test/context.test.js +97 -0
- package/test/dsl.test.js +375 -0
- package/test/eip.test.js +497 -0
- package/test/exchange.test.js +42 -0
- package/test/fixtures/routes.yaml +58 -0
- package/test/message.test.js +36 -0
- package/test/pipeline.test.js +308 -0
- package/test/routeBuilder.test.js +208 -0
- package/test/routeLoader.test.js +557 -0
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { join, dirname } from 'node:path';
|
|
5
|
+
import { Exchange, CamelContext, RouteLoader } from '../src/index.js';
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const FIXTURE_PATH = join(__dirname, 'fixtures', 'routes.yaml');
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Helpers
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
function makeExchange(body) {
|
|
15
|
+
const ex = new Exchange();
|
|
16
|
+
ex.in.body = body;
|
|
17
|
+
return ex;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function runRoute(routeDefinition, exchange, context = null) {
|
|
21
|
+
const pipeline = routeDefinition.compile(context ?? new CamelContext());
|
|
22
|
+
await pipeline.run(exchange);
|
|
23
|
+
return exchange;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// loadString — YAML
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
describe('RouteLoader.loadString: YAML parsing', () => {
|
|
31
|
+
it('returns a RouteBuilder from YAML string', () => {
|
|
32
|
+
const yaml = `
|
|
33
|
+
route:
|
|
34
|
+
id: test-route
|
|
35
|
+
from:
|
|
36
|
+
uri: direct:test
|
|
37
|
+
steps:
|
|
38
|
+
- setBody:
|
|
39
|
+
constant: hello
|
|
40
|
+
`;
|
|
41
|
+
const builder = RouteLoader.loadString(yaml, 'yaml');
|
|
42
|
+
assert.equal(typeof builder.getRoutes, 'function');
|
|
43
|
+
assert.equal(builder.getRoutes().length, 1);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('fromUri is set correctly on the RouteDefinition', () => {
|
|
47
|
+
const yaml = `
|
|
48
|
+
route:
|
|
49
|
+
from:
|
|
50
|
+
uri: direct:myroute
|
|
51
|
+
steps: []
|
|
52
|
+
`;
|
|
53
|
+
const builder = RouteLoader.loadString(yaml, 'yaml');
|
|
54
|
+
assert.equal(builder.getRoutes()[0].fromUri, 'direct:myroute');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('multiple routes in one file create multiple RouteDefinitions', () => {
|
|
58
|
+
const yaml = `
|
|
59
|
+
routes:
|
|
60
|
+
- route:
|
|
61
|
+
from:
|
|
62
|
+
uri: direct:r1
|
|
63
|
+
steps: []
|
|
64
|
+
- route:
|
|
65
|
+
from:
|
|
66
|
+
uri: direct:r2
|
|
67
|
+
steps: []
|
|
68
|
+
`;
|
|
69
|
+
const builder = RouteLoader.loadString(yaml, 'yaml');
|
|
70
|
+
assert.equal(builder.getRoutes().length, 2);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
// loadString — JSON
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
describe('RouteLoader.loadString: JSON parsing', () => {
|
|
79
|
+
it('returns a RouteBuilder from JSON string', () => {
|
|
80
|
+
const json = JSON.stringify({
|
|
81
|
+
route: {
|
|
82
|
+
from: { uri: 'direct:json-test', steps: [{ setBody: { constant: 'from-json' } }] },
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
const builder = RouteLoader.loadString(json, 'json');
|
|
86
|
+
assert.equal(builder.getRoutes().length, 1);
|
|
87
|
+
assert.equal(builder.getRoutes()[0].fromUri, 'direct:json-test');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('auto-detects JSON format when format omitted', () => {
|
|
91
|
+
const json = JSON.stringify({ route: { from: { uri: 'direct:auto', steps: [] } } });
|
|
92
|
+
const builder = RouteLoader.loadString(json);
|
|
93
|
+
assert.equal(builder.getRoutes().length, 1);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// Step mappings — each tested individually
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
describe('RouteLoader step mapping: setBody', () => {
|
|
102
|
+
it('constant value sets body', async () => {
|
|
103
|
+
const yaml = `
|
|
104
|
+
route:
|
|
105
|
+
from:
|
|
106
|
+
uri: direct:test
|
|
107
|
+
steps:
|
|
108
|
+
- setBody:
|
|
109
|
+
constant: hello-world
|
|
110
|
+
`;
|
|
111
|
+
const builder = RouteLoader.loadString(yaml, 'yaml');
|
|
112
|
+
const [route] = builder.getRoutes();
|
|
113
|
+
const ex = makeExchange(null);
|
|
114
|
+
await runRoute(route, ex);
|
|
115
|
+
assert.equal(ex.in.body, 'hello-world');
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('simple expression sets body from header', async () => {
|
|
119
|
+
const yaml = `
|
|
120
|
+
route:
|
|
121
|
+
from:
|
|
122
|
+
uri: direct:test
|
|
123
|
+
steps:
|
|
124
|
+
- setBody:
|
|
125
|
+
simple: "\${header.X-Value}"
|
|
126
|
+
`;
|
|
127
|
+
const builder = RouteLoader.loadString(yaml, 'yaml');
|
|
128
|
+
const [route] = builder.getRoutes();
|
|
129
|
+
const ex = makeExchange(null);
|
|
130
|
+
ex.in.setHeader('X-Value', 'from-header');
|
|
131
|
+
await runRoute(route, ex);
|
|
132
|
+
assert.equal(ex.in.body, 'from-header');
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('js expression sets body', async () => {
|
|
136
|
+
const yaml = `
|
|
137
|
+
route:
|
|
138
|
+
from:
|
|
139
|
+
uri: direct:test
|
|
140
|
+
steps:
|
|
141
|
+
- setBody:
|
|
142
|
+
js: "exchange.in.body.toUpperCase()"
|
|
143
|
+
`;
|
|
144
|
+
const builder = RouteLoader.loadString(yaml, 'yaml');
|
|
145
|
+
const [route] = builder.getRoutes();
|
|
146
|
+
const ex = makeExchange('lower');
|
|
147
|
+
await runRoute(route, ex);
|
|
148
|
+
assert.equal(ex.in.body, 'LOWER');
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe('RouteLoader step mapping: setHeader', () => {
|
|
153
|
+
it('sets a header with constant value', async () => {
|
|
154
|
+
const yaml = `
|
|
155
|
+
route:
|
|
156
|
+
from:
|
|
157
|
+
uri: direct:test
|
|
158
|
+
steps:
|
|
159
|
+
- setHeader:
|
|
160
|
+
name: X-Env
|
|
161
|
+
constant: production
|
|
162
|
+
`;
|
|
163
|
+
const builder = RouteLoader.loadString(yaml, 'yaml');
|
|
164
|
+
const [route] = builder.getRoutes();
|
|
165
|
+
const ex = makeExchange('body');
|
|
166
|
+
await runRoute(route, ex);
|
|
167
|
+
assert.equal(ex.in.getHeader('X-Env'), 'production');
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe('RouteLoader step mapping: setProperty', () => {
|
|
172
|
+
it('sets a property with js expression', async () => {
|
|
173
|
+
const yaml = `
|
|
174
|
+
route:
|
|
175
|
+
from:
|
|
176
|
+
uri: direct:test
|
|
177
|
+
steps:
|
|
178
|
+
- setBody:
|
|
179
|
+
constant: myvalue
|
|
180
|
+
- setProperty:
|
|
181
|
+
name: saved
|
|
182
|
+
js: "exchange.in.body"
|
|
183
|
+
`;
|
|
184
|
+
const builder = RouteLoader.loadString(yaml, 'yaml');
|
|
185
|
+
const [route] = builder.getRoutes();
|
|
186
|
+
const ex = makeExchange(null);
|
|
187
|
+
await runRoute(route, ex);
|
|
188
|
+
assert.equal(ex.getProperty('saved'), 'myvalue');
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
describe('RouteLoader step mapping: removeHeader', () => {
|
|
193
|
+
it('removes a named header', async () => {
|
|
194
|
+
const yaml = `
|
|
195
|
+
route:
|
|
196
|
+
from:
|
|
197
|
+
uri: direct:test
|
|
198
|
+
steps:
|
|
199
|
+
- removeHeader:
|
|
200
|
+
name: X-Temp
|
|
201
|
+
`;
|
|
202
|
+
const builder = RouteLoader.loadString(yaml, 'yaml');
|
|
203
|
+
const [route] = builder.getRoutes();
|
|
204
|
+
const ex = makeExchange('body');
|
|
205
|
+
ex.in.setHeader('X-Temp', 'to-remove');
|
|
206
|
+
await runRoute(route, ex);
|
|
207
|
+
assert.equal(ex.in.getHeader('X-Temp'), undefined);
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
describe('RouteLoader step mapping: marshal/unmarshal', () => {
|
|
212
|
+
it('marshal serialises to JSON string, unmarshal deserialises', async () => {
|
|
213
|
+
const yaml = `
|
|
214
|
+
route:
|
|
215
|
+
from:
|
|
216
|
+
uri: direct:test
|
|
217
|
+
steps:
|
|
218
|
+
- marshal:
|
|
219
|
+
format: json
|
|
220
|
+
- unmarshal:
|
|
221
|
+
format: json
|
|
222
|
+
`;
|
|
223
|
+
const builder = RouteLoader.loadString(yaml, 'yaml');
|
|
224
|
+
const [route] = builder.getRoutes();
|
|
225
|
+
const original = { name: 'Widget', price: 9.99 };
|
|
226
|
+
const ex = makeExchange(original);
|
|
227
|
+
await runRoute(route, ex);
|
|
228
|
+
assert.deepEqual(ex.in.body, original);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
describe('RouteLoader step mapping: convertBodyTo', () => {
|
|
233
|
+
it('converts body to String', async () => {
|
|
234
|
+
const yaml = `
|
|
235
|
+
route:
|
|
236
|
+
from:
|
|
237
|
+
uri: direct:test
|
|
238
|
+
steps:
|
|
239
|
+
- convertBodyTo: String
|
|
240
|
+
`;
|
|
241
|
+
const builder = RouteLoader.loadString(yaml, 'yaml');
|
|
242
|
+
const [route] = builder.getRoutes();
|
|
243
|
+
const ex = makeExchange(42);
|
|
244
|
+
await runRoute(route, ex);
|
|
245
|
+
assert.equal(ex.in.body, '42');
|
|
246
|
+
assert.equal(typeof ex.in.body, 'string');
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
describe('RouteLoader step mapping: log', () => {
|
|
251
|
+
it('log step does not throw and does not modify body', async () => {
|
|
252
|
+
const yaml = `
|
|
253
|
+
route:
|
|
254
|
+
from:
|
|
255
|
+
uri: direct:test
|
|
256
|
+
steps:
|
|
257
|
+
- log: "route executed"
|
|
258
|
+
`;
|
|
259
|
+
const builder = RouteLoader.loadString(yaml, 'yaml');
|
|
260
|
+
const [route] = builder.getRoutes();
|
|
261
|
+
const ex = makeExchange('unchanged');
|
|
262
|
+
await assert.doesNotReject(() => runRoute(route, ex));
|
|
263
|
+
assert.equal(ex.in.body, 'unchanged');
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
describe('RouteLoader step mapping: stop', () => {
|
|
268
|
+
it('stop halts exchange — subsequent steps do not execute', async () => {
|
|
269
|
+
const yaml = `
|
|
270
|
+
route:
|
|
271
|
+
from:
|
|
272
|
+
uri: direct:test
|
|
273
|
+
steps:
|
|
274
|
+
- setBody:
|
|
275
|
+
constant: before-stop
|
|
276
|
+
- stop: {}
|
|
277
|
+
- setBody:
|
|
278
|
+
constant: after-stop
|
|
279
|
+
`;
|
|
280
|
+
const builder = RouteLoader.loadString(yaml, 'yaml');
|
|
281
|
+
const [route] = builder.getRoutes();
|
|
282
|
+
const ex = makeExchange(null);
|
|
283
|
+
await runRoute(route, ex);
|
|
284
|
+
assert.equal(ex.in.body, 'before-stop');
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
describe('RouteLoader step mapping: bean', () => {
|
|
289
|
+
it('bean with string name looks up from context at runtime', async () => {
|
|
290
|
+
const yaml = `
|
|
291
|
+
route:
|
|
292
|
+
from:
|
|
293
|
+
uri: direct:test
|
|
294
|
+
steps:
|
|
295
|
+
- bean: myProcessor
|
|
296
|
+
`;
|
|
297
|
+
const builder = RouteLoader.loadString(yaml, 'yaml');
|
|
298
|
+
const [route] = builder.getRoutes();
|
|
299
|
+
const ctx = new CamelContext();
|
|
300
|
+
ctx.registerBean('myProcessor', async (ex) => { ex.in.body = 'from-bean'; });
|
|
301
|
+
const ex = makeExchange('original');
|
|
302
|
+
await runRoute(route, ex, ctx);
|
|
303
|
+
assert.equal(ex.in.body, 'from-bean');
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
describe('RouteLoader step mapping: filter', () => {
|
|
308
|
+
it('filter with nested steps — passes when predicate true', async () => {
|
|
309
|
+
const yaml = `
|
|
310
|
+
route:
|
|
311
|
+
from:
|
|
312
|
+
uri: direct:test
|
|
313
|
+
steps:
|
|
314
|
+
- filter:
|
|
315
|
+
simple: "\${header.pass} == 'yes'"
|
|
316
|
+
steps:
|
|
317
|
+
- setBody:
|
|
318
|
+
constant: "passed"
|
|
319
|
+
`;
|
|
320
|
+
const builder = RouteLoader.loadString(yaml, 'yaml');
|
|
321
|
+
const [route] = builder.getRoutes();
|
|
322
|
+
|
|
323
|
+
// Exchange that passes
|
|
324
|
+
const ex1 = makeExchange('original');
|
|
325
|
+
ex1.in.setHeader('pass', 'yes');
|
|
326
|
+
await runRoute(route, ex1);
|
|
327
|
+
assert.equal(ex1.in.body, 'passed');
|
|
328
|
+
|
|
329
|
+
// Exchange that is filtered — body unchanged
|
|
330
|
+
const ex2 = makeExchange('original');
|
|
331
|
+
ex2.in.setHeader('pass', 'no');
|
|
332
|
+
await runRoute(route, ex2);
|
|
333
|
+
assert.equal(ex2.in.body, 'original');
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
describe('RouteLoader step mapping: choice', () => {
|
|
338
|
+
it('choice/when routes to correct branch', async () => {
|
|
339
|
+
const yaml = `
|
|
340
|
+
routes:
|
|
341
|
+
- route:
|
|
342
|
+
from:
|
|
343
|
+
uri: direct:choice-test
|
|
344
|
+
steps:
|
|
345
|
+
- choice:
|
|
346
|
+
when:
|
|
347
|
+
- simple: "\${header.type} == 'A'"
|
|
348
|
+
to: direct:branch-a
|
|
349
|
+
otherwise:
|
|
350
|
+
to: direct:branch-default
|
|
351
|
+
- route:
|
|
352
|
+
from:
|
|
353
|
+
uri: direct:branch-a
|
|
354
|
+
steps:
|
|
355
|
+
- setBody:
|
|
356
|
+
constant: branch-a-body
|
|
357
|
+
- route:
|
|
358
|
+
from:
|
|
359
|
+
uri: direct:branch-default
|
|
360
|
+
steps:
|
|
361
|
+
- setBody:
|
|
362
|
+
constant: default-body
|
|
363
|
+
`;
|
|
364
|
+
const builder = RouteLoader.loadString(yaml, 'yaml');
|
|
365
|
+
const ctx = new CamelContext();
|
|
366
|
+
ctx.addComponent('direct', await import('../src/index.js').then(m => {
|
|
367
|
+
// We need a real context start to test cross-route dispatch.
|
|
368
|
+
// Instead, test the choice step type selection via compiled pipeline directly.
|
|
369
|
+
return null;
|
|
370
|
+
}));
|
|
371
|
+
|
|
372
|
+
// Simpler: just verify the route definitions were created
|
|
373
|
+
const routes = builder.getRoutes();
|
|
374
|
+
assert.equal(routes.length, 3);
|
|
375
|
+
assert.equal(routes[0].fromUri, 'direct:choice-test');
|
|
376
|
+
assert.equal(routes[1].fromUri, 'direct:branch-a');
|
|
377
|
+
assert.equal(routes[2].fromUri, 'direct:branch-default');
|
|
378
|
+
});
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
describe('RouteLoader step mapping: unknown key', () => {
|
|
382
|
+
it('warns and skips unknown step keys without throwing', () => {
|
|
383
|
+
const yaml = `
|
|
384
|
+
route:
|
|
385
|
+
from:
|
|
386
|
+
uri: direct:test
|
|
387
|
+
steps:
|
|
388
|
+
- setBody:
|
|
389
|
+
constant: ok
|
|
390
|
+
- unknownStep:
|
|
391
|
+
foo: bar
|
|
392
|
+
- setBody:
|
|
393
|
+
constant: after-unknown
|
|
394
|
+
`;
|
|
395
|
+
// Should not throw during load
|
|
396
|
+
assert.doesNotThrow(() => RouteLoader.loadString(yaml, 'yaml'));
|
|
397
|
+
const builder = RouteLoader.loadString(yaml, 'yaml');
|
|
398
|
+
const [route] = builder.getRoutes();
|
|
399
|
+
// Route compiles correctly despite unknown step
|
|
400
|
+
assert.doesNotThrow(() => route.compile(new CamelContext()));
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
// ---------------------------------------------------------------------------
|
|
405
|
+
// loadFile integration test
|
|
406
|
+
// ---------------------------------------------------------------------------
|
|
407
|
+
|
|
408
|
+
describe('RouteLoader.loadFile integration', () => {
|
|
409
|
+
it('loads from disk, parses all route types', async () => {
|
|
410
|
+
const builder = await RouteLoader.loadFile(FIXTURE_PATH);
|
|
411
|
+
const routes = builder.getRoutes();
|
|
412
|
+
|
|
413
|
+
// Fixture has 4 routes
|
|
414
|
+
assert.equal(routes.length, 4);
|
|
415
|
+
|
|
416
|
+
const uris = routes.map(r => r.fromUri);
|
|
417
|
+
assert.ok(uris.includes('direct:loader-test'));
|
|
418
|
+
assert.ok(uris.includes('direct:choice-test'));
|
|
419
|
+
assert.ok(uris.includes('direct:bean-test'));
|
|
420
|
+
assert.ok(uris.includes('direct:filter-test'));
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
it('full-step-coverage route executes stop after convertBodyTo', async () => {
|
|
424
|
+
const builder = await RouteLoader.loadFile(FIXTURE_PATH);
|
|
425
|
+
const routes = builder.getRoutes();
|
|
426
|
+
const fullRoute = routes.find(r => r.fromUri === 'direct:loader-test');
|
|
427
|
+
assert.ok(fullRoute, 'loader-test route should exist');
|
|
428
|
+
|
|
429
|
+
const ex = makeExchange(null);
|
|
430
|
+
await runRoute(fullRoute, ex);
|
|
431
|
+
|
|
432
|
+
// After stop: body is the String-coerced JSON-stringified object
|
|
433
|
+
// setBody(constant({amount:100,currency:'USD'})) → marshal → unmarshal → convertBodyTo String → stop
|
|
434
|
+
// After convertBodyTo String, body = '[object Object]' because we used constant({...})
|
|
435
|
+
// then marshal → '{"amount":100,"currency":"USD"}' → unmarshal → {amount:100,currency:'USD'} → convertBodyTo String → '[object Object]'
|
|
436
|
+
// Actually: after unmarshal we get the object back, then convertBodyTo String → '[object Object]'
|
|
437
|
+
// The important thing is stop() fired and no exception on the exchange
|
|
438
|
+
assert.equal(ex.exception, null, 'stop() should not leave an exception');
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
it('multi-line js expression string survives YAML parse and executes', async () => {
|
|
442
|
+
const yaml = `
|
|
443
|
+
route:
|
|
444
|
+
from:
|
|
445
|
+
uri: direct:multiline-test
|
|
446
|
+
steps:
|
|
447
|
+
- setBody:
|
|
448
|
+
js: |
|
|
449
|
+
const body = exchange.in.body;
|
|
450
|
+
const result = body.items.map(x => x * 2);
|
|
451
|
+
return result;
|
|
452
|
+
`;
|
|
453
|
+
const builder = RouteLoader.loadString(yaml, 'yaml');
|
|
454
|
+
const [route] = builder.getRoutes();
|
|
455
|
+
const ex = makeExchange({ items: [1, 2, 3] });
|
|
456
|
+
await runRoute(route, ex);
|
|
457
|
+
assert.deepEqual(ex.in.body, [2, 4, 6]);
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
// ---------------------------------------------------------------------------
|
|
462
|
+
// loadStream
|
|
463
|
+
// ---------------------------------------------------------------------------
|
|
464
|
+
|
|
465
|
+
describe('RouteLoader.loadStream', () => {
|
|
466
|
+
it('loads YAML from a readable stream (content-sniff)', async () => {
|
|
467
|
+
const { Readable } = await import('node:stream');
|
|
468
|
+
const yaml = `
|
|
469
|
+
route:
|
|
470
|
+
from:
|
|
471
|
+
uri: direct:stream-test
|
|
472
|
+
steps:
|
|
473
|
+
- setBody:
|
|
474
|
+
constant: from-stream
|
|
475
|
+
`;
|
|
476
|
+
const stream = Readable.from([yaml]);
|
|
477
|
+
const builder = await RouteLoader.loadStream(stream);
|
|
478
|
+
const [route] = builder.getRoutes();
|
|
479
|
+
assert.equal(route.fromUri, 'direct:stream-test');
|
|
480
|
+
const ex = makeExchange('original');
|
|
481
|
+
await runRoute(route, ex);
|
|
482
|
+
assert.equal(ex.in.body, 'from-stream');
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
it('loads JSON from a readable stream (content-sniff)', async () => {
|
|
486
|
+
const { Readable } = await import('node:stream');
|
|
487
|
+
const json = JSON.stringify({
|
|
488
|
+
route: {
|
|
489
|
+
from: {
|
|
490
|
+
uri: 'direct:stream-json-test',
|
|
491
|
+
steps: [{ setBody: { constant: 'json-stream' } }]
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
const stream = Readable.from([json]);
|
|
496
|
+
const builder = await RouteLoader.loadStream(stream);
|
|
497
|
+
const [route] = builder.getRoutes();
|
|
498
|
+
assert.equal(route.fromUri, 'direct:stream-json-test');
|
|
499
|
+
const ex = makeExchange('original');
|
|
500
|
+
await runRoute(route, ex);
|
|
501
|
+
assert.equal(ex.in.body, 'json-stream');
|
|
502
|
+
});
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
// ---------------------------------------------------------------------------
|
|
506
|
+
// loadObject
|
|
507
|
+
// ---------------------------------------------------------------------------
|
|
508
|
+
|
|
509
|
+
describe('RouteLoader.loadObject', () => {
|
|
510
|
+
it('loads a single route from { route: { from: ... } }', async () => {
|
|
511
|
+
const obj = { route: { from: { uri: 'direct:obj-single', steps: [{ setBody: { constant: 'obj-result' } }] } } };
|
|
512
|
+
const builder = RouteLoader.loadObject(obj);
|
|
513
|
+
const [route] = builder.getRoutes();
|
|
514
|
+
assert.equal(route.fromUri, 'direct:obj-single');
|
|
515
|
+
const ex = makeExchange('original');
|
|
516
|
+
await runRoute(route, ex);
|
|
517
|
+
assert.equal(ex.in.body, 'obj-result');
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
it('loads multiple routes from { routes: [...] }', async () => {
|
|
521
|
+
const obj = {
|
|
522
|
+
routes: [
|
|
523
|
+
{ route: { from: { uri: 'direct:obj-a', steps: [{ setBody: { constant: 'a' } }] } } },
|
|
524
|
+
{ route: { from: { uri: 'direct:obj-b', steps: [{ setBody: { constant: 'b' } }] } } },
|
|
525
|
+
]
|
|
526
|
+
};
|
|
527
|
+
const builder = RouteLoader.loadObject(obj);
|
|
528
|
+
const routes = builder.getRoutes();
|
|
529
|
+
assert.equal(routes.length, 2);
|
|
530
|
+
assert.equal(routes[0].fromUri, 'direct:obj-a');
|
|
531
|
+
assert.equal(routes[1].fromUri, 'direct:obj-b');
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
it('loads routes from a bare array', async () => {
|
|
535
|
+
const obj = [
|
|
536
|
+
{ route: { from: { uri: 'direct:arr-1', steps: [] } } },
|
|
537
|
+
{ route: { from: { uri: 'direct:arr-2', steps: [] } } },
|
|
538
|
+
];
|
|
539
|
+
const builder = RouteLoader.loadObject(obj);
|
|
540
|
+
assert.equal(builder.getRoutes().length, 2);
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
it('loads a bare single route { from: { uri, steps } }', async () => {
|
|
544
|
+
const obj = { from: { uri: 'direct:bare', steps: [{ setBody: { constant: 'bare' } }] } };
|
|
545
|
+
const builder = RouteLoader.loadObject(obj);
|
|
546
|
+
const [route] = builder.getRoutes();
|
|
547
|
+
assert.equal(route.fromUri, 'direct:bare');
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
it('throws on null input', () => {
|
|
551
|
+
assert.throws(() => RouteLoader.loadObject(null), /must be a non-null object/);
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
it('throws on non-object input', () => {
|
|
555
|
+
assert.throws(() => RouteLoader.loadObject('yaml string'), /expected object/);
|
|
556
|
+
});
|
|
557
|
+
});
|