@@ -1037,13 +1037,80 @@ class VaultRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_b
1037
1037
let updatedCipher = cipher. update ( collectionIds: [ " 6 " , " 7 " ] )
1038
1038
1039
1039
XCTAssertEqual ( cipherService. shareCipherWithServerCiphers, [ Cipher ( cipherView: updatedCipher) ] )
1040
- XCTAssertEqual ( clientCiphers. encryptedCiphers, [ updatedCipher] )
1040
+ XCTAssertEqual ( clientCiphers. encryptedCiphers. last , updatedCipher)
1041
1041
XCTAssertEqual ( clientCiphers. moveToOrganizationCipher, cipher)
1042
1042
XCTAssertEqual ( clientCiphers. moveToOrganizationOrganizationId, " 5 " )
1043
1043
1044
1044
XCTAssertEqual ( cipherService. shareCipherWithServerCiphers. last, Cipher ( cipherView: updatedCipher) )
1045
1045
}
1046
1046
1047
+ /// `shareCipher()` migrates any attachments without an attachment key.
1048
+ func test_shareCipher_attachmentMigration( ) async throws {
1049
+ let account = Account . fixtureAccountLogin ( )
1050
+ stateService. activeAccount = account
1051
+
1052
+ // The original cipher.
1053
+ let cipherViewOriginal = CipherView . fixture (
1054
+ attachments: [
1055
+ . fixture( fileName: " file.txt " , id: " 1 " , key: nil ) ,
1056
+ . fixture( fileName: " existing-attachment-key.txt " , id: " 2 " , key: " abc " ) ,
1057
+ ] ,
1058
+ id: " 1 "
1059
+ )
1060
+
1061
+ // The cipher after saving the new attachment, encrypted with an attachment key.
1062
+ let cipherAfterAttachmentSave = Cipher . fixture (
1063
+ attachments: [
1064
+ . fixture( id: " 1 " , fileName: " file.txt " , key: nil ) ,
1065
+ . fixture( id: " 2 " , fileName: " existing-attachment-key.txt " , key: " abc " ) ,
1066
+ . fixture( id: " 3 " , fileName: " file.txt " , key: " def " ) ,
1067
+ ] ,
1068
+ id: " 1 "
1069
+ )
1070
+ cipherService. saveAttachmentWithServerResult = . success( cipherAfterAttachmentSave)
1071
+
1072
+ // The cipher after deleting the old attachment without an attachment key.
1073
+ let cipherAfterAttachmentDelete = Cipher . fixture (
1074
+ attachments: [
1075
+ . fixture( id: " 2 " , fileName: " existing-attachment-key.txt " , key: " abc " ) ,
1076
+ . fixture( id: " 3 " , fileName: " file.txt " , key: " def " ) ,
1077
+ ] ,
1078
+ id: " 1 "
1079
+ )
1080
+ cipherService. deleteAttachmentWithServerResult = . success( cipherAfterAttachmentDelete)
1081
+ clientService. mockVault. clientCiphers. moveToOrganizationResult = . success(
1082
+ CipherView ( cipher: cipherAfterAttachmentDelete)
1083
+ )
1084
+
1085
+ // Temporary download file (would normally be created by the network layer).
1086
+ let downloadUrl = FileManager . default. temporaryDirectory. appendingPathComponent ( " file.txt " )
1087
+ try Data ( " 📁 " . utf8) . write ( to: downloadUrl)
1088
+ cipherService. downloadAttachmentResult = . success( downloadUrl)
1089
+
1090
+ // Decrypted download file (would normally be created by the SDK when decrypting the attachment).
1091
+ let attachmentsUrl = try FileManager . default. attachmentsUrl ( for: account. profile. userId)
1092
+ try FileManager . default. createDirectory ( at: attachmentsUrl, withIntermediateDirectories: true )
1093
+ let decryptUrl = attachmentsUrl. appendingPathComponent ( " file.txt " )
1094
+ try Data ( " 🗂️ " . utf8) . write ( to: decryptUrl)
1095
+
1096
+ try await subject. shareCipher ( cipherViewOriginal, newOrganizationId: " 5 " , newCollectionIds: [ " 6 " , " 7 " ] )
1097
+
1098
+ let updatedCipherView = CipherView ( cipher: cipherAfterAttachmentDelete) . update ( collectionIds: [ " 6 " , " 7 " ] )
1099
+
1100
+ // Attachment migration: download attachment, save updated and delete old.
1101
+ XCTAssertEqual ( cipherService. downloadAttachmentId, " 1 " )
1102
+ XCTAssertEqual ( cipherService. saveAttachmentWithServerCipher, Cipher ( cipherView: cipherViewOriginal) )
1103
+ XCTAssertEqual ( cipherService. deleteAttachmentWithServerAttachmentId, " 1 " )
1104
+ XCTAssertThrowsError ( try Data ( contentsOf: downloadUrl) )
1105
+ XCTAssertThrowsError ( try Data ( contentsOf: decryptUrl) )
1106
+
1107
+ // Share cipher with updated attachments.
1108
+ XCTAssertEqual ( cipherService. shareCipherWithServerCiphers, [ Cipher ( cipherView: updatedCipherView) ] )
1109
+ XCTAssertEqual ( clientCiphers. encryptedCiphers. last, updatedCipherView)
1110
+ XCTAssertEqual ( clientCiphers. moveToOrganizationCipher, CipherView ( cipher: cipherAfterAttachmentDelete) )
1111
+ XCTAssertEqual ( clientCiphers. moveToOrganizationOrganizationId, " 5 " )
1112
+ }
1113
+
1047
1114
/// `shouldShowUnassignedCiphersAlert` is true if the feature flag is on,
1048
1115
/// we should check for this user, the user has organizations, and the user has unassigned ciphers.
1049
1116
func test_shouldShowUnassignedCiphersAlert( ) async {
0 commit comments