Skip to content

Commit e119bf9

Browse files
dbkrMidhunSureshRrichvdh
authored
Support for creator power level (#4937)
* Support for creator power level Adds support for infinite power level specified by [MSC4289](matrix-org/matrix-spec-proposals#4289). * Update unit test * Hardcode versions as room versions strings aren't ordered * Add test for v12 rooms * Use more compact syntax Co-authored-by: R Midhun Suresh <[email protected]> * Fix doc Co-authored-by: R Midhun Suresh <[email protected]> * Fix additionalCreators from PR edit * Split out hydra room version check * Move power level logic into room state Which already has knowledge of the room create event * Add docs * Fix unused bits * Fix docs * Fix lying docstring * Reverse logic for hydra semantics Assume unknown room versions do use hydra * Use backticks Co-authored-by: Richard van der Hoff <[email protected]> * Switch back to hardcoding just the two hydra versions --------- Co-authored-by: R Midhun Suresh <[email protected]> Co-authored-by: Richard van der Hoff <[email protected]>
1 parent c7f982e commit e119bf9

File tree

6 files changed

+304
-200
lines changed

6 files changed

+304
-200
lines changed

spec/unit/room-member.spec.ts

Lines changed: 19 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import * as utils from "../test-utils/test-utils";
2020
import { RoomMember, RoomMemberEvent } from "../../src/models/room-member";
2121
import {
2222
createClient,
23-
EventType,
2423
type MatrixClient,
24+
MatrixEvent,
2525
type RoomState,
2626
UNSTABLE_MSC2666_MUTUAL_ROOMS,
2727
UNSTABLE_MSC2666_QUERY_MUTUAL_ROOMS,
@@ -101,158 +101,32 @@ describe("RoomMember", function () {
101101
});
102102
});
103103

104-
describe("setPowerLevelEvent", function () {
105-
it("should set 'powerLevel' and 'powerLevelNorm'.", function () {
106-
const event = utils.mkEvent({
107-
type: "m.room.power_levels",
108-
room: roomId,
109-
user: userA,
110-
content: {
111-
users_default: 20,
112-
users: {
113-
"@bertha:bar": 200,
114-
"@invalid:user": 10, // shouldn't barf on this.
115-
},
116-
},
117-
event: true,
118-
});
119-
member.setPowerLevelEvent(event);
120-
expect(member.powerLevel).toEqual(20);
121-
expect(member.powerLevelNorm).toEqual(10);
122-
123-
const memberB = new RoomMember(roomId, userB);
124-
memberB.setPowerLevelEvent(event);
125-
expect(memberB.powerLevel).toEqual(200);
126-
expect(memberB.powerLevelNorm).toEqual(100);
104+
describe("setPowerLevel", function () {
105+
it("should set 'powerLevel'.", function () {
106+
member.setPowerLevel(0, new MatrixEvent());
107+
expect(member.powerLevel).toEqual(0);
108+
member.setPowerLevel(200, new MatrixEvent());
109+
expect(member.powerLevel).toEqual(200);
127110
});
128111

129-
it("should emit 'RoomMember.powerLevel' if the power level changes.", function () {
130-
const event = utils.mkEvent({
131-
type: "m.room.power_levels",
132-
room: roomId,
133-
user: userA,
134-
content: {
135-
users_default: 20,
136-
users: {
137-
"@bertha:bar": 200,
138-
"@invalid:user": 10, // shouldn't barf on this.
139-
},
140-
},
141-
event: true,
142-
});
143-
let emitCount = 0;
112+
it("should emit when power level set", function () {
113+
const onEmit = jest.fn();
114+
member.on(RoomMemberEvent.PowerLevel, onEmit);
144115

145-
member.on(RoomMemberEvent.PowerLevel, function (emitEvent, emitMember) {
146-
emitCount += 1;
147-
expect(emitMember).toEqual(member);
148-
expect(emitEvent).toEqual(event);
149-
});
116+
const aMatrixEvent = new MatrixEvent();
117+
member.setPowerLevel(10, aMatrixEvent);
150118

151-
member.setPowerLevelEvent(event);
152-
expect(emitCount).toEqual(1);
153-
member.setPowerLevelEvent(event); // no-op
154-
expect(emitCount).toEqual(1);
119+
expect(onEmit).toHaveBeenCalledWith(aMatrixEvent, member);
155120
});
156121

157-
it("should honour power levels of zero.", function () {
158-
const event = utils.mkEvent({
159-
type: "m.room.power_levels",
160-
room: roomId,
161-
user: userA,
162-
content: {
163-
users_default: 20,
164-
users: {
165-
"@alice:bar": 0,
166-
},
167-
},
168-
event: true,
169-
});
170-
let emitCount = 0;
171-
172-
// set the power level to something other than zero or we
173-
// won't get an event
174-
member.powerLevel = 1;
175-
member.on(RoomMemberEvent.PowerLevel, function (emitEvent, emitMember) {
176-
emitCount += 1;
177-
expect(emitMember.userId).toEqual("@alice:bar");
178-
expect(emitMember.powerLevel).toEqual(0);
179-
expect(emitEvent).toEqual(event);
180-
});
181-
182-
member.setPowerLevelEvent(event);
183-
expect(member.powerLevel).toEqual(0);
184-
expect(emitCount).toEqual(1);
185-
});
186-
187-
it("should not honor string power levels.", function () {
188-
const event = utils.mkEvent({
189-
type: "m.room.power_levels",
190-
room: roomId,
191-
user: userA,
192-
content: {
193-
users_default: 20,
194-
users: {
195-
"@alice:bar": "5",
196-
},
197-
},
198-
event: true,
199-
});
200-
let emitCount = 0;
122+
it("should not emit if new power level is the same", function () {
123+
const onEmit = jest.fn();
124+
member.on(RoomMemberEvent.PowerLevel, onEmit);
201125

202-
member.on(RoomMemberEvent.PowerLevel, function (emitEvent, emitMember) {
203-
emitCount += 1;
204-
expect(emitMember.userId).toEqual("@alice:bar");
205-
expect(emitMember.powerLevel).toEqual(20);
206-
expect(emitEvent).toEqual(event);
207-
});
208-
209-
member.setPowerLevelEvent(event);
210-
expect(member.powerLevel).toEqual(20);
211-
expect(emitCount).toEqual(1);
212-
});
126+
const aMatrixEvent = new MatrixEvent();
127+
member.setPowerLevel(0, aMatrixEvent);
213128

214-
it("should no-op if given a non-state or unrelated event", () => {
215-
const fn = jest.spyOn(member, "emit");
216-
expect(fn).not.toHaveBeenCalledWith(RoomMemberEvent.PowerLevel);
217-
member.setPowerLevelEvent(
218-
utils.mkEvent({
219-
type: EventType.RoomPowerLevels,
220-
room: roomId,
221-
user: userA,
222-
content: {
223-
users_default: 20,
224-
users: {
225-
"@alice:bar": "5",
226-
},
227-
},
228-
skey: "invalid",
229-
event: true,
230-
}),
231-
);
232-
const nonStateEv = utils.mkEvent({
233-
type: EventType.RoomPowerLevels,
234-
room: roomId,
235-
user: userA,
236-
content: {
237-
users_default: 20,
238-
users: {
239-
"@alice:bar": "5",
240-
},
241-
},
242-
event: true,
243-
});
244-
delete nonStateEv.event.state_key;
245-
member.setPowerLevelEvent(nonStateEv);
246-
member.setPowerLevelEvent(
247-
utils.mkEvent({
248-
type: EventType.Sticker,
249-
room: roomId,
250-
user: userA,
251-
content: {},
252-
event: true,
253-
}),
254-
);
255-
expect(fn).not.toHaveBeenCalledWith(RoomMemberEvent.PowerLevel);
129+
expect(onEmit).not.toHaveBeenCalled();
256130
});
257131
});
258132

spec/unit/room-state.spec.ts

Lines changed: 156 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import * as utils from "../test-utils/test-utils";
2020
import { makeBeaconEvent, makeBeaconInfoEvent } from "../test-utils/beacon";
2121
import { filterEmitCallsByEventType } from "../test-utils/emitter";
2222
import { RoomState, RoomStateEvent } from "../../src/models/room-state";
23+
import { RoomMemberEvent } from "../../src/models/room-member";
2324
import { type Beacon, BeaconEvent, getBeaconInfoIdentifier } from "../../src/models/beacon";
2425
import { EventType, RelationType, UNSTABLE_MSC2716_MARKER } from "../../src/@types/event";
2526
import { MatrixEvent, MatrixEventEvent } from "../../src/models/event";
@@ -259,7 +260,7 @@ describe("RoomState", function () {
259260
expect(emitCount).toEqual(2);
260261
});
261262

262-
it("should call setPowerLevelEvent on each RoomMember for m.room.power_levels", function () {
263+
it("should call setPowerLevel on each RoomMember for m.room.power_levels", function () {
263264
const powerLevelEvent = utils.mkEvent({
264265
type: "m.room.power_levels",
265266
room: roomId,
@@ -273,12 +274,12 @@ describe("RoomState", function () {
273274
});
274275

275276
// spy on the room members
276-
jest.spyOn(state.members[userA], "setPowerLevelEvent");
277-
jest.spyOn(state.members[userB], "setPowerLevelEvent");
277+
jest.spyOn(state.members[userA], "setPowerLevel");
278+
jest.spyOn(state.members[userB], "setPowerLevel");
278279
state.setStateEvents([powerLevelEvent]);
279280

280-
expect(state.members[userA].setPowerLevelEvent).toHaveBeenCalledWith(powerLevelEvent);
281-
expect(state.members[userB].setPowerLevelEvent).toHaveBeenCalledWith(powerLevelEvent);
281+
expect(state.members[userA].setPowerLevel).toHaveBeenCalledWith(10, powerLevelEvent);
282+
expect(state.members[userB].setPowerLevel).toHaveBeenCalledWith(10, powerLevelEvent);
282283
});
283284

284285
it("should call setPowerLevelEvent on a new RoomMember if power levels exist", function () {
@@ -310,6 +311,156 @@ describe("RoomState", function () {
310311
expect(state.members[userC].powerLevel).toEqual(10);
311312
});
312313

314+
it("should calculate power level correctly", function () {
315+
const powerLevelEvent = utils.mkEvent({
316+
type: "m.room.power_levels",
317+
room: roomId,
318+
user: userA,
319+
content: {
320+
users_default: 20,
321+
users: {
322+
[userB]: 200,
323+
"@invalid:user": 10, // shouldn't barf on this.
324+
},
325+
},
326+
event: true,
327+
});
328+
state.setStateEvents([powerLevelEvent]);
329+
330+
expect(state.getMember(userA)?.powerLevel).toEqual(20);
331+
expect(state.getMember(userB)?.powerLevel).toEqual(200);
332+
});
333+
334+
it("should set 'powerLevel' with a v12 room.", function () {
335+
const createEventV12 = utils.mkEvent({
336+
type: "m.room.create",
337+
room: roomId,
338+
sender: userA,
339+
content: { room_version: "12" },
340+
event: true,
341+
});
342+
const powerLevelEvent = utils.mkEvent({
343+
type: "m.room.power_levels",
344+
room: roomId,
345+
user: userA,
346+
content: {
347+
users_default: 20,
348+
users: {
349+
[userB]: 200,
350+
"@invalid:user": 10, // shouldn't barf on this.
351+
},
352+
},
353+
event: true,
354+
});
355+
state.setStateEvents([createEventV12, powerLevelEvent]);
356+
expect(state.getMember(userA)?.powerLevel).toEqual(Infinity);
357+
});
358+
359+
it("should honour power levels of zero.", function () {
360+
const powerLevelEvent = utils.mkEvent({
361+
type: "m.room.power_levels",
362+
room: roomId,
363+
user: userA,
364+
content: {
365+
users_default: 20,
366+
users: {
367+
"@alice:bar": 0,
368+
},
369+
},
370+
event: true,
371+
});
372+
let emitCount = 0;
373+
374+
const memberA = state.getMember(userA)!;
375+
// set the power level to something other than zero or we
376+
// won't get an event
377+
memberA.powerLevel = 1;
378+
memberA.on(RoomMemberEvent.PowerLevel, function (emitEvent, emitMember) {
379+
emitCount += 1;
380+
expect(emitMember.userId).toEqual("@alice:bar");
381+
expect(emitMember.powerLevel).toEqual(0);
382+
expect(emitEvent).toEqual(powerLevelEvent);
383+
});
384+
385+
state.setStateEvents([powerLevelEvent]);
386+
expect(memberA.powerLevel).toEqual(0);
387+
expect(emitCount).toEqual(1);
388+
});
389+
390+
it("should not honor string power levels.", function () {
391+
const powerLevelEvent = utils.mkEvent({
392+
type: "m.room.power_levels",
393+
room: roomId,
394+
user: userA,
395+
content: {
396+
users_default: 20,
397+
users: {
398+
"@alice:bar": "5",
399+
},
400+
},
401+
event: true,
402+
});
403+
let emitCount = 0;
404+
405+
const memberA = state.getMember(userA)!;
406+
memberA.on(RoomMemberEvent.PowerLevel, function (emitEvent, emitMember) {
407+
emitCount += 1;
408+
expect(emitMember.userId).toEqual("@alice:bar");
409+
expect(emitMember.powerLevel).toEqual(20);
410+
expect(emitEvent).toEqual(powerLevelEvent);
411+
});
412+
413+
state.setStateEvents([powerLevelEvent]);
414+
expect(memberA.powerLevel).toEqual(20);
415+
expect(emitCount).toEqual(1);
416+
});
417+
418+
it("should no-op if given a non-state or unrelated event", () => {
419+
const memberA = state.getMember(userA)!;
420+
const fn = jest.spyOn(memberA, "emit");
421+
expect(fn).not.toHaveBeenCalledWith(RoomMemberEvent.PowerLevel);
422+
423+
const powerLevelEvent = utils.mkEvent({
424+
type: EventType.RoomPowerLevels,
425+
room: roomId,
426+
user: userA,
427+
content: {
428+
users_default: 20,
429+
users: {
430+
"@alice:bar": "5",
431+
},
432+
},
433+
skey: "invalid",
434+
event: true,
435+
});
436+
437+
state.setStateEvents([powerLevelEvent]);
438+
const nonStateEv = utils.mkEvent({
439+
type: EventType.RoomPowerLevels,
440+
room: roomId,
441+
user: userA,
442+
content: {
443+
users_default: 20,
444+
users: {
445+
"@alice:bar": "5",
446+
},
447+
},
448+
event: true,
449+
});
450+
delete nonStateEv.event.state_key;
451+
state.setStateEvents([nonStateEv]);
452+
state.setStateEvents([
453+
utils.mkEvent({
454+
type: EventType.Sticker,
455+
room: roomId,
456+
user: userA,
457+
content: {},
458+
event: true,
459+
}),
460+
]);
461+
expect(fn).not.toHaveBeenCalledWith(RoomMemberEvent.PowerLevel);
462+
});
463+
313464
it("should call setMembershipEvent on the right RoomMember", function () {
314465
const memberEvent = utils.mkMembership({
315466
user: userB,

0 commit comments

Comments
 (0)