diff --git a/Sources/DocUploadBundle/DocUploadBundle.swift b/Sources/DocUploadBundle/DocUploadBundle.swift index 35bd459..4049468 100644 --- a/Sources/DocUploadBundle/DocUploadBundle.swift +++ b/Sources/DocUploadBundle/DocUploadBundle.swift @@ -15,7 +15,6 @@ import Foundation import Dependencies -import Zip public struct DocUploadBundle { @@ -107,26 +106,17 @@ public struct DocUploadBundle { } public func zip(to workDir: String) throws -> String { - let archiveURL = URL(fileURLWithPath: "\(workDir)/\(archiveName)") - let metadataURL = URL(fileURLWithPath: "\(workDir)/metadata.json") - try JSONEncoder().encode(metadata).write(to: metadataURL) - - try Zip.zipFiles( - paths: [metadataURL, URL(fileURLWithPath: sourcePath)], - zipFilePath: archiveURL, - password: nil, - progress: nil - ) - - return archiveURL.path + let archiveURL = URL(fileURLWithPath: "\(workDir)/\(archiveName)") + let metadataURL = URL(fileURLWithPath: "\(workDir)/metadata.json") + try JSONEncoder().encode(metadata).write(to: metadataURL) + + try Zipping.zip(paths: [metadataURL, URL(fileURLWithPath: sourcePath)], to: archiveURL) + + return archiveURL.path } public static func unzip(bundle: String, outputPath: String, fileOutputHandler: ((_ unzippedFile: URL) -> Void)? = nil) throws -> Metadata { - try Zip.unzipFile(URL(fileURLWithPath: bundle), - destination: URL(fileURLWithPath: outputPath), - overwrite: true, - password: nil, - fileOutputHandler: fileOutputHandler) + try Zipping.unzip(from: bundle, to: outputPath, fileOutputHandler: fileOutputHandler) let metadataURL = URL(fileURLWithPath: "\(outputPath)/metadata.json") let data = try Data(contentsOf: metadataURL) return try JSONDecoder().decode(Metadata.self, from: data) diff --git a/Sources/DocUploadBundle/Zipping.swift b/Sources/DocUploadBundle/Zipping.swift new file mode 100644 index 0000000..441f5de --- /dev/null +++ b/Sources/DocUploadBundle/Zipping.swift @@ -0,0 +1,35 @@ +// Copyright Dave Verwer, Sven A. Schmidt, and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import Foundation + +import Zip + + +enum Zipping { + static func zip(paths inputPaths: [URL], to outputPath: URL) throws { + try Zip.zipFiles(paths: inputPaths, zipFilePath: outputPath, password: nil, progress: nil) + } + + static func unzip(from inputPath: URL, to outputPath: URL, fileOutputHandler: ((_ unzippedFile: URL) -> Void)? = nil) throws { + try Zip.unzipFile(inputPath, destination: outputPath, overwrite: true, password: nil, fileOutputHandler: fileOutputHandler) + } +} + + +extension Zipping { + static func unzip(from inputPath: String, to outputPath: String, fileOutputHandler: ((_ unzippedFile: URL) -> Void)? = nil) throws { + try unzip(from: URL(fileURLWithPath: inputPath), to: URL(fileURLWithPath: outputPath), fileOutputHandler: fileOutputHandler) + } +} diff --git a/Tests/DocUploadBundleTests/Fixtures/out.zip b/Tests/DocUploadBundleTests/Fixtures/out.zip new file mode 100644 index 0000000..60a896e Binary files /dev/null and b/Tests/DocUploadBundleTests/Fixtures/out.zip differ diff --git a/Tests/DocUploadBundleTests/Utils.swift b/Tests/DocUploadBundleTests/Utils.swift new file mode 100644 index 0000000..c2a5632 --- /dev/null +++ b/Tests/DocUploadBundleTests/Utils.swift @@ -0,0 +1,70 @@ +// Copyright Dave Verwer, Sven A. Schmidt, and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +import Foundation + + +// Helpers + +func fixtureData(for fixture: String) throws -> Data { + try Data(contentsOf: fixtureUrl(for: fixture)) +} + + +func fixtureUrl(for fixture: String) -> URL { + fixturesDirectory().appendingPathComponent(fixture) +} + + +func fixturesDirectory(path: String = #file) -> URL { + let url = URL(fileURLWithPath: path) + let testsDir = url.deletingLastPathComponent() + return testsDir.appendingPathComponent("Fixtures") +} + + +// TempDir + +enum TempDirError: LocalizedError { + case invalidPath(String) +} + + +class TempDir { + let path: String + + init() throws { + let tempDir = FileManager.default.temporaryDirectory + .appendingPathComponent(UUID().uuidString) + path = tempDir.path + try FileManager.default.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: nil) + precondition(FileManager.default.fileExists(atPath: path), "failed to create temp dir") + } + + deinit { + do { + try FileManager.default.removeItem(atPath: path) + } catch { + print("⚠️ failed to delete temp directory: \(error.localizedDescription)") + } + } + +} + + +func withTempDir(body: (String) async throws -> T) async throws -> T { + let tmp = try TempDir() + return try await body(tmp.path) +} diff --git a/Tests/DocUploadBundleTests/ZipTests.swift b/Tests/DocUploadBundleTests/ZipTests.swift new file mode 100644 index 0000000..8719f44 --- /dev/null +++ b/Tests/DocUploadBundleTests/ZipTests.swift @@ -0,0 +1,53 @@ +// Copyright Dave Verwer, Sven A. Schmidt, and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import XCTest + +@testable import DocUploadBundle + + +final class ZipTests: XCTestCase { + + func test_zip() async throws { + // Test basic zip behaviour we expect from the library we use + try await withTempDir { tempDir in + let tempURL = URL(filePath: tempDir) + let fileA = tempURL.appending(path: "a.txt") + let fileB = tempURL.appending(path: "b.txt") + try "a".write(to: fileA, atomically: true, encoding: .utf8) + try "b".write(to: fileB, atomically: true, encoding: .utf8) + let zipFile = tempURL.appending(path: "out.zip") + try Zipping.zip(paths: [fileA, fileB], to: zipFile) + XCTAssert(FileManager.default.fileExists(atPath: zipFile.path())) + } + } + + func test_unzip() async throws { + // Test basic unzip behaviour we expect from the library we use + try await withTempDir { tempDir in + let tempURL = URL(filePath: tempDir) + let zipFile = fixtureUrl(for: "out.zip") + let outDir = tempURL.appending(path: "out") + try Zipping.unzip(from: zipFile, to: outDir) + XCTAssert(FileManager.default.fileExists(atPath: outDir.path())) + let fileA = outDir.appending(path: "a.txt") + let fileB = outDir.appending(path: "b.txt") + XCTAssert(FileManager.default.fileExists(atPath: fileA.path())) + XCTAssert(FileManager.default.fileExists(atPath: fileB.path())) + XCTAssertEqual(try String(contentsOf: fileA), "a") + XCTAssertEqual(try String(contentsOf: fileB), "b") + } + } + +}