@sockethub/server 5.0.0-alpha.3 → 5.0.0-alpha.6
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 +54 -60
- package/bin/sockethub +4 -3
- package/package.json +42 -60
- package/res/socket.io.js +4908 -0
- package/res/sockethub-client.js +602 -0
- package/res/sockethub-client.min.js +19 -0
- package/sockethub.config.example.json +2 -3
- package/src/bootstrap/init.d.ts +20 -7
- package/src/bootstrap/init.test.ts +211 -0
- package/src/bootstrap/init.ts +152 -75
- package/src/bootstrap/load-platforms.ts +151 -0
- package/src/config.test.ts +27 -22
- package/src/config.ts +82 -78
- package/src/defaults.json +24 -16
- package/src/index.ts +67 -27
- package/src/janitor.test.ts +211 -0
- package/src/janitor.ts +145 -77
- package/src/listener.ts +151 -57
- package/src/middleware/create-activity-object.test.ts +28 -8
- package/src/middleware/create-activity-object.ts +17 -8
- package/src/middleware/expand-activity-stream.test.data.ts +332 -346
- package/src/middleware/expand-activity-stream.test.ts +65 -66
- package/src/middleware/expand-activity-stream.ts +29 -19
- package/src/middleware/store-credentials.test.ts +74 -62
- package/src/middleware/store-credentials.ts +15 -15
- package/src/middleware/validate.test.data.ts +240 -242
- package/src/middleware/validate.test.ts +39 -78
- package/src/middleware/validate.ts +63 -39
- package/src/middleware.test.ts +168 -138
- package/src/middleware.ts +62 -43
- package/src/platform-instance.test.ts +507 -213
- package/src/platform-instance.ts +337 -219
- package/src/platform.test.ts +375 -0
- package/src/platform.ts +306 -139
- package/src/process-manager.ts +75 -51
- package/src/routes.test.ts +43 -89
- package/src/routes.ts +40 -77
- package/src/sentry.test.ts +106 -0
- package/src/sentry.ts +19 -0
- package/src/sockethub.ts +186 -153
- package/src/util.ts +5 -0
- package/coverage/tmp/coverage-93126-1649152190997-0.json +0 -1
- package/dist/bootstrap/init.d.ts +0 -18
- package/dist/bootstrap/init.js +0 -63
- package/dist/bootstrap/init.js.map +0 -1
- package/dist/bootstrap/platforms.js +0 -75
- package/dist/common.d.ts +0 -3
- package/dist/common.js +0 -20
- package/dist/common.js.map +0 -1
- package/dist/config.d.ts +0 -6
- package/dist/config.js +0 -102
- package/dist/config.js.map +0 -1
- package/dist/crypto.d.ts +0 -10
- package/dist/crypto.js +0 -38
- package/dist/crypto.js.map +0 -1
- package/dist/defaults.json +0 -28
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -25
- package/dist/index.js.map +0 -1
- package/dist/janitor.d.ts +0 -15
- package/dist/janitor.js +0 -89
- package/dist/janitor.js.map +0 -1
- package/dist/listener.d.ts +0 -28
- package/dist/listener.js +0 -91
- package/dist/listener.js.map +0 -1
- package/dist/middleware/create-activity-object.d.ts +0 -6
- package/dist/middleware/create-activity-object.js +0 -19
- package/dist/middleware/create-activity-object.js.map +0 -1
- package/dist/middleware/expand-activity-stream.d.ts +0 -2
- package/dist/middleware/expand-activity-stream.js +0 -33
- package/dist/middleware/expand-activity-stream.js.map +0 -1
- package/dist/middleware/expand-activity-stream.test.data.d.ts +0 -480
- package/dist/middleware/expand-activity-stream.test.data.js +0 -360
- package/dist/middleware/expand-activity-stream.test.data.js.map +0 -1
- package/dist/middleware/store-credentials.d.ts +0 -3
- package/dist/middleware/store-credentials.js +0 -19
- package/dist/middleware/store-credentials.js.map +0 -1
- package/dist/middleware/validate.d.ts +0 -2
- package/dist/middleware/validate.js +0 -58
- package/dist/middleware/validate.js.map +0 -1
- package/dist/middleware/validate.test.data.d.ts +0 -532
- package/dist/middleware/validate.test.data.js +0 -263
- package/dist/middleware/validate.test.data.js.map +0 -1
- package/dist/middleware.d.ts +0 -10
- package/dist/middleware.js +0 -54
- package/dist/middleware.js.map +0 -1
- package/dist/platform-instance.d.ts +0 -77
- package/dist/platform-instance.js +0 -211
- package/dist/platform-instance.js.map +0 -1
- package/dist/platform.d.ts +0 -6
- package/dist/platform.js +0 -187
- package/dist/platform.js.map +0 -1
- package/dist/process-manager.d.ts +0 -11
- package/dist/process-manager.js +0 -78
- package/dist/process-manager.js.map +0 -1
- package/dist/routes.d.ts +0 -13
- package/dist/routes.js +0 -83
- package/dist/routes.js.map +0 -1
- package/dist/sockethub.d.ts +0 -39
- package/dist/sockethub.js +0 -119
- package/dist/sockethub.js.map +0 -1
- package/dist/store.d.ts +0 -5
- package/dist/store.js +0 -17
- package/dist/store.js.map +0 -1
- package/src/bootstrap/platforms.js +0 -75
- package/src/common.test.ts +0 -54
- package/src/common.ts +0 -14
- package/src/config.d.ts +0 -2
- package/src/crypto.d.ts +0 -5
- package/src/crypto.test.ts +0 -41
- package/src/crypto.ts +0 -41
- package/src/janitor.d.ts +0 -8
- package/src/middleware/validate.d.ts +0 -1
- package/src/middleware.d.ts +0 -21
- package/src/sockethub.d.ts +0 -1
- package/src/store.test.ts +0 -28
- package/src/store.ts +0 -17
- package/test/init-suite.js +0 -41
- package/test/queue.functional.test.js +0 -0
- package/test/sockethub-suite.js +0 -25
- package/tsconfig.json +0 -18
- package/views/examples/dummy.ejs +0 -93
- package/views/examples/feeds.ejs +0 -90
- package/views/examples/irc.ejs +0 -239
- package/views/examples/shared.js +0 -72
- package/views/examples/xmpp.ejs +0 -191
- package/views/index.ejs +0 -17
|
@@ -1,237 +1,531 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
|
2
|
+
import * as sinon from "sinon";
|
|
3
|
+
import { __dirname } from "./util.js";
|
|
4
|
+
const FORK_PATH = __dirname + "/platform.js";
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
proxyquire.noCallThru();
|
|
7
|
-
|
|
8
|
-
const FORK_PATH = __dirname + '/platform.js';
|
|
6
|
+
import PlatformInstance, { platformInstances } from "./platform-instance.js";
|
|
9
7
|
|
|
10
8
|
describe("PlatformInstance", () => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
sandbox = sinon.createSandbox();
|
|
15
|
-
forkFake = sandbox.fake();
|
|
16
|
-
socketMock = {
|
|
17
|
-
emit: sandbox.spy()
|
|
18
|
-
};
|
|
19
|
-
getSocketFake = sinon.fake.resolves(socketMock);
|
|
20
|
-
|
|
21
|
-
const PlatformInstanceMod = proxyquire('./platform-instance', {
|
|
22
|
-
'bull': sandbox.stub().returns({
|
|
23
|
-
on: sandbox.stub()
|
|
24
|
-
}),
|
|
25
|
-
'./store': {
|
|
26
|
-
redisConfig: {
|
|
27
|
-
createClient: () => {}
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
'child_process': {
|
|
31
|
-
fork: forkFake,
|
|
32
|
-
ChildProcess: sandbox.stub()
|
|
33
|
-
},
|
|
34
|
-
'./listener': {
|
|
35
|
-
'io': {
|
|
36
|
-
'in': sandbox.stub().returns({
|
|
37
|
-
fetchSockets: () => {
|
|
38
|
-
return [socketMock];
|
|
39
|
-
}
|
|
40
|
-
})
|
|
41
|
-
},
|
|
42
|
-
getSocket: getSocketFake
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
PlatformInstance = PlatformInstanceMod.default;
|
|
46
|
-
platformInstances = PlatformInstanceMod.platformInstances;
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
afterEach(() => {
|
|
50
|
-
sinon.restore();
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
describe('private instance per-actor', () => {
|
|
54
|
-
it("is set as non-global when an actor is provided", () => {
|
|
55
|
-
const pi = new PlatformInstance({
|
|
56
|
-
identifier: 'id',
|
|
57
|
-
platform: 'name',
|
|
58
|
-
parentId: 'parentId',
|
|
59
|
-
actor: 'actor string'
|
|
60
|
-
});
|
|
61
|
-
expect(pi.global).to.be.equal(false);
|
|
62
|
-
sandbox.assert.calledWith(forkFake, FORK_PATH, ['parentId', 'name', 'id']);
|
|
63
|
-
pi.destroy();
|
|
64
|
-
});
|
|
65
|
-
});
|
|
9
|
+
let pi, sandbox, forkFake, socketMock, getSocketFake;
|
|
66
10
|
|
|
67
|
-
describe('PlatformInstance objects', () => {
|
|
68
11
|
beforeEach(() => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
pi.process = {
|
|
77
|
-
on: sandbox.spy(),
|
|
78
|
-
removeListener: sandbox.spy(),
|
|
79
|
-
removeAllListeners: sandbox.spy(),
|
|
80
|
-
unref: sandbox.spy(),
|
|
81
|
-
kill: sandbox.spy(),
|
|
82
|
-
};
|
|
12
|
+
sandbox = sinon.createSandbox();
|
|
13
|
+
socketMock = {
|
|
14
|
+
emit: sandbox.spy(),
|
|
15
|
+
};
|
|
16
|
+
getSocketFake = sinon.fake.resolves(socketMock);
|
|
17
|
+
forkFake = sandbox.fake();
|
|
83
18
|
});
|
|
84
19
|
|
|
20
|
+
function getTestPlatformInstanceClass() {
|
|
21
|
+
class TestPlatformInstance extends PlatformInstance {
|
|
22
|
+
createQueue() {
|
|
23
|
+
this.JobQueue = sandbox.stub().returns({
|
|
24
|
+
shutdown: sandbox.stub(),
|
|
25
|
+
on: sandbox.stub(),
|
|
26
|
+
getJob: sandbox.stub(),
|
|
27
|
+
initResultEvents: sandbox.stub(),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
initProcess(parentId, name, id, env) {
|
|
32
|
+
this.process = forkFake(FORK_PATH, [parentId, name, id], env);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
createGetSocket() {
|
|
36
|
+
this.getSocket = getSocketFake;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return TestPlatformInstance;
|
|
40
|
+
}
|
|
41
|
+
|
|
85
42
|
afterEach(() => {
|
|
86
|
-
|
|
43
|
+
sinon.restore();
|
|
87
44
|
});
|
|
88
45
|
|
|
89
|
-
|
|
90
|
-
|
|
46
|
+
describe("private instance per-actor", () => {
|
|
47
|
+
it("is set as non-global when an actor is provided", async () => {
|
|
48
|
+
const TestPlatformInstance = getTestPlatformInstanceClass();
|
|
49
|
+
const pi = new TestPlatformInstance({
|
|
50
|
+
identifier: "id",
|
|
51
|
+
platform: "name",
|
|
52
|
+
parentId: "parentId",
|
|
53
|
+
actor: "actor string",
|
|
54
|
+
});
|
|
55
|
+
expect(pi.global).toEqual(false);
|
|
56
|
+
sandbox.assert.calledWith(forkFake, FORK_PATH, [
|
|
57
|
+
"parentId",
|
|
58
|
+
"name",
|
|
59
|
+
"id",
|
|
60
|
+
]);
|
|
61
|
+
await pi.shutdown();
|
|
62
|
+
});
|
|
91
63
|
});
|
|
92
64
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
65
|
+
describe("PlatformInstance objects", () => {
|
|
66
|
+
beforeEach(() => {
|
|
67
|
+
const TestPlatformInstance = getTestPlatformInstanceClass();
|
|
68
|
+
pi = new TestPlatformInstance({
|
|
69
|
+
identifier: "platform identifier",
|
|
70
|
+
platform: "a platform name",
|
|
71
|
+
parentId: "the parentId",
|
|
72
|
+
});
|
|
73
|
+
platformInstances.set(pi.id, pi);
|
|
96
74
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
])).to.be.ok;
|
|
106
|
-
});
|
|
75
|
+
pi.process = {
|
|
76
|
+
on: sandbox.spy(),
|
|
77
|
+
removeListener: sandbox.spy(),
|
|
78
|
+
removeAllListeners: sandbox.spy(),
|
|
79
|
+
unref: sandbox.spy(),
|
|
80
|
+
kill: sandbox.spy(),
|
|
81
|
+
};
|
|
82
|
+
});
|
|
107
83
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('adds a close and message handler when a session is registered', () => {
|
|
114
|
-
pi.registerSession('my session id');
|
|
115
|
-
expect(pi.callbackFunction.callCount).to.equal(2);
|
|
116
|
-
sandbox.assert.calledWith(pi.callbackFunction, 'close', 'my session id');
|
|
117
|
-
sandbox.assert.calledWith(pi.callbackFunction, 'message', 'my session id');
|
|
118
|
-
expect(pi.sessions.has('my session id')).to.be.equal(true);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('is able to generate failure reports', () => {
|
|
122
|
-
pi.registerSession('my session id');
|
|
123
|
-
expect(pi.sessions.has('my session id')).to.be.equal(true);
|
|
124
|
-
pi.reportError('my session id', 'an error message');
|
|
125
|
-
pi.sendToClient = sandbox.stub();
|
|
126
|
-
pi.destroy = sandbox.stub();
|
|
127
|
-
expect(pi.sessions.size).to.be.equal(0);
|
|
128
|
-
});
|
|
129
|
-
});
|
|
84
|
+
afterEach(async () => {
|
|
85
|
+
await pi.shutdown();
|
|
86
|
+
});
|
|
130
87
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
});
|
|
88
|
+
it("has expected properties", () => {
|
|
89
|
+
const TestPlatformInstance = getTestPlatformInstanceClass();
|
|
90
|
+
expect(typeof TestPlatformInstance).toEqual("function");
|
|
91
|
+
});
|
|
136
92
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
expect(platformInstances.has('platform identifier')).to.be.true;
|
|
141
|
-
await pi.destroy();
|
|
142
|
-
expect(pi.queue).not.to.be.ok;
|
|
143
|
-
expect(platformInstances.has('platform identifier')).to.be.false;
|
|
144
|
-
});
|
|
93
|
+
it("should have a platformInstances Map", () => {
|
|
94
|
+
expect(platformInstances instanceof Map).toEqual(true);
|
|
95
|
+
});
|
|
145
96
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
97
|
+
it("has certain accessible properties", () => {
|
|
98
|
+
expect(pi.id).toEqual("platform identifier");
|
|
99
|
+
expect(pi.name).toEqual("a platform name");
|
|
100
|
+
expect(pi.parentId).toEqual("the parentId");
|
|
101
|
+
expect(pi.flaggedForTermination).toEqual(false);
|
|
102
|
+
expect(pi.global).toEqual(true);
|
|
103
|
+
expect(
|
|
104
|
+
forkFake.calledWith(FORK_PATH, [
|
|
105
|
+
"the parentId",
|
|
106
|
+
"a platform name",
|
|
107
|
+
"platform identifier",
|
|
108
|
+
]),
|
|
109
|
+
).toEqual(true);
|
|
110
|
+
});
|
|
152
111
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
sandbox.assert.calledOnce(getSocketFake);
|
|
158
|
-
sandbox.assert.calledWith(getSocketFake, 'my session id');
|
|
159
|
-
sandbox.assert.calledOnce(socketMock.emit);
|
|
160
|
-
sandbox.assert.calledWith(
|
|
161
|
-
socketMock.emit, 'message', {foo:'this is a message object', context: 'a platform name'});
|
|
162
|
-
});
|
|
112
|
+
describe("registerSession", () => {
|
|
113
|
+
beforeEach(() => {
|
|
114
|
+
pi.callbackFunction = sandbox.fake();
|
|
115
|
+
});
|
|
163
116
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
117
|
+
it("adds a close and message handler when a session is registered", () => {
|
|
118
|
+
pi.registerSession("my session id");
|
|
119
|
+
expect(pi.callbackFunction.callCount).toEqual(2);
|
|
120
|
+
sandbox.assert.calledWith(
|
|
121
|
+
pi.callbackFunction,
|
|
122
|
+
"close",
|
|
123
|
+
"my session id",
|
|
124
|
+
);
|
|
125
|
+
sandbox.assert.calledWith(
|
|
126
|
+
pi.callbackFunction,
|
|
127
|
+
"message",
|
|
128
|
+
"my session id",
|
|
129
|
+
);
|
|
130
|
+
expect(pi.sessions.has("my session id")).toEqual(true);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("is able to generate failure reports", async () => {
|
|
134
|
+
pi.registerSession("my session id");
|
|
135
|
+
expect(pi.sessions.has("my session id")).toEqual(true);
|
|
136
|
+
pi.sendToClient = sandbox.stub();
|
|
137
|
+
pi.shutdown = sandbox.stub();
|
|
138
|
+
await pi.reportError("my session id", "an error message");
|
|
139
|
+
expect(pi.sessions.size).toEqual(0);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("initializes the job queue", () => {
|
|
144
|
+
expect(pi.queue).toBeUndefined();
|
|
145
|
+
pi.initQueue("a secret");
|
|
146
|
+
expect(pi.queue).toBeDefined();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("cleans up its references when shutdown", async () => {
|
|
150
|
+
pi.initQueue("a secret");
|
|
151
|
+
expect(pi.queue).toBeDefined();
|
|
152
|
+
expect(platformInstances.has("platform identifier")).toBeTrue();
|
|
153
|
+
await pi.shutdown();
|
|
154
|
+
expect(pi.queue).toBeUndefined();
|
|
155
|
+
expect(platformInstances.has("platform identifier")).toBeFalse();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("updates its identifier when changed", () => {
|
|
159
|
+
pi.updateIdentifier("foo bar");
|
|
160
|
+
expect(pi.id).toEqual("foo bar");
|
|
161
|
+
expect(platformInstances.has("platform identifier")).toBeFalse();
|
|
162
|
+
expect(platformInstances.has("foo bar")).toBeTrue();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("sends messages to client using socket session id", async () => {
|
|
166
|
+
await pi.sendToClient("my session id", {
|
|
167
|
+
foo: "this is a message object",
|
|
168
|
+
sessionSecret: "private data",
|
|
169
|
+
});
|
|
170
|
+
expect(getSocketFake.callCount).toEqual(1);
|
|
171
|
+
sandbox.assert.calledOnce(getSocketFake);
|
|
172
|
+
sandbox.assert.calledWith(getSocketFake, "my session id");
|
|
173
|
+
sandbox.assert.calledOnce(socketMock.emit);
|
|
174
|
+
sandbox.assert.calledWith(socketMock.emit, "message", {
|
|
175
|
+
foo: "this is a message object",
|
|
176
|
+
context: "a platform name",
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it("broadcasts to peers", async () => {
|
|
181
|
+
pi.sessions.add("other peer");
|
|
182
|
+
pi.sessions.add("another peer");
|
|
183
|
+
await pi.broadcastToSharedPeers("myself", { foo: "bar" });
|
|
184
|
+
expect(getSocketFake.callCount).toEqual(2);
|
|
185
|
+
sandbox.assert.calledWith(getSocketFake, "other peer");
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
describe("handleJobResult", () => {
|
|
189
|
+
beforeEach(() => {
|
|
190
|
+
pi.sendToClient = sandbox.fake();
|
|
191
|
+
pi.broadcastToSharedPeers = sandbox.fake();
|
|
192
|
+
pi.config = { persist: false };
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("broadcasts to peers when handling a completed job", async () => {
|
|
196
|
+
pi.sessions.add("other peer");
|
|
197
|
+
await pi.handleJobResult(
|
|
198
|
+
"completed",
|
|
199
|
+
{ msg: { foo: "bar" } },
|
|
200
|
+
undefined,
|
|
201
|
+
);
|
|
202
|
+
expect(pi.sendToClient.callCount).toEqual(1);
|
|
203
|
+
expect(pi.broadcastToSharedPeers.callCount).toEqual(1);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("appends completed result message when present", async () => {
|
|
207
|
+
await pi.handleJobResult(
|
|
208
|
+
"completed",
|
|
209
|
+
{ sessionId: "a session id", msg: { foo: "bar" } },
|
|
210
|
+
"a good result message",
|
|
211
|
+
);
|
|
212
|
+
expect(pi.broadcastToSharedPeers.callCount).toEqual(1);
|
|
213
|
+
sandbox.assert.calledWith(pi.sendToClient, "a session id", {
|
|
214
|
+
foo: "bar",
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it("appends failed result message when present", async () => {
|
|
219
|
+
await pi.handleJobResult(
|
|
220
|
+
"failed",
|
|
221
|
+
{ sessionId: "a session id", msg: { foo: "bar" } },
|
|
222
|
+
"a bad result message",
|
|
223
|
+
);
|
|
224
|
+
expect(pi.broadcastToSharedPeers.callCount).toEqual(1);
|
|
225
|
+
sandbox.assert.calledWith(pi.sendToClient, "a session id", {
|
|
226
|
+
foo: "bar",
|
|
227
|
+
error: "a bad result message",
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
describe("callbackFunction", () => {
|
|
233
|
+
beforeEach(() => {
|
|
234
|
+
pi.reportError = sandbox.fake();
|
|
235
|
+
pi.sendToClient = sandbox.fake();
|
|
236
|
+
pi.updateIdentifier = sandbox.fake();
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("close events from platform thread are reported", async () => {
|
|
240
|
+
// Mock process as connected and not flagged for termination
|
|
241
|
+
pi.process.connected = true;
|
|
242
|
+
pi.flaggedForTermination = false;
|
|
243
|
+
|
|
244
|
+
const close = pi.callbackFunction("close", "my session id");
|
|
245
|
+
await close("error msg");
|
|
246
|
+
sandbox.assert.calledWith(
|
|
247
|
+
pi.reportError,
|
|
248
|
+
"my session id",
|
|
249
|
+
"Error: session thread closed unexpectedly: error msg",
|
|
250
|
+
);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it("close events skip error reporting when process disconnected", async () => {
|
|
254
|
+
// Mock process as disconnected
|
|
255
|
+
pi.process.connected = false;
|
|
256
|
+
pi.flaggedForTermination = false;
|
|
257
|
+
pi.shutdown = sandbox.stub();
|
|
258
|
+
|
|
259
|
+
const close = pi.callbackFunction("close", "my session id");
|
|
260
|
+
await close("error msg");
|
|
261
|
+
|
|
262
|
+
// Should NOT attempt to report error
|
|
263
|
+
sandbox.assert.notCalled(pi.reportError);
|
|
264
|
+
// Should call shutdown
|
|
265
|
+
sandbox.assert.called(pi.shutdown);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it("close events skip error reporting when flagged for termination", async () => {
|
|
269
|
+
// Mock process as flagged for termination
|
|
270
|
+
pi.process.connected = true;
|
|
271
|
+
pi.flaggedForTermination = true;
|
|
272
|
+
pi.shutdown = sandbox.stub();
|
|
273
|
+
|
|
274
|
+
const close = pi.callbackFunction("close", "my session id");
|
|
275
|
+
await close("error msg");
|
|
276
|
+
|
|
277
|
+
// Should NOT attempt to report error
|
|
278
|
+
sandbox.assert.notCalled(pi.reportError);
|
|
279
|
+
// Should call shutdown
|
|
280
|
+
sandbox.assert.called(pi.shutdown);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it("message events from platform thread are route based on command: error", () => {
|
|
284
|
+
const message = pi.callbackFunction("message", "my session id");
|
|
285
|
+
message(["error", "error message"]);
|
|
286
|
+
sandbox.assert.calledWith(
|
|
287
|
+
pi.reportError,
|
|
288
|
+
"my session id",
|
|
289
|
+
"error message",
|
|
290
|
+
);
|
|
291
|
+
});
|
|
171
292
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
it('appends completed result message when present', async () => {
|
|
187
|
-
await pi.handleJobResult('completed', {sessionId: 'a session id', msg: {foo: 'bar'}},
|
|
188
|
-
'a good result message');
|
|
189
|
-
expect(pi.broadcastToSharedPeers.callCount).to.equal(1);
|
|
190
|
-
sandbox.assert.calledWith(pi.sendToClient, 'a session id',
|
|
191
|
-
{foo: 'bar'});
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
it('appends failed result message when present', async () => {
|
|
195
|
-
await pi.handleJobResult('failed', {sessionId: 'a session id', msg: {foo: 'bar'}},
|
|
196
|
-
'a bad result message');
|
|
197
|
-
expect(pi.broadcastToSharedPeers.callCount).to.equal(1);
|
|
198
|
-
sandbox.assert.calledWith(pi.sendToClient, 'a session id',
|
|
199
|
-
{foo: 'bar', error: 'a bad result message'});
|
|
200
|
-
});
|
|
293
|
+
it("message events from platform thread are route based on command: updateActor", () => {
|
|
294
|
+
const message = pi.callbackFunction("message", "my session id");
|
|
295
|
+
message(["updateActor", undefined, { foo: "bar" }]);
|
|
296
|
+
sandbox.assert.calledWith(pi.updateIdentifier, { foo: "bar" });
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it("message events from platform thread are route based on command: else", () => {
|
|
300
|
+
const message = pi.callbackFunction("message", "my session id");
|
|
301
|
+
message(["blah", { foo: "bar" }]);
|
|
302
|
+
sandbox.assert.calledWith(pi.sendToClient, "my session id", {
|
|
303
|
+
foo: "bar",
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
});
|
|
201
307
|
});
|
|
202
308
|
|
|
203
|
-
describe(
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
309
|
+
describe("credential failure handling", () => {
|
|
310
|
+
let queueMock: any;
|
|
311
|
+
let processMock: any;
|
|
312
|
+
|
|
313
|
+
beforeEach(() => {
|
|
314
|
+
queueMock = {
|
|
315
|
+
pause: sandbox.stub().resolves(),
|
|
316
|
+
resume: sandbox.stub().resolves(),
|
|
317
|
+
shutdown: sandbox.stub().resolves(),
|
|
318
|
+
on: sandbox.stub(),
|
|
319
|
+
getJob: sandbox.stub(),
|
|
320
|
+
initResultEvents: sandbox.stub(),
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
processMock = {
|
|
324
|
+
removeAllListeners: sandbox.stub(),
|
|
325
|
+
unref: sandbox.stub(),
|
|
326
|
+
kill: sandbox.stub(),
|
|
327
|
+
};
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
describe("POSITIVE: Platform initialized - credential failure should NOT terminate", () => {
|
|
331
|
+
it("should keep platform alive when credential job fails on initialized platform", async () => {
|
|
332
|
+
const TestPlatformInstance = getTestPlatformInstanceClass();
|
|
333
|
+
pi = new TestPlatformInstance({
|
|
334
|
+
identifier: "test-platform-id",
|
|
335
|
+
platform: "xmpp",
|
|
336
|
+
parentId: "test-parent",
|
|
337
|
+
actor: "testuser@localhost",
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
// Override queue with our mock
|
|
341
|
+
pi.queue = queueMock;
|
|
342
|
+
pi.process = processMock;
|
|
343
|
+
|
|
344
|
+
// Setup: Platform is already initialized
|
|
345
|
+
pi.config = {
|
|
346
|
+
persist: true,
|
|
347
|
+
initialized: true,
|
|
348
|
+
requireCredentials: ["connect"],
|
|
349
|
+
};
|
|
350
|
+
pi.flaggedForTermination = false;
|
|
351
|
+
|
|
352
|
+
const job = {
|
|
353
|
+
sessionId: "session123",
|
|
354
|
+
msg: {
|
|
355
|
+
type: "connect",
|
|
356
|
+
context: "xmpp",
|
|
357
|
+
actor: { id: "testuser@localhost", type: "person" },
|
|
358
|
+
},
|
|
359
|
+
title: "xmpp-1",
|
|
360
|
+
sessionSecret: "secret",
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
const errorResult = "credentials mismatch for testuser@localhost";
|
|
364
|
+
|
|
365
|
+
pi.sendToClient = sandbox.stub();
|
|
366
|
+
|
|
367
|
+
// Simulate job failure
|
|
368
|
+
await pi.handleJobResult("failed", job, errorResult);
|
|
369
|
+
|
|
370
|
+
// ASSERTIONS
|
|
371
|
+
// 1. Platform should NOT be flagged for termination
|
|
372
|
+
expect(pi.flaggedForTermination).toBe(false);
|
|
373
|
+
|
|
374
|
+
// 2. Queue should NOT be paused
|
|
375
|
+
sinon.assert.notCalled(queueMock.pause);
|
|
376
|
+
|
|
377
|
+
// 3. Platform should remain initialized
|
|
378
|
+
expect(pi.config.initialized).toBe(true);
|
|
379
|
+
|
|
380
|
+
// 4. Error should still be sent to client
|
|
381
|
+
sinon.assert.called(pi.sendToClient);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it("should allow subsequent jobs after non-fatal credential error", async () => {
|
|
385
|
+
const TestPlatformInstance = getTestPlatformInstanceClass();
|
|
386
|
+
pi = new TestPlatformInstance({
|
|
387
|
+
identifier: "test-platform-id",
|
|
388
|
+
platform: "xmpp",
|
|
389
|
+
parentId: "test-parent",
|
|
390
|
+
actor: "testuser@localhost",
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
pi.queue = queueMock;
|
|
394
|
+
pi.process = processMock;
|
|
395
|
+
|
|
396
|
+
pi.config = {
|
|
397
|
+
persist: true,
|
|
398
|
+
initialized: true,
|
|
399
|
+
requireCredentials: ["connect"],
|
|
400
|
+
};
|
|
401
|
+
pi.flaggedForTermination = false;
|
|
402
|
+
pi.sendToClient = sandbox.stub();
|
|
403
|
+
|
|
404
|
+
const failedJob = {
|
|
405
|
+
sessionId: "session123",
|
|
406
|
+
msg: {
|
|
407
|
+
type: "connect",
|
|
408
|
+
context: "xmpp",
|
|
409
|
+
actor: { id: "testuser@localhost", type: "person" },
|
|
410
|
+
},
|
|
411
|
+
title: "xmpp-1",
|
|
412
|
+
sessionSecret: "secret",
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
// First job fails with credential error
|
|
416
|
+
await pi.handleJobResult("failed", failedJob, "credential error");
|
|
417
|
+
|
|
418
|
+
// Platform should still be operational
|
|
419
|
+
expect(pi.flaggedForTermination).toBe(false);
|
|
420
|
+
expect(pi.config.initialized).toBe(true);
|
|
421
|
+
|
|
422
|
+
// Second job succeeds
|
|
423
|
+
const successJob = {
|
|
424
|
+
sessionId: "session456",
|
|
425
|
+
msg: {
|
|
426
|
+
type: "send",
|
|
427
|
+
context: "xmpp",
|
|
428
|
+
actor: { id: "testuser@localhost", type: "person" },
|
|
429
|
+
},
|
|
430
|
+
title: "xmpp-2",
|
|
431
|
+
sessionSecret: "secret",
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
await pi.handleJobResult("completed", successJob, undefined);
|
|
435
|
+
|
|
436
|
+
// Platform should still be alive
|
|
437
|
+
expect(pi.flaggedForTermination).toBe(false);
|
|
438
|
+
expect(pi.config.initialized).toBe(true);
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
describe("NEGATIVE: Platform NOT initialized - credential failure SHOULD terminate", () => {
|
|
443
|
+
it("should terminate platform when credential job fails on uninitialized platform", async () => {
|
|
444
|
+
const TestPlatformInstance = getTestPlatformInstanceClass();
|
|
445
|
+
pi = new TestPlatformInstance({
|
|
446
|
+
identifier: "test-platform-id",
|
|
447
|
+
platform: "xmpp",
|
|
448
|
+
parentId: "test-parent",
|
|
449
|
+
actor: "testuser@localhost",
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
pi.queue = queueMock;
|
|
453
|
+
pi.process = processMock;
|
|
454
|
+
|
|
455
|
+
// Setup: Platform is NOT initialized
|
|
456
|
+
pi.config = {
|
|
457
|
+
persist: true,
|
|
458
|
+
initialized: false,
|
|
459
|
+
requireCredentials: ["connect"],
|
|
460
|
+
};
|
|
461
|
+
pi.flaggedForTermination = false;
|
|
462
|
+
pi.sendToClient = sandbox.stub();
|
|
463
|
+
|
|
464
|
+
const job = {
|
|
465
|
+
sessionId: "session123",
|
|
466
|
+
msg: {
|
|
467
|
+
type: "connect",
|
|
468
|
+
context: "xmpp",
|
|
469
|
+
actor: { id: "testuser@localhost", type: "person" },
|
|
470
|
+
},
|
|
471
|
+
title: "xmpp-1",
|
|
472
|
+
sessionSecret: "secret",
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
const errorResult = "invalid credentials for testuser@localhost";
|
|
476
|
+
|
|
477
|
+
// Simulate job failure on uninitialized platform
|
|
478
|
+
await pi.handleJobResult("failed", job, errorResult);
|
|
479
|
+
|
|
480
|
+
// ASSERTIONS
|
|
481
|
+
// 1. Platform SHOULD be flagged for termination
|
|
482
|
+
expect(pi.flaggedForTermination).toBe(true);
|
|
483
|
+
|
|
484
|
+
// 2. Queue SHOULD be paused
|
|
485
|
+
sinon.assert.calledOnce(queueMock.pause);
|
|
486
|
+
|
|
487
|
+
// 3. Platform should remain uninitialized
|
|
488
|
+
expect(pi.config.initialized).toBe(false);
|
|
489
|
+
|
|
490
|
+
// 4. Error should still be sent to client
|
|
491
|
+
sinon.assert.called(pi.sendToClient);
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
it("should pause queue when credential initialization fails", async () => {
|
|
495
|
+
const TestPlatformInstance = getTestPlatformInstanceClass();
|
|
496
|
+
pi = new TestPlatformInstance({
|
|
497
|
+
identifier: "test-platform-id",
|
|
498
|
+
platform: "xmpp",
|
|
499
|
+
parentId: "test-parent",
|
|
500
|
+
actor: "testuser@localhost",
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
pi.queue = queueMock;
|
|
504
|
+
pi.process = processMock;
|
|
505
|
+
|
|
506
|
+
pi.config = {
|
|
507
|
+
persist: true,
|
|
508
|
+
initialized: false,
|
|
509
|
+
requireCredentials: ["connect"],
|
|
510
|
+
};
|
|
511
|
+
pi.sendToClient = sandbox.stub();
|
|
512
|
+
|
|
513
|
+
const job = {
|
|
514
|
+
sessionId: "session123",
|
|
515
|
+
msg: {
|
|
516
|
+
type: "connect",
|
|
517
|
+
context: "xmpp",
|
|
518
|
+
actor: { id: "testuser@localhost", type: "person" },
|
|
519
|
+
},
|
|
520
|
+
title: "xmpp-1",
|
|
521
|
+
sessionSecret: "secret",
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
await pi.handleJobResult("failed", job, "connection failed");
|
|
525
|
+
|
|
526
|
+
// Queue must be paused
|
|
527
|
+
sinon.assert.calledOnce(queueMock.pause);
|
|
528
|
+
});
|
|
529
|
+
});
|
|
235
530
|
});
|
|
236
|
-
});
|
|
237
531
|
});
|