ultravisor-beacon 0.0.1
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/package.json +30 -0
- package/source/Ultravisor-Beacon-CLI.cjs +143 -0
- package/source/Ultravisor-Beacon-CapabilityAdapter.cjs +116 -0
- package/source/Ultravisor-Beacon-CapabilityManager.cjs +132 -0
- package/source/Ultravisor-Beacon-CapabilityProvider.cjs +129 -0
- package/source/Ultravisor-Beacon-Client.cjs +568 -0
- package/source/Ultravisor-Beacon-ConnectivityHTTP.cjs +52 -0
- package/source/Ultravisor-Beacon-Executor.cjs +500 -0
- package/source/Ultravisor-Beacon-ProviderRegistry.cjs +330 -0
- package/source/Ultravisor-Beacon-Service.cjs +288 -0
- package/source/providers/Ultravisor-Beacon-Provider-FileSystem.cjs +331 -0
- package/source/providers/Ultravisor-Beacon-Provider-LLM.cjs +966 -0
- package/source/providers/Ultravisor-Beacon-Provider-Shell.cjs +95 -0
- package/test/Ultravisor-Beacon-Service_tests.js +608 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ultravisor Beacon Provider — Shell
|
|
3
|
+
*
|
|
4
|
+
* Built-in provider that executes shell commands via child_process.exec().
|
|
5
|
+
*
|
|
6
|
+
* Capability: 'Shell'
|
|
7
|
+
* Actions: 'Execute' — run a shell command with optional parameters.
|
|
8
|
+
*
|
|
9
|
+
* Provider config:
|
|
10
|
+
* MaxBufferBytes {number} — max stdout/stderr buffer (default: 10MB)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const libChildProcess = require('child_process');
|
|
14
|
+
|
|
15
|
+
const libBeaconCapabilityProvider = require('../Ultravisor-Beacon-CapabilityProvider.cjs');
|
|
16
|
+
|
|
17
|
+
class UltravisorBeaconProviderShell extends libBeaconCapabilityProvider
|
|
18
|
+
{
|
|
19
|
+
constructor(pProviderConfig)
|
|
20
|
+
{
|
|
21
|
+
super(pProviderConfig);
|
|
22
|
+
|
|
23
|
+
this.Name = 'Shell';
|
|
24
|
+
this.Capability = 'Shell';
|
|
25
|
+
|
|
26
|
+
this._MaxBufferBytes = this._ProviderConfig.MaxBufferBytes || 10485760;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get actions()
|
|
30
|
+
{
|
|
31
|
+
return {
|
|
32
|
+
'Execute':
|
|
33
|
+
{
|
|
34
|
+
Description: 'Execute a shell command.',
|
|
35
|
+
SettingsSchema:
|
|
36
|
+
[
|
|
37
|
+
{ Name: 'Command', DataType: 'String', Required: true, Description: 'The command to run' },
|
|
38
|
+
{ Name: 'Parameters', DataType: 'String', Required: false, Description: 'Command-line arguments' }
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
execute(pAction, pWorkItem, pContext, fCallback, fReportProgress)
|
|
45
|
+
{
|
|
46
|
+
let tmpSettings = pWorkItem.Settings || {};
|
|
47
|
+
let tmpCommand = tmpSettings.Command || '';
|
|
48
|
+
let tmpParameters = tmpSettings.Parameters || '';
|
|
49
|
+
|
|
50
|
+
if (!tmpCommand)
|
|
51
|
+
{
|
|
52
|
+
return fCallback(null, {
|
|
53
|
+
Outputs: { StdOut: 'No command specified.', ExitCode: -1, Result: '' },
|
|
54
|
+
Log: ['Shell Provider: no command specified.']
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let tmpFullCommand = tmpParameters ? (tmpCommand + ' ' + tmpParameters) : tmpCommand;
|
|
59
|
+
let tmpTimeout = pWorkItem.TimeoutMs || 300000;
|
|
60
|
+
|
|
61
|
+
console.log(` [Shell] Running: ${tmpFullCommand}`);
|
|
62
|
+
|
|
63
|
+
libChildProcess.exec(tmpFullCommand,
|
|
64
|
+
{
|
|
65
|
+
cwd: pContext.StagingPath || process.cwd(),
|
|
66
|
+
timeout: tmpTimeout,
|
|
67
|
+
maxBuffer: this._MaxBufferBytes
|
|
68
|
+
},
|
|
69
|
+
function (pError, pStdOut, pStdErr)
|
|
70
|
+
{
|
|
71
|
+
if (pError)
|
|
72
|
+
{
|
|
73
|
+
return fCallback(null, {
|
|
74
|
+
Outputs: {
|
|
75
|
+
StdOut: (pStdOut || '') + (pStdErr || ''),
|
|
76
|
+
ExitCode: pError.code || 1,
|
|
77
|
+
Result: ''
|
|
78
|
+
},
|
|
79
|
+
Log: [`Command failed: ${pError.message}`, pStdErr || ''].filter(Boolean)
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return fCallback(null, {
|
|
84
|
+
Outputs: {
|
|
85
|
+
StdOut: pStdOut || '',
|
|
86
|
+
ExitCode: 0,
|
|
87
|
+
Result: pStdOut || ''
|
|
88
|
+
},
|
|
89
|
+
Log: [`Command executed: ${tmpFullCommand}`]
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
module.exports = UltravisorBeaconProviderShell;
|
|
@@ -0,0 +1,608 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ultravisor Beacon Service Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests the Fable service layer: CapabilityManager, CapabilityAdapter,
|
|
5
|
+
* ConnectivityHTTP, and the main BeaconService.
|
|
6
|
+
*
|
|
7
|
+
* NOTE: These tests do NOT require a running Ultravisor server.
|
|
8
|
+
* They verify the service registration, capability management, and
|
|
9
|
+
* adapter bridging logic in isolation.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const libAssert = require('assert');
|
|
13
|
+
|
|
14
|
+
const libCapabilityProvider = require('../source/Ultravisor-Beacon-CapabilityProvider.cjs');
|
|
15
|
+
const libCapabilityAdapter = require('../source/Ultravisor-Beacon-CapabilityAdapter.cjs');
|
|
16
|
+
const libCapabilityManager = require('../source/Ultravisor-Beacon-CapabilityManager.cjs');
|
|
17
|
+
const libConnectivityHTTP = require('../source/Ultravisor-Beacon-ConnectivityHTTP.cjs');
|
|
18
|
+
const libProviderRegistry = require('../source/Ultravisor-Beacon-ProviderRegistry.cjs');
|
|
19
|
+
|
|
20
|
+
// We can require the service without Fable for standalone testing
|
|
21
|
+
const libBeaconService = require('../source/Ultravisor-Beacon-Service.cjs');
|
|
22
|
+
|
|
23
|
+
suite
|
|
24
|
+
(
|
|
25
|
+
'Ultravisor Beacon',
|
|
26
|
+
function ()
|
|
27
|
+
{
|
|
28
|
+
// ============================================================
|
|
29
|
+
// CapabilityProvider Base Class
|
|
30
|
+
// ============================================================
|
|
31
|
+
suite
|
|
32
|
+
(
|
|
33
|
+
'CapabilityProvider Base',
|
|
34
|
+
function ()
|
|
35
|
+
{
|
|
36
|
+
test
|
|
37
|
+
(
|
|
38
|
+
'Should instantiate with defaults',
|
|
39
|
+
function ()
|
|
40
|
+
{
|
|
41
|
+
let tmpProvider = new libCapabilityProvider();
|
|
42
|
+
libAssert.strictEqual(tmpProvider.Name, 'BaseProvider');
|
|
43
|
+
libAssert.strictEqual(tmpProvider.Capability, 'Unknown');
|
|
44
|
+
libAssert.deepStrictEqual(tmpProvider.actions, {});
|
|
45
|
+
libAssert.deepStrictEqual(tmpProvider.getCapabilities(), ['Unknown']);
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
test
|
|
50
|
+
(
|
|
51
|
+
'Should return error from default execute',
|
|
52
|
+
function (fDone)
|
|
53
|
+
{
|
|
54
|
+
let tmpProvider = new libCapabilityProvider();
|
|
55
|
+
tmpProvider.execute('DoSomething', {}, {}, function (pError)
|
|
56
|
+
{
|
|
57
|
+
libAssert.ok(pError);
|
|
58
|
+
libAssert.ok(pError.message.includes('has not implemented execute()'));
|
|
59
|
+
fDone();
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
test
|
|
65
|
+
(
|
|
66
|
+
'Should have no-op initialize and shutdown',
|
|
67
|
+
function (fDone)
|
|
68
|
+
{
|
|
69
|
+
let tmpProvider = new libCapabilityProvider();
|
|
70
|
+
tmpProvider.initialize(function (pError)
|
|
71
|
+
{
|
|
72
|
+
libAssert.ifError(pError);
|
|
73
|
+
tmpProvider.shutdown(function (pError2)
|
|
74
|
+
{
|
|
75
|
+
libAssert.ifError(pError2);
|
|
76
|
+
fDone();
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
// ============================================================
|
|
85
|
+
// CapabilityAdapter
|
|
86
|
+
// ============================================================
|
|
87
|
+
suite
|
|
88
|
+
(
|
|
89
|
+
'CapabilityAdapter',
|
|
90
|
+
function ()
|
|
91
|
+
{
|
|
92
|
+
test
|
|
93
|
+
(
|
|
94
|
+
'Should create adapter from descriptor',
|
|
95
|
+
function ()
|
|
96
|
+
{
|
|
97
|
+
let tmpAdapter = new libCapabilityAdapter({
|
|
98
|
+
Capability: 'TestCap',
|
|
99
|
+
Name: 'TestProvider',
|
|
100
|
+
actions: {
|
|
101
|
+
'DoThing': {
|
|
102
|
+
Description: 'Does a thing',
|
|
103
|
+
SettingsSchema: [{ Name: 'Input', DataType: 'String' }],
|
|
104
|
+
Handler: function () {}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
libAssert.strictEqual(tmpAdapter.Name, 'TestProvider');
|
|
110
|
+
libAssert.strictEqual(tmpAdapter.Capability, 'TestCap');
|
|
111
|
+
libAssert.deepStrictEqual(tmpAdapter.getCapabilities(), ['TestCap']);
|
|
112
|
+
|
|
113
|
+
let tmpActions = tmpAdapter.actions;
|
|
114
|
+
libAssert.ok(tmpActions.DoThing);
|
|
115
|
+
libAssert.strictEqual(tmpActions.DoThing.Description, 'Does a thing');
|
|
116
|
+
// Handler should not leak into actions
|
|
117
|
+
libAssert.strictEqual(tmpActions.DoThing.Handler, undefined);
|
|
118
|
+
}
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
test
|
|
122
|
+
(
|
|
123
|
+
'Should be an instance of CapabilityProvider',
|
|
124
|
+
function ()
|
|
125
|
+
{
|
|
126
|
+
let tmpAdapter = new libCapabilityAdapter({
|
|
127
|
+
Capability: 'TestCap',
|
|
128
|
+
Name: 'TestProvider',
|
|
129
|
+
actions: {}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
libAssert.ok(tmpAdapter instanceof libCapabilityProvider);
|
|
133
|
+
}
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
test
|
|
137
|
+
(
|
|
138
|
+
'Should execute action via Handler',
|
|
139
|
+
function (fDone)
|
|
140
|
+
{
|
|
141
|
+
let tmpHandlerCalled = false;
|
|
142
|
+
|
|
143
|
+
let tmpAdapter = new libCapabilityAdapter({
|
|
144
|
+
Capability: 'TestCap',
|
|
145
|
+
Name: 'TestProvider',
|
|
146
|
+
actions: {
|
|
147
|
+
'ReadFile': {
|
|
148
|
+
Description: 'Read a file',
|
|
149
|
+
Handler: function (pWorkItem, pContext, fCallback)
|
|
150
|
+
{
|
|
151
|
+
tmpHandlerCalled = true;
|
|
152
|
+
fCallback(null, {
|
|
153
|
+
Outputs: { Content: 'Hello World' },
|
|
154
|
+
Log: ['Read complete']
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
let tmpWorkItem = { WorkItemHash: 'test-123', Settings: { FilePath: '/tmp/test.md' } };
|
|
162
|
+
let tmpContext = { StagingPath: '/tmp' };
|
|
163
|
+
|
|
164
|
+
tmpAdapter.execute('ReadFile', tmpWorkItem, tmpContext, function (pError, pResult)
|
|
165
|
+
{
|
|
166
|
+
libAssert.ifError(pError);
|
|
167
|
+
libAssert.ok(tmpHandlerCalled);
|
|
168
|
+
libAssert.strictEqual(pResult.Outputs.Content, 'Hello World');
|
|
169
|
+
libAssert.strictEqual(pResult.Log[0], 'Read complete');
|
|
170
|
+
fDone();
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
test
|
|
176
|
+
(
|
|
177
|
+
'Should error on unknown action',
|
|
178
|
+
function (fDone)
|
|
179
|
+
{
|
|
180
|
+
let tmpAdapter = new libCapabilityAdapter({
|
|
181
|
+
Capability: 'TestCap',
|
|
182
|
+
Name: 'TestProvider',
|
|
183
|
+
actions: {}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
tmpAdapter.execute('NonExistent', {}, {}, function (pError)
|
|
187
|
+
{
|
|
188
|
+
libAssert.ok(pError);
|
|
189
|
+
libAssert.ok(pError.message.includes('no Handler'));
|
|
190
|
+
fDone();
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
test
|
|
196
|
+
(
|
|
197
|
+
'Should delegate initialize and shutdown',
|
|
198
|
+
function (fDone)
|
|
199
|
+
{
|
|
200
|
+
let tmpInitCalled = false;
|
|
201
|
+
let tmpShutdownCalled = false;
|
|
202
|
+
|
|
203
|
+
let tmpAdapter = new libCapabilityAdapter({
|
|
204
|
+
Capability: 'TestCap',
|
|
205
|
+
Name: 'TestProvider',
|
|
206
|
+
actions: {},
|
|
207
|
+
initialize: function (fCallback)
|
|
208
|
+
{
|
|
209
|
+
tmpInitCalled = true;
|
|
210
|
+
fCallback(null);
|
|
211
|
+
},
|
|
212
|
+
shutdown: function (fCallback)
|
|
213
|
+
{
|
|
214
|
+
tmpShutdownCalled = true;
|
|
215
|
+
fCallback(null);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
tmpAdapter.initialize(function (pError)
|
|
220
|
+
{
|
|
221
|
+
libAssert.ifError(pError);
|
|
222
|
+
libAssert.ok(tmpInitCalled);
|
|
223
|
+
|
|
224
|
+
tmpAdapter.shutdown(function (pError2)
|
|
225
|
+
{
|
|
226
|
+
libAssert.ifError(pError2);
|
|
227
|
+
libAssert.ok(tmpShutdownCalled);
|
|
228
|
+
fDone();
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
test
|
|
235
|
+
(
|
|
236
|
+
'Should catch Handler exceptions',
|
|
237
|
+
function (fDone)
|
|
238
|
+
{
|
|
239
|
+
let tmpAdapter = new libCapabilityAdapter({
|
|
240
|
+
Capability: 'TestCap',
|
|
241
|
+
Name: 'TestProvider',
|
|
242
|
+
actions: {
|
|
243
|
+
'Boom': {
|
|
244
|
+
Description: 'Throws',
|
|
245
|
+
Handler: function ()
|
|
246
|
+
{
|
|
247
|
+
throw new Error('Intentional explosion');
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
tmpAdapter.execute('Boom', {}, {}, function (pError)
|
|
254
|
+
{
|
|
255
|
+
libAssert.ok(pError);
|
|
256
|
+
libAssert.strictEqual(pError.message, 'Intentional explosion');
|
|
257
|
+
fDone();
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
// ============================================================
|
|
265
|
+
// CapabilityManager
|
|
266
|
+
// ============================================================
|
|
267
|
+
suite
|
|
268
|
+
(
|
|
269
|
+
'CapabilityManager',
|
|
270
|
+
function ()
|
|
271
|
+
{
|
|
272
|
+
test
|
|
273
|
+
(
|
|
274
|
+
'Should register and list capabilities',
|
|
275
|
+
function ()
|
|
276
|
+
{
|
|
277
|
+
let tmpManager = new libCapabilityManager();
|
|
278
|
+
|
|
279
|
+
tmpManager.registerCapability({
|
|
280
|
+
Capability: 'ContentSystem',
|
|
281
|
+
Name: 'ContentProvider',
|
|
282
|
+
actions: {
|
|
283
|
+
'ReadFile': { Description: 'Read', Handler: function () {} }
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
tmpManager.registerCapability({
|
|
288
|
+
Capability: 'MediaProcessing',
|
|
289
|
+
Name: 'MediaProvider',
|
|
290
|
+
actions: {
|
|
291
|
+
'GenThumbnail': { Description: 'Thumbnail', Handler: function () {} }
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
let tmpNames = tmpManager.getCapabilityNames();
|
|
296
|
+
libAssert.strictEqual(tmpNames.length, 2);
|
|
297
|
+
libAssert.ok(tmpNames.includes('ContentSystem'));
|
|
298
|
+
libAssert.ok(tmpNames.includes('MediaProcessing'));
|
|
299
|
+
}
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
test
|
|
303
|
+
(
|
|
304
|
+
'Should reject descriptor without Capability',
|
|
305
|
+
function ()
|
|
306
|
+
{
|
|
307
|
+
let tmpManager = new libCapabilityManager();
|
|
308
|
+
let tmpResult = tmpManager.registerCapability({ Name: 'BadProvider' });
|
|
309
|
+
libAssert.strictEqual(tmpResult, false);
|
|
310
|
+
libAssert.strictEqual(tmpManager.getCapabilityNames().length, 0);
|
|
311
|
+
}
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
test
|
|
315
|
+
(
|
|
316
|
+
'Should remove capabilities',
|
|
317
|
+
function ()
|
|
318
|
+
{
|
|
319
|
+
let tmpManager = new libCapabilityManager();
|
|
320
|
+
|
|
321
|
+
tmpManager.registerCapability({
|
|
322
|
+
Capability: 'TestCap',
|
|
323
|
+
actions: { 'Do': { Handler: function () {} } }
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
libAssert.strictEqual(tmpManager.getCapabilityNames().length, 1);
|
|
327
|
+
|
|
328
|
+
tmpManager.removeCapability('TestCap');
|
|
329
|
+
libAssert.strictEqual(tmpManager.getCapabilityNames().length, 0);
|
|
330
|
+
}
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
test
|
|
334
|
+
(
|
|
335
|
+
'Should build provider descriptors as adapter instances',
|
|
336
|
+
function ()
|
|
337
|
+
{
|
|
338
|
+
let tmpManager = new libCapabilityManager();
|
|
339
|
+
|
|
340
|
+
tmpManager.registerCapability({
|
|
341
|
+
Capability: 'ContentSystem',
|
|
342
|
+
Name: 'ContentProvider',
|
|
343
|
+
actions: {
|
|
344
|
+
'ReadFile': { Description: 'Read', Handler: function () {} },
|
|
345
|
+
'SaveFile': { Description: 'Save', Handler: function () {} }
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
let tmpDescriptors = tmpManager.buildProviderDescriptors();
|
|
350
|
+
libAssert.strictEqual(tmpDescriptors.length, 1);
|
|
351
|
+
|
|
352
|
+
let tmpAdapter = tmpDescriptors[0];
|
|
353
|
+
libAssert.ok(tmpAdapter instanceof libCapabilityProvider);
|
|
354
|
+
libAssert.strictEqual(tmpAdapter.Capability, 'ContentSystem');
|
|
355
|
+
|
|
356
|
+
let tmpActions = tmpAdapter.actions;
|
|
357
|
+
libAssert.ok(tmpActions.ReadFile);
|
|
358
|
+
libAssert.ok(tmpActions.SaveFile);
|
|
359
|
+
}
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
test
|
|
363
|
+
(
|
|
364
|
+
'Built adapters should be registrable with ProviderRegistry',
|
|
365
|
+
function ()
|
|
366
|
+
{
|
|
367
|
+
let tmpManager = new libCapabilityManager();
|
|
368
|
+
|
|
369
|
+
tmpManager.registerCapability({
|
|
370
|
+
Capability: 'TestCap',
|
|
371
|
+
Name: 'TestProvider',
|
|
372
|
+
actions: {
|
|
373
|
+
'DoThing': { Description: 'Do a thing', Handler: function () {} }
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
let tmpDescriptors = tmpManager.buildProviderDescriptors();
|
|
378
|
+
let tmpRegistry = new libProviderRegistry();
|
|
379
|
+
|
|
380
|
+
let tmpResult = tmpRegistry.registerProvider(tmpDescriptors[0]);
|
|
381
|
+
libAssert.strictEqual(tmpResult, true);
|
|
382
|
+
|
|
383
|
+
let tmpCapabilities = tmpRegistry.getCapabilities();
|
|
384
|
+
libAssert.ok(tmpCapabilities.includes('TestCap'));
|
|
385
|
+
|
|
386
|
+
let tmpResolved = tmpRegistry.resolve('TestCap', 'DoThing');
|
|
387
|
+
libAssert.ok(tmpResolved);
|
|
388
|
+
libAssert.strictEqual(tmpResolved.action, 'DoThing');
|
|
389
|
+
}
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
// ============================================================
|
|
395
|
+
// ConnectivityHTTP
|
|
396
|
+
// ============================================================
|
|
397
|
+
suite
|
|
398
|
+
(
|
|
399
|
+
'ConnectivityHTTP',
|
|
400
|
+
function ()
|
|
401
|
+
{
|
|
402
|
+
test
|
|
403
|
+
(
|
|
404
|
+
'Should return transport config',
|
|
405
|
+
function ()
|
|
406
|
+
{
|
|
407
|
+
let tmpConn = new libConnectivityHTTP({
|
|
408
|
+
ServerURL: 'http://myserver:9999',
|
|
409
|
+
Password: 'secret',
|
|
410
|
+
PollIntervalMs: 3000
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
let tmpConfig = tmpConn.getTransportConfig();
|
|
414
|
+
libAssert.strictEqual(tmpConfig.ServerURL, 'http://myserver:9999');
|
|
415
|
+
libAssert.strictEqual(tmpConfig.Password, 'secret');
|
|
416
|
+
libAssert.strictEqual(tmpConfig.PollIntervalMs, 3000);
|
|
417
|
+
}
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
test
|
|
421
|
+
(
|
|
422
|
+
'Should report HTTP transport type',
|
|
423
|
+
function ()
|
|
424
|
+
{
|
|
425
|
+
let tmpConn = new libConnectivityHTTP();
|
|
426
|
+
libAssert.strictEqual(tmpConn.getTransportType(), 'HTTP');
|
|
427
|
+
}
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
test
|
|
431
|
+
(
|
|
432
|
+
'Should use defaults',
|
|
433
|
+
function ()
|
|
434
|
+
{
|
|
435
|
+
let tmpConn = new libConnectivityHTTP();
|
|
436
|
+
let tmpConfig = tmpConn.getTransportConfig();
|
|
437
|
+
libAssert.strictEqual(tmpConfig.ServerURL, 'http://localhost:54321');
|
|
438
|
+
libAssert.strictEqual(tmpConfig.PollIntervalMs, 5000);
|
|
439
|
+
libAssert.strictEqual(tmpConfig.HeartbeatIntervalMs, 30000);
|
|
440
|
+
}
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
// ============================================================
|
|
446
|
+
// Beacon Service (standalone, no Fable)
|
|
447
|
+
// ============================================================
|
|
448
|
+
suite
|
|
449
|
+
(
|
|
450
|
+
'BeaconService Standalone',
|
|
451
|
+
function ()
|
|
452
|
+
{
|
|
453
|
+
test
|
|
454
|
+
(
|
|
455
|
+
'Should instantiate without Fable',
|
|
456
|
+
function ()
|
|
457
|
+
{
|
|
458
|
+
let tmpService = new libBeaconService({
|
|
459
|
+
ServerURL: 'http://localhost:54321',
|
|
460
|
+
Name: 'test-beacon'
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
libAssert.strictEqual(tmpService.serviceType, 'UltravisorBeacon');
|
|
464
|
+
libAssert.strictEqual(tmpService.isEnabled(), false);
|
|
465
|
+
libAssert.strictEqual(tmpService.options.ServerURL, 'http://localhost:54321');
|
|
466
|
+
}
|
|
467
|
+
);
|
|
468
|
+
|
|
469
|
+
test
|
|
470
|
+
(
|
|
471
|
+
'Should register capabilities via public API',
|
|
472
|
+
function ()
|
|
473
|
+
{
|
|
474
|
+
let tmpService = new libBeaconService({ Name: 'test' });
|
|
475
|
+
|
|
476
|
+
let tmpResult = tmpService.registerCapability({
|
|
477
|
+
Capability: 'ContentSystem',
|
|
478
|
+
Name: 'ContentProvider',
|
|
479
|
+
actions: {
|
|
480
|
+
'ReadFile': { Description: 'Read', Handler: function () {} }
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
// Should be chainable
|
|
485
|
+
libAssert.strictEqual(tmpResult, tmpService);
|
|
486
|
+
|
|
487
|
+
let tmpNames = tmpService.getCapabilityNames();
|
|
488
|
+
libAssert.strictEqual(tmpNames.length, 1);
|
|
489
|
+
libAssert.strictEqual(tmpNames[0], 'ContentSystem');
|
|
490
|
+
}
|
|
491
|
+
);
|
|
492
|
+
|
|
493
|
+
test
|
|
494
|
+
(
|
|
495
|
+
'Should chain multiple registerCapability calls',
|
|
496
|
+
function ()
|
|
497
|
+
{
|
|
498
|
+
let tmpService = new libBeaconService({ Name: 'test' });
|
|
499
|
+
|
|
500
|
+
tmpService
|
|
501
|
+
.registerCapability({
|
|
502
|
+
Capability: 'ContentSystem',
|
|
503
|
+
actions: { 'Read': { Handler: function () {} } }
|
|
504
|
+
})
|
|
505
|
+
.registerCapability({
|
|
506
|
+
Capability: 'MediaProcessing',
|
|
507
|
+
actions: { 'Thumb': { Handler: function () {} } }
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
libAssert.strictEqual(tmpService.getCapabilityNames().length, 2);
|
|
511
|
+
}
|
|
512
|
+
);
|
|
513
|
+
|
|
514
|
+
test
|
|
515
|
+
(
|
|
516
|
+
'Should export sub-components',
|
|
517
|
+
function ()
|
|
518
|
+
{
|
|
519
|
+
libAssert.ok(libBeaconService.BeaconClient);
|
|
520
|
+
libAssert.ok(libBeaconService.CapabilityManager);
|
|
521
|
+
libAssert.ok(libBeaconService.CapabilityAdapter);
|
|
522
|
+
libAssert.ok(libBeaconService.CapabilityProvider);
|
|
523
|
+
libAssert.ok(libBeaconService.ProviderRegistry);
|
|
524
|
+
libAssert.ok(libBeaconService.ConnectivityHTTP);
|
|
525
|
+
}
|
|
526
|
+
);
|
|
527
|
+
|
|
528
|
+
test
|
|
529
|
+
(
|
|
530
|
+
'Should report not enabled when disable called without enable',
|
|
531
|
+
function (fDone)
|
|
532
|
+
{
|
|
533
|
+
let tmpService = new libBeaconService({ Name: 'test' });
|
|
534
|
+
|
|
535
|
+
tmpService.disable(function (pError)
|
|
536
|
+
{
|
|
537
|
+
libAssert.ifError(pError);
|
|
538
|
+
libAssert.strictEqual(tmpService.isEnabled(), false);
|
|
539
|
+
fDone();
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
);
|
|
543
|
+
|
|
544
|
+
test
|
|
545
|
+
(
|
|
546
|
+
'Should return null thin client when not enabled',
|
|
547
|
+
function ()
|
|
548
|
+
{
|
|
549
|
+
let tmpService = new libBeaconService({ Name: 'test' });
|
|
550
|
+
libAssert.strictEqual(tmpService.getThinClient(), null);
|
|
551
|
+
}
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
);
|
|
555
|
+
|
|
556
|
+
// ============================================================
|
|
557
|
+
// Integration: Full adapter round-trip
|
|
558
|
+
// ============================================================
|
|
559
|
+
suite
|
|
560
|
+
(
|
|
561
|
+
'Integration',
|
|
562
|
+
function ()
|
|
563
|
+
{
|
|
564
|
+
test
|
|
565
|
+
(
|
|
566
|
+
'Capability registered with service should be executable through adapter',
|
|
567
|
+
function (fDone)
|
|
568
|
+
{
|
|
569
|
+
let tmpService = new libBeaconService({ Name: 'integration-test' });
|
|
570
|
+
|
|
571
|
+
tmpService.registerCapability({
|
|
572
|
+
Capability: 'TestSystem',
|
|
573
|
+
Name: 'TestProvider',
|
|
574
|
+
actions: {
|
|
575
|
+
'Greet': {
|
|
576
|
+
Description: 'Say hello',
|
|
577
|
+
SettingsSchema: [{ Name: 'Name', DataType: 'String' }],
|
|
578
|
+
Handler: function (pWorkItem, pContext, fCallback)
|
|
579
|
+
{
|
|
580
|
+
let tmpName = pWorkItem.Settings.Name || 'World';
|
|
581
|
+
fCallback(null, {
|
|
582
|
+
Outputs: { Greeting: `Hello, ${tmpName}!` },
|
|
583
|
+
Log: ['Greeted successfully']
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
// Build adapters and test them directly
|
|
591
|
+
let tmpAdapters = tmpService.getCapabilityManager().buildProviderDescriptors();
|
|
592
|
+
libAssert.strictEqual(tmpAdapters.length, 1);
|
|
593
|
+
|
|
594
|
+
let tmpAdapter = tmpAdapters[0];
|
|
595
|
+
let tmpWorkItem = { WorkItemHash: 'int-test-1', Settings: { Name: 'Ultravisor' } };
|
|
596
|
+
|
|
597
|
+
tmpAdapter.execute('Greet', tmpWorkItem, {}, function (pError, pResult)
|
|
598
|
+
{
|
|
599
|
+
libAssert.ifError(pError);
|
|
600
|
+
libAssert.strictEqual(pResult.Outputs.Greeting, 'Hello, Ultravisor!');
|
|
601
|
+
fDone();
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
);
|