@sockethub/platform-xmpp 5.0.0-alpha.4 → 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/API.md +146 -97
- package/README.md +75 -18
- package/package.json +15 -20
- package/src/incoming-handlers.js +298 -263
- package/src/incoming-handlers.test.data.js +260 -0
- package/src/incoming-handlers.test.js +87 -29
- package/src/index.js +696 -454
- package/src/index.test.js +477 -319
- package/src/schema.js +60 -48
- package/src/utils.js +21 -18
- package/src/utils.test.js +53 -33
- package/.eslintignore +0 -2
- package/coverage/tmp/coverage-39286-1663949499396-0.json +0 -1
- package/src/incoming-handlers.data.js +0 -198
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
export const stanzas = [
|
|
2
|
+
[
|
|
3
|
+
"presence error 1",
|
|
4
|
+
`<presence type="error" to="hermes@5apps.com/hyperchannel" from="xmpp.5apps.com/#watercooler" xmlns:stream="http://etherx.jabber.org/streams"><error type="cancel"> <remote-server-not-found xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error></presence>`,
|
|
5
|
+
{
|
|
6
|
+
context: "xmpp",
|
|
7
|
+
type: "join",
|
|
8
|
+
actor: { id: "xmpp.5apps.com/#watercooler", type: "room" },
|
|
9
|
+
error: "remote server not found xmpp.5apps.com/#watercooler",
|
|
10
|
+
target: { id: "hermes@5apps.com/hyperchannel", type: "person" },
|
|
11
|
+
},
|
|
12
|
+
],
|
|
13
|
+
[
|
|
14
|
+
"presence error 2",
|
|
15
|
+
`<presence type="error" to="hermes@5apps.com/hyperchannel" from="xmpp.5apps.com/#watercooler" xmlns:stream="http://etherx.jabber.org/streams"><error type="cancel"><not-allowed xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/><text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">Communication with remote domains is not enabled</text></error></presence>`,
|
|
16
|
+
{
|
|
17
|
+
context: "xmpp",
|
|
18
|
+
type: "update",
|
|
19
|
+
actor: { id: "xmpp.5apps.com/#watercooler", type: "room" },
|
|
20
|
+
error:
|
|
21
|
+
'<error type="cancel"><not-allowed xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>' +
|
|
22
|
+
'<text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">Communication with remote domains is not enabled</text>' +
|
|
23
|
+
"</error>",
|
|
24
|
+
target: { id: "hermes@5apps.com/hyperchannel", type: "person" },
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
[
|
|
28
|
+
"initial presence",
|
|
29
|
+
`<presence to="foo@bar.org" from="baz@bag.org"></presence>`,
|
|
30
|
+
{
|
|
31
|
+
context: "xmpp",
|
|
32
|
+
type: "update",
|
|
33
|
+
actor: { id: "baz@bag.org", type: "person" },
|
|
34
|
+
target: { id: "foo@bar.org", type: "person" },
|
|
35
|
+
object: { type: "presence", presence: "online" },
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
[
|
|
39
|
+
"presence body",
|
|
40
|
+
`<presence to="foo@bar.org" from="baz@bag.org"><show>online</show> <status>away message!</status></presence>`,
|
|
41
|
+
{
|
|
42
|
+
context: "xmpp",
|
|
43
|
+
type: "update",
|
|
44
|
+
actor: { id: "baz@bag.org", type: "person" },
|
|
45
|
+
target: { id: "foo@bar.org", type: "person" },
|
|
46
|
+
object: {
|
|
47
|
+
type: "presence",
|
|
48
|
+
content: "away message!",
|
|
49
|
+
presence: "online",
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
[
|
|
54
|
+
"presence unavailable",
|
|
55
|
+
`<presence to="foo@bar.org/hyperchannel" from="baz@bag.org/yarg" type="unavailable"><x xmlns="http://jabber.org/protocol/muc#user"><item affiliation="owner" role="none"></item></x></presence>`,
|
|
56
|
+
{
|
|
57
|
+
context: "xmpp",
|
|
58
|
+
type: "update",
|
|
59
|
+
actor: { type: "person", id: "baz@bag.org/yarg" },
|
|
60
|
+
target: { id: "foo@bar.org/hyperchannel", type: "person" },
|
|
61
|
+
object: { type: "presence", presence: "offline" },
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
[
|
|
65
|
+
"presence away",
|
|
66
|
+
`<presence to="foo@bar.org/hyperchannel" from="baz@bag.org/yarg" type="available"><show>away</show><x xmlns="http://jabber.org/protocol/muc#user"><item affiliation="owner" role="none"></item></x></presence>`,
|
|
67
|
+
{
|
|
68
|
+
context: "xmpp",
|
|
69
|
+
type: "update",
|
|
70
|
+
actor: { type: "person", id: "baz@bag.org/yarg" },
|
|
71
|
+
target: { id: "foo@bar.org/hyperchannel", type: "person" },
|
|
72
|
+
object: { type: "presence", presence: "away" },
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
[
|
|
76
|
+
"attendance",
|
|
77
|
+
`<iq id="muc_id" type="result" to="ernie@jabber.net/Home" from="PartyChatRoom@jabber.net" xmlns:stream="http://etherx.jabber.org/streams"> <query xmlns="http://jabber.org/protocol/disco#items"> <item jid="PartyChatRoom@jabber.net/ernie" name="ernie"/> <item jid="PartyChatRoom@jabber.net/bert" name="bert"/><item jid="PartyChatRoom@jabber.net/oscar" name="oscar"/> <item jid="PartyChatRoom@jabber.net/big_bird" name="big_bird"/> <item jid="PartyChatRoom@jabber.net/elmo" name="elmo"/></query></iq>`,
|
|
78
|
+
{
|
|
79
|
+
context: "xmpp",
|
|
80
|
+
type: "query",
|
|
81
|
+
actor: { id: "PartyChatRoom@jabber.net", type: "room" },
|
|
82
|
+
target: { id: "ernie@jabber.net/Home", type: "person" },
|
|
83
|
+
object: {
|
|
84
|
+
type: "attendance",
|
|
85
|
+
members: ["ernie", "bert", "oscar", "big_bird", "elmo"],
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
[
|
|
90
|
+
"message",
|
|
91
|
+
`<message from="radical@example.org/thinkpad" to="user@jabber.org" type="chat" id="purple9840c15f" xmlns:stream="http://etherx.jabber.org/streams">
|
|
92
|
+
<active xmlns="http://jabber.org/protocol/chatstates" />
|
|
93
|
+
<stanza-id xmlns="urn:xmpp:sid:0" id="123456789" />
|
|
94
|
+
<body>ohai</body>
|
|
95
|
+
</message>`,
|
|
96
|
+
{
|
|
97
|
+
context: "xmpp",
|
|
98
|
+
type: "send",
|
|
99
|
+
actor: { type: "person", id: "radical@example.org/thinkpad" },
|
|
100
|
+
target: { type: "person", id: "user@jabber.org" },
|
|
101
|
+
object: {
|
|
102
|
+
type: "message",
|
|
103
|
+
content: "ohai",
|
|
104
|
+
id: "purple9840c15f",
|
|
105
|
+
"xmpp:stanza-id": "123456789",
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
[
|
|
110
|
+
"message with delay (e.g. offline message)",
|
|
111
|
+
`<message from="radical@example.org/thinkpad" to="user@jabber.org" type="chat" id="purple9840c15f" xmlns:stream="http://etherx.jabber.org/streams">
|
|
112
|
+
<active xmlns="http://jabber.org/protocol/chatstates" />
|
|
113
|
+
<stanza-id xmlns="urn:xmpp:sid:0" id="123456789" />
|
|
114
|
+
<delay xmlns="urn:xmpp:delay" from="jabber.org" stamp="2021-04-17T18:50:25Z">Offline Storage</delay>
|
|
115
|
+
<body>ohai</body>
|
|
116
|
+
</message>`,
|
|
117
|
+
{
|
|
118
|
+
context: "xmpp",
|
|
119
|
+
type: "send",
|
|
120
|
+
published: "2021-04-17T18:50:25.000Z",
|
|
121
|
+
actor: { type: "person", id: "radical@example.org/thinkpad" },
|
|
122
|
+
target: { type: "person", id: "user@jabber.org" },
|
|
123
|
+
object: {
|
|
124
|
+
type: "message",
|
|
125
|
+
content: "ohai",
|
|
126
|
+
id: "purple9840c15f",
|
|
127
|
+
"xmpp:stanza-id": "123456789",
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
[
|
|
132
|
+
"message correction (XEP-0308)",
|
|
133
|
+
`<message from="radical@example.org/thinkpad" to="user@jabber.org" type="chat" id="purple9840c15f" xmlns:stream="http://etherx.jabber.org/streams">
|
|
134
|
+
<active xmlns="http://jabber.org/protocol/chatstates" />
|
|
135
|
+
<stanza-id xmlns="urn:xmpp:sid:0" id="123456789" />
|
|
136
|
+
<replace id="purple1234c15f" />
|
|
137
|
+
<body>oh hey</body>
|
|
138
|
+
</message>`,
|
|
139
|
+
{
|
|
140
|
+
context: "xmpp",
|
|
141
|
+
type: "send",
|
|
142
|
+
actor: { type: "person", id: "radical@example.org/thinkpad" },
|
|
143
|
+
target: { type: "person", id: "user@jabber.org" },
|
|
144
|
+
object: {
|
|
145
|
+
type: "message",
|
|
146
|
+
content: "oh hey",
|
|
147
|
+
id: "purple9840c15f",
|
|
148
|
+
"xmpp:stanza-id": "123456789",
|
|
149
|
+
"xmpp:replace": { id: "purple1234c15f" },
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
[
|
|
154
|
+
"group presence",
|
|
155
|
+
`<presence from='room@xmpp.example.org/speedboat'><show>chat</show> <status>brrroom!</status></presence>`,
|
|
156
|
+
{
|
|
157
|
+
context: "xmpp",
|
|
158
|
+
type: "update",
|
|
159
|
+
actor: {
|
|
160
|
+
id: "room@xmpp.example.org/speedboat",
|
|
161
|
+
type: "person",
|
|
162
|
+
name: "speedboat",
|
|
163
|
+
},
|
|
164
|
+
object: { type: "presence", content: "brrroom!", presence: "chat" },
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
[
|
|
168
|
+
"group message",
|
|
169
|
+
`<message from='coven@chat.shakespeare.lit/thirdwitch' id='hysf1v37' to='crone1@shakespeare.lit/desktop' type='groupchat'>
|
|
170
|
+
<stanza-id id="123456789" />
|
|
171
|
+
<body>Thrice the brinded cat hath mew'd.</body>
|
|
172
|
+
</message>`,
|
|
173
|
+
{
|
|
174
|
+
context: "xmpp",
|
|
175
|
+
type: "send",
|
|
176
|
+
actor: {
|
|
177
|
+
type: "person",
|
|
178
|
+
id: "coven@chat.shakespeare.lit/thirdwitch",
|
|
179
|
+
name: "thirdwitch",
|
|
180
|
+
},
|
|
181
|
+
target: { type: "room", id: "coven@chat.shakespeare.lit" },
|
|
182
|
+
object: {
|
|
183
|
+
id: "hysf1v37",
|
|
184
|
+
type: "message",
|
|
185
|
+
content: "Thrice the brinded cat hath mew'd.",
|
|
186
|
+
"xmpp:stanza-id": "123456789",
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
// [
|
|
191
|
+
// 'subscribe',
|
|
192
|
+
// `<presence from=’user1@example.com’ to=’user2@example.com’ type=’subscribe’></presence>`,
|
|
193
|
+
// {}
|
|
194
|
+
// ],
|
|
195
|
+
// [
|
|
196
|
+
// 'subscribed',
|
|
197
|
+
// `<presence from=’user2@example.com’ to=’user1@example.com’ type=’subscribed’></presence>`,
|
|
198
|
+
// {}
|
|
199
|
+
// ],
|
|
200
|
+
// [
|
|
201
|
+
// 'status',
|
|
202
|
+
// `<presence><show>away</show><status>feeding the chickens</status></presence>`,
|
|
203
|
+
// {}
|
|
204
|
+
// ],
|
|
205
|
+
// [
|
|
206
|
+
// 'request roster',
|
|
207
|
+
// `<iq from=’abc@example.com’ type=’get’ id=’xyz123’>
|
|
208
|
+
// <query xmlns=’jabber:iq:roster’/> </iq>`,
|
|
209
|
+
// {}
|
|
210
|
+
// ],
|
|
211
|
+
// [
|
|
212
|
+
// 'present roster',
|
|
213
|
+
// `<iq to=’abc@example.com’ type=’result’ id=’xyz123’> <query xmlns=’jabber:iq:roster’>
|
|
214
|
+
// <item jid=’efg@example.com’ name=’EFG’/> <item jid=’hij’@ example.com’ name=’HIJ’/>
|
|
215
|
+
// </query> </iq>`,
|
|
216
|
+
// {}
|
|
217
|
+
// ],
|
|
218
|
+
// [
|
|
219
|
+
// 'join room',
|
|
220
|
+
// `<presence from='hav66@shakespeare.lit/pda' id='n13mt3l'
|
|
221
|
+
// to='coven@chat.shakespeare.lit/thirdwitch'>
|
|
222
|
+
// <x xmlns='http://jabber.org/protocol/muc'/> </presence>`,
|
|
223
|
+
// {}
|
|
224
|
+
// ],
|
|
225
|
+
[
|
|
226
|
+
"JID malformed",
|
|
227
|
+
`<presence from='coven@chat.shakespeare.lit' id='273hs51g' to='hag66@shakespeare.lit/pda' type='error'> <error by='coven@chat.shakespeare.lit' type='modify'> <jid-malformed xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </presence>`,
|
|
228
|
+
{
|
|
229
|
+
context: "xmpp",
|
|
230
|
+
type: "update",
|
|
231
|
+
actor: { id: "coven@chat.shakespeare.lit", type: "room" },
|
|
232
|
+
error: `<error by="coven@chat.shakespeare.lit" type="modify"> <jid-malformed xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/> </error>`,
|
|
233
|
+
target: { id: "hag66@shakespeare.lit/pda", type: "person" },
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
// [
|
|
237
|
+
// 'presence affiliation owner',
|
|
238
|
+
// `<presence from='coven@chat.shakespeare.lit/firstwitch'
|
|
239
|
+
// id='3DCB0401-D7CF-4E31-BE05-EDF8D057BFBD' to='hag66@shakespeare.lit/pda'>
|
|
240
|
+
// <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='owner'
|
|
241
|
+
// role='moderator'/> </x> </presence>`,
|
|
242
|
+
// {}
|
|
243
|
+
// ],
|
|
244
|
+
// [
|
|
245
|
+
// 'presence affiliation member',
|
|
246
|
+
// `<presence from='coven@chat.shakespeare.lit/thirdwitch'
|
|
247
|
+
// id='27C55F89-1C6A-459A-9EB5-77690145D624' to='crone1@shakespeare.lit/desktop'>
|
|
248
|
+
// <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='member'
|
|
249
|
+
// role='participant'/> </x> </presence>`,
|
|
250
|
+
// {}
|
|
251
|
+
// ],
|
|
252
|
+
// [
|
|
253
|
+
// 'presence affiliation none',
|
|
254
|
+
// `<presence from='coven@chat.shakespeare.lit/thirdwitch'
|
|
255
|
+
// id='17232D15-134F-43C8-9A29-61C20A64B236' to='crone1@shakespeare.lit/desktop'>
|
|
256
|
+
// <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='none'
|
|
257
|
+
// jid='hag66@shakespeare.lit/pda' role='participant'/> </x> </presence>`,
|
|
258
|
+
// {}
|
|
259
|
+
// ]
|
|
260
|
+
];
|
|
@@ -1,34 +1,92 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
describe(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
import { beforeEach, describe, expect, it } from "bun:test";
|
|
2
|
+
import sinon from "sinon";
|
|
3
|
+
|
|
4
|
+
import * as schemas from "@sockethub/schemas";
|
|
5
|
+
import parse from "@xmpp/xml/lib/parse.js";
|
|
6
|
+
|
|
7
|
+
import { IncomingHandlers } from "./incoming-handlers.js";
|
|
8
|
+
import { stanzas } from "./incoming-handlers.test.data.js";
|
|
9
|
+
|
|
10
|
+
describe("Incoming handlers", () => {
|
|
11
|
+
describe("XML stanzas result in the expected AS objects", () => {
|
|
12
|
+
let ih, sendToClient;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
sendToClient = sinon.fake();
|
|
16
|
+
ih = new IncomingHandlers({
|
|
17
|
+
sendToClient: sendToClient,
|
|
18
|
+
debug: sinon.fake(),
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
stanzas.forEach(([name, stanza, asobject]) => {
|
|
23
|
+
it(name, () => {
|
|
24
|
+
const xmlObj = parse(stanza);
|
|
25
|
+
ih.stanza(xmlObj);
|
|
26
|
+
sinon.assert.calledWith(sendToClient, asobject);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it(`${name} - passes @sockethub/schemas validator`, () => {
|
|
30
|
+
expect(schemas.validateActivityStream(asobject)).toEqual("");
|
|
31
|
+
});
|
|
32
|
+
});
|
|
20
33
|
});
|
|
21
34
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
35
|
+
describe("Error handling edge cases", () => {
|
|
36
|
+
it("close() should handle undefined session gracefully", () => {
|
|
37
|
+
const ih = new IncomingHandlers();
|
|
38
|
+
ih.session = undefined;
|
|
39
|
+
|
|
40
|
+
// This should not throw an error
|
|
41
|
+
expect(() => ih.close()).not.toThrow();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("close() should handle session without actor gracefully", () => {
|
|
45
|
+
const sendToClient = sinon.fake();
|
|
46
|
+
const debug = sinon.fake();
|
|
47
|
+
|
|
48
|
+
const ih = new IncomingHandlers({
|
|
49
|
+
sendToClient: sendToClient,
|
|
50
|
+
debug: debug,
|
|
51
|
+
actor: undefined,
|
|
52
|
+
connection: undefined
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// This should not throw an error
|
|
56
|
+
expect(() => ih.close()).not.toThrow();
|
|
57
|
+
|
|
58
|
+
// Should still call debug
|
|
59
|
+
sinon.assert.calledWith(debug, "received close event with no handler specified");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("close() should handle session without connection gracefully", () => {
|
|
63
|
+
const sendToClient = sinon.fake();
|
|
64
|
+
const debug = sinon.fake();
|
|
65
|
+
|
|
66
|
+
const ih = new IncomingHandlers({
|
|
67
|
+
sendToClient: sendToClient,
|
|
68
|
+
debug: debug,
|
|
69
|
+
actor: { id: "test@example.com" },
|
|
70
|
+
connection: undefined
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// This should not throw an error
|
|
74
|
+
expect(() => ih.close()).not.toThrow();
|
|
75
|
+
});
|
|
28
76
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
77
|
+
it("close() should handle session with invalid connection gracefully", () => {
|
|
78
|
+
const sendToClient = sinon.fake();
|
|
79
|
+
const debug = sinon.fake();
|
|
80
|
+
|
|
81
|
+
const ih = new IncomingHandlers({
|
|
82
|
+
sendToClient: sendToClient,
|
|
83
|
+
debug: debug,
|
|
84
|
+
actor: { id: "test@example.com" },
|
|
85
|
+
connection: { disconnect: null } // invalid disconnect method
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// This should not throw an error
|
|
89
|
+
expect(() => ih.close()).not.toThrow();
|
|
90
|
+
});
|
|
32
91
|
});
|
|
33
|
-
});
|
|
34
92
|
});
|