-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(backup): enhance error handling and test coverage
Refactored the backup import process to improve error handling by introducing more granular failure types and modularized the logic for decrypting and unzipping archives. Added comprehensive test coverage, including tests for edge cases in decryption, parsing, and unzipping.
- Loading branch information
1 parent
429f53d
commit b761599
Showing
6 changed files
with
268 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
48 changes: 48 additions & 0 deletions
48
backup/src/commonTest/kotlin/com/wire/backup/envelope/header/FakeHeaderSerializer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
/* | ||
* Wire | ||
* Copyright (C) 2025 Wire Swiss GmbH | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see http://www.gnu.org/licenses/. | ||
*/ | ||
package com.wire.backup.envelope.header | ||
|
||
import com.wire.backup.envelope.BackupHeader | ||
import com.wire.backup.envelope.BackupHeaderSerializer | ||
import com.wire.backup.envelope.HashData | ||
import com.wire.backup.envelope.HeaderParseResult | ||
import okio.Source | ||
|
||
internal class FakeHeaderSerializer( | ||
private val bytes: ByteArray = byteArrayOf(), | ||
private val parseResult: HeaderParseResult = HeaderParseResult.Success(fakeBackupHeader()) | ||
) : BackupHeaderSerializer { | ||
override fun headerToBytes(header: BackupHeader): ByteArray { | ||
return bytes | ||
} | ||
|
||
override fun parseHeader(source: Source): HeaderParseResult { | ||
return parseResult | ||
} | ||
} | ||
|
||
internal fun fakeBackupHeader() = BackupHeader( | ||
version = 1, | ||
isEncrypted = true, | ||
hashData = HashData( | ||
hashedUserId = UByteArray(HashData.HASHED_USER_ID_SIZE_IN_BYTES), | ||
salt = UByteArray(HashData.SALT_SIZE_IN_BYTES), | ||
operationsLimit = 4UL, | ||
hashingMemoryLimit = HashData.MINIMUM_MEMORY_LIMIT | ||
) | ||
) |
124 changes: 124 additions & 0 deletions
124
backup/src/commonTest/kotlin/com/wire/backup/ingest/MPBackupImporterTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
/* | ||
* Wire | ||
* Copyright (C) 2025 Wire Swiss GmbH | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see http://www.gnu.org/licenses/. | ||
*/ | ||
package com.wire.backup.ingest | ||
|
||
import com.wire.backup.encryption.DecryptionResult | ||
import com.wire.backup.encryption.EncryptedStream | ||
import com.wire.backup.encryption.XChaChaPoly1305AuthenticationData | ||
import com.wire.backup.envelope.BackupHeaderSerializer | ||
import com.wire.backup.envelope.HeaderParseResult | ||
import com.wire.backup.envelope.header.FakeHeaderSerializer | ||
import com.wire.backup.filesystem.EntryStorage | ||
import com.wire.backup.filesystem.InMemoryEntryStorage | ||
import kotlinx.coroutines.test.runTest | ||
import okio.Buffer | ||
import okio.Sink | ||
import okio.Source | ||
import kotlin.test.Test | ||
import kotlin.test.assertEquals | ||
import kotlin.test.assertIs | ||
|
||
class MPBackupImporterTest { | ||
|
||
private fun createSubject( | ||
unzipEntries: () -> EntryStorage = { InMemoryEntryStorage() }, | ||
encryptedStream: EncryptedStream<XChaChaPoly1305AuthenticationData> = EncryptedStream.XChaCha20Poly1305, | ||
headerSerializer: BackupHeaderSerializer = BackupHeaderSerializer.Default, | ||
): CommonMPBackupImporter = object : CommonMPBackupImporter(encryptedStream, headerSerializer) { | ||
override fun getUnencryptedArchiveSink(): Sink = Buffer() | ||
|
||
override suspend fun unzipAllEntries() = unzipEntries() | ||
} | ||
|
||
@Test | ||
fun givenFailureToParseHeader_whenImporting_thenFailureIsReturned() = runTest { | ||
val subject = createSubject( | ||
headerSerializer = FakeHeaderSerializer(parseResult = HeaderParseResult.Failure.UnknownFormat) | ||
) | ||
val result = subject.importBackup(Buffer(), null) | ||
|
||
assertEquals(BackupImportResult.Failure.ParsingFailure, result) | ||
} | ||
|
||
@Test | ||
fun givenNoPasswordAndBackupIsEncrypted_whenImporting_thenMissingPasswordIsReturned() = runTest { | ||
val subject = createSubject( | ||
headerSerializer = FakeHeaderSerializer() | ||
) | ||
val result = subject.importBackup(Buffer(), null) | ||
|
||
assertEquals(BackupImportResult.Failure.MissingOrWrongPassphrase, result) | ||
} | ||
|
||
@Test | ||
fun givenDecryptionFailsForUnknownReason_whenImporting_thenFailureIsReturned() = runTest { | ||
val decryptionResult = DecryptionResult.Failure.Unknown("Oopsie") | ||
val subject = createSubject( | ||
headerSerializer = FakeHeaderSerializer(), | ||
encryptedStream = object : EncryptedStream<XChaChaPoly1305AuthenticationData> by EncryptedStream.XChaCha20Poly1305 { | ||
override suspend fun decrypt( | ||
source: Source, | ||
outputSink: Sink, | ||
authenticationData: XChaChaPoly1305AuthenticationData | ||
): DecryptionResult = decryptionResult | ||
} | ||
) | ||
val result = subject.importBackup(Buffer(), "pass") | ||
|
||
assertIs<BackupImportResult.Failure.UnknownError>(result) | ||
assertEquals(decryptionResult.message, result.message) | ||
} | ||
|
||
@Test | ||
fun givenDecryptionFailsDueToWrongPassword_whenImporting_thenFailureIsReturned() = runTest { | ||
val decryptionResult = DecryptionResult.Failure.AuthenticationFailure | ||
val subject = createSubject( | ||
headerSerializer = FakeHeaderSerializer(), | ||
encryptedStream = object : EncryptedStream<XChaChaPoly1305AuthenticationData> by EncryptedStream.XChaCha20Poly1305 { | ||
override suspend fun decrypt( | ||
source: Source, | ||
outputSink: Sink, | ||
authenticationData: XChaChaPoly1305AuthenticationData | ||
): DecryptionResult = decryptionResult | ||
} | ||
) | ||
val result = subject.importBackup(Buffer(), "pass") | ||
|
||
assertIs<BackupImportResult.Failure.MissingOrWrongPassphrase>(result) | ||
} | ||
|
||
@Test | ||
fun givenUnzippingThrows_whenImporting_thenFailureIsReturned() = runTest { | ||
val throwable = IllegalStateException("something went wrong") | ||
val subject = createSubject( | ||
unzipEntries = { throw throwable }, | ||
encryptedStream = object : EncryptedStream<XChaChaPoly1305AuthenticationData> by EncryptedStream.XChaCha20Poly1305 { | ||
override suspend fun decrypt( | ||
source: Source, | ||
outputSink: Sink, | ||
authenticationData: XChaChaPoly1305AuthenticationData | ||
): DecryptionResult = DecryptionResult.Success | ||
}, | ||
headerSerializer = FakeHeaderSerializer() | ||
) | ||
val result = subject.importBackup(Buffer(), "pass") | ||
|
||
assertIs<BackupImportResult.Failure.UnzippingError>(result) | ||
assertEquals(throwable.message, result.message) | ||
} | ||
} |