Skip to content

Commit dde5fb7

Browse files
committed
Check for valid fields on state future send
Also commonize error checking between message/state events/futures
1 parent 29b2628 commit dde5fb7

File tree

2 files changed

+223
-120
lines changed

2 files changed

+223
-120
lines changed

spec/unit/embedded.spec.ts

Lines changed: 167 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,21 @@ class MockWidgetApi extends EventEmitter {
6060
public requestCapabilityToSendToDevice = jest.fn();
6161
public requestCapabilityToReceiveToDevice = jest.fn();
6262
public sendRoomEvent = jest.fn(
63-
(eventType: string, content: unknown, roomId?: string, futureTimeout?: number, futureGroupId?: string) =>
63+
(
64+
eventType: string,
65+
content: unknown,
66+
roomId?: string,
67+
futureTimeout?: number,
68+
futureGroupId?: string
69+
) =>
6470
futureTimeout === undefined && futureGroupId === undefined
6571
? { event_id: `$${Math.random()}` }
6672
: {
6773
future_group_id: futureGroupId ?? `fg-${Math.random()}`,
6874
send_token: `st-${Math.random()}`,
6975
cancel_token: `ct-${Math.random()}`,
7076
refresh_token: `rt-${Math.random()}`,
71-
},
77+
}
7278
);
7379
public sendStateEvent = jest.fn(
7480
(
@@ -86,7 +92,7 @@ class MockWidgetApi extends EventEmitter {
8692
send_token: `st-${Math.random()}`,
8793
cancel_token: `ct-${Math.random()}`,
8894
refresh_token: `rt-${Math.random()}`,
89-
},
95+
}
9096
);
9197
public sendToDevice = jest.fn();
9298
public requestOpenIDConnectToken = jest.fn(() => {
@@ -188,102 +194,173 @@ describe("RoomWidgetClient", () => {
188194
});
189195

190196
describe("futures", () => {
191-
const doesServerSupportUnstableFeatureMock = jest.fn((feature) =>
192-
Promise.resolve(feature === "org.matrix.msc4140"),
193-
);
197+
describe("when supported", () => {
198+
const doesServerSupportUnstableFeatureMock = jest.fn((feature) =>
199+
Promise.resolve(feature === "org.matrix.msc4140")
200+
);
194201

195-
beforeAll(() => {
196-
MatrixClient.prototype.doesServerSupportUnstableFeature = doesServerSupportUnstableFeatureMock;
197-
});
202+
beforeAll(() => {
203+
MatrixClient.prototype.doesServerSupportUnstableFeature = doesServerSupportUnstableFeatureMock;
204+
});
198205

199-
afterAll(() => {
200-
doesServerSupportUnstableFeatureMock.mockClear();
201-
});
206+
afterAll(() => {
207+
doesServerSupportUnstableFeatureMock.mockReset();
208+
});
202209

203-
it("sends futures in a new group for message events", async () => {
204-
await makeClient({ sendEvent: ["org.matrix.rageshake_request"] });
205-
expect(widgetApi.requestCapabilityForRoomTimeline).toHaveBeenCalledWith("!1:example.org");
206-
expect(widgetApi.requestCapabilityToSendEvent).toHaveBeenCalledWith("org.matrix.rageshake_request");
207-
const futureOpts = {
208-
future_timeout: 2000,
209-
};
210-
await client._unstable_sendFuture("!1:example.org", futureOpts, null, "org.matrix.rageshake_request", {
211-
request_id: 123,
210+
it("sends futures in a new group for message events", async () => {
211+
await makeClient({ sendEvent: ["org.matrix.rageshake_request"] });
212+
expect(widgetApi.requestCapabilityForRoomTimeline).toHaveBeenCalledWith("!1:example.org");
213+
expect(widgetApi.requestCapabilityToSendEvent).toHaveBeenCalledWith("org.matrix.rageshake_request");
214+
await client._unstable_sendFuture(
215+
"!1:example.org",
216+
{ future_timeout: 2000 },
217+
null,
218+
"org.matrix.rageshake_request",
219+
{ request_id: 123 },
220+
);
221+
expect(widgetApi.sendRoomEvent).toHaveBeenCalledWith(
222+
"org.matrix.rageshake_request",
223+
{ request_id: 123 },
224+
"!1:example.org",
225+
2000,
226+
undefined,
227+
);
212228
});
213-
expect(widgetApi.sendRoomEvent).toHaveBeenCalledWith(
214-
"org.matrix.rageshake_request",
215-
{ request_id: 123 },
216-
"!1:example.org",
217-
futureOpts.future_timeout,
218-
undefined,
219-
);
220-
});
221229

222-
it("sends action futures in an existing group for message events", async () => {
223-
await makeClient({ sendEvent: ["org.matrix.rageshake_request"] });
224-
expect(widgetApi.requestCapabilityForRoomTimeline).toHaveBeenCalledWith("!1:example.org");
225-
expect(widgetApi.requestCapabilityToSendEvent).toHaveBeenCalledWith("org.matrix.rageshake_request");
226-
const futureOpts = {
227-
future_group_id: "fg",
228-
};
229-
await client._unstable_sendFuture("!1:example.org", futureOpts, null, "org.matrix.rageshake_request", {
230-
request_id: 123,
230+
it("sends action futures in an existing group for message events", async () => {
231+
await makeClient({ sendEvent: ["org.matrix.rageshake_request"] });
232+
expect(widgetApi.requestCapabilityForRoomTimeline).toHaveBeenCalledWith("!1:example.org");
233+
expect(widgetApi.requestCapabilityToSendEvent).toHaveBeenCalledWith("org.matrix.rageshake_request");
234+
const futureGroupId = `fg-${Math.random()}`;
235+
await client._unstable_sendFuture(
236+
"!1:example.org",
237+
{ future_group_id: futureGroupId },
238+
null,
239+
"org.matrix.rageshake_request",
240+
{ request_id: 123 },
241+
);
242+
expect(widgetApi.sendRoomEvent).toHaveBeenCalledWith(
243+
"org.matrix.rageshake_request",
244+
{ request_id: 123 },
245+
"!1:example.org",
246+
undefined,
247+
futureGroupId,
248+
);
231249
});
232-
expect(widgetApi.sendRoomEvent).toHaveBeenCalledWith(
233-
"org.matrix.rageshake_request",
234-
{ request_id: 123 },
235-
"!1:example.org",
236-
undefined,
237-
futureOpts.future_group_id,
238-
);
239-
});
240250

241-
it("sends futures in a new group for state events", async () => {
242-
await makeClient({ sendState: [{ eventType: "org.example.foo", stateKey: "bar" }] });
243-
expect(widgetApi.requestCapabilityForRoomTimeline).toHaveBeenCalledWith("!1:example.org");
244-
expect(widgetApi.requestCapabilityToSendState).toHaveBeenCalledWith("org.example.foo", "bar");
245-
const futureOpts = {
246-
future_timeout: 2000,
247-
};
248-
await client._unstable_sendStateFuture(
249-
"!1:example.org",
250-
futureOpts,
251-
"org.example.foo",
252-
{ hello: "world" },
253-
"bar",
254-
);
255-
expect(widgetApi.sendStateEvent).toHaveBeenCalledWith(
256-
"org.example.foo",
257-
"bar",
258-
{ hello: "world" },
259-
"!1:example.org",
260-
futureOpts.future_timeout,
261-
undefined,
262-
);
251+
it("sends futures in a new group for state events", async () => {
252+
await makeClient({ sendState: [{ eventType: "org.example.foo", stateKey: "bar" }] });
253+
expect(widgetApi.requestCapabilityForRoomTimeline).toHaveBeenCalledWith("!1:example.org");
254+
expect(widgetApi.requestCapabilityToSendState).toHaveBeenCalledWith("org.example.foo", "bar");
255+
await client._unstable_sendStateFuture(
256+
"!1:example.org",
257+
{ future_timeout: 2000 },
258+
"org.example.foo",
259+
{ hello: "world" },
260+
"bar",
261+
);
262+
expect(widgetApi.sendStateEvent).toHaveBeenCalledWith(
263+
"org.example.foo",
264+
"bar",
265+
{ hello: "world" },
266+
"!1:example.org",
267+
2000,
268+
undefined,
269+
);
270+
});
271+
272+
it("sends action futures in an existing group for state events", async () => {
273+
await makeClient({ sendState: [{ eventType: "org.example.foo", stateKey: "bar" }] });
274+
expect(widgetApi.requestCapabilityForRoomTimeline).toHaveBeenCalledWith("!1:example.org");
275+
expect(widgetApi.requestCapabilityToSendState).toHaveBeenCalledWith("org.example.foo", "bar");
276+
const futureGroupId = `fg-${Math.random()}`;
277+
await client._unstable_sendStateFuture(
278+
"!1:example.org",
279+
{ future_group_id: futureGroupId },
280+
"org.example.foo",
281+
{ hello: "world" },
282+
"bar",
283+
);
284+
expect(widgetApi.sendStateEvent).toHaveBeenCalledWith(
285+
"org.example.foo",
286+
"bar",
287+
{ hello: "world" },
288+
"!1:example.org",
289+
undefined,
290+
futureGroupId,
291+
);
292+
});
293+
294+
describe("when improperly implemented", () => {
295+
const response = {
296+
future_group_id: `fg-${Math.random()}`,
297+
send_token: `st-${Math.random()}`,
298+
cancel_token: `ct-${Math.random()}`,
299+
refresh_token: `rt-${Math.random()}`,
300+
};
301+
302+
for (const missingField of Object.keys(response)) {
303+
const badResponse: Record<string, string> = {...response};
304+
delete badResponse[missingField];
305+
const fullBadResponse = { ...badResponse, room_id: "!1:example.org" };
306+
307+
it(`fails to send message futures without a ${missingField}`, async () => {
308+
jest.spyOn(widgetApi, "sendRoomEvent").mockResolvedValue(fullBadResponse);
309+
await makeClient({ sendEvent: ["org.matrix.rageshake_request"] });
310+
await expect(
311+
client._unstable_sendFuture(
312+
"!1:example.org",
313+
{ future_timeout: 2000 },
314+
null,
315+
"org.matrix.rageshake_request",
316+
{ request_id: 123 },
317+
)
318+
).rejects.toThrow(`'${missingField}' absent from response`);
319+
});
320+
321+
it(`fails to send state futures without a ${missingField}`, async () => {
322+
jest.spyOn(widgetApi, "sendStateEvent").mockResolvedValue(fullBadResponse);
323+
await makeClient({ sendState: [{ eventType: "org.example.foo", stateKey: "bar" }] });
324+
await expect(
325+
client._unstable_sendStateFuture(
326+
"!1:example.org",
327+
{ future_timeout: 2000, },
328+
"org.example.foo",
329+
{ hello: "world" },
330+
"bar",
331+
)
332+
).rejects.toThrow(`'${missingField}' absent from response`);
333+
});
334+
}
335+
});
263336
});
264337

265-
it("sends action futures in an existing group for state events", async () => {
266-
await makeClient({ sendState: [{ eventType: "org.example.foo", stateKey: "bar" }] });
267-
expect(widgetApi.requestCapabilityForRoomTimeline).toHaveBeenCalledWith("!1:example.org");
268-
expect(widgetApi.requestCapabilityToSendState).toHaveBeenCalledWith("org.example.foo", "bar");
269-
const futureOpts = {
270-
future_group_id: "fg",
271-
};
272-
await client._unstable_sendStateFuture(
273-
"!1:example.org",
274-
futureOpts,
275-
"org.example.foo",
276-
{ hello: "world" },
277-
"bar",
278-
);
279-
expect(widgetApi.sendStateEvent).toHaveBeenCalledWith(
280-
"org.example.foo",
281-
"bar",
282-
{ hello: "world" },
283-
"!1:example.org",
284-
undefined,
285-
futureOpts.future_group_id,
286-
);
338+
describe("when unsupported", () => {
339+
it("fails to send message futures", async () => {
340+
await makeClient({ sendEvent: ["org.matrix.rageshake_request"] });
341+
await expect(
342+
client._unstable_sendFuture(
343+
"!1:example.org",
344+
{ future_timeout: 2000 },
345+
null,
346+
"org.matrix.rageshake_request",
347+
{ request_id: 123 },
348+
)
349+
).rejects.toThrow("Server does not support the Futures API");
350+
});
351+
352+
it("fails to send state futures", async () => {
353+
await makeClient({ sendState: [{ eventType: "org.example.foo", stateKey: "bar" }] });
354+
await expect(
355+
client._unstable_sendStateFuture(
356+
"!1:example.org",
357+
{ future_timeout: 2000, },
358+
"org.example.foo",
359+
{ hello: "world" },
360+
"bar",
361+
)
362+
).rejects.toThrow("Server does not support the Futures API");
363+
});
287364
});
288365
});
289366

0 commit comments

Comments
 (0)