Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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)")
}
}

Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion Sources/Utils/GenericError.swift
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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)
}
Expand Down
Loading