ultravisor-beacon-capability 1.0.0

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.
Files changed (38) hide show
  1. package/README.md +106 -0
  2. package/docs/.nojekyll +0 -0
  3. package/docs/README.md +103 -0
  4. package/docs/_brand.json +18 -0
  5. package/docs/_cover.md +13 -0
  6. package/docs/_sidebar.md +31 -0
  7. package/docs/_topbar.md +5 -0
  8. package/docs/_version.json +7 -0
  9. package/docs/api/README.md +44 -0
  10. package/docs/api/action-convention.md +148 -0
  11. package/docs/api/add-action.md +68 -0
  12. package/docs/api/beacon-capability.md +89 -0
  13. package/docs/api/build-action-map.md +88 -0
  14. package/docs/api/connect.md +81 -0
  15. package/docs/api/disconnect.md +50 -0
  16. package/docs/api/is-connected.md +33 -0
  17. package/docs/api/lifecycle-hooks.md +115 -0
  18. package/docs/architecture.md +237 -0
  19. package/docs/css/docuserve.css +327 -0
  20. package/docs/examples/README.md +58 -0
  21. package/docs/examples/certificate-expiry-monitor.md +212 -0
  22. package/docs/examples/docker-container-management.md +265 -0
  23. package/docs/examples/log-archive-and-upload.md +214 -0
  24. package/docs/examples/log-file-cleanup.md +199 -0
  25. package/docs/examples/mysql-maintenance.md +253 -0
  26. package/docs/examples/postgresql-aggregation.md +247 -0
  27. package/docs/examples/rest-api-health-check.md +213 -0
  28. package/docs/examples/rest-endpoint-sync.md +240 -0
  29. package/docs/examples/server-metrics-collection.md +199 -0
  30. package/docs/examples/shell-commands.md +176 -0
  31. package/docs/index.html +39 -0
  32. package/docs/quickstart.md +199 -0
  33. package/docs/retold-catalog.json +85 -0
  34. package/docs/retold-keyword-index.json +10642 -0
  35. package/package.json +33 -0
  36. package/source/Ultravisor-Beacon-Capability-ActionMap.cjs +132 -0
  37. package/source/Ultravisor-Beacon-Capability.cjs +276 -0
  38. package/test/Ultravisor-Beacon-Capability_tests.js +744 -0
@@ -0,0 +1,744 @@
1
+ /**
2
+ * Unit tests for ultravisor-beacon-capability
3
+ *
4
+ * @license MIT
5
+ */
6
+
7
+ const libAssert = require('assert');
8
+ const libFable = require('fable');
9
+
10
+ const libActionMap = require('../source/Ultravisor-Beacon-Capability-ActionMap.cjs');
11
+ const libBeaconCapability = require('../source/Ultravisor-Beacon-Capability.cjs');
12
+
13
+ suite
14
+ (
15
+ 'Ultravisor Beacon Capability',
16
+ () =>
17
+ {
18
+ // ============================================================
19
+ // ActionMap Discovery
20
+ // ============================================================
21
+ suite
22
+ (
23
+ 'ActionMap Discovery',
24
+ () =>
25
+ {
26
+ test
27
+ (
28
+ 'Should discover action methods on a class prototype',
29
+ (fDone) =>
30
+ {
31
+ class TestCapability extends libBeaconCapability
32
+ {
33
+ constructor(pFable, pOptions, pServiceHash)
34
+ {
35
+ super(pFable, pOptions, pServiceHash);
36
+ this.capabilityName = 'Test';
37
+ }
38
+
39
+ actionDoWork(pSettings, pWorkItem, fCallback)
40
+ {
41
+ return fCallback(null, { Outputs: { Done: true } });
42
+ }
43
+ }
44
+
45
+ let tmpFable = new libFable({});
46
+ let tmpCap = new TestCapability(tmpFable, {}, 'TestCap');
47
+ let tmpMap = libActionMap.buildActionMap(tmpCap);
48
+
49
+ libAssert.strictEqual(Object.keys(tmpMap).length, 1, 'Should discover one action');
50
+ libAssert.ok(tmpMap.DoWork, 'Action name should be "DoWork"');
51
+ libAssert.strictEqual(typeof tmpMap.DoWork.Handler, 'function', 'Handler should be a function');
52
+ libAssert.strictEqual(tmpMap.DoWork.Description, '', 'Missing description should default to empty string');
53
+ libAssert.deepStrictEqual(tmpMap.DoWork.Schema || tmpMap.DoWork.SettingsSchema, [], 'Missing schema should default to empty array');
54
+
55
+ return fDone();
56
+ }
57
+ );
58
+
59
+ test
60
+ (
61
+ 'Should resolve _Schema and _Description companions (getters)',
62
+ (fDone) =>
63
+ {
64
+ class TestCapability extends libBeaconCapability
65
+ {
66
+ constructor(pFable, pOptions, pServiceHash)
67
+ {
68
+ super(pFable, pOptions, pServiceHash);
69
+ this.capabilityName = 'Test';
70
+ }
71
+
72
+ get actionTransform_Description()
73
+ {
74
+ return 'Transform some data';
75
+ }
76
+
77
+ get actionTransform_Schema()
78
+ {
79
+ return [
80
+ { Name: 'InputData', DataType: 'Object', Required: true },
81
+ { Name: 'Format', DataType: 'String', Required: false }
82
+ ];
83
+ }
84
+
85
+ actionTransform(pSettings, pWorkItem, fCallback)
86
+ {
87
+ return fCallback(null, { Outputs: { Transformed: true } });
88
+ }
89
+ }
90
+
91
+ let tmpFable = new libFable({});
92
+ let tmpCap = new TestCapability(tmpFable, {}, 'TestCap');
93
+ let tmpMap = libActionMap.buildActionMap(tmpCap);
94
+
95
+ libAssert.strictEqual(tmpMap.Transform.Description, 'Transform some data');
96
+ libAssert.strictEqual(tmpMap.Transform.SettingsSchema.length, 2);
97
+ libAssert.strictEqual(tmpMap.Transform.SettingsSchema[0].Name, 'InputData');
98
+ libAssert.strictEqual(tmpMap.Transform.SettingsSchema[1].Name, 'Format');
99
+
100
+ return fDone();
101
+ }
102
+ );
103
+
104
+ test
105
+ (
106
+ 'Should discover multiple actions',
107
+ (fDone) =>
108
+ {
109
+ class TestCapability extends libBeaconCapability
110
+ {
111
+ constructor(pFable, pOptions, pServiceHash)
112
+ {
113
+ super(pFable, pOptions, pServiceHash);
114
+ this.capabilityName = 'Multi';
115
+ }
116
+
117
+ actionAlpha(pSettings, pWorkItem, fCallback) { return fCallback(null, {}); }
118
+ actionBeta(pSettings, pWorkItem, fCallback) { return fCallback(null, {}); }
119
+ actionGamma(pSettings, pWorkItem, fCallback) { return fCallback(null, {}); }
120
+ }
121
+
122
+ let tmpFable = new libFable({});
123
+ let tmpCap = new TestCapability(tmpFable, {}, 'TestCap');
124
+ let tmpMap = libActionMap.buildActionMap(tmpCap);
125
+
126
+ libAssert.strictEqual(Object.keys(tmpMap).length, 3);
127
+ libAssert.ok(tmpMap.Alpha);
128
+ libAssert.ok(tmpMap.Beta);
129
+ libAssert.ok(tmpMap.Gamma);
130
+
131
+ return fDone();
132
+ }
133
+ );
134
+
135
+ test
136
+ (
137
+ 'Should walk prototype chain (multi-level inheritance)',
138
+ (fDone) =>
139
+ {
140
+ class BaseCapability extends libBeaconCapability
141
+ {
142
+ constructor(pFable, pOptions, pServiceHash)
143
+ {
144
+ super(pFable, pOptions, pServiceHash);
145
+ this.capabilityName = 'Chain';
146
+ }
147
+
148
+ actionBaseAction(pSettings, pWorkItem, fCallback)
149
+ {
150
+ return fCallback(null, { Outputs: { Source: 'base' } });
151
+ }
152
+ }
153
+
154
+ class DerivedCapability extends BaseCapability
155
+ {
156
+ actionDerivedAction(pSettings, pWorkItem, fCallback)
157
+ {
158
+ return fCallback(null, { Outputs: { Source: 'derived' } });
159
+ }
160
+ }
161
+
162
+ let tmpFable = new libFable({});
163
+ let tmpCap = new DerivedCapability(tmpFable, {}, 'TestCap');
164
+ let tmpMap = libActionMap.buildActionMap(tmpCap);
165
+
166
+ libAssert.strictEqual(Object.keys(tmpMap).length, 2);
167
+ libAssert.ok(tmpMap.BaseAction, 'Should discover base class action');
168
+ libAssert.ok(tmpMap.DerivedAction, 'Should discover derived class action');
169
+
170
+ return fDone();
171
+ }
172
+ );
173
+
174
+ test
175
+ (
176
+ 'Subclass overrides should win over base class',
177
+ (fDone) =>
178
+ {
179
+ class BaseCapability extends libBeaconCapability
180
+ {
181
+ constructor(pFable, pOptions, pServiceHash)
182
+ {
183
+ super(pFable, pOptions, pServiceHash);
184
+ this.capabilityName = 'Override';
185
+ }
186
+
187
+ get actionDoWork_Description() { return 'base description'; }
188
+ actionDoWork(pSettings, pWorkItem, fCallback)
189
+ {
190
+ return fCallback(null, { Outputs: { Source: 'base' } });
191
+ }
192
+ }
193
+
194
+ class DerivedCapability extends BaseCapability
195
+ {
196
+ get actionDoWork_Description() { return 'derived description'; }
197
+ actionDoWork(pSettings, pWorkItem, fCallback)
198
+ {
199
+ return fCallback(null, { Outputs: { Source: 'derived' } });
200
+ }
201
+ }
202
+
203
+ let tmpFable = new libFable({});
204
+ let tmpCap = new DerivedCapability(tmpFable, {}, 'TestCap');
205
+ let tmpMap = libActionMap.buildActionMap(tmpCap);
206
+
207
+ libAssert.strictEqual(Object.keys(tmpMap).length, 1);
208
+ libAssert.strictEqual(tmpMap.DoWork.Description, 'derived description');
209
+
210
+ // Verify handler calls the derived version
211
+ let tmpWorkItem = { Settings: {} };
212
+ tmpMap.DoWork.Handler(tmpWorkItem, {}, (pError, pResult) =>
213
+ {
214
+ libAssert.strictEqual(pResult.Outputs.Source, 'derived');
215
+ return fDone();
216
+ });
217
+ }
218
+ );
219
+
220
+ test
221
+ (
222
+ 'Should not discover non-action methods or companion properties',
223
+ (fDone) =>
224
+ {
225
+ class TestCapability extends libBeaconCapability
226
+ {
227
+ constructor(pFable, pOptions, pServiceHash)
228
+ {
229
+ super(pFable, pOptions, pServiceHash);
230
+ this.capabilityName = 'Filter';
231
+ }
232
+
233
+ // This IS an action
234
+ actionValidAction(pSettings, pWorkItem, fCallback)
235
+ {
236
+ return fCallback(null, {});
237
+ }
238
+
239
+ // These are NOT actions
240
+ get actionValidAction_Schema() { return []; }
241
+ get actionValidAction_Description() { return 'valid'; }
242
+ helperMethod() { return 'not an action'; }
243
+ someOtherFunction() { return 'also not'; }
244
+ }
245
+
246
+ let tmpFable = new libFable({});
247
+ let tmpCap = new TestCapability(tmpFable, {}, 'TestCap');
248
+ let tmpMap = libActionMap.buildActionMap(tmpCap);
249
+
250
+ libAssert.strictEqual(Object.keys(tmpMap).length, 1, 'Should only discover actionValidAction');
251
+ libAssert.ok(tmpMap.ValidAction);
252
+
253
+ return fDone();
254
+ }
255
+ );
256
+ }
257
+ );
258
+
259
+ // ============================================================
260
+ // Handler Delegation
261
+ // ============================================================
262
+ suite
263
+ (
264
+ 'Handler Delegation',
265
+ () =>
266
+ {
267
+ test
268
+ (
269
+ 'Handler should pre-extract pSettings from pWorkItem.Settings',
270
+ (fDone) =>
271
+ {
272
+ let tmpReceivedSettings = null;
273
+
274
+ class TestCapability extends libBeaconCapability
275
+ {
276
+ constructor(pFable, pOptions, pServiceHash)
277
+ {
278
+ super(pFable, pOptions, pServiceHash);
279
+ this.capabilityName = 'Extract';
280
+ }
281
+
282
+ actionProcess(pSettings, pWorkItem, fCallback)
283
+ {
284
+ tmpReceivedSettings = pSettings;
285
+ return fCallback(null, { Outputs: {} });
286
+ }
287
+ }
288
+
289
+ let tmpFable = new libFable({});
290
+ let tmpCap = new TestCapability(tmpFable, {}, 'TestCap');
291
+ let tmpMap = libActionMap.buildActionMap(tmpCap);
292
+
293
+ let tmpWorkItem = { Settings: { Name: 'test', Value: 42 } };
294
+ tmpMap.Process.Handler(tmpWorkItem, {}, (pError) =>
295
+ {
296
+ libAssert.strictEqual(tmpReceivedSettings.Name, 'test');
297
+ libAssert.strictEqual(tmpReceivedSettings.Value, 42);
298
+ return fDone();
299
+ });
300
+ }
301
+ );
302
+
303
+ test
304
+ (
305
+ 'Handler should default to empty object when Settings is missing',
306
+ (fDone) =>
307
+ {
308
+ let tmpReceivedSettings = null;
309
+
310
+ class TestCapability extends libBeaconCapability
311
+ {
312
+ constructor(pFable, pOptions, pServiceHash)
313
+ {
314
+ super(pFable, pOptions, pServiceHash);
315
+ this.capabilityName = 'Empty';
316
+ }
317
+
318
+ actionProcess(pSettings, pWorkItem, fCallback)
319
+ {
320
+ tmpReceivedSettings = pSettings;
321
+ return fCallback(null, { Outputs: {} });
322
+ }
323
+ }
324
+
325
+ let tmpFable = new libFable({});
326
+ let tmpCap = new TestCapability(tmpFable, {}, 'TestCap');
327
+ let tmpMap = libActionMap.buildActionMap(tmpCap);
328
+
329
+ tmpMap.Process.Handler({}, {}, (pError) =>
330
+ {
331
+ libAssert.deepStrictEqual(tmpReceivedSettings, {});
332
+ return fDone();
333
+ });
334
+ }
335
+ );
336
+
337
+ test
338
+ (
339
+ 'Handler should preserve this context (access to instance members)',
340
+ (fDone) =>
341
+ {
342
+ class TestCapability extends libBeaconCapability
343
+ {
344
+ constructor(pFable, pOptions, pServiceHash)
345
+ {
346
+ super(pFable, pOptions, pServiceHash);
347
+ this.capabilityName = 'Context';
348
+ this._ConnectionString = 'mysql://localhost/test';
349
+ }
350
+
351
+ actionQuery(pSettings, pWorkItem, fCallback)
352
+ {
353
+ return fCallback(null, { Outputs: { ConnectionUsed: this._ConnectionString } });
354
+ }
355
+ }
356
+
357
+ let tmpFable = new libFable({});
358
+ let tmpCap = new TestCapability(tmpFable, {}, 'TestCap');
359
+ let tmpMap = libActionMap.buildActionMap(tmpCap);
360
+
361
+ tmpMap.Query.Handler({ Settings: {} }, {}, (pError, pResult) =>
362
+ {
363
+ libAssert.strictEqual(pResult.Outputs.ConnectionUsed, 'mysql://localhost/test');
364
+ return fDone();
365
+ });
366
+ }
367
+ );
368
+
369
+ test
370
+ (
371
+ 'Handler should pass fReportProgress to action method',
372
+ (fDone) =>
373
+ {
374
+ let tmpProgressReceived = false;
375
+
376
+ class TestCapability extends libBeaconCapability
377
+ {
378
+ constructor(pFable, pOptions, pServiceHash)
379
+ {
380
+ super(pFable, pOptions, pServiceHash);
381
+ this.capabilityName = 'Progress';
382
+ }
383
+
384
+ actionLongTask(pSettings, pWorkItem, fCallback, fReportProgress)
385
+ {
386
+ fReportProgress({ Percent: 50, Message: 'halfway' });
387
+ return fCallback(null, { Outputs: {} });
388
+ }
389
+ }
390
+
391
+ let tmpFable = new libFable({});
392
+ let tmpCap = new TestCapability(tmpFable, {}, 'TestCap');
393
+ let tmpMap = libActionMap.buildActionMap(tmpCap);
394
+
395
+ let tmpProgressFn = (pData) =>
396
+ {
397
+ tmpProgressReceived = true;
398
+ libAssert.strictEqual(pData.Percent, 50);
399
+ libAssert.strictEqual(pData.Message, 'halfway');
400
+ };
401
+
402
+ tmpMap.LongTask.Handler({ Settings: {} }, {}, (pError) =>
403
+ {
404
+ libAssert.ok(tmpProgressReceived, 'Progress function should have been called');
405
+ return fDone();
406
+ }, tmpProgressFn);
407
+ }
408
+ );
409
+ }
410
+ );
411
+
412
+ // ============================================================
413
+ // Explicit Action Registration
414
+ // ============================================================
415
+ suite
416
+ (
417
+ 'Explicit Action Registration',
418
+ () =>
419
+ {
420
+ test
421
+ (
422
+ 'addAction should register an action',
423
+ (fDone) =>
424
+ {
425
+ let tmpFable = new libFable({});
426
+ let tmpCap = new libBeaconCapability(tmpFable, {}, 'TestCap');
427
+ tmpCap.capabilityName = 'Explicit';
428
+
429
+ tmpCap.addAction('ManualAction',
430
+ {
431
+ Description: 'A manually registered action',
432
+ SettingsSchema: [{ Name: 'Input', DataType: 'String', Required: true }],
433
+ Handler: function (pWorkItem, pContext, fCallback)
434
+ {
435
+ return fCallback(null, { Outputs: { Manual: true } });
436
+ }
437
+ });
438
+
439
+ let tmpActions = tmpCap._buildActions();
440
+
441
+ libAssert.ok(tmpActions.ManualAction, 'Should have ManualAction');
442
+ libAssert.strictEqual(tmpActions.ManualAction.Description, 'A manually registered action');
443
+ libAssert.strictEqual(tmpActions.ManualAction.SettingsSchema.length, 1);
444
+ libAssert.strictEqual(typeof tmpActions.ManualAction.Handler, 'function');
445
+
446
+ return fDone();
447
+ }
448
+ );
449
+
450
+ test
451
+ (
452
+ 'Explicit action should override discovered action on name collision',
453
+ (fDone) =>
454
+ {
455
+ class TestCapability extends libBeaconCapability
456
+ {
457
+ constructor(pFable, pOptions, pServiceHash)
458
+ {
459
+ super(pFable, pOptions, pServiceHash);
460
+ this.capabilityName = 'Collision';
461
+ }
462
+
463
+ get actionDoWork_Description() { return 'discovered'; }
464
+ actionDoWork(pSettings, pWorkItem, fCallback)
465
+ {
466
+ return fCallback(null, { Outputs: { Source: 'discovered' } });
467
+ }
468
+ }
469
+
470
+ let tmpFable = new libFable({});
471
+ let tmpCap = new TestCapability(tmpFable, {}, 'TestCap');
472
+
473
+ tmpCap.addAction('DoWork',
474
+ {
475
+ Description: 'explicit override',
476
+ Handler: function (pWorkItem, pContext, fCallback)
477
+ {
478
+ return fCallback(null, { Outputs: { Source: 'explicit' } });
479
+ }
480
+ });
481
+
482
+ let tmpActions = tmpCap._buildActions();
483
+
484
+ libAssert.strictEqual(tmpActions.DoWork.Description, 'explicit override');
485
+
486
+ return fDone();
487
+ }
488
+ );
489
+
490
+ test
491
+ (
492
+ 'addAction should reject missing name or handler',
493
+ (fDone) =>
494
+ {
495
+ let tmpFable = new libFable({});
496
+ let tmpCap = new libBeaconCapability(tmpFable, {}, 'TestCap');
497
+
498
+ // Missing name
499
+ tmpCap.addAction('', { Handler: () => {} });
500
+ libAssert.strictEqual(Object.keys(tmpCap._ExplicitActions).length, 0);
501
+
502
+ // Missing handler
503
+ tmpCap.addAction('NoHandler', { Description: 'oops' });
504
+ libAssert.strictEqual(Object.keys(tmpCap._ExplicitActions).length, 0);
505
+
506
+ return fDone();
507
+ }
508
+ );
509
+ }
510
+ );
511
+
512
+ // ============================================================
513
+ // Capability Descriptor Shape
514
+ // ============================================================
515
+ suite
516
+ (
517
+ 'Descriptor Building',
518
+ () =>
519
+ {
520
+ test
521
+ (
522
+ 'connect should fail without ServerURL',
523
+ (fDone) =>
524
+ {
525
+ let tmpFable = new libFable({});
526
+ let tmpCap = new libBeaconCapability(tmpFable, {}, 'TestCap');
527
+ tmpCap.capabilityName = 'Test';
528
+
529
+ tmpCap.connect({}, (pError) =>
530
+ {
531
+ libAssert.ok(pError, 'Should return an error');
532
+ libAssert.ok(pError.message.includes('ServerURL'));
533
+ return fDone();
534
+ });
535
+ }
536
+ );
537
+
538
+ test
539
+ (
540
+ 'connect should fail without capabilityName',
541
+ (fDone) =>
542
+ {
543
+ let tmpFable = new libFable({});
544
+ let tmpCap = new libBeaconCapability(tmpFable, {}, 'TestCap');
545
+
546
+ tmpCap.connect({ ServerURL: 'http://localhost:54321' }, (pError) =>
547
+ {
548
+ libAssert.ok(pError, 'Should return an error');
549
+ libAssert.ok(pError.message.includes('capabilityName'));
550
+ return fDone();
551
+ });
552
+ }
553
+ );
554
+
555
+ test
556
+ (
557
+ '_buildActions should produce correct descriptor shape',
558
+ (fDone) =>
559
+ {
560
+ class TestCapability extends libBeaconCapability
561
+ {
562
+ constructor(pFable, pOptions, pServiceHash)
563
+ {
564
+ super(pFable, pOptions, pServiceHash);
565
+ this.capabilityName = 'ShapeTest';
566
+ }
567
+
568
+ get actionCreate_Description() { return 'Create a record'; }
569
+ get actionCreate_Schema()
570
+ {
571
+ return [
572
+ { Name: 'Name', DataType: 'String', Required: true }
573
+ ];
574
+ }
575
+ actionCreate(pSettings, pWorkItem, fCallback)
576
+ {
577
+ return fCallback(null, { Outputs: {} });
578
+ }
579
+
580
+ actionDelete(pSettings, pWorkItem, fCallback)
581
+ {
582
+ return fCallback(null, { Outputs: {} });
583
+ }
584
+ }
585
+
586
+ let tmpFable = new libFable({});
587
+ let tmpCap = new TestCapability(tmpFable, {}, 'TestCap');
588
+ let tmpActions = tmpCap._buildActions();
589
+
590
+ // Verify shape matches what CapabilityManager.registerCapability expects
591
+ libAssert.ok(tmpActions.Create, 'Should have Create action');
592
+ libAssert.ok(tmpActions.Delete, 'Should have Delete action');
593
+
594
+ // Create action should have full metadata
595
+ libAssert.strictEqual(tmpActions.Create.Description, 'Create a record');
596
+ libAssert.ok(Array.isArray(tmpActions.Create.SettingsSchema));
597
+ libAssert.strictEqual(tmpActions.Create.SettingsSchema[0].Name, 'Name');
598
+ libAssert.strictEqual(typeof tmpActions.Create.Handler, 'function');
599
+
600
+ // Delete action should have defaults
601
+ libAssert.strictEqual(tmpActions.Delete.Description, '');
602
+ libAssert.deepStrictEqual(tmpActions.Delete.SettingsSchema, []);
603
+ libAssert.strictEqual(typeof tmpActions.Delete.Handler, 'function');
604
+
605
+ return fDone();
606
+ }
607
+ );
608
+ }
609
+ );
610
+
611
+ // ============================================================
612
+ // Lifecycle
613
+ // ============================================================
614
+ suite
615
+ (
616
+ 'Lifecycle',
617
+ () =>
618
+ {
619
+ test
620
+ (
621
+ 'Default onInitialize and onShutdown should be no-ops',
622
+ (fDone) =>
623
+ {
624
+ let tmpFable = new libFable({});
625
+ let tmpCap = new libBeaconCapability(tmpFable, {}, 'TestCap');
626
+
627
+ tmpCap.onInitialize((pInitError) =>
628
+ {
629
+ libAssert.ifError(pInitError);
630
+ tmpCap.onShutdown((pShutdownError) =>
631
+ {
632
+ libAssert.ifError(pShutdownError);
633
+ return fDone();
634
+ });
635
+ });
636
+ }
637
+ );
638
+
639
+ test
640
+ (
641
+ 'Custom onInitialize and onShutdown should be called',
642
+ (fDone) =>
643
+ {
644
+ let tmpInitCalled = false;
645
+ let tmpShutdownCalled = false;
646
+
647
+ class TestCapability extends libBeaconCapability
648
+ {
649
+ constructor(pFable, pOptions, pServiceHash)
650
+ {
651
+ super(pFable, pOptions, pServiceHash);
652
+ this.capabilityName = 'Lifecycle';
653
+ }
654
+
655
+ onInitialize(fCallback)
656
+ {
657
+ tmpInitCalled = true;
658
+ return fCallback(null);
659
+ }
660
+
661
+ onShutdown(fCallback)
662
+ {
663
+ tmpShutdownCalled = true;
664
+ return fCallback(null);
665
+ }
666
+ }
667
+
668
+ let tmpFable = new libFable({});
669
+ let tmpCap = new TestCapability(tmpFable, {}, 'TestCap');
670
+
671
+ tmpCap.onInitialize((pInitError) =>
672
+ {
673
+ libAssert.ok(tmpInitCalled, 'onInitialize should have been called');
674
+ tmpCap.onShutdown((pShutdownError) =>
675
+ {
676
+ libAssert.ok(tmpShutdownCalled, 'onShutdown should have been called');
677
+ return fDone();
678
+ });
679
+ });
680
+ }
681
+ );
682
+
683
+ test
684
+ (
685
+ 'isConnected should return false when not connected',
686
+ (fDone) =>
687
+ {
688
+ let tmpFable = new libFable({});
689
+ let tmpCap = new libBeaconCapability(tmpFable, {}, 'TestCap');
690
+
691
+ libAssert.strictEqual(tmpCap.isConnected(), false);
692
+
693
+ return fDone();
694
+ }
695
+ );
696
+
697
+ test
698
+ (
699
+ 'disconnect should be safe to call when not connected',
700
+ (fDone) =>
701
+ {
702
+ let tmpFable = new libFable({});
703
+ let tmpCap = new libBeaconCapability(tmpFable, {}, 'TestCap');
704
+
705
+ tmpCap.disconnect((pError) =>
706
+ {
707
+ libAssert.ifError(pError);
708
+ return fDone();
709
+ });
710
+ }
711
+ );
712
+ }
713
+ );
714
+
715
+ // ============================================================
716
+ // Module Export
717
+ // ============================================================
718
+ suite
719
+ (
720
+ 'Module Export',
721
+ () =>
722
+ {
723
+ test
724
+ (
725
+ 'Module should export the base class',
726
+ (fDone) =>
727
+ {
728
+ libAssert.ok(libBeaconCapability, 'Module should export');
729
+ libAssert.strictEqual(typeof libBeaconCapability, 'function', 'Export should be a constructor');
730
+
731
+ let tmpFable = new libFable({});
732
+ let tmpCap = new libBeaconCapability(tmpFable, {}, 'TestCap');
733
+
734
+ libAssert.strictEqual(tmpCap.serviceType, 'UltravisorBeaconCapability');
735
+ libAssert.strictEqual(tmpCap.capabilityName, '');
736
+ libAssert.strictEqual(tmpCap.providerName, '');
737
+
738
+ return fDone();
739
+ }
740
+ );
741
+ }
742
+ );
743
+ }
744
+ );