Skip to content

Commit

Permalink
Ensure appservice intents update their internal membership cache (#243)
Browse files Browse the repository at this point in the history
* Ensure appservice intents update their internal membership cache

Fixes #113

* Fix appservice tests
  • Loading branch information
turt2live authored Jul 6, 2022
1 parent c8ec5a8 commit ebfb01a
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 2 deletions.
7 changes: 5 additions & 2 deletions src/appservice/Appservice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -608,9 +608,12 @@ export class Appservice extends EventEmitter {
return event;
}

private processMembershipEvent(event: any): void {
private async processMembershipEvent(event: any): Promise<void> {
if (!event["content"]) return;

// Update the target intent's joined rooms (fixes transition errors with the cache, like join->kick->join)
await this.getIntentForUserId(event['state_key']).refreshJoinedRooms();

const targetMembership = event["content"]["membership"];
if (targetMembership === "join") {
this.emit("room.join", event["room_id"], event);
Expand Down Expand Up @@ -830,7 +833,7 @@ export class Appservice extends EventEmitter {
this.emit("room.message", event["room_id"], event);
}
if (event['type'] === 'm.room.member' && this.isNamespacedUser(event['state_key'])) {
this.processMembershipEvent(event);
await this.processMembershipEvent(event);
}
if (event['type'] === 'm.room.tombstone' && event['state_key'] === '') {
this.emit("room.archived", event['room_id'], event);
Expand Down
95 changes: 95 additions & 0 deletions test/appservice/AppserviceTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1429,6 +1429,9 @@ describe('Appservice', () => {
return null;
};

const intent = appservice.getIntentForSuffix("test");
intent.refreshJoinedRooms = () => Promise.resolve([]);

await appservice.begin();

try {
Expand Down Expand Up @@ -1532,6 +1535,98 @@ describe('Appservice', () => {
}
});

it('should refresh membership information of intents when actions are performed against them', async () => {
const port = await getPort();
const hsToken = "s3cret_token";
const appservice = new Appservice({
port: port,
bindAddress: '',
homeserverName: 'example.org',
homeserverUrl: 'https://localhost',
registration: {
as_token: "",
hs_token: hsToken,
sender_localpart: "_bot_",
namespaces: {
users: [{ exclusive: true, regex: "@_prefix_.*:.+" }],
rooms: [],
aliases: [],
},
},
});
appservice.botIntent.ensureRegistered = () => {
return null;
};

await appservice.begin();

try {
const intent = appservice.getIntentForSuffix("test");
const refreshSpy = simple.stub().callFn(() => Promise.resolve([]));
intent.refreshJoinedRooms = refreshSpy;

// polyfill the dummy user too
const intent2 = appservice.getIntentForSuffix("test___WRONGUSER");
intent2.refreshJoinedRooms = () => Promise.resolve([]);

const joinTxn = {
events: [
{
type: "m.room.member",
room_id: "!AAA:example.org",
content: { membership: "join" },
state_key: "@_prefix_test:example.org",
sender: "@_prefix_test:example.org",
},
{
type: "m.room.member",
room_id: "!AAA:example.org",
content: { membership: "join" },
state_key: "@_prefix_test___WRONGUSER:example.org",
sender: "@_prefix_test:example.org",
},
],
};
const kickTxn = {
events: [
{
type: "m.room.member",
room_id: "!AAA:example.org",
content: { membership: "leave" },
state_key: "@_prefix_test:example.org",
sender: "@someone_else:example.org",
},
{
type: "m.room.member",
room_id: "!AAA:example.org",
content: { membership: "leave" },
state_key: "@_prefix_test___WRONGUSER:example.org",
sender: "@someone_else:example.org",
},
],
};

// eslint-disable-next-line no-inner-declarations
async function doCall(route: string, opts: any = {}) {
const res = await requestPromise({
uri: `http://localhost:${port}${route}`,
method: "PUT",
qs: { access_token: hsToken },
...opts,
});
expect(res).toMatchObject({});

expect(refreshSpy.callCount).toBe(1);
refreshSpy.callCount = 0;
}

await doCall("/transactions/1", { json: joinTxn });
await doCall("/_matrix/app/v1/transactions/2", { json: kickTxn });
} finally {
appservice.stop();
}
});

it('should handle room upgrade events in transactions', async () => {
const port = await getPort();
const hsToken = "s3cret_token";
Expand Down

0 comments on commit ebfb01a

Please sign in to comment.