From 33ae67fc58543704f3515564e9c4263ef8520eca Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Fri, 7 Feb 2025 21:50:50 +0100 Subject: [PATCH] Implemented the "createFile(at: with: )" function for the FileService service in the library target. --- .../Public/Protocols/FileServicing.swift | 3 ++ .../Sources/Public/Services/FileService.swift | 32 +++++++++++++----- .../Public/Services/FileServiceTests.swift | 33 ++++++++++++++++++- .../Helpers/Mocks/FileServiceMock.swift | 14 ++++++++ .../Helpers/Spies/FileServiceSpy.swift | 5 +++ 5 files changed, 78 insertions(+), 9 deletions(-) diff --git a/Library/Sources/Public/Protocols/FileServicing.swift b/Library/Sources/Public/Protocols/FileServicing.swift index 4de66e3..db07292 100644 --- a/Library/Sources/Public/Protocols/FileServicing.swift +++ b/Library/Sources/Public/Protocols/FileServicing.swift @@ -9,6 +9,7 @@ public protocol FileServicing { // MARK: Functions func copyFile(from source: URL, to destination: URL) async throws (FileServiceError) + func createFile(at location: URL, with data: Data) async throws (FileServiceError) func createFolder(at location: URL) async throws (FileServiceError) func deleteItem(at location: URL) async throws (FileServiceError) func isItemExists(at location: URL) async throws (FileServiceError) -> Bool @@ -18,6 +19,8 @@ public protocol FileServicing { // MARK: - Errors public enum FileServiceError: Error, Equatable { + case fileDataIsEmpty + case fileNotCreated case folderNotCreated case itemAlreadyExists case itemEmptyData diff --git a/Library/Sources/Public/Services/FileService.swift b/Library/Sources/Public/Services/FileService.swift index d9f56c5..47a3c0e 100644 --- a/Library/Sources/Public/Services/FileService.swift +++ b/Library/Sources/Public/Services/FileService.swift @@ -30,7 +30,7 @@ extension FileService: FileServicing { public func copyFile(from source: URL, to destination: URL) async throws (FileServiceError) { guard try await !isItemExists(at: destination) else { - throw FileServiceError.itemAlreadyExists + throw .itemAlreadyExists } var itemData: Data? @@ -38,43 +38,59 @@ extension FileService: FileServicing { do { itemData = try Data(contentsOf: source) } catch { - throw FileServiceError.itemEmptyData + throw .itemEmptyData } do { try itemData?.write(to: destination, options: .atomic) } catch { - throw FileServiceError.itemNotCopied + throw .itemNotCopied + } + } + + public func createFile(at location: URL, with data: Data) async throws (FileServiceError) { + guard try await !isItemExists(at: location) else { + throw .itemAlreadyExists + } + + guard !data.isEmpty else { + throw .fileDataIsEmpty + } + + do { + try data.write(to: location, options: .atomic) + } catch { + throw .fileNotCreated } } public func createFolder(at location: URL) async throws (FileServiceError) { guard try await !isItemExists(at: location) else { - throw FileServiceError.itemAlreadyExists + throw .itemAlreadyExists } do { try fileManager.createDirectory(at: location, withIntermediateDirectories: true) } catch { - throw FileServiceError.folderNotCreated + throw .folderNotCreated } } public func deleteItem(at location: URL) async throws (FileServiceError) { guard try await isItemExists(at: location) else { - throw FileServiceError.itemNotExists + throw .itemNotExists } do { try fileManager.removeItem(at: location) } catch { - throw FileServiceError.itemNotDeleted + throw .itemNotDeleted } } public func isItemExists(at location: URL) async throws (FileServiceError) -> Bool { guard location.isFileURL else { - throw FileServiceError.itemNotFileURL + throw .itemNotFileURL } let filePath = location.pathString diff --git a/Test/Sources/Cases/Public/Services/FileServiceTests.swift b/Test/Sources/Cases/Public/Services/FileServiceTests.swift index bf8f909..2292f71 100644 --- a/Test/Sources/Cases/Public/Services/FileServiceTests.swift +++ b/Test/Sources/Cases/Public/Services/FileServiceTests.swift @@ -35,7 +35,7 @@ struct FileServiceTests { // WHEN try await service.copyFile(from: source, to: destination) - // THENn + // THEN #expect(spy.actions.count == 1) let action = try #require(spy.actions.last) @@ -57,6 +57,37 @@ struct FileServiceTests { #expect(spy.actions.isEmpty == true) } + @Test(arguments: zip([URL.someNewFile], + [Data("some data goes here...".utf8)])) + func createFile(with location: URL, and data: Data) async throws { + // GIVEN + let service = service(action: .createFile(location, data)) + + // WHEN + try await service.createFile(at: location, with: data) + + // THEN + #expect(spy.actions.count == 1) + + let action = try #require(spy.actions.last) + + #expect(action == .fileCreated(location, data)) + } + + @Test(arguments: [FileServiceError.itemAlreadyExists, .fileDataIsEmpty, .fileNotCreated]) + func createFile(throws error: FileServiceError) async throws { + // GIVEN + let service = service(action: .error(error)) + + // WHEN + // THEN + await #expect(throws: error) { + try await service.createFile(at: .someNewFile, with: .init()) + } + + #expect(spy.actions.isEmpty == true) + } + @Test(arguments: [URL.someNewFolder, .someNewFile]) func createFolder(with location: URL) async throws { // GIVEN diff --git a/Test/Sources/Helpers/Mocks/FileServiceMock.swift b/Test/Sources/Helpers/Mocks/FileServiceMock.swift index 99bd117..e0e70dd 100644 --- a/Test/Sources/Helpers/Mocks/FileServiceMock.swift +++ b/Test/Sources/Helpers/Mocks/FileServiceMock.swift @@ -64,6 +64,19 @@ extension FileServiceMock: FileServicing { } } + func createFile(at location: URL, with data: Data) async throws (FileServiceError) { + guard let nextAction else { return } + + switch nextAction { + case .error(let error): + throw error + case let .createFile(location, data): + try await spy?.createFile(at: location, with: data) + default: + break + } + } + func createFolder(at location: URL) async throws (FileServiceError) { guard let nextAction else { return } @@ -127,6 +140,7 @@ private extension FileServiceMock { extension FileServiceMock { enum Action { case copyFile(URL, URL) + case createFile(URL, Data) case createFolder(URL) case deleteItem(URL) case error(FileServiceError) diff --git a/Test/Sources/Helpers/Spies/FileServiceSpy.swift b/Test/Sources/Helpers/Spies/FileServiceSpy.swift index 971e0a9..3b9e90f 100644 --- a/Test/Sources/Helpers/Spies/FileServiceSpy.swift +++ b/Test/Sources/Helpers/Spies/FileServiceSpy.swift @@ -21,6 +21,10 @@ extension FileServiceSpy: FileServicing { actions.append(.fileCopied(source, destination)) } + func createFile(at location: URL, with data: Data) async throws (FileServiceError) { + actions.append(.fileCreated(location, data)) + } + func createFolder(at location: URL) async throws (FileServiceError) { actions.append(.folderCreated(location)) } @@ -42,6 +46,7 @@ extension FileServiceSpy: FileServicing { extension FileServiceSpy { enum Action: Equatable { + case fileCreated(_ location: URL, _ data: Data) case fileCopied(_ source: URL, _ destination: URL) case folderCreated(_ location: URL) case itemDeleted(_ location: URL)