776 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Dart
		
	
	
	
			
		
		
	
	
			776 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Dart
		
	
	
	
| import 'package:test/test.dart';
 | |
| import 'package:webrtc_interface/webrtc_interface.dart';
 | |
| 
 | |
| import 'package:matrix/matrix.dart';
 | |
| import 'package:matrix/src/voip/models/call_membership.dart';
 | |
| import 'package:matrix/src/voip/models/call_options.dart';
 | |
| import 'package:matrix/src/voip/models/voip_id.dart';
 | |
| import 'fake_client.dart';
 | |
| import 'webrtc_stub.dart';
 | |
| 
 | |
| void main() {
 | |
|   late Client matrix;
 | |
|   late Room room;
 | |
|   late VoIP voip;
 | |
|   group('Call tests', () {
 | |
|     Logs().level = Level.info;
 | |
|     setUp(() async {
 | |
|       matrix = await getClient();
 | |
| 
 | |
|       voip = VoIP(matrix, MockWebRTCDelegate());
 | |
|       VoIP.customTxid = '1234';
 | |
|       final id = '!calls:example.com';
 | |
|       room = matrix.getRoomById(id)!;
 | |
|     });
 | |
| 
 | |
|     test('Test call methods', () async {
 | |
|       final call = CallSession(
 | |
|         CallOptions(
 | |
|           callId: '1234',
 | |
|           type: CallType.kVoice,
 | |
|           dir: CallDirection.kOutgoing,
 | |
|           localPartyId: '4567',
 | |
|           voip: voip,
 | |
|           room: room,
 | |
|           iceServers: [],
 | |
|         ),
 | |
|       );
 | |
|       await call.sendInviteToCall(room, '1234', 1234, '4567', 'sdp',
 | |
|           txid: '1234');
 | |
|       await call.sendAnswerCall(room, '1234', 'sdp', '4567', txid: '1234');
 | |
|       await call.sendCallCandidates(room, '1234', '4567', [], txid: '1234');
 | |
|       await call.sendSelectCallAnswer(room, '1234', '4567', '6789',
 | |
|           txid: '1234');
 | |
|       await call.sendCallReject(room, '1234', '4567', txid: '1234');
 | |
|       await call.sendCallNegotiate(room, '1234', 1234, '4567', 'sdp',
 | |
|           txid: '1234');
 | |
|       await call.sendHangupCall(room, '1234', '4567', 'userHangup',
 | |
|           txid: '1234');
 | |
|       await call.sendAssertedIdentity(
 | |
|           room,
 | |
|           '1234',
 | |
|           '4567',
 | |
|           AssertedIdentity()
 | |
|             ..displayName = 'name'
 | |
|             ..id = 'some_id',
 | |
|           txid: '1234');
 | |
|       await call.sendCallReplaces(room, '1234', '4567', CallReplaces(),
 | |
|           txid: '1234');
 | |
|       await call.sendSDPStreamMetadataChanged(
 | |
|           room, '1234', '4567', SDPStreamMetadata({}),
 | |
|           txid: '1234');
 | |
|     });
 | |
| 
 | |
|     test('Call lifetime and age', () async {
 | |
|       expect(voip.currentCID, null);
 | |
|       await matrix.handleSync(SyncUpdate(
 | |
|           nextBatch: 'something',
 | |
|           rooms: RoomsUpdate(join: {
 | |
|             room.id: JoinedRoomUpdate(
 | |
|                 timeline: TimelineUpdate(events: [
 | |
|               MatrixEvent(
 | |
|                 type: 'm.call.invite',
 | |
|                 content: {
 | |
|                   'lifetime': 60000,
 | |
|                   'call_id': '1702472924955oq1uQbNAfU7wAaEA',
 | |
|                   'party_id': 'DPCIPPBGPO',
 | |
|                   'offer': {'type': 'offer', 'sdp': 'sdp'}
 | |
|                 },
 | |
|                 senderId: '@alice:testing.com',
 | |
|                 eventId: 'newevent',
 | |
|                 originServerTs: DateTime.utc(1969),
 | |
|               )
 | |
|             ]))
 | |
|           })));
 | |
|       await Future.delayed(Duration(seconds: 2));
 | |
|       // confirm that no call got created after 3 seconds, which is
 | |
|       // expected in this case because the originTs was old asf
 | |
|       expect(voip.currentCID, null);
 | |
| 
 | |
|       await matrix.handleSync(SyncUpdate(
 | |
|           nextBatch: 'something',
 | |
|           rooms: RoomsUpdate(join: {
 | |
|             room.id: JoinedRoomUpdate(
 | |
|                 timeline: TimelineUpdate(events: [
 | |
|               MatrixEvent(
 | |
|                 unsigned: {'age': 60001},
 | |
|                 type: 'm.call.invite',
 | |
|                 content: {
 | |
|                   'lifetime': 60000,
 | |
|                   'call_id': 'unsignedTsInvalidCall',
 | |
|                   'party_id': 'DPCIPPBGPO',
 | |
|                   'offer': {'type': 'offer', 'sdp': 'sdp'}
 | |
|                 },
 | |
|                 senderId: '@alice:testing.com',
 | |
|                 eventId: 'newevent',
 | |
|                 originServerTs: DateTime.now(),
 | |
|               )
 | |
|             ]))
 | |
|           })));
 | |
|       await Future.delayed(Duration(seconds: 2));
 | |
|       // confirm that no call got created after 3 seconds, which is
 | |
|       // expected in this case because age was older than lifetime
 | |
|       expect(voip.currentCID, null);
 | |
|     });
 | |
|     test('Call connection and hanging up', () async {
 | |
|       expect(voip.currentCID, null);
 | |
|       await matrix.handleSync(SyncUpdate(
 | |
|           nextBatch: 'something',
 | |
|           rooms: RoomsUpdate(join: {
 | |
|             room.id: JoinedRoomUpdate(
 | |
|                 timeline: TimelineUpdate(events: [
 | |
|               MatrixEvent(
 | |
|                 type: 'm.call.invite',
 | |
|                 content: {
 | |
|                   'lifetime': 60000,
 | |
|                   'call_id': 'originTsValidCall',
 | |
|                   'party_id': 'GHTYAJCE_caller',
 | |
|                   'version': '1',
 | |
|                   'offer': {'type': 'offer', 'sdp': 'sdp'}
 | |
|                 },
 | |
|                 senderId: '@alice:testing.com',
 | |
|                 eventId: 'callerInviteEvent',
 | |
|                 originServerTs: DateTime.now(),
 | |
|               )
 | |
|             ]))
 | |
|           })));
 | |
|       await matrix.handleSync(SyncUpdate(
 | |
|           nextBatch: 'something',
 | |
|           rooms: RoomsUpdate(join: {
 | |
|             room.id: JoinedRoomUpdate(
 | |
|                 timeline: TimelineUpdate(events: [
 | |
|               MatrixEvent(
 | |
|                 type: 'm.call.candidates',
 | |
|                 content: {
 | |
|                   'call_id': 'originTsValidCall',
 | |
|                   'party_id': 'GHTYAJCE_caller',
 | |
|                   'version': '1',
 | |
|                   'candidates': [
 | |
|                     {
 | |
|                       'candidate': 'candidate:01UDP2122252543uwu50184typhost',
 | |
|                       'sdpMid': '0',
 | |
|                       'sdpMLineIndex': 0
 | |
|                     },
 | |
|                     {
 | |
|                       'candidate':
 | |
|                           'candidate:31TCP2105524479uwu9typhosttcptypeactive',
 | |
|                       'sdpMid': '0',
 | |
|                       'sdpMLineIndex': 0
 | |
|                     }
 | |
|                   ],
 | |
|                 },
 | |
|                 senderId: '@alice:testing.com',
 | |
|                 eventId: 'callerCallCandidatesEvent',
 | |
|                 originServerTs: DateTime.now(),
 | |
|               )
 | |
|             ]))
 | |
|           })));
 | |
|       while (voip.currentCID !=
 | |
|           VoipId(roomId: room.id, callId: 'originTsValidCall')) {
 | |
|         // call invite looks valid, call should be created now :D
 | |
|         await Future.delayed(Duration(milliseconds: 50));
 | |
|         Logs().d('Waiting for currentCID to update');
 | |
|       }
 | |
|       expect(voip.currentCID,
 | |
|           VoipId(roomId: room.id, callId: 'originTsValidCall'));
 | |
|       final call = voip.calls[voip.currentCID]!;
 | |
|       expect(call.state, CallState.kRinging);
 | |
|       await call.answer(txid: '1234');
 | |
| 
 | |
|       call.pc!.onIceGatheringState!
 | |
|           .call(RTCIceGatheringState.RTCIceGatheringStateComplete);
 | |
|       // we send them manually anyway because our stub sends empty list of
 | |
|       // candidates
 | |
|       await call.sendCallCandidates(
 | |
|           room,
 | |
|           'originTsValidCall',
 | |
|           'GHTYAJCE',
 | |
|           [
 | |
|             {
 | |
|               'candidate': 'candidate:0 1 UDP 2122252543 uwu 50184 typ host',
 | |
|               'sdpMid': '0',
 | |
|               'sdpMLineIndex': 0
 | |
|             },
 | |
|             {
 | |
|               'candidate':
 | |
|                   'candidate:3 1 TCP 2105524479 uwu 9 typ host tcptype active',
 | |
|               'sdpMid': '0',
 | |
|               'sdpMLineIndex': 0
 | |
|             }
 | |
|           ],
 | |
|           txid: '1234');
 | |
| 
 | |
|       expect(call.state, CallState.kConnecting);
 | |
| 
 | |
|       // caller sends select answer
 | |
|       await matrix.handleSync(SyncUpdate(
 | |
|           nextBatch: 'something',
 | |
|           rooms: RoomsUpdate(join: {
 | |
|             room.id: JoinedRoomUpdate(
 | |
|                 timeline: TimelineUpdate(events: [
 | |
|               MatrixEvent(
 | |
|                 type: 'm.call.select_answer',
 | |
|                 content: {
 | |
|                   'call_id': 'originTsValidCall',
 | |
|                   'party_id': 'GHTYAJCE_caller',
 | |
|                   'version': '1',
 | |
|                   'lifetime': 10000,
 | |
|                   'selected_party_id': 'GHTYAJCE'
 | |
|                 },
 | |
|                 senderId: '@alice:testing.com',
 | |
|                 eventId: 'callerSelectAnswerEvent',
 | |
|                 originServerTs: DateTime.now(),
 | |
|               )
 | |
|             ]))
 | |
|           })));
 | |
| 
 | |
|       call.pc!.onIceConnectionState!
 | |
|           .call(RTCIceConnectionState.RTCIceConnectionStateChecking);
 | |
|       call.pc!.onIceConnectionState!
 | |
|           .call(RTCIceConnectionState.RTCIceConnectionStateConnected);
 | |
|       // just to make sure there are no errors after running functions
 | |
|       // that are supposed to run once iceConnectionState is connected
 | |
|       await Future.delayed(Duration(seconds: 2));
 | |
| 
 | |
|       expect(call.state, CallState.kConnected);
 | |
| 
 | |
|       await call.hangup(reason: CallErrorCode.userHangup);
 | |
|       expect(call.state, CallState.kEnded);
 | |
|       expect(voip.currentCID, null);
 | |
|     });
 | |
| 
 | |
|     test('Call answered elsewhere', () async {
 | |
|       expect(voip.currentCID, null);
 | |
|       await matrix.handleSync(SyncUpdate(
 | |
|           nextBatch: 'something',
 | |
|           rooms: RoomsUpdate(join: {
 | |
|             room.id: JoinedRoomUpdate(
 | |
|                 timeline: TimelineUpdate(events: [
 | |
|               MatrixEvent(
 | |
|                 type: 'm.call.invite',
 | |
|                 content: {
 | |
|                   'lifetime': 60000,
 | |
|                   'call_id': 'answer_elseWhere',
 | |
|                   'party_id': 'GHTYAJCE_caller',
 | |
|                   'version': '1',
 | |
|                   'offer': {'type': 'offer', 'sdp': 'sdp'}
 | |
|                 },
 | |
|                 senderId: '@alice:testing.com',
 | |
|                 eventId: 'callerInviteEvent',
 | |
|                 originServerTs: DateTime.now(),
 | |
|               )
 | |
|             ]))
 | |
|           })));
 | |
|       await matrix.handleSync(SyncUpdate(
 | |
|           nextBatch: 'something',
 | |
|           rooms: RoomsUpdate(join: {
 | |
|             room.id: JoinedRoomUpdate(
 | |
|                 timeline: TimelineUpdate(events: [
 | |
|               MatrixEvent(
 | |
|                 type: 'm.call.candidates',
 | |
|                 content: {
 | |
|                   'call_id': 'answer_elseWhere',
 | |
|                   'party_id': 'GHTYAJCE_caller',
 | |
|                   'version': '1',
 | |
|                   'candidates': [
 | |
|                     {
 | |
|                       'candidate': 'candidate:01UDP2122252543uwu50184typhost',
 | |
|                       'sdpMid': '0',
 | |
|                       'sdpMLineIndex': 0
 | |
|                     },
 | |
|                     {
 | |
|                       'candidate':
 | |
|                           'candidate:31TCP2105524479uwu9typhosttcptypeactive',
 | |
|                       'sdpMid': '0',
 | |
|                       'sdpMLineIndex': 0
 | |
|                     }
 | |
|                   ],
 | |
|                 },
 | |
|                 senderId: '@alice:testing.com',
 | |
|                 eventId: 'callerCallCandidatesEvent',
 | |
|                 originServerTs: DateTime.now(),
 | |
|               )
 | |
|             ]))
 | |
|           })));
 | |
|       while (voip.currentCID !=
 | |
|           VoipId(roomId: room.id, callId: 'answer_elseWhere')) {
 | |
|         // call invite looks valid, call should be created now :D
 | |
|         await Future.delayed(Duration(milliseconds: 50));
 | |
|         Logs().d('Waiting for currentCID to update');
 | |
|       }
 | |
|       expect(
 | |
|           voip.currentCID, VoipId(roomId: room.id, callId: 'answer_elseWhere'));
 | |
|       final call = voip.calls[voip.currentCID]!;
 | |
|       expect(call.state, CallState.kRinging);
 | |
| 
 | |
|       // caller sends select answer
 | |
|       await matrix.handleSync(SyncUpdate(
 | |
|           nextBatch: 'something',
 | |
|           rooms: RoomsUpdate(join: {
 | |
|             room.id: JoinedRoomUpdate(
 | |
|                 timeline: TimelineUpdate(events: [
 | |
|               MatrixEvent(
 | |
|                 type: 'm.call.select_answer',
 | |
|                 content: {
 | |
|                   'call_id': 'answer_elseWhere',
 | |
|                   'party_id': 'GHTYAJCE_caller',
 | |
|                   'version': '1',
 | |
|                   'lifetime': 10000,
 | |
|                   'selected_party_id':
 | |
|                       'not_us' // selected some other device for answer
 | |
|                 },
 | |
|                 senderId: '@alice:testing.com',
 | |
|                 eventId: 'callerSelectAnswerEvent',
 | |
|                 originServerTs: DateTime.now(),
 | |
|               )
 | |
|             ]))
 | |
|           })));
 | |
|       // wait for select answer to end the call
 | |
|       await Future.delayed(Duration(seconds: 2));
 | |
|       // call ended because answered elsewhere
 | |
|       expect(call.state, CallState.kEnded);
 | |
|       expect(voip.currentCID, null);
 | |
|     });
 | |
| 
 | |
|     test('Reject incoming call', () async {
 | |
|       expect(voip.currentCID, null);
 | |
|       await matrix.handleSync(SyncUpdate(
 | |
|           nextBatch: 'something',
 | |
|           rooms: RoomsUpdate(join: {
 | |
|             room.id: JoinedRoomUpdate(
 | |
|                 timeline: TimelineUpdate(events: [
 | |
|               MatrixEvent(
 | |
|                 type: 'm.call.invite',
 | |
|                 content: {
 | |
|                   'lifetime': 60000,
 | |
|                   'call_id': 'reject_call',
 | |
|                   'party_id': 'GHTYAJCE_caller',
 | |
|                   'version': '1',
 | |
|                   'offer': {'type': 'offer', 'sdp': 'sdp'}
 | |
|                 },
 | |
|                 senderId: '@alice:testing.com',
 | |
|                 eventId: 'callerInviteEvent',
 | |
|                 originServerTs: DateTime.now(),
 | |
|               )
 | |
|             ]))
 | |
|           })));
 | |
|       await matrix.handleSync(SyncUpdate(
 | |
|           nextBatch: 'something',
 | |
|           rooms: RoomsUpdate(join: {
 | |
|             room.id: JoinedRoomUpdate(
 | |
|                 timeline: TimelineUpdate(events: [
 | |
|               MatrixEvent(
 | |
|                 type: 'm.call.candidates',
 | |
|                 content: {
 | |
|                   'call_id': 'reject_call',
 | |
|                   'party_id': 'GHTYAJCE_caller',
 | |
|                   'version': '1',
 | |
|                   'candidates': [
 | |
|                     {
 | |
|                       'candidate': 'candidate:01UDP2122252543uwu50184typhost',
 | |
|                       'sdpMid': '0',
 | |
|                       'sdpMLineIndex': 0
 | |
|                     },
 | |
|                     {
 | |
|                       'candidate':
 | |
|                           'candidate:31TCP2105524479uwu9typhosttcptypeactive',
 | |
|                       'sdpMid': '0',
 | |
|                       'sdpMLineIndex': 0
 | |
|                     }
 | |
|                   ],
 | |
|                 },
 | |
|                 senderId: '@alice:testing.com',
 | |
|                 eventId: 'callerCallCandidatesEvent',
 | |
|                 originServerTs: DateTime.now(),
 | |
|               )
 | |
|             ]))
 | |
|           })));
 | |
|       while (
 | |
|           voip.currentCID != VoipId(roomId: room.id, callId: 'reject_call')) {
 | |
|         // call invite looks valid, call should be created now :D
 | |
|         await Future.delayed(Duration(milliseconds: 50));
 | |
|         Logs().d('Waiting for currentCID to update');
 | |
|       }
 | |
|       expect(voip.currentCID, VoipId(roomId: room.id, callId: 'reject_call'));
 | |
|       final call = voip.calls[voip.currentCID]!;
 | |
|       expect(call.state, CallState.kRinging);
 | |
| 
 | |
|       await call.reject();
 | |
| 
 | |
|       // call ended because answered elsewhere
 | |
|       expect(call.state, CallState.kEnded);
 | |
|       expect(voip.currentCID, null);
 | |
|     });
 | |
| 
 | |
|     test('Glare after invite was sent', () async {
 | |
|       expect(voip.currentCID, null);
 | |
|       final firstCall = await voip.inviteToCall(
 | |
|         room,
 | |
|         CallType.kVoice,
 | |
|         userId: '@alice:testing.com',
 | |
|       );
 | |
|       await firstCall.pc!.onRenegotiationNeeded!.call();
 | |
|       expect(firstCall.state, CallState.kInviteSent);
 | |
|       // KABOOM YOU JUST GLARED
 | |
|       await Future.delayed(Duration(seconds: 3));
 | |
|       await matrix.handleSync(SyncUpdate(
 | |
|           nextBatch: 'something',
 | |
|           rooms: RoomsUpdate(join: {
 | |
|             room.id: JoinedRoomUpdate(
 | |
|                 timeline: TimelineUpdate(events: [
 | |
|               MatrixEvent(
 | |
|                 type: 'm.call.invite',
 | |
|                 content: {
 | |
|                   'lifetime': 60000,
 | |
|                   'call_id': 'zzzz_glare_2nd_call',
 | |
|                   'party_id': 'GHTYAJCE_caller',
 | |
|                   'version': '1',
 | |
|                   'offer': {'type': 'offer', 'sdp': 'sdp'}
 | |
|                 },
 | |
|                 senderId: '@alice:testing.com',
 | |
|                 eventId: 'callerInviteEvent',
 | |
|                 originServerTs: DateTime.now(),
 | |
|               )
 | |
|             ]))
 | |
|           })));
 | |
|       await Future.delayed(Duration(seconds: 3));
 | |
|       expect(
 | |
|           voip.currentCID, VoipId(roomId: room.id, callId: firstCall.callId));
 | |
|       await firstCall.hangup(reason: CallErrorCode.userBusy);
 | |
|     });
 | |
|     test('Glare before invite was sent', () async {
 | |
|       expect(voip.currentCID, null);
 | |
|       final firstCall = await voip.inviteToCall(
 | |
|         room,
 | |
|         CallType.kVoice,
 | |
|         userId: '@alice:testing.com',
 | |
|       );
 | |
|       expect(firstCall.state, CallState.kCreateOffer);
 | |
|       // KABOOM YOU JUST GLARED, but this tiem you were still preparing your call
 | |
|       // so just cancel that instead
 | |
|       await Future.delayed(Duration(seconds: 3));
 | |
|       await matrix.handleSync(SyncUpdate(
 | |
|           nextBatch: 'something',
 | |
|           rooms: RoomsUpdate(join: {
 | |
|             room.id: JoinedRoomUpdate(
 | |
|                 timeline: TimelineUpdate(events: [
 | |
|               MatrixEvent(
 | |
|                 type: 'm.call.invite',
 | |
|                 content: {
 | |
|                   'lifetime': 60000,
 | |
|                   'call_id': 'zzzz_glare_2nd_call',
 | |
|                   'party_id': 'GHTYAJCE_caller',
 | |
|                   'version': '1',
 | |
|                   'offer': {'type': 'offer', 'sdp': 'sdp'}
 | |
|                 },
 | |
|                 senderId: '@alice:testing.com',
 | |
|                 eventId: 'callerInviteEvent',
 | |
|                 originServerTs: DateTime.now(),
 | |
|               )
 | |
|             ]))
 | |
|           })));
 | |
|       await Future.delayed(Duration(seconds: 3));
 | |
|       expect(voip.currentCID,
 | |
|           VoipId(roomId: room.id, callId: 'zzzz_glare_2nd_call'));
 | |
|     });
 | |
| 
 | |
|     test('getFamedlyCallEvents sort order', () {
 | |
|       room.setState(
 | |
|         Event(
 | |
|           content: {
 | |
|             'memberships': [
 | |
|               CallMembership(
 | |
|                 userId: '@test1:example.com',
 | |
|                 callId: '1111',
 | |
|                 backend: MeshBackend(),
 | |
|                 deviceId: '1111',
 | |
|                 expiresTs: DateTime.now()
 | |
|                     .add(Duration(hours: 12))
 | |
|                     .millisecondsSinceEpoch,
 | |
|                 roomId: room.id,
 | |
|                 membershipId: voip.currentSessionId,
 | |
|               ).toJson(),
 | |
|             ]
 | |
|           },
 | |
|           type: EventTypes.GroupCallMember,
 | |
|           eventId: 'asdfasdf',
 | |
|           senderId: '@test1:example.com',
 | |
|           originServerTs: DateTime.now().add(Duration(hours: 12)),
 | |
|           room: room,
 | |
|           stateKey: '@test1:example.com',
 | |
|         ),
 | |
|       );
 | |
|       room.setState(
 | |
|         Event(
 | |
|           content: {
 | |
|             'memberships': [
 | |
|               CallMembership(
 | |
|                 userId: '@test2:example.com',
 | |
|                 callId: '1111',
 | |
|                 backend: MeshBackend(),
 | |
|                 deviceId: '1111',
 | |
|                 expiresTs: DateTime.now().millisecondsSinceEpoch,
 | |
|                 roomId: room.id,
 | |
|                 membershipId: voip.currentSessionId,
 | |
|               ).toJson(),
 | |
|             ]
 | |
|           },
 | |
|           type: EventTypes.GroupCallMember,
 | |
|           eventId: 'asdfasdf',
 | |
|           senderId: '@test2:example.com',
 | |
|           originServerTs: DateTime.now(),
 | |
|           room: room,
 | |
|           stateKey: '@test2:example.com',
 | |
|         ),
 | |
|       );
 | |
|       room.setState(
 | |
|         Event(
 | |
|           content: {
 | |
|             'memberships': [
 | |
|               CallMembership(
 | |
|                 userId: '@test2.0:example.com',
 | |
|                 callId: '1111',
 | |
|                 backend: MeshBackend(),
 | |
|                 deviceId: '1111',
 | |
|                 expiresTs: DateTime.now().millisecondsSinceEpoch,
 | |
|                 roomId: room.id,
 | |
|                 membershipId: voip.currentSessionId,
 | |
|               ).toJson(),
 | |
|             ]
 | |
|           },
 | |
|           type: EventTypes.GroupCallMember,
 | |
|           eventId: 'asdfasdf',
 | |
|           senderId: '@test2.0:example.com',
 | |
|           originServerTs: DateTime.now(),
 | |
|           room: room,
 | |
|           stateKey: '@test2.0:example.com',
 | |
|         ),
 | |
|       );
 | |
|       room.setState(
 | |
|         Event(
 | |
|           content: {
 | |
|             'memberships': [
 | |
|               CallMembership(
 | |
|                 userId: '@test3:example.com',
 | |
|                 callId: '1111',
 | |
|                 backend: MeshBackend(),
 | |
|                 deviceId: '1111',
 | |
|                 expiresTs: DateTime.now()
 | |
|                     .subtract(Duration(hours: 1))
 | |
|                     .millisecondsSinceEpoch,
 | |
|                 roomId: room.id,
 | |
|                 membershipId: voip.currentSessionId,
 | |
|               ).toJson(),
 | |
|             ]
 | |
|           },
 | |
|           type: EventTypes.GroupCallMember,
 | |
|           eventId: 'asdfasdf',
 | |
|           senderId: '@test3:example.com',
 | |
|           originServerTs: DateTime.now().subtract(Duration(hours: 1)),
 | |
|           room: room,
 | |
|           stateKey: '@test3:example.com',
 | |
|         ),
 | |
|       );
 | |
|       expect(room.getFamedlyCallEvents().entries.elementAt(0).key,
 | |
|           '@test3:example.com');
 | |
|       expect(room.getFamedlyCallEvents().entries.elementAt(1).key,
 | |
|           '@test2:example.com');
 | |
|       expect(room.getFamedlyCallEvents().entries.elementAt(2).key,
 | |
|           '@test2.0:example.com');
 | |
|       expect(room.getFamedlyCallEvents().entries.elementAt(3).key,
 | |
|           '@test1:example.com');
 | |
|       expect(room.getCallMembershipsFromRoom().entries.elementAt(0).key,
 | |
|           '@test3:example.com');
 | |
|       expect(room.getCallMembershipsFromRoom().entries.elementAt(1).key,
 | |
|           '@test2:example.com');
 | |
|       expect(room.getCallMembershipsFromRoom().entries.elementAt(2).key,
 | |
|           '@test2.0:example.com');
 | |
|       expect(room.getCallMembershipsFromRoom().entries.elementAt(3).key,
 | |
|           '@test1:example.com');
 | |
|     });
 | |
| 
 | |
|     test('Enabling group calls', () async {
 | |
|       // users default is 0 and so group calls not enabled
 | |
|       room.setState(
 | |
|         Event(
 | |
|           senderId: '@test:example.com',
 | |
|           type: 'm.room.power_levels',
 | |
|           room: room,
 | |
|           eventId: '123a',
 | |
|           content: {
 | |
|             'events': {EventTypes.GroupCallMember: 100},
 | |
|             'state_default': 50,
 | |
|             'users_default': 0
 | |
|           },
 | |
|           originServerTs: DateTime.now(),
 | |
|           stateKey: '',
 | |
|         ),
 | |
|       );
 | |
|       expect(room.canJoinGroupCall, false);
 | |
|       expect(room.groupCallsEnabledForEveryone, false);
 | |
| 
 | |
|       room.setState(
 | |
|         Event(
 | |
|           senderId: '@test:example.com',
 | |
|           type: 'm.room.power_levels',
 | |
|           room: room,
 | |
|           eventId: '123a',
 | |
|           content: {
 | |
|             'events': {EventTypes.GroupCallMember: 27},
 | |
|             'state_default': 50,
 | |
|             'users_default': 49
 | |
|           },
 | |
|           originServerTs: DateTime.now(),
 | |
|           stateKey: '',
 | |
|         ),
 | |
|       );
 | |
|       expect(room.canJoinGroupCall, true);
 | |
|       expect(room.groupCallsEnabledForEveryone, true);
 | |
| 
 | |
|       // state_default 50 and user_default 0, use enableGroupCall
 | |
|       room.setState(
 | |
|         Event(
 | |
|             senderId: '@test:example.com',
 | |
|             type: 'm.room.power_levels',
 | |
|             room: room,
 | |
|             eventId: '123',
 | |
|             content: {
 | |
|               'state_default': 50,
 | |
|               'users': {'@test:fakeServer.notExisting': 100},
 | |
|               'users_default': 0
 | |
|             },
 | |
|             originServerTs: DateTime.now(),
 | |
|             stateKey: ''),
 | |
|       );
 | |
|       expect(room.canJoinGroupCall, true); // because admin
 | |
|       expect(room.groupCallsEnabledForEveryone, false);
 | |
|       await room.enableGroupCalls();
 | |
|       expect(room.canJoinGroupCall, true);
 | |
|       expect(room.groupCallsEnabledForEveryone, true);
 | |
| 
 | |
|       // state_default 50 and user_default unspecified, use enableGroupCall
 | |
|       room.setState(
 | |
|         Event(
 | |
|           senderId: '@test:example.com',
 | |
|           type: 'm.room.power_levels',
 | |
|           room: room,
 | |
|           eventId: '123',
 | |
|           content: {
 | |
|             'state_default': 50,
 | |
|             'users': {'@test:fakeServer.notExisting': 100},
 | |
|           },
 | |
|           originServerTs: DateTime.now(),
 | |
|           stateKey: '',
 | |
|         ),
 | |
|       );
 | |
| 
 | |
|       expect(room.canJoinGroupCall, true); // because admin
 | |
|       expect(room.groupCallsEnabledForEveryone, false);
 | |
|       await room.enableGroupCalls();
 | |
|       expect(room.canJoinGroupCall, true);
 | |
|       expect(room.groupCallsEnabledForEveryone, true);
 | |
| 
 | |
|       // state_default is 0 so users should be able to send state events
 | |
|       room.setState(
 | |
|         Event(
 | |
|           senderId: '@test:example.com',
 | |
|           type: 'm.room.power_levels',
 | |
|           room: room,
 | |
|           eventId: '123',
 | |
|           content: {
 | |
|             'state_default': 0,
 | |
|             'users': {'@test:fakeServer.notExisting': 100},
 | |
|           },
 | |
|           originServerTs: DateTime.now(),
 | |
|           stateKey: '',
 | |
|         ),
 | |
|       );
 | |
|       expect(room.canJoinGroupCall, true);
 | |
|       expect(room.groupCallsEnabledForEveryone, true);
 | |
|     });
 | |
| 
 | |
|     test('group call participants count', () {
 | |
|       room.setState(
 | |
|         Event(
 | |
|             senderId: '@test1:example.com',
 | |
|             type: EventTypes.GroupCallMember,
 | |
|             room: room,
 | |
|             eventId: '1234177',
 | |
|             content: {
 | |
|               'memberships': [
 | |
|                 CallMembership(
 | |
|                   userId: '@test1:example.com',
 | |
|                   callId: 'participants_count',
 | |
|                   backend: MeshBackend(),
 | |
|                   deviceId: '1111',
 | |
|                   expiresTs: DateTime.now()
 | |
|                       .subtract(Duration(hours: 1))
 | |
|                       .millisecondsSinceEpoch,
 | |
|                   roomId: room.id,
 | |
|                   membershipId: voip.currentSessionId,
 | |
|                 ).toJson(),
 | |
|               ]
 | |
|             },
 | |
|             originServerTs: DateTime.now(),
 | |
|             stateKey: '@test1:example.com'),
 | |
|       );
 | |
| 
 | |
|       expect(room.groupCallParticipantCount('participants_count'), 0);
 | |
|       expect(room.hasActiveGroupCall, false);
 | |
| 
 | |
|       room.setState(
 | |
|         Event(
 | |
|             senderId: '@test2:example.com',
 | |
|             type: EventTypes.GroupCallMember,
 | |
|             room: room,
 | |
|             eventId: '1234177',
 | |
|             content: {
 | |
|               'memberships': [
 | |
|                 CallMembership(
 | |
|                   userId: '@test2:example.com',
 | |
|                   callId: 'participants_count',
 | |
|                   backend: MeshBackend(),
 | |
|                   deviceId: '1111',
 | |
|                   expiresTs: DateTime.now()
 | |
|                       .add(Duration(hours: 1))
 | |
|                       .millisecondsSinceEpoch,
 | |
|                   roomId: room.id,
 | |
|                   membershipId: voip.currentSessionId,
 | |
|                 ).toJson(),
 | |
|               ]
 | |
|             },
 | |
|             originServerTs: DateTime.now(),
 | |
|             stateKey: '@test2:example.com'),
 | |
|       );
 | |
|       expect(room.groupCallParticipantCount('participants_count'), 1);
 | |
|       expect(room.hasActiveGroupCall, true);
 | |
| 
 | |
|       room.setState(
 | |
|         Event(
 | |
|             senderId: '@test3:example.com',
 | |
|             type: EventTypes.GroupCallMember,
 | |
|             room: room,
 | |
|             eventId: '1231234124123',
 | |
|             content: {
 | |
|               'memberships': [
 | |
|                 CallMembership(
 | |
|                   userId: '@test3:example.com',
 | |
|                   callId: 'participants_count',
 | |
|                   backend: MeshBackend(),
 | |
|                   deviceId: '1111',
 | |
|                   expiresTs: DateTime.now().millisecondsSinceEpoch,
 | |
|                   roomId: room.id,
 | |
|                   membershipId: voip.currentSessionId,
 | |
|                 ).toJson(),
 | |
|               ]
 | |
|             },
 | |
|             originServerTs: DateTime.now(),
 | |
|             stateKey: '@test3:example.com'),
 | |
|       );
 | |
| 
 | |
|       expect(room.groupCallParticipantCount('participants_count'), 2);
 | |
|       expect(room.hasActiveGroupCall, true);
 | |
|     });
 | |
|   });
 | |
| }
 |