diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 29af07427..41890c3f1 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -43,6 +43,7 @@ 039FE1757084092146C0B616 /* ClientFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 176A581FDC61737676782607 /* ClientFactoryProtocol.swift */; }; 03BD83E8BDD23AE059802E0D /* UITestsScreenIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEBE5EA91E8691EDF364EC2 /* UITestsScreenIdentifier.swift */; }; 03CDCA6243F89B194E3FAD17 /* EncryptionAuthenticity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955336CBD5ED73C792D1F580 /* EncryptionAuthenticity.swift */; }; + 03F192C5C011AFA6068C9EAA /* SpaceServiceSDKMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90B1E4D1F0A6FAAE64B54D76 /* SpaceServiceSDKMock.swift */; }; 0437765FF480249486893CC7 /* ScreenTrackerViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 196004E7695FBA292A7944AF /* ScreenTrackerViewModifier.swift */; }; 044DD8F80231BC30570F7965 /* UserDiscoveryService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65AAD845E53B0C8B5E0812C2 /* UserDiscoveryService.swift */; }; 04F17DE71A50206336749BAC /* UserPreferenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA241DEEF7C8A7181C0AEDC9 /* UserPreferenceTests.swift */; }; @@ -419,6 +420,7 @@ 441857143FC100E0A7DE42A8 /* RoomPowerLevelProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D635709C1D6D37C225AD40E /* RoomPowerLevelProxyProtocol.swift */; }; 446BCD2D0AE27E0CFD1BDC8F /* ImageMediaEventsTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FF584D757E768EA7776A532 /* ImageMediaEventsTimelineView.swift */; }; 44BDD670FF9095ACE240A3A2 /* VoiceMessageMediaManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC4F10BDD56FA77FEC742333 /* VoiceMessageMediaManagerTests.swift */; }; + 44D3EA8583C54BA3D8058499 /* SyncServiceSDKMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33DBBBC78047C669166C9F7C /* SyncServiceSDKMock.swift */; }; 44F0E1B576C7599DF8022071 /* Version in Frameworks */ = {isa = PBXBuildFile; productRef = A05AF81DDD14AD58CB0E1B9B /* Version */; }; 454311EAC17D778E19F46592 /* NotificationPermissionsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91868EB98818044E6FEBE532 /* NotificationPermissionsScreenCoordinator.swift */; }; 454F8DDC4442C0DE54094902 /* LABiometryType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3F219838588C62198E726E3 /* LABiometryType.swift */; }; @@ -655,6 +657,7 @@ 69B9CC733A880E1BB097C113 /* LinkNewDeviceScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4D09290C6791D6EF04F569E /* LinkNewDeviceScreenModels.swift */; }; 69C7B956B74BEC3DB88224EA /* NavigationSplitCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78913D6E120D46138E97C107 /* NavigationSplitCoordinatorTests.swift */; }; 69DE29C3E3180BB17D840690 /* ProgressCursorModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97C8E13A1FBA717B0C277ECC /* ProgressCursorModifier.swift */; }; + 6A16F0ADF0B5E81AEB611D3B /* SyncServiceBuilderSDKMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70373EF23A73181D2568D79 /* SyncServiceBuilderSDKMock.swift */; }; 6A38D0A2BC3943A92D82576E /* EditRoomAddressScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B18089ED50324583BB2FB7 /* EditRoomAddressScreenViewModelProtocol.swift */; }; 6A54F52443EC52AC5CD772C0 /* JoinRoomScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 869A8A4632E511351BFE2EC4 /* JoinRoomScreen.swift */; }; 6A5FDF9306CBD62C7EDDB552 /* CLLocationManagerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C723327DC4A3093CD9675B27 /* CLLocationManagerMock.swift */; }; @@ -936,6 +939,7 @@ 93BAF04D9CCBC0A8841414D0 /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D67E616BCA82D8A1258D488 /* NetworkMonitor.swift */; }; 93DC8297B8287B50B2A4B57D /* ExpiringTaskRunner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B25F959A434BB9923A3223F /* ExpiringTaskRunner.swift */; }; 9408CE8B8865C0C8DD4C9869 /* NoticeRoomTimelineItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FD51B4D5173F7FC886F5360 /* NoticeRoomTimelineItemContent.swift */; }; + 9415ED7F6200904F2E179EF9 /* RoomListSDKMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2B8AA3199BAAD066E36CB07 /* RoomListSDKMock.swift */; }; 944EAB7BC2408B2C6570E722 /* AppHooks.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6E4AB573FAEBB7B853DD04C /* AppHooks.swift */; }; 9462C62798F47E39DCC182D2 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA89A2DD51B6BBE1DA55E263 /* Application.swift */; }; 94871CA883F44022086FE750 /* AuthenticationStartLogo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98784280D98C852727BE0111 /* AuthenticationStartLogo.swift */; }; @@ -1018,6 +1022,7 @@ A10D6CCDE2010C09EEA1A593 /* HomeScreenRoomList.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7661EFFCAA307A97D71132A /* HomeScreenRoomList.swift */; }; A14A9419105A1CD42F0511C4 /* UserIndicatorModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43005941B3A2C9671E23C85 /* UserIndicatorModalView.swift */; }; A17FAD2EBC53E17B5FD384DB /* InviteUsersScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22730A30C50AC2E3D5BA8642 /* InviteUsersScreenViewModelProtocol.swift */; }; + A180F7B8048C3697453760F2 /* SessionDirectories.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A98F895BD60A4448E2A278C /* SessionDirectories.swift */; }; A1BA8D6BABAFA9BAAEAA3FFD /* NotificationSettingsProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FDD775CFD72DD2D3C8A8390 /* NotificationSettingsProxyProtocol.swift */; }; A1DF0E1E526A981ED6D5DF44 /* UserIndicatorControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2429224EB0EEA34D35CE9249 /* UserIndicatorControllerTests.swift */; }; A2091F4B1332D9BF273B09D5 /* SpaceServiceProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF36FC3DE25B20B7FA91F1FD /* SpaceServiceProxyMock.swift */; }; @@ -1074,6 +1079,7 @@ A950C95855C474F75B30CA7B /* PollFormScreenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCDAB580109C09A6AA97AF7E /* PollFormScreenTests.swift */; }; A969147E0EEE0E27EE226570 /* MediaUploadPreviewScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47F29139BC2A804CE5E0757E /* MediaUploadPreviewScreenViewModel.swift */; }; A975D60EA49F6AF73308809F /* RoomMembersListScreenMemberCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC03209FDE8CE0810617BFFF /* RoomMembersListScreenMemberCell.swift */; }; + A98CB75060E4C9BB5D41531B /* EncryptionSDKMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2734CCFE59E5EDC216602910 /* EncryptionSDKMock.swift */; }; A9A5801D5EE3D4D91F6DDADB /* AnalyticsSettingsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58C2527813FDAE23E72A9063 /* AnalyticsSettingsScreenViewModelTests.swift */; }; A9D349478F7D4A2B1E40CEF9 /* LegalInformationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8977176AB534AA41630395BC /* LegalInformationScreenViewModelProtocol.swift */; }; AA050DF4AEE54A641BA7CA22 /* RoomSummaryProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10CC626F97AD70FF0420C115 /* RoomSummaryProviderProtocol.swift */; }; @@ -1110,6 +1116,7 @@ B1387648C6F71F1B98244803 /* SecureBackupRecoveryKeyScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 596AA8843AC1A234F3387767 /* SecureBackupRecoveryKeyScreenCoordinator.swift */; }; B14BC354E56616B6B7D9A3D7 /* NotificationServiceExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27A1AD6389A4659AF0CEAE62 /* NotificationServiceExtension.swift */; }; B188D0907A4D38AAAF6FEFA8 /* AppLockSetupFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DBB08A95EFA668F2CF27211 /* AppLockSetupFlowCoordinator.swift */; }; + B1ABFEB75E0202370BD7353A /* RoomListServiceSDKMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3454C79D9E2444BBDBF9F5A /* RoomListServiceSDKMock.swift */; }; B1B255CE0E4306DD6E09D936 /* EncryptionResetPasswordScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54A5E6F398C269AD52C9AE21 /* EncryptionResetPasswordScreenModels.swift */; }; B22D857D1E8FCA6DD74A58E3 /* UserSessionScreenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F899D02CF26EA7675EEBE74C /* UserSessionScreenTests.swift */; }; B245583C63F8F90357B87FAE /* PostHog in Frameworks */ = {isa = PBXBuildFile; productRef = CCE5BF78B125320CBF3BB834 /* PostHog */; }; @@ -1304,6 +1311,7 @@ D16B3134A7FF4FBA0749014A /* AuthenticationClassicAppAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5C45578F406698388B97C07 /* AuthenticationClassicAppAccountView.swift */; }; D18B70975644C24F60656C0D /* KnockRequestProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07851F4EA81AA3339806A7B /* KnockRequestProxyProtocol.swift */; }; D19A748E95E2FAB2940570F0 /* CallScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4103AB4340F2974D690A12A /* CallScreen.swift */; }; + D1C942421F607285C50FB792 /* SearchServiceSDKMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EF5721148A5D59B476BA976 /* SearchServiceSDKMock.swift */; }; D2048FD56760BDABA3DB5FC2 /* AppLockServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EAAB54C6CE91D64B69A9F8 /* AppLockServiceProtocol.swift */; }; D22345698F6548C1EE960940 /* IdentityConfirmedScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBE70FFB7936F35811772C1 /* IdentityConfirmedScreenModels.swift */; }; D23BA23864EA7BA3F353C0D1 /* LabsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF847A34FC4C8C937CD39E08 /* LabsScreenViewModelProtocol.swift */; }; @@ -1401,6 +1409,7 @@ E32E71A1AF5E5E69E8363B26 /* OAuthAuthenticationPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEA11EF88B2848B12FA6FA36 /* OAuthAuthenticationPresenter.swift */; }; E3AC72E3E58F364EF15C1CC7 /* NotificationSettingsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 514363244AE7D68080D44C6F /* NotificationSettingsScreenViewModelTests.swift */; }; E3CA565A4B9704F191B191F0 /* JoinedRoomSize+MemberCount.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBF9AEA706926DD0DA2B954C /* JoinedRoomSize+MemberCount.swift */; }; + E3E1B656F2E8F2D8075F6558 /* UserSessionStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F7E6F5D5A54C68DC2EDD71 /* UserSessionStoreTests.swift */; }; E3E1E255DC8CB34BD8573E0D /* UserIndicatorControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A12D3B1BCF920880CA8BBB6B /* UserIndicatorControllerProtocol.swift */; }; E3EBC3BF7CE3960B41757BAA /* Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7310D8DFE01AF45F0689C3AA /* Publisher.swift */; }; E468CC731C3F4D678499E52F /* LAContextMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BA5A62DA4B543827FF82354 /* LAContextMock.swift */; }; @@ -1810,6 +1819,7 @@ 0E8BDC092D817B68CD9040C5 /* UserSessionStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionStore.swift; sourceTree = ""; }; 0E95B3BDB80531C85CD50AE6 /* InvitedRoomProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitedRoomProxy.swift; sourceTree = ""; }; 0EE9EAF0309A2A1D67D8FAF5 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = sv; path = sv.lproj/Localizable.stringsdict; sourceTree = ""; }; + 0EF5721148A5D59B476BA976 /* SearchServiceSDKMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchServiceSDKMock.swift; sourceTree = ""; }; 0F0AF1A13D358CA0BDA6065F /* NotificationContentBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationContentBuilderTests.swift; sourceTree = ""; }; 0F5567A7EF6F2AB9473236F6 /* DocumentPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentPicker.swift; sourceTree = ""; }; 0F60FDB3AEFC60830BD289FE /* DeveloperOptionsScreenHook.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperOptionsScreenHook.swift; sourceTree = ""; }; @@ -1884,6 +1894,7 @@ 1A18F6CE4D694D21E4EA9B25 /* Strings+Untranslated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Strings+Untranslated.swift"; sourceTree = ""; }; 1A786DE47C0DBCE0CD163193 /* StopButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StopButton.swift; sourceTree = ""; }; 1A7ED2EF5BDBAD2A7DBC4636 /* GeoURITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeoURITests.swift; sourceTree = ""; }; + 1A98F895BD60A4448E2A278C /* SessionDirectories.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionDirectories.swift; sourceTree = ""; }; 1A9E7C89E4BE90383BE235C5 /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = cy; path = cy.lproj/Localizable.stringsdict; sourceTree = ""; }; 1B2AC540DE619B36832A5DB5 /* LocationRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationRoomTimelineItem.swift; sourceTree = ""; }; 1B30CD19ED6243FEDFBA8400 /* ManageRoomMemberSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManageRoomMemberSheetView.swift; sourceTree = ""; }; @@ -1964,6 +1975,7 @@ 26EAAB54C6CE91D64B69A9F8 /* AppLockServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockServiceProtocol.swift; sourceTree = ""; }; 2711E5996016ABD6EAAEB58A /* LogLevel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogLevel.swift; sourceTree = ""; }; 2721D7B051F0159AA919DA05 /* RoomChangePermissionsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangePermissionsScreenViewModelProtocol.swift; sourceTree = ""; }; + 2734CCFE59E5EDC216602910 /* EncryptionSDKMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionSDKMock.swift; sourceTree = ""; }; 2757B1BE23DF8AA239937243 /* AudioConverterProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioConverterProtocol.swift; sourceTree = ""; }; 276833816F5DEB1CE3B8BE1E /* ReportRoomScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportRoomScreen.swift; sourceTree = ""; }; 27A1AD6389A4659AF0CEAE62 /* NotificationServiceExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationServiceExtension.swift; sourceTree = ""; }; @@ -2034,6 +2046,7 @@ 33752AE856E93CE62412B7A1 /* LiveLocationManagerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveLocationManagerProtocol.swift; sourceTree = ""; }; 337AD37403015231CAE3E80B /* SentryEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryEvent.swift; sourceTree = ""; }; 33AE897D86784CCA5E4E9227 /* ElementCallService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementCallService.swift; sourceTree = ""; }; + 33DBBBC78047C669166C9F7C /* SyncServiceSDKMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncServiceSDKMock.swift; sourceTree = ""; }; 33E49C5C6F802B4D94CA78D1 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/Localizable.strings; sourceTree = ""; }; 3423C39324CF851A72EC9976 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/SAS.strings; sourceTree = ""; }; 342BEBC3C5FC3F9943C41C4C /* TemplateScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreenViewModelProtocol.swift; sourceTree = ""; }; @@ -2574,6 +2587,7 @@ 90791B9C739C716A40E1B230 /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = ""; }; 907FA4DE17DEA1A3738EFB83 /* AudioRecorder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRecorder.swift; sourceTree = ""; }; 90A55430639712CFACA34F43 /* TextRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRoomTimelineItem.swift; sourceTree = ""; }; + 90B1E4D1F0A6FAAE64B54D76 /* SpaceServiceSDKMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpaceServiceSDKMock.swift; sourceTree = ""; }; 90DFF217B3D9D0941283278C /* RoomRolesAndPermissionsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomRolesAndPermissionsScreenViewModelProtocol.swift; sourceTree = ""; }; 90F2F8998E5632668B0AD848 /* RoomTimelineItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemView.swift; sourceTree = ""; }; 913C8E13B8B602C7B6C0C4AE /* PillTextAttachmentData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillTextAttachmentData.swift; sourceTree = ""; }; @@ -2870,6 +2884,7 @@ C1FA515B3B0D61EF1E907D2D /* BadgeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeView.swift; sourceTree = ""; }; C258C9C815272911A5B132C3 /* FormattedBodyText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormattedBodyText.swift; sourceTree = ""; }; C2886615BEBAE33A0AA4D5F8 /* RoomScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenModels.swift; sourceTree = ""; }; + C2B8AA3199BAAD066E36CB07 /* RoomListSDKMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomListSDKMock.swift; sourceTree = ""; }; C2E9B841EE4878283ECDB554 /* InviteUsersScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteUsersScreen.swift; sourceTree = ""; }; C30F45308428A4D9FFDB2FB8 /* BannedRoomProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BannedRoomProxy.swift; sourceTree = ""; }; C3141EE5B2CBEC45270E01EB /* CreateRoomSpaceSelectionSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomSpaceSelectionSheet.swift; sourceTree = ""; }; @@ -3053,6 +3068,7 @@ E2F96CCBEAAA7F2185BFA354 /* ClientProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientProxyMock.swift; sourceTree = ""; }; E3059CFA00C67D8787273B20 /* ServerSelectionScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionScreenViewModel.swift; sourceTree = ""; }; E321E840DCC63790049984F4 /* ElementCallServiceMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementCallServiceMock.swift; sourceTree = ""; }; + E3454C79D9E2444BBDBF9F5A /* RoomListServiceSDKMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomListServiceSDKMock.swift; sourceTree = ""; }; E36CB905A2B9EC2C92A2DA7C /* KeychainController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainController.swift; sourceTree = ""; }; E3877D3CFAC1D33823359BAF /* HTMLParserStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTMLParserStyle.swift; sourceTree = ""; }; E3B97591B2D3D4D67553506D /* AnalyticsClientProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsClientProtocol.swift; sourceTree = ""; }; @@ -3176,7 +3192,9 @@ F5D1BAA90F3A073D91B4F16B /* RoomNotificationSettingsProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsProxyMock.swift; sourceTree = ""; }; F5D8FEB1FED10E995CB002F7 /* TimelineBubbleLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineBubbleLayout.swift; sourceTree = ""; }; F5E23D8EE6CBACF32F1EC874 /* MediaPlayerProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPlayerProviderProtocol.swift; sourceTree = ""; }; + F5F7E6F5D5A54C68DC2EDD71 /* UserSessionStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionStoreTests.swift; sourceTree = ""; }; F64A8582F65567AC38C2976A /* PollFormScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFormScreenViewModel.swift; sourceTree = ""; }; + F70373EF23A73181D2568D79 /* SyncServiceBuilderSDKMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncServiceBuilderSDKMock.swift; sourceTree = ""; }; F72EFC8C634469F9262659C7 /* NSItemProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSItemProvider.swift; sourceTree = ""; }; F78B4E56DBFFD4A7A39D10F5 /* AudioPlaybackSpeed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlaybackSpeed.swift; sourceTree = ""; }; F7E247E29C7A0E421DB839D7 /* RoomPowerLevel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPowerLevel.swift; sourceTree = ""; }; @@ -3403,6 +3421,7 @@ isa = PBXGroup; children = ( 79351405CD99312A182B2488 /* NavigationCoordinators.swift */, + 1A98F895BD60A4448E2A278C /* SessionDirectories.swift */, ); path = Extensions; sourceTree = ""; @@ -5266,6 +5285,7 @@ BA241DEEF7C8A7181C0AEDC9 /* UserPreferenceTests.swift */, 71E2E5103702D13361D09100 /* UserProfileScreenViewModelTests.swift */, 4FA29BAE9B0F2D90E57B261C /* UserSessionFlowCoordinatorTests.swift */, + F5F7E6F5D5A54C68DC2EDD71 /* UserSessionStoreTests.swift */, 283974987DA7EC61D2AB57D9 /* VoiceMessageCacheTests.swift */, AC4F10BDD56FA77FEC742333 /* VoiceMessageMediaManagerTests.swift */, D93C94C30E3135BC9290DE13 /* VoiceMessageRecorderTests.swift */, @@ -7025,9 +7045,16 @@ children = ( 4699C72A1E5B3CD5033D147B /* ClientBuilderSDKMock.swift */, 8EAF4A49F3ACD8BB8B0D2371 /* ClientSDKMock.swift */, + 2734CCFE59E5EDC216602910 /* EncryptionSDKMock.swift */, 0A81FD0C60175FA081EB19AD /* EventTimelineItem.swift */, 5EFB1D29B0870AFB6A56E9B8 /* IdentityResetHandleSDKMock.swift */, 580BDCD23DD02481AB5FFB47 /* LeaveSpaceHandleSDKMock.swift */, + C2B8AA3199BAAD066E36CB07 /* RoomListSDKMock.swift */, + E3454C79D9E2444BBDBF9F5A /* RoomListServiceSDKMock.swift */, + 0EF5721148A5D59B476BA976 /* SearchServiceSDKMock.swift */, + 90B1E4D1F0A6FAAE64B54D76 /* SpaceServiceSDKMock.swift */, + F70373EF23A73181D2568D79 /* SyncServiceBuilderSDKMock.swift */, + 33DBBBC78047C669166C9F7C /* SyncServiceSDKMock.swift */, ); path = SDK; sourceTree = ""; @@ -8329,6 +8356,7 @@ EB87DF90CF6F8D5D12404C6E /* SecureBackupLogoutConfirmationScreenViewModelTests.swift in Sources */, CB9FB2BEF313072C705AC9B5 /* SecurityAndPrivacyScreenViewModelTests.swift in Sources */, 67ECD32538F6DAFE38A623F9 /* ServerSelectionScreenViewModelTests.swift in Sources */, + A180F7B8048C3697453760F2 /* SessionDirectories.swift in Sources */, CC1C948F67A5510A340FD7F0 /* SessionDirectoriesTests.swift in Sources */, 86675910612A12409262DFBD /* SessionVerificationStateMachineTests.swift in Sources */, 755727E0B756430DFFEC4732 /* SessionVerificationViewModelTests.swift in Sources */, @@ -8356,6 +8384,7 @@ 04F17DE71A50206336749BAC /* UserPreferenceTests.swift in Sources */, 73F547BEB41D3DAFAAF6E0AF /* UserProfileScreenViewModelTests.swift in Sources */, 627139A3D79F032BA81E3A53 /* UserSessionFlowCoordinatorTests.swift in Sources */, + E3E1B656F2E8F2D8075F6558 /* UserSessionStoreTests.swift in Sources */, 21AFEFB8CEFE56A3811A1F5B /* VoiceMessageCacheTests.swift in Sources */, 44BDD670FF9095ACE240A3A2 /* VoiceMessageMediaManagerTests.swift in Sources */, A3D7110C1E75E7B4A73BE71C /* VoiceMessageRecorderTests.swift in Sources */, @@ -8748,6 +8777,7 @@ 97BAEDD9054FB5F233EE928B /* EncryptionResetScreenModels.swift in Sources */, EC3320639828BED8B3E5F2C6 /* EncryptionResetScreenViewModel.swift in Sources */, A0868BDE84D2140A885BE3C9 /* EncryptionResetScreenViewModelProtocol.swift in Sources */, + A98CB75060E4C9BB5D41531B /* EncryptionSDKMock.swift in Sources */, 4D2B54233C7B2C04B4ABE55A /* EncryptionSettingsFlowCoordinator.swift in Sources */, 50539366B408780B232C1910 /* EstimatedWaveformView.swift in Sources */, F78BAD28482A467287A9A5A3 /* EventBasedMessageTimelineItemProtocol.swift in Sources */, @@ -9165,6 +9195,8 @@ 4A9CEEE612D6D8B3DDBD28BA /* RoomListFilterView.swift in Sources */, BD0BE20DBCE31253AE4490A1 /* RoomListFiltersEmptyStateView.swift in Sources */, 33F1FB19F222BA9930AB1A00 /* RoomListFiltersView.swift in Sources */, + 9415ED7F6200904F2E179EF9 /* RoomListSDKMock.swift in Sources */, + B1ABFEB75E0202370BD7353A /* RoomListServiceSDKMock.swift in Sources */, 18FD4EA36456910FD9CB1B95 /* RoomLiveLocationService.swift in Sources */, 84196A8F1963D55726261879 /* RoomLiveLocationServiceMock.swift in Sources */, 811D9AA152443037BEDA6F22 /* RoomLiveLocationServiceProtocol.swift in Sources */, @@ -9268,6 +9300,7 @@ 54804A7D8298F7E8ACAFDD76 /* SearchScreenViewModelProtocol.swift in Sources */, 51A075328D68FB4AE65C43B5 /* SearchServiceProxy.swift in Sources */, BEB3D97EA51B25C87A37997E /* SearchServiceProxyProtocol.swift in Sources */, + D1C942421F607285C50FB792 /* SearchServiceSDKMock.swift in Sources */, 8AC8FDECC74D710CA97A2991 /* Secrets.swift in Sources */, 339BC18777912E1989F2F17D /* Section.swift in Sources */, F833D5B5BE6707F961FA88DB /* SecureBackupController.swift in Sources */, @@ -9363,6 +9396,7 @@ A2091F4B1332D9BF273B09D5 /* SpaceServiceProxyMock.swift in Sources */, DB5200B87C4CE9DF0024AC4E /* SpaceServiceProxyProtocol.swift in Sources */, 612196AF7826846A50C07FA6 /* SpaceServiceRoom.swift in Sources */, + 03F192C5C011AFA6068C9EAA /* SpaceServiceSDKMock.swift in Sources */, D0E257557DAC8A34C7B52A9F /* SpaceSettingsFlowCoordinator.swift in Sources */, 383063A7924F06D54BA9B24C /* SpaceSettingsScreen.swift in Sources */, E1428612B08ED3030EC1FEC3 /* SpacesScreen.swift in Sources */, @@ -9394,6 +9428,8 @@ A7D48E44D485B143AADDB77D /* Strings+Untranslated.swift in Sources */, 066A1E9B94723EE9F3038044 /* Strings.swift in Sources */, FF34BF2AF731340AF9414A18 /* SwipeRightAction.swift in Sources */, + 6A16F0ADF0B5E81AEB611D3B /* SyncServiceBuilderSDKMock.swift in Sources */, + 44D3EA8583C54BA3D8058499 /* SyncServiceSDKMock.swift in Sources */, C097D5453640E27D397943CB /* TargetConfiguration.swift in Sources */, E290C78E7F09F47FD2662986 /* Task.swift in Sources */, 1555A7643D85187D4851040C /* TemplateScreen.swift in Sources */, diff --git a/ElementX/Sources/Mocks/ClientFactoryMock.swift b/ElementX/Sources/Mocks/ClientFactoryMock.swift index 2863217a3..3094df788 100644 --- a/ElementX/Sources/Mocks/ClientFactoryMock.swift +++ b/ElementX/Sources/Mocks/ClientFactoryMock.swift @@ -59,5 +59,13 @@ extension ClientFactoryMock { } return client } + + makeAppClientCredentialsClientSessionDelegateAppSettingsAppHooksClosure = { credentials, _, _, _ in + ClientSDKMock(.init(userID: credentials.userID)) + } + + makeNSEClientCredentialsRoomIDClientSessionDelegateAppSettingsAppHooksClosure = { credentials, _, _, _, _ in + ClientSDKMock(.init(userID: credentials.userID)) + } } } diff --git a/ElementX/Sources/Mocks/SDK/ClientBuilderSDKMock.swift b/ElementX/Sources/Mocks/SDK/ClientBuilderSDKMock.swift index 232034246..40738d5e2 100644 --- a/ElementX/Sources/Mocks/SDK/ClientBuilderSDKMock.swift +++ b/ElementX/Sources/Mocks/SDK/ClientBuilderSDKMock.swift @@ -19,6 +19,6 @@ nonisolated extension ClientBuilderSDKMock { convenience init(_ configuration: Configuration) { self.init() - disableWellKnownLookupDisableWellKnownLookupReturnValue = self + disableWellKnownLookupDisableWellKnownLookupClosure = { [unowned self] _ in self } } } diff --git a/ElementX/Sources/Mocks/SDK/ClientSDKMock.swift b/ElementX/Sources/Mocks/SDK/ClientSDKMock.swift index 4994add09..769f7cef6 100644 --- a/ElementX/Sources/Mocks/SDK/ClientSDKMock.swift +++ b/ElementX/Sources/Mocks/SDK/ClientSDKMock.swift @@ -68,6 +68,22 @@ nonisolated extension ClientSDKMock { throw MockError.generic } } + + encryptionReturnValue = EncryptionSDKMock(.init()) + getNotificationSettingsReturnValue = NotificationSettingsSDKMock() + homeserverCapabilitiesReturnValue = HomeserverCapabilitiesSDKMock() + spaceServiceReturnValue = SpaceServiceSDKMock(.init()) + searchServiceReturnValue = SearchServiceSDKMock(.init()) + syncServiceReturnValue = SyncServiceBuilderSDKMock(.init()) + + setDelegateDelegateReturnValue = TaskHandleSDKMock() + subscribeToIgnoredUsersListenerReturnValue = TaskHandleSDKMock() + subscribeToSendQueueStatusListenerReturnValue = TaskHandleSDKMock() + subscribeToSendQueueUpdatesListenerReturnValue = TaskHandleSDKMock() + subscribeToMediaPreviewConfigListenerReturnValue = TaskHandleSDKMock() + subscribeToOwnBeaconInfoUpdatesListenerReturnValue = TaskHandleSDKMock() + isProfilesSlidingSyncExtensionSupportedReturnValue = false + isUserStatusSupportedReturnValue = false } } diff --git a/ElementX/Sources/Mocks/SDK/EncryptionSDKMock.swift b/ElementX/Sources/Mocks/SDK/EncryptionSDKMock.swift new file mode 100644 index 000000000..b8130c19f --- /dev/null +++ b/ElementX/Sources/Mocks/SDK/EncryptionSDKMock.swift @@ -0,0 +1,25 @@ +// +// Copyright 2026 Element Creations Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. +// Please see LICENSE files in the repository root for full details. +// + +import Foundation +import MatrixRustSDK +import MatrixRustSDKMocks + +nonisolated extension EncryptionSDKMock { + struct Configuration { + var verificationState: VerificationState = .unknown + } + + convenience init(_ configuration: Configuration) { + self.init() + + verificationStateReturnValue = configuration.verificationState + backupStateListenerListenerReturnValue = TaskHandleSDKMock() + recoveryStateListenerListenerReturnValue = TaskHandleSDKMock() + verificationStateListenerListenerReturnValue = TaskHandleSDKMock() + } +} diff --git a/ElementX/Sources/Mocks/SDK/RoomListSDKMock.swift b/ElementX/Sources/Mocks/SDK/RoomListSDKMock.swift new file mode 100644 index 000000000..9ddb829cf --- /dev/null +++ b/ElementX/Sources/Mocks/SDK/RoomListSDKMock.swift @@ -0,0 +1,28 @@ +// +// Copyright 2026 Element Creations Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. +// Please see LICENSE files in the repository root for full details. +// + +import Foundation +import MatrixRustSDK +import MatrixRustSDKMocks + +nonisolated extension RoomListSDKMock { + struct Configuration { + var loadingState: RoomListLoadingState = .notLoaded + } + + convenience init(_ configuration: Configuration) { + self.init() + + let dynamicAdapters = RoomListEntriesWithDynamicAdaptersResultSDKMock() + let entriesController = RoomListDynamicEntriesControllerSDKMock() + entriesController.setFilterKindReturnValue = true + dynamicAdapters.controllerReturnValue = entriesController + + entriesWithDynamicAdaptersPageSizeListenerReturnValue = dynamicAdapters + loadingStateListenerReturnValue = .init(state: configuration.loadingState, stateStream: TaskHandleSDKMock()) + } +} diff --git a/ElementX/Sources/Mocks/SDK/RoomListServiceSDKMock.swift b/ElementX/Sources/Mocks/SDK/RoomListServiceSDKMock.swift new file mode 100644 index 000000000..6cb809eec --- /dev/null +++ b/ElementX/Sources/Mocks/SDK/RoomListServiceSDKMock.swift @@ -0,0 +1,24 @@ +// +// Copyright 2026 Element Creations Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. +// Please see LICENSE files in the repository root for full details. +// + +import Foundation +import MatrixRustSDK +import MatrixRustSDKMocks + +nonisolated extension RoomListServiceSDKMock { + struct Configuration { + var roomList = RoomListSDKMock(.init()) + } + + convenience init(_ configuration: Configuration) { + self.init() + + allRoomsReturnValue = configuration.roomList + stateListenerReturnValue = TaskHandleSDKMock() + syncIndicatorDelayBeforeShowingInMsDelayBeforeHidingInMsListenerReturnValue = TaskHandleSDKMock() + } +} diff --git a/ElementX/Sources/Mocks/SDK/SearchServiceSDKMock.swift b/ElementX/Sources/Mocks/SDK/SearchServiceSDKMock.swift new file mode 100644 index 000000000..a995454b0 --- /dev/null +++ b/ElementX/Sources/Mocks/SDK/SearchServiceSDKMock.swift @@ -0,0 +1,21 @@ +// +// Copyright 2026 Element Creations Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. +// Please see LICENSE files in the repository root for full details. +// + +import Foundation +import MatrixRustSDK +import MatrixRustSDKMocks + +nonisolated extension SearchServiceSDKMock { + struct Configuration { } + + convenience init(_ configuration: Configuration) { + self.init() + + paginationStateReturnValue = .idle(endReached: true) + subscribeToPaginationStateUpdatesListenerReturnValue = TaskHandleSDKMock() + } +} diff --git a/ElementX/Sources/Mocks/SDK/SpaceServiceSDKMock.swift b/ElementX/Sources/Mocks/SDK/SpaceServiceSDKMock.swift new file mode 100644 index 000000000..3db032b48 --- /dev/null +++ b/ElementX/Sources/Mocks/SDK/SpaceServiceSDKMock.swift @@ -0,0 +1,21 @@ +// +// Copyright 2026 Element Creations Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. +// Please see LICENSE files in the repository root for full details. +// + +import Foundation +import MatrixRustSDK +import MatrixRustSDKMocks + +nonisolated extension SpaceServiceSDKMock { + struct Configuration { } + + convenience init(_ configuration: Configuration) { + self.init() + + subscribeToTopLevelJoinedSpacesListenerReturnValue = TaskHandleSDKMock() + subscribeToSpaceFiltersListenerReturnValue = TaskHandleSDKMock() + } +} diff --git a/ElementX/Sources/Mocks/SDK/SyncServiceBuilderSDKMock.swift b/ElementX/Sources/Mocks/SDK/SyncServiceBuilderSDKMock.swift new file mode 100644 index 000000000..9be28e973 --- /dev/null +++ b/ElementX/Sources/Mocks/SDK/SyncServiceBuilderSDKMock.swift @@ -0,0 +1,25 @@ +// +// Copyright 2026 Element Creations Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. +// Please see LICENSE files in the repository root for full details. +// + +import Foundation +import MatrixRustSDK +import MatrixRustSDKMocks + +nonisolated extension SyncServiceBuilderSDKMock { + struct Configuration { + var syncService = SyncServiceSDKMock(.init()) + } + + convenience init(_ configuration: Configuration) { + self.init() + + withOfflineModeClosure = { [unowned self] in self } + withSharePosEnableClosure = { [unowned self] _ in self } + withProfilesExtensionClosure = { [unowned self] in self } + finishReturnValue = configuration.syncService + } +} diff --git a/ElementX/Sources/Mocks/SDK/SyncServiceSDKMock.swift b/ElementX/Sources/Mocks/SDK/SyncServiceSDKMock.swift new file mode 100644 index 000000000..d59b617bc --- /dev/null +++ b/ElementX/Sources/Mocks/SDK/SyncServiceSDKMock.swift @@ -0,0 +1,23 @@ +// +// Copyright 2026 Element Creations Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. +// Please see LICENSE files in the repository root for full details. +// + +import Foundation +import MatrixRustSDK +import MatrixRustSDKMocks + +nonisolated extension SyncServiceSDKMock { + struct Configuration { + var roomListService = RoomListServiceSDKMock(.init()) + } + + convenience init(_ configuration: Configuration) { + self.init() + + roomListServiceReturnValue = configuration.roomListService + stateListenerReturnValue = TaskHandleSDKMock() + } +} diff --git a/ElementX/Sources/Services/Keychain/KeychainControllerMock.swift b/ElementX/Sources/Services/Keychain/KeychainControllerMock.swift index 9db5b1d4a..b08bba936 100644 --- a/ElementX/Sources/Services/Keychain/KeychainControllerMock.swift +++ b/ElementX/Sources/Services/Keychain/KeychainControllerMock.swift @@ -8,6 +8,18 @@ import MatrixRustSDK +extension KeychainControllerMock { + struct Configuration { + var restorationTokens: [KeychainCredentials] = [] + } + + convenience init(_ configuration: Configuration) { + self.init() + + restorationTokensReturnValue = configuration.restorationTokens + } +} + /// Adds the missing methods for conformance to the protocol. extension KeychainControllerMock { func retrieveSessionFromKeychain(userId: String) throws -> Session { diff --git a/UnitTests/Sources/SessionDirectoriesTests.swift b/UnitTests/Sources/SessionDirectoriesTests.swift index afa3f9652..be81bfd4e 100644 --- a/UnitTests/Sources/SessionDirectoriesTests.swift +++ b/UnitTests/Sources/SessionDirectoriesTests.swift @@ -95,29 +95,3 @@ struct SessionDirectoriesTests { sessionDirectories.delete() } } - -private extension SessionDirectories { - var mockStateStorePath: String { - dataDirectory.appending(component: "matrix-sdk-state.sqlite3").path(percentEncoded: false) - } - - var mockCryptoStorePath: String { - dataDirectory.appending(component: "matrix-sdk-crypto.sqlite3").path(percentEncoded: false) - } - - var mockEventCachePath: String { - cacheDirectory.appending(component: "matrix-sdk-event-cache.sqlite3").path(percentEncoded: false) - } - - func generateMockData() { - generateMockDatabase(atPath: mockStateStorePath) - generateMockDatabase(atPath: mockCryptoStorePath) - generateMockDatabase(atPath: mockEventCachePath) - } - - private func generateMockDatabase(atPath path: String) { - FileManager.default.createFile(atPath: path, contents: nil) - FileManager.default.createFile(atPath: path + "-shm", contents: nil) - FileManager.default.createFile(atPath: path + "-wal", contents: nil) - } -} diff --git a/UnitTests/Sources/TestUtilities/Extensions/SessionDirectories.swift b/UnitTests/Sources/TestUtilities/Extensions/SessionDirectories.swift new file mode 100644 index 000000000..82eaf3a2c --- /dev/null +++ b/UnitTests/Sources/TestUtilities/Extensions/SessionDirectories.swift @@ -0,0 +1,36 @@ +// +// Copyright 2025 Element Creations Ltd. +// Copyright 2024-2025 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. +// Please see LICENSE files in the repository root for full details. +// + +@testable import ElementX +import Foundation + +extension SessionDirectories { + var mockStateStorePath: String { + dataDirectory.appending(component: "matrix-sdk-state.sqlite3").path(percentEncoded: false) + } + + var mockCryptoStorePath: String { + dataDirectory.appending(component: "matrix-sdk-crypto.sqlite3").path(percentEncoded: false) + } + + var mockEventCachePath: String { + cacheDirectory.appending(component: "matrix-sdk-event-cache.sqlite3").path(percentEncoded: false) + } + + func generateMockData() { + generateMockDatabase(atPath: mockStateStorePath) + generateMockDatabase(atPath: mockCryptoStorePath) + generateMockDatabase(atPath: mockEventCachePath) + } + + private func generateMockDatabase(atPath path: String) { + FileManager.default.createFile(atPath: path, contents: nil) + FileManager.default.createFile(atPath: path + "-shm", contents: nil) + FileManager.default.createFile(atPath: path + "-wal", contents: nil) + } +} diff --git a/UnitTests/Sources/UserSessionStoreTests.swift b/UnitTests/Sources/UserSessionStoreTests.swift new file mode 100644 index 000000000..3d9248c3f --- /dev/null +++ b/UnitTests/Sources/UserSessionStoreTests.swift @@ -0,0 +1,192 @@ +// +// Copyright 2026 Element Creations Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. +// Please see LICENSE files in the repository root for full details. +// + +@testable import ElementX +import Foundation +import MatrixRustSDK +import MatrixRustSDKMocks +import Testing + +@MainActor +struct UserSessionStoreTests { + let keychainController = KeychainControllerMock(.init()) + let clientFactory = ClientFactoryMock(.init()) + let store: UserSessionStore + + init() { + store = UserSessionStore(keychainController: keychainController, + clientFactory: clientFactory, + appSettings: .volatile(), + analyticsService: AnalyticsServiceMock(.init()), + appHooks: AppHooks(), + networkMonitor: NetworkMonitorMock(.init())) + } + + // MARK: - Accessors + + @Test + func hasSessions() { + #expect(!store.hasSessions) + + keychainController.restorationTokensReturnValue = [makeCredentials(sessionDirectories: .init())] + #expect(store.hasSessions) + } + + @Test + func userIDs() { + keychainController.restorationTokensReturnValue = [makeCredentials(sessionDirectories: .init(), userID: "@alice:matrix.org"), + makeCredentials(sessionDirectories: .init(), userID: "@bob:matrix.org")] + #expect(store.userIDs == ["@alice:matrix.org", "@bob:matrix.org"]) + } + + // MARK: - Restoration + + @Test + func restoreWithoutCredentials() async { + guard case .failure(.missingCredentials) = await store.restoreUserSession() else { + Issue.record("Restoration should fail when there are no credentials.") + return + } + } + + @Test + func restoreWithMissingUserData() async { + // Given credentials whose session data is no longer on disk. + let credentials = makeCredentials(sessionDirectories: .init()) + keychainController.restorationTokensReturnValue = [credentials] + + // When restoring the session. + guard case .failure(.failedRestoringLogin) = await store.restoreUserSession() else { + Issue.record("Restoration should fail when the non-transient user data is missing.") + return + } + + // Then the credentials should have been discarded. + #expect(keychainController.removeRestorationTokenForUsernameReceivedInvocations == [credentials.userID]) + } + + @Test + func restoreWhenClientCreationFails() async throws { + // Given valid session data but a client factory that fails. + let sessionDirectories = try makeValidSessionDirectories() + defer { sessionDirectories.delete() } + let credentials = makeCredentials(sessionDirectories: sessionDirectories) + keychainController.restorationTokensReturnValue = [credentials] + clientFactory.makeAppClientCredentialsClientSessionDelegateAppSettingsAppHooksThrowableError = TestError.generic + + // When restoring the session. + guard case .failure(.failedRestoringLogin) = await store.restoreUserSession() else { + Issue.record("Restoration should fail when the client can't be created.") + return + } + + // Then the credentials should have been discarded. + #expect(keychainController.removeRestorationTokenForUsernameReceivedInvocations == [credentials.userID]) + } + + @Test + func restoreSucceeds() async throws { + // Given valid session data and a client factory that returns a client. + let sessionDirectories = try makeValidSessionDirectories() + defer { sessionDirectories.delete() } + keychainController.restorationTokensReturnValue = [makeCredentials(sessionDirectories: sessionDirectories)] + + // When restoring the session. + guard case .success(let userSession) = await store.restoreUserSession() else { + Issue.record("Restoration should succeed.") + return + } + + // Then a user session should be built for the restored client. + #expect(userSession.clientProxy.userID == "@alice:matrix.org") + } + + // MARK: - Session creation + + @Test + func userSessionForClientSucceeds() async { + // Given a freshly authenticated client from the SDK. + let client = ClientSDKMock(.init(userID: "@alice:matrix.org")) + let sessionDirectories = SessionDirectories() + + // When creating a user session for it. + guard case .success(let userSession) = await store.userSession(for: client, sessionDirectories: sessionDirectories, passphrase: "passphrase") else { + Issue.record("Creating the session should succeed.") + return + } + + // Then the session should be built and its restoration token persisted. + #expect(userSession.clientProxy.userID == "@alice:matrix.org") + #expect(keychainController.setRestorationTokenForUsernameReceivedArguments?.forUsername == "@alice:matrix.org") + #expect(keychainController.setRestorationTokenForUsernameReceivedArguments?.restorationToken.passphrase == "passphrase") + } + + @Test + func userSessionForClientFails() async { + // Given a freshly authenticated client that fails to provide its session. + let client = ClientSDKMock(.init(userID: "@alice:matrix.org")) + client.sessionThrowableError = TestError.generic + + // When creating a user session for it. + guard case .failure(.failedSettingUpSession) = await store.userSession(for: client, sessionDirectories: .init(), passphrase: "passphrase") else { + Issue.record("Creating the session should fail.") + return + } + + // Then nothing should have been persisted. + #expect(!keychainController.setRestorationTokenForUsernameCalled) + } + + // MARK: - Logout/Reset + + @Test + func logout() { + // Given a stored session. + let userID = "@alice:matrix.org" + keychainController.restorationTokensReturnValue = [makeCredentials(sessionDirectories: .init(), userID: userID)] + let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: userID)))) + + // When logging out. + store.logout(userSession: userSession) + + // Then the stored credentials should be removed. + #expect(keychainController.removeRestorationTokenForUsernameReceivedInvocations == [userID]) + } + + @Test + func reset() { + store.reset() + #expect(keychainController.removeAllRestorationTokensCalled) + } + + // MARK: - Helpers + + private enum TestError: Error { case generic } + + private func makeCredentials(sessionDirectories: SessionDirectories, userID: String = "@alice:matrix.org") -> KeychainCredentials { + let session = Session(accessToken: "accessToken", + refreshToken: nil, + userId: userID, + deviceId: "deviceID", + homeserverUrl: "https://matrix.org", + oauthData: nil, + slidingSyncVersion: .native) + let restorationToken = RestorationToken(session: session, + sessionDirectories: sessionDirectories, + passphrase: "passphrase", + pusherNotificationClientIdentifier: nil) + return KeychainCredentials(userID: userID, restorationToken: restorationToken) + } + + private func makeValidSessionDirectories() throws -> SessionDirectories { + let sessionDirectories = SessionDirectories() + try FileManager.default.createDirectory(at: sessionDirectories.dataDirectory, withIntermediateDirectories: true) + try FileManager.default.createDirectory(at: sessionDirectories.cacheDirectory, withIntermediateDirectories: true) + sessionDirectories.generateMockData() + return sessionDirectories + } +}