@@ -19,13 +19,15 @@ import {
1919 CallMembership ,
2020 type SessionMembershipData ,
2121 DEFAULT_EXPIRE_DURATION ,
22+ type RtcMembershipData ,
2223} from "../../../src/matrixrtc/CallMembership" ;
2324import { membershipTemplate } from "./mocks" ;
2425
2526function makeMockEvent ( originTs = 0 ) : MatrixEvent {
2627 return {
2728 getTs : jest . fn ( ) . mockReturnValue ( originTs ) ,
2829 getSender : jest . fn ( ) . mockReturnValue ( "@alice:example.org" ) ,
30+ getId : jest . fn ( ) . mockReturnValue ( "$eventid" ) ,
2931 } as unknown as MatrixEvent ;
3032}
3133
@@ -40,12 +42,13 @@ describe("CallMembership", () => {
4042 } ) ;
4143
4244 const membershipTemplate : SessionMembershipData = {
43- call_id : "" ,
44- scope : "m.room" ,
45- application : "m.call" ,
46- device_id : "AAAAAAA" ,
47- focus_active : { type : "livekit" } ,
48- foci_preferred : [ { type : "livekit" } ] ,
45+ "call_id" : "" ,
46+ "scope" : "m.room" ,
47+ "application" : "m.call" ,
48+ "device_id" : "AAAAAAA" ,
49+ "focus_active" : { type : "livekit" , focus_selection : "oldest_membership" } ,
50+ "foci_preferred" : [ { type : "livekit" } ] ,
51+ "m.call.intent" : "voice" ,
4952 } ;
5053
5154 it ( "rejects membership with no device_id" , ( ) => {
@@ -94,11 +97,271 @@ describe("CallMembership", () => {
9497 it ( "returns preferred foci" , ( ) => {
9598 const fakeEvent = makeMockEvent ( ) ;
9699 const mockFocus = { type : "this_is_a_mock_focus" } ;
97- const membership = new CallMembership (
98- fakeEvent ,
99- Object . assign ( { } , membershipTemplate , { foci_preferred : [ mockFocus ] } ) ,
100- ) ;
101- expect ( membership . getPreferredFoci ( ) ) . toEqual ( [ mockFocus ] ) ;
100+ const membership = new CallMembership ( fakeEvent , { ...membershipTemplate , foci_preferred : [ mockFocus ] } ) ;
101+ expect ( membership . transports ) . toEqual ( [ mockFocus ] ) ;
102+ } ) ;
103+
104+ describe ( "getTransport" , ( ) => {
105+ const mockFocus = { type : "this_is_a_mock_focus" } ;
106+ const oldestMembership = new CallMembership ( makeMockEvent ( ) , membershipTemplate ) ;
107+ it ( "gets the correct active transport with oldest_membership" , ( ) => {
108+ const membership = new CallMembership ( makeMockEvent ( ) , {
109+ ...membershipTemplate ,
110+ foci_preferred : [ mockFocus ] ,
111+ focus_active : { type : "livekit" , focus_selection : "oldest_membership" } ,
112+ } ) ;
113+
114+ // if we are the oldest member we use our focus.
115+ expect ( membership . getTransport ( membership ) ) . toStrictEqual ( mockFocus ) ;
116+
117+ // If there is an older member we use its focus.
118+ expect ( membership . getTransport ( oldestMembership ) ) . toBe ( membershipTemplate . foci_preferred [ 0 ] ) ;
119+ } ) ;
120+
121+ it ( "gets the correct active transport with multi_sfu" , ( ) => {
122+ const membership = new CallMembership ( makeMockEvent ( ) , {
123+ ...membershipTemplate ,
124+ foci_preferred : [ mockFocus ] ,
125+ focus_active : { type : "livekit" , focus_selection : "multi_sfu" } ,
126+ } ) ;
127+
128+ // if we are the oldest member we use our focus.
129+ expect ( membership . getTransport ( membership ) ) . toStrictEqual ( mockFocus ) ;
130+
131+ // If there is an older member we still use our own focus in multi sfu.
132+ expect ( membership . getTransport ( oldestMembership ) ) . toBe ( mockFocus ) ;
133+ } ) ;
134+ it ( "does not provide focus if the selection method is unknown" , ( ) => {
135+ const membership = new CallMembership ( makeMockEvent ( ) , {
136+ ...membershipTemplate ,
137+ foci_preferred : [ mockFocus ] ,
138+ focus_active : { type : "livekit" , focus_selection : "unknown" } ,
139+ } ) ;
140+
141+ // if we are the oldest member we use our focus.
142+ expect ( membership . getTransport ( membership ) ) . toBeUndefined ( ) ;
143+ } ) ;
144+ } ) ;
145+ describe ( "correct values from computed fields" , ( ) => {
146+ const membership = new CallMembership ( makeMockEvent ( ) , membershipTemplate ) ;
147+ it ( "returns correct sender" , ( ) => {
148+ expect ( membership . sender ) . toBe ( "@alice:example.org" ) ;
149+ } ) ;
150+ it ( "returns correct eventId" , ( ) => {
151+ expect ( membership . eventId ) . toBe ( "$eventid" ) ;
152+ } ) ;
153+ it ( "returns correct slot_id" , ( ) => {
154+ expect ( membership . slotId ) . toBe ( "m.call#" ) ;
155+ expect ( membership . slotDescription ) . toStrictEqual ( { id : "" , application : "m.call" } ) ;
156+ } ) ;
157+ it ( "returns correct deviceId" , ( ) => {
158+ expect ( membership . deviceId ) . toBe ( "AAAAAAA" ) ;
159+ } ) ;
160+ it ( "returns correct call intent" , ( ) => {
161+ expect ( membership . callIntent ) . toBe ( "voice" ) ;
162+ } ) ;
163+ it ( "returns correct application" , ( ) => {
164+ expect ( membership . application ) . toStrictEqual ( "m.call" ) ;
165+ } ) ;
166+ it ( "returns correct applicationData" , ( ) => {
167+ expect ( membership . applicationData ) . toStrictEqual ( { "type" : "m.call" , "m.call.intent" : "voice" } ) ;
168+ } ) ;
169+ it ( "returns correct scope" , ( ) => {
170+ expect ( membership . scope ) . toBe ( "m.room" ) ;
171+ } ) ;
172+ it ( "returns correct membershipID" , ( ) => {
173+ expect ( membership . membershipID ) . toBe ( "0" ) ;
174+ } ) ;
175+ it ( "returns correct unused fields" , ( ) => {
176+ expect ( membership . getAbsoluteExpiry ( ) ) . toBe ( DEFAULT_EXPIRE_DURATION ) ;
177+ expect ( membership . getMsUntilExpiry ( ) ) . toBe ( DEFAULT_EXPIRE_DURATION - Date . now ( ) ) ;
178+ expect ( membership . isExpired ( ) ) . toBe ( true ) ;
179+ } ) ;
180+ } ) ;
181+ } ) ;
182+
183+ describe ( "RtcMembershipData" , ( ) => {
184+ const membershipTemplate : RtcMembershipData = {
185+ slot_id : "m.call#" ,
186+ application : { "type" : "m.call" , "m.call.id" : "" , "m.call.intent" : "voice" } ,
187+ member : { user_id : "@alice:example.org" , device_id : "AAAAAAA" , id : "xyzHASHxyz" } ,
188+ rtc_transports : [ { type : "livekit" } ] ,
189+ versions : [ ] ,
190+ msc4354_sticky_key : "abc123" ,
191+ } ;
192+
193+ it ( "rejects membership with no slot_id" , ( ) => {
194+ expect ( ( ) => {
195+ new CallMembership ( makeMockEvent ( ) , { ...membershipTemplate , slot_id : undefined } ) ;
196+ } ) . toThrow ( ) ;
197+ } ) ;
198+ it ( "rejects membership with invalid slot_id" , ( ) => {
199+ expect ( ( ) => {
200+ new CallMembership ( makeMockEvent ( ) , { ...membershipTemplate , slot_id : "invalid_slot_id" } ) ;
201+ } ) . toThrow ( ) ;
202+ } ) ;
203+ it ( "accepts membership with valid slot_id" , ( ) => {
204+ expect ( ( ) => {
205+ new CallMembership ( makeMockEvent ( ) , { ...membershipTemplate , slot_id : "m.call#" } ) ;
206+ } ) . not . toThrow ( ) ;
207+ } ) ;
208+
209+ it ( "rejects membership with no application" , ( ) => {
210+ expect ( ( ) => {
211+ new CallMembership ( makeMockEvent ( ) , { ...membershipTemplate , application : undefined } ) ;
212+ } ) . toThrow ( ) ;
213+ } ) ;
214+
215+ it ( "rejects membership with incorrect application" , ( ) => {
216+ expect ( ( ) => {
217+ new CallMembership ( makeMockEvent ( ) , {
218+ ...membershipTemplate ,
219+ application : { wrong_type_key : "unknown" } ,
220+ } ) ;
221+ } ) . toThrow ( ) ;
222+ } ) ;
223+
224+ it ( "rejects membership with no member" , ( ) => {
225+ expect ( ( ) => {
226+ new CallMembership ( makeMockEvent ( ) , { ...membershipTemplate , member : undefined } ) ;
227+ } ) . toThrow ( ) ;
228+ } ) ;
229+
230+ it ( "rejects membership with incorrect member" , ( ) => {
231+ expect ( ( ) => {
232+ new CallMembership ( makeMockEvent ( ) , { ...membershipTemplate , member : { i : "test" } } ) ;
233+ } ) . toThrow ( ) ;
234+ expect ( ( ) => {
235+ new CallMembership ( makeMockEvent ( ) , {
236+ ...membershipTemplate ,
237+ member : { id : "test" , device_id : "test" , user_id_wrong : "test" } ,
238+ } ) ;
239+ } ) . toThrow ( ) ;
240+ expect ( ( ) => {
241+ new CallMembership ( makeMockEvent ( ) , {
242+ ...membershipTemplate ,
243+ member : { id : "test" , device_id_wrong : "test" , user_id_wrong : "test" } ,
244+ } ) ;
245+ } ) . toThrow ( ) ;
246+ expect ( ( ) => {
247+ new CallMembership ( makeMockEvent ( ) , {
248+ ...membershipTemplate ,
249+ member : { id : "test" , device_id : "test" , user_id : "@@test" } ,
250+ } ) ;
251+ } ) . toThrow ( ) ;
252+ expect ( ( ) => {
253+ new CallMembership ( makeMockEvent ( ) , {
254+ ...membershipTemplate ,
255+ member : { id : "test" , device_id : "test" , user_id : "@test-wrong-user:user.id" } ,
256+ } ) ;
257+ } ) . toThrow ( ) ;
258+ } ) ;
259+ it ( "rejects membership with incorrect sticky_key" , ( ) => {
260+ expect ( ( ) => {
261+ new CallMembership ( makeMockEvent ( ) , membershipTemplate ) ;
262+ } ) . not . toThrow ( ) ;
263+ expect ( ( ) => {
264+ new CallMembership ( makeMockEvent ( ) , {
265+ ...membershipTemplate ,
266+ sticky_key : 1 ,
267+ msc4354_sticky_key : undefined ,
268+ } ) ;
269+ } ) . toThrow ( ) ;
270+ expect ( ( ) => {
271+ new CallMembership ( makeMockEvent ( ) , {
272+ ...membershipTemplate ,
273+ sticky_key : "1" ,
274+ msc4354_sticky_key : undefined ,
275+ } ) ;
276+ } ) . not . toThrow ( ) ;
277+ expect ( ( ) => {
278+ new CallMembership ( makeMockEvent ( ) , { ...membershipTemplate , msc4354_sticky_key : undefined } ) ;
279+ } ) . toThrow ( ) ;
280+ expect ( ( ) => {
281+ new CallMembership ( makeMockEvent ( ) , {
282+ ...membershipTemplate ,
283+ msc4354_sticky_key : 1 ,
284+ sticky_key : "valid" ,
285+ } ) ;
286+ } ) . toThrow ( ) ;
287+ expect ( ( ) => {
288+ new CallMembership ( makeMockEvent ( ) , {
289+ ...membershipTemplate ,
290+ msc4354_sticky_key : "valid" ,
291+ sticky_key : "valid" ,
292+ } ) ;
293+ } ) . not . toThrow ( ) ;
294+ expect ( ( ) => {
295+ new CallMembership ( makeMockEvent ( ) , {
296+ ...membershipTemplate ,
297+ msc4354_sticky_key : "valid_but_different" ,
298+ sticky_key : "valid" ,
299+ } ) ;
300+ } ) . toThrow ( ) ;
301+ } ) ;
302+
303+ it ( "considers memberships unexpired if local age low enough" , ( ) => {
304+ // TODO link prev event
305+ } ) ;
306+
307+ it ( "considers memberships expired if local age large enough" , ( ) => {
308+ // TODO link prev event
309+ } ) ;
310+
311+ describe ( "getTransport" , ( ) => {
312+ it ( "gets the correct active transport with oldest_membership" , ( ) => {
313+ const oldestMembership = new CallMembership ( makeMockEvent ( ) , {
314+ ...membershipTemplate ,
315+ rtc_transports : [ { type : "oldest_transport" } ] ,
316+ } ) ;
317+ const membership = new CallMembership ( makeMockEvent ( ) , membershipTemplate ) ;
318+
319+ // if we are the oldest member we use our focus.
320+ expect ( membership . getTransport ( membership ) ) . toStrictEqual ( { type : "livekit" } ) ;
321+
322+ // If there is an older member we use our own focus focus. (RtcMembershipData always uses multi sfu)
323+ expect ( membership . getTransport ( oldestMembership ) ) . toStrictEqual ( { type : "livekit" } ) ;
324+ } ) ;
325+ } ) ;
326+ describe ( "correct values from computed fields" , ( ) => {
327+ const membership = new CallMembership ( makeMockEvent ( ) , membershipTemplate ) ;
328+ it ( "returns correct sender" , ( ) => {
329+ expect ( membership . sender ) . toBe ( "@alice:example.org" ) ;
330+ } ) ;
331+ it ( "returns correct eventId" , ( ) => {
332+ expect ( membership . eventId ) . toBe ( "$eventid" ) ;
333+ } ) ;
334+ it ( "returns correct slot_id" , ( ) => {
335+ expect ( membership . slotId ) . toBe ( "m.call#" ) ;
336+ expect ( membership . slotDescription ) . toStrictEqual ( { id : "" , application : "m.call" } ) ;
337+ } ) ;
338+ it ( "returns correct deviceId" , ( ) => {
339+ expect ( membership . deviceId ) . toBe ( "AAAAAAA" ) ;
340+ } ) ;
341+ it ( "returns correct call intent" , ( ) => {
342+ expect ( membership . callIntent ) . toBe ( "voice" ) ;
343+ } ) ;
344+ it ( "returns correct application" , ( ) => {
345+ expect ( membership . application ) . toStrictEqual ( "m.call" ) ;
346+ } ) ;
347+ it ( "returns correct applicationData" , ( ) => {
348+ expect ( membership . applicationData ) . toStrictEqual ( {
349+ "type" : "m.call" ,
350+ "m.call.id" : "" ,
351+ "m.call.intent" : "voice" ,
352+ } ) ;
353+ } ) ;
354+ it ( "returns correct scope" , ( ) => {
355+ expect ( membership . scope ) . toBe ( undefined ) ;
356+ } ) ;
357+ it ( "returns correct membershipID" , ( ) => {
358+ expect ( membership . membershipID ) . toBe ( "xyzHASHxyz" ) ;
359+ } ) ;
360+ it ( "returns correct unused fields" , ( ) => {
361+ expect ( membership . getAbsoluteExpiry ( ) ) . toBe ( undefined ) ;
362+ expect ( membership . getMsUntilExpiry ( ) ) . toBe ( undefined ) ;
363+ expect ( membership . isExpired ( ) ) . toBe ( false ) ;
364+ } ) ;
102365 } ) ;
103366 } ) ;
104367
0 commit comments