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.
- package/README.md +106 -0
- package/docs/.nojekyll +0 -0
- package/docs/README.md +103 -0
- package/docs/_brand.json +18 -0
- package/docs/_cover.md +13 -0
- package/docs/_sidebar.md +31 -0
- package/docs/_topbar.md +5 -0
- package/docs/_version.json +7 -0
- package/docs/api/README.md +44 -0
- package/docs/api/action-convention.md +148 -0
- package/docs/api/add-action.md +68 -0
- package/docs/api/beacon-capability.md +89 -0
- package/docs/api/build-action-map.md +88 -0
- package/docs/api/connect.md +81 -0
- package/docs/api/disconnect.md +50 -0
- package/docs/api/is-connected.md +33 -0
- package/docs/api/lifecycle-hooks.md +115 -0
- package/docs/architecture.md +237 -0
- package/docs/css/docuserve.css +327 -0
- package/docs/examples/README.md +58 -0
- package/docs/examples/certificate-expiry-monitor.md +212 -0
- package/docs/examples/docker-container-management.md +265 -0
- package/docs/examples/log-archive-and-upload.md +214 -0
- package/docs/examples/log-file-cleanup.md +199 -0
- package/docs/examples/mysql-maintenance.md +253 -0
- package/docs/examples/postgresql-aggregation.md +247 -0
- package/docs/examples/rest-api-health-check.md +213 -0
- package/docs/examples/rest-endpoint-sync.md +240 -0
- package/docs/examples/server-metrics-collection.md +199 -0
- package/docs/examples/shell-commands.md +176 -0
- package/docs/index.html +39 -0
- package/docs/quickstart.md +199 -0
- package/docs/retold-catalog.json +85 -0
- package/docs/retold-keyword-index.json +10642 -0
- package/package.json +33 -0
- package/source/Ultravisor-Beacon-Capability-ActionMap.cjs +132 -0
- package/source/Ultravisor-Beacon-Capability.cjs +276 -0
- 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
|
+
);
|