diff --git a/Sources/BinaryDependencyManager/DepeneciesResolverRunner.swift b/Sources/BinaryDependencyManager/DepeneciesResolverRunner.swift index 6bb8836..26cf9e7 100644 --- a/Sources/BinaryDependencyManager/DepeneciesResolverRunner.swift +++ b/Sources/BinaryDependencyManager/DepeneciesResolverRunner.swift @@ -91,7 +91,7 @@ public struct DependenciesResolverRunner { let checksum = try runThrowable("Calculating checksum") { try calculateChecksum(fileURL: downloadFileURL) } guard checksum == asset.checksum else { - throw NSError(domain: "Checksum is incorrect. \(checksum) != \(asset.checksum)", code: 0) + throw GenericError("Checksum is incorrect. \(checksum) != \(asset.checksum)") } } @@ -184,7 +184,7 @@ public struct DependenciesResolverRunner { downloadsDirectoryURL .appending(components: dependency.repo, dependency.tag, asset.outputDirectory ?? "", directoryHint: .isDirectory) } - + /// Location of the file where the downloaded dependency will be placed func downloadURL(for dependency: Dependency, asset: Dependency.Asset) -> URL { downloadDirectoryURL(for: dependency, asset: asset) diff --git a/Sources/Utils/GenericError.swift b/Sources/Utils/GenericError.swift index abf5976..38e601d 100644 --- a/Sources/Utils/GenericError.swift +++ b/Sources/Utils/GenericError.swift @@ -1,7 +1,7 @@ /// An error type that is presented to the user as an error with parsing their /// command-line input. -public struct GenericError: Error, CustomStringConvertible { +public struct GenericError: Error, CustomStringConvertible, Equatable { /// The error message represented by this instance, this string is presented to /// the user when a `ValidationError` is thrown from either; `run()`, /// `validate()` or a transform closure. diff --git a/Tests/BinaryDependencyManagerTests/DependenciesResolverRunner/DependenciesResolverRunnerDownloadTests.swift b/Tests/BinaryDependencyManagerTests/DependenciesResolverRunner/DependenciesResolverRunnerDownloadTests.swift index 62da36e..e23c0a8 100644 --- a/Tests/BinaryDependencyManagerTests/DependenciesResolverRunner/DependenciesResolverRunnerDownloadTests.swift +++ b/Tests/BinaryDependencyManagerTests/DependenciesResolverRunner/DependenciesResolverRunnerDownloadTests.swift @@ -4,33 +4,34 @@ import Foundation import Utils @Suite("DependenciesResolverRunner Download Method Tests") -struct DependenciesResolverRunnerDownloadTests { - let sampleDependency: Dependency = Dependency( +class DependenciesResolverRunnerDownloadTests { + let sampleAsset = Dependency.Asset( + checksum: "abc123", + pattern: "asset.zip", + contents: nil, + outputDirectory: nil + ) + + lazy var sampleDependency: Dependency = Dependency( repo: "org/repo", tag: "1.0.0", assets: [ - Dependency.Asset( - checksum: "abc123", - pattern: "asset.zip", - contents: nil, - outputDirectory: nil - ) + sampleAsset, ] ) - + + lazy var fileManager = FileManagerProtocolMock(tempDir: tempDir) + let downloaderMock = BinaryDependenciesDownloaderMock() + let checksumCalculatorMock = ChecksumCalculatorProtocolMock() + let tempDir: URL = { FileManager.default.temporaryDirectory .appending(components: "binary-dependency-manager-tests", UUID().uuidString, directoryHint: .isDirectory) }() - func makeRunner( - fileManager: FileManagerProtocolMock = FileManagerProtocolMock(), - dependencies: [Dependency]? = nil, - downloaderMock: BinaryDependenciesDownloaderMock = BinaryDependenciesDownloaderMock(), - checksumCalculatorMock: ChecksumCalculatorProtocolMock = ChecksumCalculatorProtocolMock() - ) -> DependenciesResolverRunner { + func makeRunner() -> DependenciesResolverRunner { DependenciesResolverRunner.mock( - dependencies: dependencies ?? [sampleDependency], + dependencies: [sampleDependency], outputDirectoryURL: tempDir.appending(path: "output", directoryHint: .isDirectory), cacheDirectoryURL: tempDir.appending(path: "cache", directoryHint: .isDirectory), fileManager: fileManager, @@ -43,15 +44,8 @@ struct DependenciesResolverRunnerDownloadTests { @Test("download creates directory and downloads file when not already downloaded") func download_success() async throws { // GIVEN - let fileManager = FileManagerProtocolMock(tempDir: tempDir) - let downloaderMock = BinaryDependenciesDownloaderMock() - let checksumCalculatorMock = ChecksumCalculatorProtocolMock() - let runner = makeRunner( - fileManager: fileManager, - downloaderMock: downloaderMock, - checksumCalculatorMock: checksumCalculatorMock - ) - let sampleAsset = sampleDependency.assets[0] + + let runner = makeRunner() let downloadURL = runner.downloadURL(for: sampleDependency, asset: sampleAsset) // Set up checksum calculation to return the expected checksum @@ -80,15 +74,7 @@ struct DependenciesResolverRunnerDownloadTests { @Test("download skips download when file already exists with correct checksum") func download_skipsWhenAlreadyDownloaded() async throws { // GIVEN - let fileManager = FileManagerProtocolMock(tempDir: tempDir) - let downloaderMock = BinaryDependenciesDownloaderMock() - let checksumCalculatorMock = ChecksumCalculatorProtocolMock() - let runner = makeRunner( - fileManager: fileManager, - downloaderMock: downloaderMock, - checksumCalculatorMock: checksumCalculatorMock - ) - let sampleAsset = sampleDependency.assets[0] + let runner = makeRunner() let downloadURL = runner.downloadURL(for: sampleDependency, asset: sampleAsset) // File already exists @@ -107,100 +93,43 @@ struct DependenciesResolverRunnerDownloadTests { @Test("download throws error when checksums don't match") func download_throwsErrorOnChecksumMismatch() async throws { // GIVEN - let fileManager = FileManagerProtocolMock(tempDir: tempDir) - let downloaderMock = BinaryDependenciesDownloaderMock() - let checksumCalculatorMock = ChecksumCalculatorProtocolMock() - let runner = makeRunner( - fileManager: fileManager, - downloaderMock: downloaderMock, - checksumCalculatorMock: checksumCalculatorMock - ) - let sampleAsset = sampleDependency.assets[0] + let runner = makeRunner() let downloadURL = runner.downloadURL(for: sampleDependency, asset: sampleAsset) // Set up checksum calculation to return wrong checksum - checksumCalculatorMock.checksums[downloadURL] = "wrong_checksum" - + let wrongChecksum = "wrong_checksum" + checksumCalculatorMock.checksums[downloadURL] = wrongChecksum + // WHEN & THEN - #expect(throws: Error.self) { + // runner must throw + let error = try #require(throws: GenericError.self) { try runner.download(sampleDependency, asset: sampleAsset, with: downloaderMock) } - - // Verify downloader was called - #expect(downloaderMock.downloadReleaseAssetCalls.count == 1) - } - - @Test("download redownloads when existing file has wrong checksum") - func download_redownloadsOnChecksumMismatch() async throws { - // GIVEN - let fileManager = FileManagerProtocolMock(tempDir: tempDir) - let downloaderMock = BinaryDependenciesDownloaderMock() - let checksumCalculatorMock = ChecksumCalculatorProtocolMock() - let runner = makeRunner( - fileManager: fileManager, - downloaderMock: downloaderMock, - checksumCalculatorMock: checksumCalculatorMock + #expect( + error.message.contains("Checksum is incorrect. \(wrongChecksum) != \(sampleAsset.checksum)"), + "Thrown error describes invalid checksum" ) - let sampleAsset = sampleDependency.assets[0] - let downloadURL = runner.downloadURL(for: sampleDependency, asset: sampleAsset) - - // File already exists - fileManager.existingFiles.insert(downloadURL.path(percentEncoded: false)) - // First checksum call returns wrong checksum, second returns correct - checksumCalculatorMock.checksums[downloadURL] = "wrong_checksum" - - // WHEN & THEN - #expect(throws: Error.self) { - try runner.download(sampleDependency, asset: sampleAsset, with: downloaderMock) - } - - // Verify file was removed due to checksum mismatch - #expect(fileManager.removedItems.contains(downloadURL)) - - // Verify downloader was called - #expect(downloaderMock.downloadReleaseAssetCalls.count == 1) + + // Verify check sum calc was called + #expect(checksumCalculatorMock.checksumCalls.count == 1) } - + @Test("download propagates downloader errors") func download_propagatesDownloaderErrors() async throws { // GIVEN - let fileManager = FileManagerProtocolMock(tempDir: tempDir) - let downloaderMock = BinaryDependenciesDownloaderMock(throwError: GenericError("Download failed")) - let checksumCalculatorMock = ChecksumCalculatorProtocolMock() - let runner = makeRunner( - fileManager: fileManager, - downloaderMock: downloaderMock, - checksumCalculatorMock: checksumCalculatorMock - ) - let sampleAsset = sampleDependency.assets[0] - - // WHEN & THEN - #expect(throws: Error.self) { - try runner.download(sampleDependency, asset: sampleAsset, with: downloaderMock) - } - - // Verify downloader was called - #expect(downloaderMock.downloadReleaseAssetCalls.count == 1) - } - - @Test("download propagates checksum calculation errors") - func download_propagatesChecksumErrors() async throws { - // GIVEN - let fileManager = FileManagerProtocolMock(tempDir: tempDir) - let downloaderMock = BinaryDependenciesDownloaderMock() - let checksumCalculatorMock = ChecksumCalculatorProtocolMock(errorToThrow: GenericError("Checksum calculation failed")) - let runner = makeRunner( - fileManager: fileManager, - downloaderMock: downloaderMock, - checksumCalculatorMock: checksumCalculatorMock - ) - let sampleAsset = sampleDependency.assets[0] - + // Downloader throws error + let downloadError = GenericError("Download failed") + downloaderMock.throwError = downloadError + + let runner = makeRunner() + // WHEN & THEN - #expect(throws: Error.self) { + // runner must throw + let error = try #require(throws: GenericError.self) { try runner.download(sampleDependency, asset: sampleAsset, with: downloaderMock) } - + #expect(error == downloadError, "thrown error by runner equals to the error thrown by downloader") + // Verify downloader was called #expect(downloaderMock.downloadReleaseAssetCalls.count == 1) }