@sockethub/platform-xmpp 5.0.0-alpha.3

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.
@@ -0,0 +1,367 @@
1
+ const proxyquire = require('proxyquire');
2
+ const chai = require('chai');
3
+ const sinon = require('sinon');
4
+ const expect = chai.expect;
5
+
6
+ proxyquire.noPreserveCache();
7
+ proxyquire.noCallThru();
8
+
9
+ const actor = {
10
+ type: 'person',
11
+ id: 'testingham@jabber.net',
12
+ name:'testing ham'
13
+ };
14
+
15
+ const credentials = {
16
+ actor: actor,
17
+ object: {
18
+ type: 'credentials',
19
+ userAddress: 'testingham@jabber.net',
20
+ password: 'foobar',
21
+ resource: 'home'
22
+ }
23
+ };
24
+
25
+ const target = {
26
+ mrfoobar: {
27
+ type: 'person',
28
+ id: 'mrfoobar@jabber.net',
29
+ name: 'Mr FooBar'
30
+ },
31
+ partyroom: {
32
+ type: 'room',
33
+ id: 'partyroom@jabber.net'
34
+ },
35
+ roomuser: {
36
+ type: 'room',
37
+ id: 'partyroom@jabber.net/ms tut'
38
+ }
39
+ };
40
+
41
+ const job = {
42
+ connect: {
43
+ context: 'xmpp',
44
+ type: 'connect',
45
+ actor: {
46
+ id: 'slvrbckt@jabber.net/Home',
47
+ type: 'person',
48
+ name: 'Nick Jennings',
49
+ userName: 'slvrbckt'
50
+ }
51
+ },
52
+ join: {
53
+ actor: actor,
54
+ object: {
55
+ type: 'update',
56
+ name: 'Frank'
57
+ },
58
+ target: target.partyroom
59
+ },
60
+ leave: {
61
+ actor: actor,
62
+ target: target.partyroom
63
+ },
64
+ send: {
65
+ chat: {
66
+ actor: actor,
67
+ object: {
68
+ type: 'message',
69
+ id: 'hc-1234abcd',
70
+ content: 'hello'
71
+ },
72
+ target: target.mrfoobar
73
+ },
74
+ groupchat: {
75
+ actor: actor,
76
+ object: {
77
+ type: 'message',
78
+ id: 'hc-1234abcd',
79
+ content: 'hi all'
80
+ },
81
+ target: target.roomuser
82
+ },
83
+ correction: {
84
+ actor: actor,
85
+ object: {
86
+ type: 'message',
87
+ id: 'hc-1234abcd',
88
+ content: 'hi yall',
89
+ 'xmpp:replace': { id: 'hc-234bcde' }
90
+ },
91
+ target: target.roomuser
92
+ }
93
+ },
94
+ update: {
95
+ presenceOnline: {
96
+ actor: actor,
97
+ object: {
98
+ type: 'presence',
99
+ presence: 'online',
100
+ content: 'ready to chat'
101
+ }
102
+ },
103
+ presenceUnavailable: {
104
+ actor: actor,
105
+ object: {
106
+ type: 'presence',
107
+ presence: 'away',
108
+ content: 'eating popcorn'
109
+ }
110
+ },
111
+ presenceOffline: {
112
+ actor: actor,
113
+ object: {
114
+ type: 'presence',
115
+ presence: 'offline',
116
+ content: ''
117
+ }
118
+ }
119
+ },
120
+ 'request-friend': {
121
+ actor: actor,
122
+ target: target.mrfoobar
123
+ },
124
+ 'remove-friend': {
125
+ actor: actor,
126
+ target: target.mrfoobar
127
+ },
128
+ 'make-friend': {
129
+ actor: actor,
130
+ target: target.mrfoobar
131
+ },
132
+ query: {
133
+ actor: actor,
134
+ target: target.partyroom,
135
+ object: {
136
+ type: 'attendance'
137
+ }
138
+ }
139
+ };
140
+
141
+ describe('Platform', () => {
142
+ let shXmpp, clientFake, xmlFake, clientObjectFake, xp;
143
+
144
+ beforeEach(() => {
145
+ clientObjectFake = {
146
+ on: sinon.fake(),
147
+ start: sinon.fake.resolves(),
148
+ send: sinon.fake.resolves(),
149
+ join: sinon.fake.resolves(),
150
+ };
151
+ clientFake = sinon.fake.returns(clientObjectFake);
152
+ xmlFake = sinon.fake();
153
+
154
+ shXmpp = proxyquire('./index', {
155
+ '@xmpp/client': {
156
+ client: clientFake,
157
+ xml: xmlFake
158
+ },
159
+ './utils': {
160
+ buildXmppCredentials: sinon.fake()
161
+ }
162
+ });
163
+
164
+ xp = new shXmpp({
165
+ id: actor,
166
+ debug: sinon.fake(),
167
+ sendToClient: sinon.fake()
168
+ });
169
+ });
170
+
171
+ afterEach(() => {
172
+ sinon.restore();
173
+ });
174
+
175
+ describe('Successful initialization', () => {
176
+ it('works as intended', (done) => {
177
+ xp.connect(job.connect, credentials, () => {
178
+ sinon.assert.calledOnce(clientFake);
179
+ expect(xp.__client.on).to.exist;
180
+ expect(xp.__client.start).to.exist;
181
+ expect(xp.__client.send).to.exist;
182
+ expect(xp.__client.send.callCount).to.eql(0);
183
+ sinon.assert.calledOnce(clientObjectFake.start);
184
+ sinon.assert.notCalled(xp.sendToClient);
185
+ done();
186
+ });
187
+ });
188
+ });
189
+
190
+ describe('Bad initialization', () => {
191
+ it('returns the existing __client object', (done) => {
192
+ xp.__client = 'foo';
193
+ xp.connect(job.connect, credentials, () => {
194
+ expect(xp.__client).to.equal('foo');
195
+ sinon.assert.notCalled(clientFake);
196
+ sinon.assert.notCalled(xp.sendToClient);
197
+ // sinon.assert.calledOnce(clientFake);
198
+ // sinon.assert.calledOnce(xp.sendToClient);
199
+ done();
200
+ });
201
+ });
202
+
203
+ it('deletes the __client property on failed connect', (done) => {
204
+ clientObjectFake.start = sinon.fake.rejects('foo');
205
+ xp.connect(job.connect, credentials, () => {
206
+ expect(xp.__client).to.be.undefined;
207
+ sinon.assert.notCalled(xp.sendToClient);
208
+ done();
209
+ });
210
+ });
211
+ });
212
+
213
+ describe('Platform functionality', () => {
214
+ beforeEach(done => {
215
+ xp.connect(job.join, credentials, () => done());
216
+ });
217
+
218
+ describe('#join', () => {
219
+ it('calls xmpp.js correctly', (done) => {
220
+ expect(xp.__client.send).to.be.instanceof(Function);
221
+ xp.join(job.join, () => {
222
+ sinon.assert.calledOnce(xp.__client.send);
223
+ sinon.assert.calledWith(xmlFake, "presence", {
224
+ "from": "testingham@jabber.net",
225
+ "to": "partyroom@jabber.net/testing ham"
226
+ });
227
+ done();
228
+ });
229
+ });
230
+ });
231
+
232
+ describe('#leave', () => {
233
+ it('calls xmpp.js correctly', (done) => {
234
+ expect(xp.__client).to.not.be.undefined;
235
+ expect(xp.__client.send).to.be.instanceof(Function);
236
+ xp.leave(job.leave, () => {
237
+ sinon.assert.calledOnce(xp.__client.send);
238
+ sinon.assert.calledWith(xmlFake, "presence", {
239
+ from: "testingham@jabber.net",
240
+ to: "partyroom@jabber.net/testing ham",
241
+ type: "unavailable"
242
+ });
243
+ done();
244
+ });
245
+ });
246
+ });
247
+
248
+ describe('#send', () => {
249
+ it('calls xmpp.js correctly', (done) => {
250
+ expect(xp.__client).to.not.be.undefined;
251
+ expect(xp.__client.send).to.be.instanceof(Function);
252
+ xp.send(job.send.chat, () => {
253
+ sinon.assert.calledOnce(xp.__client.send);
254
+ expect(xmlFake.getCall(0).args).to.eql(["body", {}, job.send.chat.object.content]);
255
+ expect(xmlFake.getCall(1).args).to.eql(["message", {
256
+ type: 'chat', to: job.send.chat.target.id, id: job.send.chat.object.id
257
+ }, undefined, undefined]);
258
+ done();
259
+ });
260
+ });
261
+
262
+ it('calls xmpp.js correctly for a groupchat', (done) => {
263
+ xp.send(job.send.groupchat, () => {
264
+ sinon.assert.calledOnce(xp.__client.send);
265
+ expect(xmlFake.getCall(0).args).to.eql(["body", {}, job.send.groupchat.object.content]);
266
+ expect(xmlFake.getCall(1).args).to.eql(["message", {
267
+ type: 'groupchat', to: job.send.groupchat.target.id, id: job.send.groupchat.object.id
268
+ }, undefined, undefined]);
269
+ done();
270
+ });
271
+ });
272
+
273
+ it('calls xmpp.js correctly for a message correction', (done) => {
274
+ xp.send(job.send.correction, () => {
275
+ sinon.assert.calledOnce(xp.__client.send);
276
+ expect(xmlFake.getCall(0).args).to.eql(["body", {}, job.send.correction.object.content]);
277
+ expect(xmlFake.getCall(1).args).to.eql(["replace", {
278
+ id: job.send.correction.object['xmpp:replace'].id, xmlns: 'urn:xmpp:message-correct:0'
279
+ }]);
280
+ expect(xmlFake.getCall(2).args).to.eql(["message", {
281
+ type: 'groupchat', to: job.send.correction.target.id, id: job.send.correction.object.id
282
+ }, undefined, undefined]);
283
+ done();
284
+ });
285
+ });
286
+ });
287
+
288
+ describe('#update', () => {
289
+ it('calls xml() correctly for available', (done) => {
290
+ xp.update(job.update.presenceOnline, () => {
291
+ sinon.assert.calledOnce(xp.__client.send);
292
+ expect(xmlFake.getCall(0).args).to.eql(
293
+ [ 'presence', {}, {}, { status: 'ready to chat' } ]);
294
+ done();
295
+ });
296
+ });
297
+ it('calls xml() correctly for unavailable', (done) => {
298
+ xp.update(job.update.presenceUnavailable, () => {
299
+ sinon.assert.calledOnce(xp.__client.send);
300
+ expect(xmlFake.getCall(0).args).to.eql(
301
+ [ 'presence', {}, { show: 'away' }, { status: 'eating popcorn' } ]);
302
+ done();
303
+ });
304
+ });
305
+ it('calls xml() correctly for offline', (done) => {
306
+ xp.update(job.update.presenceOffline, () => {
307
+ sinon.assert.calledOnce(xp.__client.send);
308
+ expect(xmlFake.getCall(0).args).to.eql([ 'presence', { type: 'unavailable' }, {}, {} ]);
309
+ done();
310
+ });
311
+ });
312
+ });
313
+
314
+ describe('#request-friend', () => {
315
+ it('calls xmpp.js correctly', (done) => {
316
+ xp['request-friend'](job['request-friend'], () => {
317
+ sinon.assert.calledOnce(xp.__client.send);
318
+ expect(xmlFake.getCall(0).args).to.eql(["presence", {
319
+ type: "subscribe", to: job['request-friend'].target['id']
320
+ }]);
321
+ done();
322
+ });
323
+ });
324
+ });
325
+
326
+ describe('#remove-friend', () => {
327
+ it('calls xmpp.js correctly', (done) => {
328
+ xp['remove-friend'](job['remove-friend'], () => {
329
+ sinon.assert.calledOnce(xp.__client.send);
330
+ expect(xmlFake.getCall(0).args).to.eql(["presence", {
331
+ type: "unsubscribe", to: job['remove-friend'].target['id']
332
+ }]);
333
+ done();
334
+ });
335
+ });
336
+ });
337
+
338
+ describe('#make-friend', () => {
339
+ it('calls xmpp.js correctly', (done) => {
340
+ xp['remove-friend'](job['remove-friend'], () => {
341
+ sinon.assert.calledOnce(xp.__client.send);
342
+ expect(xmlFake.getCall(0).args).to.eql(["presence", {
343
+ type: "unsubscribe", to: job['make-friend'].target['id']
344
+ }]);
345
+ done();
346
+ });
347
+ });
348
+ });
349
+
350
+ describe('#query', () => {
351
+ it('calls xmpp.js correctly', (done) => {
352
+ xp.query(job.query, () => {
353
+ sinon.assert.calledOnce(xp.__client.send);
354
+ expect(xmlFake.getCall(0).args).to.eql(
355
+ ["query", {xmlns: 'http://jabber.org/protocol/disco#items'}]);
356
+ expect(xmlFake.getCall(1).args).to.eql(["iq", {
357
+ id: 'muc_id',
358
+ type: 'get',
359
+ from: "testingham@jabber.net",
360
+ to: "partyroom@jabber.net"
361
+ }, undefined]);
362
+ done();
363
+ });
364
+ });
365
+ });
366
+ });
367
+ });
package/src/schema.js ADDED
@@ -0,0 +1,49 @@
1
+ module.exports = {
2
+ "name": "xmpp",
3
+ "version": require('../package.json').version,
4
+ "messages": {
5
+ "required": ['type'],
6
+ "properties": {
7
+ "type": {
8
+ "enum": ['connect', 'update', 'send', 'join', 'leave', 'query', 'request-friend',
9
+ 'remove-friend', 'make-friend']
10
+ }
11
+ }
12
+ },
13
+ "credentials": {
14
+ "required": ['object'],
15
+ "properties": {
16
+ // TODO platforms shouldn't have to define the actor property if
17
+ // they don't want to, just credential specifics
18
+ "actor": {
19
+ "type": "object",
20
+ "required": ["id"]
21
+ },
22
+ "object": {
23
+ "type": "object",
24
+ "required": ['type', 'userAddress', 'password', 'resource'],
25
+ "additionalProperties": false,
26
+ "properties": {
27
+ "type": {
28
+ "type": "string"
29
+ },
30
+ "userAddress": {
31
+ "type": "string"
32
+ },
33
+ "password": {
34
+ "type": "string"
35
+ },
36
+ "server": {
37
+ "type": "string"
38
+ },
39
+ "port": {
40
+ "type": "number"
41
+ },
42
+ "resource": {
43
+ "type": "string"
44
+ }
45
+ }
46
+ }
47
+ }
48
+ }
49
+ };
package/src/utils.js ADDED
@@ -0,0 +1,18 @@
1
+
2
+ module.exports = {
3
+ buildXmppCredentials: function (credentials) {
4
+ const [ username, server ] = credentials.object.userAddress.split('@');
5
+ let xmpp_creds = {
6
+ service: credentials.object.server ? credentials.object.server : server ? server : undefined,
7
+ username: username,
8
+ password: credentials.object.password
9
+ };
10
+ if (credentials.object.port) {
11
+ xmpp_creds.service = `${xmpp_creds.service}:${credentials.object.port}`;
12
+ }
13
+ if (credentials.object.resource) {
14
+ xmpp_creds.resource = credentials.object.resource;
15
+ }
16
+ return xmpp_creds;
17
+ }
18
+ };
@@ -0,0 +1,38 @@
1
+ const chai = require('chai');
2
+ const utils = require( "./utils");
3
+ const expect = chai.expect;
4
+
5
+ describe('Utils', () => {
6
+ describe('buildXmppCredentials', () => {
7
+ it('returns correct credential object used for xmpp.js connect', () => {
8
+ expect(utils.buildXmppCredentials({object: {
9
+ userAddress: 'barney@dinosaur.com.au', password:'bar', resource: 'Home'
10
+ }})).to.eql({
11
+ password: 'bar',
12
+ service: "dinosaur.com.au",
13
+ username: "barney",
14
+ resource: 'Home'
15
+ });
16
+ });
17
+ });
18
+ it('allows overriding server value', () => {
19
+ expect(utils.buildXmppCredentials({object: {
20
+ userAddress: 'barney@dinosaur.com.au', server:'foo', password:'bar', resource: 'Home'
21
+ }})).to.eql({
22
+ password: 'bar',
23
+ service: "foo",
24
+ username: "barney",
25
+ resource: 'Home'
26
+ });
27
+ });
28
+ it('allows a custom port', () => {
29
+ expect(utils.buildXmppCredentials({object: {
30
+ userAddress: 'barney@dinosaur.com.au', port:123, password:'bar', resource: 'Home'
31
+ }})).to.eql({
32
+ password: 'bar',
33
+ service: "dinosaur.com.au:123",
34
+ username: "barney",
35
+ resource: 'Home'
36
+ });
37
+ });
38
+ });