From cb62b066e57dd75a051be77c2ca51e71894bad26 Mon Sep 17 00:00:00 2001 From: Jeong Dong Date: Mon, 16 Jun 2025 15:16:15 +0900 Subject: [PATCH 1/7] =?UTF-8?q?[#59]=20FirestoreFieldKey=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DTOFieldKeyMappingTests.swift | 48 ------------------- 1 file changed, 48 deletions(-) delete mode 100644 Mark-In-Tests/ValidationsTests/DTOFieldKeyMappingTests.swift diff --git a/Mark-In-Tests/ValidationsTests/DTOFieldKeyMappingTests.swift b/Mark-In-Tests/ValidationsTests/DTOFieldKeyMappingTests.swift deleted file mode 100644 index 0c7a8b1..0000000 --- a/Mark-In-Tests/ValidationsTests/DTOFieldKeyMappingTests.swift +++ /dev/null @@ -1,48 +0,0 @@ -// -// Mark_In_Tests.swift -// Mark-In-Tests -// -// Created by 이정동 on 6/4/25. -// - -import Testing -@testable import Mark_In - -struct DTOFieldKeyMappingTests { - - @Test - func test_FolderDTO의_CodingKey와_FirestoreFieldKey가_일치해야_한다() async throws { - // Given: 준비 - - // When: 실행 - - // Then: 검증 - #expect(FolderDTO.CodingKeys.id.rawValue == FirestoreFieldKey.Folder.id) - #expect(FolderDTO.CodingKeys.name.rawValue == FirestoreFieldKey.Folder.name) - #expect(FolderDTO.CodingKeys.createdAt.rawValue == FirestoreFieldKey.Folder.createdAt) - - // TearDown: 해제 - - } - - @Test - func test_WebLinkDTO의_CodingKey와_FirestoreFieldKey가_일치해야_한다() async throws { - // Given: 준비 - - // When: 실행 - - // Then: 검증 - #expect(WebLinkDTO.CodingKeys.id.rawValue == FirestoreFieldKey.Link.id) - #expect(WebLinkDTO.CodingKeys.url.rawValue == FirestoreFieldKey.Link.url) - #expect(WebLinkDTO.CodingKeys.title.rawValue == FirestoreFieldKey.Link.title) - #expect(WebLinkDTO.CodingKeys.thumbnailUrl.rawValue == FirestoreFieldKey.Link.thumbnailUrl) - #expect(WebLinkDTO.CodingKeys.faviconUrl.rawValue == FirestoreFieldKey.Link.faviconUrl) - #expect(WebLinkDTO.CodingKeys.isPinned.rawValue == FirestoreFieldKey.Link.isPinned) - #expect(WebLinkDTO.CodingKeys.createdAt.rawValue == FirestoreFieldKey.Link.createdAt) - #expect(WebLinkDTO.CodingKeys.lastAccessedAt.rawValue == FirestoreFieldKey.Link.lastAccessedAt) - #expect(WebLinkDTO.CodingKeys.folderID.rawValue == FirestoreFieldKey.Link.folderID) - - // TearDown: 해제 - - } -} From 853bdab3bbf81d47c4b7bc27d8f63b7857f355f4 Mon Sep 17 00:00:00 2001 From: Jeong Dong Date: Mon, 16 Jun 2025 15:22:39 +0900 Subject: [PATCH 2/7] =?UTF-8?q?[#59]=20AddFolderView=20=EB=82=B4=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=8D=BC=ED=8B=B0=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Mark-In/Sources/Feature/AddFolder/AddFolderView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mark-In/Sources/Feature/AddFolder/AddFolderView.swift b/Mark-In/Sources/Feature/AddFolder/AddFolderView.swift index 1cf78de..da28e77 100644 --- a/Mark-In/Sources/Feature/AddFolder/AddFolderView.swift +++ b/Mark-In/Sources/Feature/AddFolder/AddFolderView.swift @@ -29,7 +29,7 @@ struct AddFolderView: View { Text("폴더를 추가:") .frame(maxWidth: .infinity, alignment: .leading) - MarkTextField(text: $title, placeholder: "제목") + MarkTextField(text: $name, placeholder: "제목") .padding(.top, 14) .disabled(isSaving) From 5aab13f64be63d98badb1b4c1b1dd78b0f409483 Mon Sep 17 00:00:00 2001 From: Jeong Dong Date: Mon, 16 Jun 2025 20:29:33 +0900 Subject: [PATCH 3/7] =?UTF-8?q?[#59]=20FirestoreFieldKey=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Mark-In/Sources/Data/DTOs/FolderDTO.swift | 10 +++-- Mark-In/Sources/Data/DTOs/WebLinkDTO.swift | 26 ++++++------ Mark-In/Sources/Data/FirestoreFieldKey.swift | 28 ------------- .../Repositories/FolderRepositoryImpl.swift | 24 ++++------- .../Repositories/LinkRepositoryImpl.swift | 41 ++++++------------- 5 files changed, 41 insertions(+), 88 deletions(-) delete mode 100644 Mark-In/Sources/Data/FirestoreFieldKey.swift diff --git a/Mark-In/Sources/Data/DTOs/FolderDTO.swift b/Mark-In/Sources/Data/DTOs/FolderDTO.swift index 5aadbde..edd9af5 100644 --- a/Mark-In/Sources/Data/DTOs/FolderDTO.swift +++ b/Mark-In/Sources/Data/DTOs/FolderDTO.swift @@ -12,10 +12,12 @@ struct FolderDTO: Codable { var name: String var createdAt: Date - enum CodingKeys: String, CodingKey { - case id = "id" - case name = "name" - case createdAt = "createdAt" + var documentData: [String: Any] { + [ + "id": self.id, + "name": self.name, + "createdAt": self.createdAt + ] } func toEntity() -> Folder { diff --git a/Mark-In/Sources/Data/DTOs/WebLinkDTO.swift b/Mark-In/Sources/Data/DTOs/WebLinkDTO.swift index 832d300..b09b135 100644 --- a/Mark-In/Sources/Data/DTOs/WebLinkDTO.swift +++ b/Mark-In/Sources/Data/DTOs/WebLinkDTO.swift @@ -18,18 +18,20 @@ struct WebLinkDTO: Codable { var lastAccessedAt: Date? var folderID: String? - enum CodingKeys: String, CodingKey { - case id = "id" - case url = "url" - case title = "title" - case thumbnailUrl = "thumbnailUrl" - case faviconUrl = "faviconUrl" - case isPinned = "isPinned" - case createdAt = "createdAt" - case lastAccessedAt = "lastAccessedAt" - case folderID = "folderID" + var documentData: [String: Any] { + [ + "id": self.id, + "url": self.url, + "title": self.title ?? NSNull(), + "thumbnailUrl": self.thumbnailUrl ?? NSNull(), + "faviconUrl": self.faviconUrl ?? NSNull(), + "isPinned": self.isPinned, + "createdAt": self.createdAt, + "lastAccessedAt": self.lastAccessedAt ?? NSNull(), + "folderID": self.folderID ?? NSNull(), + ] } - + func toEntity() -> WebLink { WebLink( id: self.id, @@ -42,5 +44,5 @@ struct WebLinkDTO: Codable { lastAccessedAt: self.lastAccessedAt, folderID: self.folderID ) - } + } } diff --git a/Mark-In/Sources/Data/FirestoreFieldKey.swift b/Mark-In/Sources/Data/FirestoreFieldKey.swift deleted file mode 100644 index 4c11eb7..0000000 --- a/Mark-In/Sources/Data/FirestoreFieldKey.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// FirestoreFieldKey.swift -// Mark-In -// -// Created by 이정동 on 6/4/25. -// - -import Foundation - -enum FirestoreFieldKey { - enum Link { - static let id = "id" - static let url = "url" - static let title = "title" - static let thumbnailUrl = "thumbnailUrl" - static let faviconUrl = "faviconUrl" - static let isPinned = "isPinned" - static let createdAt = "createdAt" - static let lastAccessedAt = "lastAccessedAt" - static let folderID = "folderID" - } - - enum Folder { - static let id = "id" - static let name = "name" - static let createdAt = "createdAt" - } -} diff --git a/Mark-In/Sources/Data/Repositories/FolderRepositoryImpl.swift b/Mark-In/Sources/Data/Repositories/FolderRepositoryImpl.swift index 705b912..1da6da9 100644 --- a/Mark-In/Sources/Data/Repositories/FolderRepositoryImpl.swift +++ b/Mark-In/Sources/Data/Repositories/FolderRepositoryImpl.swift @@ -11,8 +11,6 @@ import FirebaseFirestore struct FolderRepositoryImpl: FolderRepository { - typealias FolderFieldKey = FirestoreFieldKey.Folder - private let db = Firestore.firestore() func create(userID: String, folder: WriteFolder) async throws -> Folder { @@ -20,24 +18,18 @@ struct FolderRepositoryImpl: FolderRepository { let path = FirebaseEndpoint.FirestoreDB.folders(userID: userID).path let folderDocRef = db.collection(path).document() - /// 2. 필드 값 생성 - let createdAt = Date() - - /// 3. Firestore에 추가 - try await folderDocRef.setData([ - FolderFieldKey.id: folderDocRef.documentID, - FolderFieldKey.name: folder.name, - FolderFieldKey.createdAt: createdAt - ]) - - /// 4. 생성된 데이터 반환 - let folderEntity = Folder( + /// 2. DTO 객체 생성 + let folderDTO = FolderDTO( id: folderDocRef.documentID, name: folder.name, - createdAt: .now + createdAt: Date() ) - return folderEntity + /// 3. Firestore에 추가 + try await folderDocRef.setData(folderDTO.documentData) + + /// 4. 생성된 데이터 반환 + return folderDTO.toEntity() } func fetchAll(userID: String) async throws -> [Folder] { diff --git a/Mark-In/Sources/Data/Repositories/LinkRepositoryImpl.swift b/Mark-In/Sources/Data/Repositories/LinkRepositoryImpl.swift index 8da3e35..b3c73cf 100644 --- a/Mark-In/Sources/Data/Repositories/LinkRepositoryImpl.swift +++ b/Mark-In/Sources/Data/Repositories/LinkRepositoryImpl.swift @@ -14,8 +14,6 @@ import LinkMetadataKitInterface struct LinkRepositoryImpl: LinkRepository { - typealias LinkFieldKey = FirestoreFieldKey.Link - private let db = Firestore.firestore() private let storage = Storage.storage().reference() @@ -40,37 +38,24 @@ struct LinkRepositoryImpl: LinkRepository { metadata: metadata ) - /// 4. 필드 값 설정 - let title = link.title ?? metadata.title - let createdAt = Date() - - /// 5. Firestore에 추가 - try await linkDocRef.setData([ - LinkFieldKey.id: linkDocRef.documentID, - LinkFieldKey.url: link.url, - LinkFieldKey.title: title ?? NSNull(), - LinkFieldKey.thumbnailUrl: imageUrls.thumbnail ?? NSNull(), - LinkFieldKey.faviconUrl: imageUrls.favicon ?? NSNull(), - LinkFieldKey.isPinned: false, - LinkFieldKey.createdAt: createdAt, - LinkFieldKey.lastAccessedAt: NSNull(), - LinkFieldKey.folderID: link.folderID ?? NSNull() - ]) - - /// 6. 생성된 데이터 반환 - let linkEntity = WebLink( + /// 4. DTO 객체 생성 + let linkDTO = WebLinkDTO( id: linkDocRef.documentID, url: link.url, - title: title, + title: link.title ?? metadata.title, thumbnailUrl: imageUrls.thumbnail, faviconUrl: imageUrls.favicon, isPinned: false, - createdAt: createdAt, + createdAt: Date(), lastAccessedAt: nil, folderID: link.folderID ) - return linkEntity + /// 5. Firestore에 추가 + try await linkDocRef.setData(linkDTO.documentData) + + /// 6. 생성된 데이터 반환 + return linkDTO.toEntity() } func fetchAll(userID: String) async throws -> [WebLink] { @@ -99,7 +84,7 @@ struct LinkRepositoryImpl: LinkRepository { /// 2. 문서 업데이트 try await linkDocRef.updateData([ - LinkFieldKey.folderID: folderID ?? NSNull() + "folderID": folderID as Any ]) } @@ -114,7 +99,7 @@ struct LinkRepositoryImpl: LinkRepository { /// 2. 조건에 해당하는 모든 문서 가져오기 let querySnapshot = try await linkColRef - .whereField(LinkFieldKey.folderID, isEqualTo: fromFolderID ?? NSNull()) + .whereField("folderID", isEqualTo: fromFolderID as Any) .getDocuments() /// 3. 병렬 작업으로 문서 업데이트 @@ -122,7 +107,7 @@ struct LinkRepositoryImpl: LinkRepository { querySnapshot.documents.forEach { document in group.addTask { try await document.reference.updateData([ - LinkFieldKey.folderID: toFolderID ?? NSNull() + "folderID": toFolderID as Any ]) } } @@ -146,7 +131,7 @@ struct LinkRepositoryImpl: LinkRepository { func deleteAllInFolder(userID: String, folderID: String?) async throws { let path = FirebaseEndpoint.FirestoreDB.links(userID: userID).path let querySnapshot = try await db.collection(path) - .whereField(LinkFieldKey.folderID, isEqualTo: folderID ?? NSNull()) + .whereField("folderID", isEqualTo: folderID as Any) .getDocuments() /// 3. 병렬 작업으로 데이터 삭제 From 73e7f90d9dc491443dae1cad3e85d2060693b2a2 Mon Sep 17 00:00:00 2001 From: Jeong Dong Date: Tue, 17 Jun 2025 13:39:14 +0900 Subject: [PATCH 4/7] =?UTF-8?q?[#59]=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=20=ED=8C=8C=EC=9D=BC=20=EC=9C=84=EC=B9=98=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UseCase}/DeleteFolderUseCaseTests.swift | 0 .../Repository}/FakeFolderRepository.swift | 0 .../Repository}/FakeLinkRepository.swift | 0 Mark-In.xcodeproj/project.pbxproj | 20 +++++++++++++++++++ 4 files changed, 20 insertions(+) rename Mark-In-Tests/{UseCaseTests => Test/UseCase}/DeleteFolderUseCaseTests.swift (100%) rename {Mark-In/Sources/Data/Tests => Mark-In-Tests/Testing/Repository}/FakeFolderRepository.swift (100%) rename {Mark-In/Sources/Data/Tests => Mark-In-Tests/Testing/Repository}/FakeLinkRepository.swift (100%) diff --git a/Mark-In-Tests/UseCaseTests/DeleteFolderUseCaseTests.swift b/Mark-In-Tests/Test/UseCase/DeleteFolderUseCaseTests.swift similarity index 100% rename from Mark-In-Tests/UseCaseTests/DeleteFolderUseCaseTests.swift rename to Mark-In-Tests/Test/UseCase/DeleteFolderUseCaseTests.swift diff --git a/Mark-In/Sources/Data/Tests/FakeFolderRepository.swift b/Mark-In-Tests/Testing/Repository/FakeFolderRepository.swift similarity index 100% rename from Mark-In/Sources/Data/Tests/FakeFolderRepository.swift rename to Mark-In-Tests/Testing/Repository/FakeFolderRepository.swift diff --git a/Mark-In/Sources/Data/Tests/FakeLinkRepository.swift b/Mark-In-Tests/Testing/Repository/FakeLinkRepository.swift similarity index 100% rename from Mark-In/Sources/Data/Tests/FakeLinkRepository.swift rename to Mark-In-Tests/Testing/Repository/FakeLinkRepository.swift diff --git a/Mark-In.xcodeproj/project.pbxproj b/Mark-In.xcodeproj/project.pbxproj index a0c4a01..31a47dd 100644 --- a/Mark-In.xcodeproj/project.pbxproj +++ b/Mark-In.xcodeproj/project.pbxproj @@ -108,6 +108,22 @@ /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 576EF9382E00393F00CA1D3F /* Exceptions for "Mark-In-Tests" folder in "Mark-In" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Testing/Repository/FakeFolderRepository.swift, + Testing/Repository/FakeLinkRepository.swift, + ); + target = 57BFD8C22DA4E19600648AD4 /* Mark-In */; + }; + 576EF9392E00393F00CA1D3F /* Exceptions for "Mark-In-Tests" folder in "Mark-In-Tests" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Testing/Repository/FakeFolderRepository.swift, + Testing/Repository/FakeLinkRepository.swift, + ); + target = 57664F672DF03A9000CA8D68 /* Mark-In-Tests */; + }; 57B7E0242DC3D44100D9225A /* Exceptions for "Mark-In" folder in "Mark-In" target */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( @@ -122,6 +138,10 @@ /* Begin PBXFileSystemSynchronizedRootGroup section */ 57664F692DF03A9000CA8D68 /* Mark-In-Tests */ = { isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + 576EF9382E00393F00CA1D3F /* Exceptions for "Mark-In-Tests" folder in "Mark-In" target */, + 576EF9392E00393F00CA1D3F /* Exceptions for "Mark-In-Tests" folder in "Mark-In-Tests" target */, + ); path = "Mark-In-Tests"; sourceTree = ""; }; From b3ebb862694e7da28dba7abc0bc3e0b510c746f4 Mon Sep 17 00:00:00 2001 From: Jeong Dong Date: Tue, 17 Jun 2025 14:00:20 +0900 Subject: [PATCH 5/7] =?UTF-8?q?[#59]=20MockLink,=20Folder=20Repository=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Repository/MockFolderRepository.swift | 29 +++++++++++++ .../Repository/MockLinkRepository.swift | 42 +++++++++++++++++++ Mark-In.xcodeproj/project.pbxproj | 2 + 3 files changed, 73 insertions(+) create mode 100644 Mark-In-Tests/Testing/Repository/MockFolderRepository.swift create mode 100644 Mark-In-Tests/Testing/Repository/MockLinkRepository.swift diff --git a/Mark-In-Tests/Testing/Repository/MockFolderRepository.swift b/Mark-In-Tests/Testing/Repository/MockFolderRepository.swift new file mode 100644 index 0000000..cf76723 --- /dev/null +++ b/Mark-In-Tests/Testing/Repository/MockFolderRepository.swift @@ -0,0 +1,29 @@ +// +// MockFolderRepository.swift +// Mark-In-Tests +// +// Created by 이정동 on 6/17/25. +// + +import Foundation + +final class MockFolderRepository: FolderRepository { + + var deleteCallCount: Int = 0 + + func create(userID: String, folder: WriteFolder) async throws -> Folder { + return Folder(name: "", createdAt: Date()) + } + + func fetchAll(userID: String) async throws -> [Folder] { + return [] + } + + func delete(userID: String, folderID: String) async throws { + deleteCallCount += 1 + } + + func deleteAll(userID: String) async throws { + + } +} diff --git a/Mark-In-Tests/Testing/Repository/MockLinkRepository.swift b/Mark-In-Tests/Testing/Repository/MockLinkRepository.swift new file mode 100644 index 0000000..c7cf129 --- /dev/null +++ b/Mark-In-Tests/Testing/Repository/MockLinkRepository.swift @@ -0,0 +1,42 @@ +// +// MockLinkRepository.swift +// Mark-In-Tests +// +// Created by 이정동 on 6/17/25. +// + +import Foundation + +final class MockLinkRepository: LinkRepository { + + var moveLinksInFolderCallCount: Int = 0 + var deleteAllInFolderCallCount: Int = 0 + + func create(userID: String, link: WriteLink) async throws -> WebLink { + return WebLink(id: "", url: "", isPinned: false, createdAt: Date()) + } + + func fetchAll(userID: String) async throws -> [WebLink] { + return [] + } + + func moveLinkInFolder(userID: String, target linkID: String, to folderID: String?) async throws { + + } + + func moveLinksInFolder(userID: String, fromFolderID: String?, toFolderID: String?) async throws { + moveLinksInFolderCallCount += 1 + } + + func delete(userID: String, linkID: String) async throws { + + } + + func deleteAllInFolder(userID: String, folderID: String?) async throws { + deleteAllInFolderCallCount += 1 + } + + func deleteAll(userID: String) async throws { + + } +} diff --git a/Mark-In.xcodeproj/project.pbxproj b/Mark-In.xcodeproj/project.pbxproj index 31a47dd..b1a0fda 100644 --- a/Mark-In.xcodeproj/project.pbxproj +++ b/Mark-In.xcodeproj/project.pbxproj @@ -113,6 +113,8 @@ membershipExceptions = ( Testing/Repository/FakeFolderRepository.swift, Testing/Repository/FakeLinkRepository.swift, + Testing/Repository/MockFolderRepository.swift, + Testing/Repository/MockLinkRepository.swift, ); target = 57BFD8C22DA4E19600648AD4 /* Mark-In */; }; From b5b89a92b2e79d8f3632c2e91068f373b102f9b8 Mon Sep 17 00:00:00 2001 From: Jeong Dong Date: Tue, 17 Jun 2025 14:19:26 +0900 Subject: [PATCH 6/7] =?UTF-8?q?[#59]=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=20=ED=8C=8C=EC=9D=BC=EC=9D=98=20=ED=83=80?= =?UTF-8?q?=EA=B2=9F=20=EB=A9=A4=EB=B2=84=EC=8B=AD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Repository/FakeFolderRepository.swift | 1 + .../Repository/FakeLinkRepository.swift | 1 + .../Repository/MockFolderRepository.swift | 1 + .../Repository/MockLinkRepository.swift | 1 + Mark-In.xcodeproj/project.pbxproj | 22 ------------------- 5 files changed, 4 insertions(+), 22 deletions(-) diff --git a/Mark-In-Tests/Testing/Repository/FakeFolderRepository.swift b/Mark-In-Tests/Testing/Repository/FakeFolderRepository.swift index 38219d3..f4141ec 100644 --- a/Mark-In-Tests/Testing/Repository/FakeFolderRepository.swift +++ b/Mark-In-Tests/Testing/Repository/FakeFolderRepository.swift @@ -5,6 +5,7 @@ // Created by 이정동 on 6/6/25. // +@testable import Mark_In import Foundation final class FakeFolderRepository: FolderRepository { diff --git a/Mark-In-Tests/Testing/Repository/FakeLinkRepository.swift b/Mark-In-Tests/Testing/Repository/FakeLinkRepository.swift index 1cbfb8a..1ccfd38 100644 --- a/Mark-In-Tests/Testing/Repository/FakeLinkRepository.swift +++ b/Mark-In-Tests/Testing/Repository/FakeLinkRepository.swift @@ -5,6 +5,7 @@ // Created by 이정동 on 6/6/25. // +@testable import Mark_In import Foundation final class FakeLinkRepository: LinkRepository { diff --git a/Mark-In-Tests/Testing/Repository/MockFolderRepository.swift b/Mark-In-Tests/Testing/Repository/MockFolderRepository.swift index cf76723..9667563 100644 --- a/Mark-In-Tests/Testing/Repository/MockFolderRepository.swift +++ b/Mark-In-Tests/Testing/Repository/MockFolderRepository.swift @@ -5,6 +5,7 @@ // Created by 이정동 on 6/17/25. // +@testable import Mark_In import Foundation final class MockFolderRepository: FolderRepository { diff --git a/Mark-In-Tests/Testing/Repository/MockLinkRepository.swift b/Mark-In-Tests/Testing/Repository/MockLinkRepository.swift index c7cf129..83ae77f 100644 --- a/Mark-In-Tests/Testing/Repository/MockLinkRepository.swift +++ b/Mark-In-Tests/Testing/Repository/MockLinkRepository.swift @@ -5,6 +5,7 @@ // Created by 이정동 on 6/17/25. // +@testable import Mark_In import Foundation final class MockLinkRepository: LinkRepository { diff --git a/Mark-In.xcodeproj/project.pbxproj b/Mark-In.xcodeproj/project.pbxproj index b1a0fda..a0c4a01 100644 --- a/Mark-In.xcodeproj/project.pbxproj +++ b/Mark-In.xcodeproj/project.pbxproj @@ -108,24 +108,6 @@ /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ - 576EF9382E00393F00CA1D3F /* Exceptions for "Mark-In-Tests" folder in "Mark-In" target */ = { - isa = PBXFileSystemSynchronizedBuildFileExceptionSet; - membershipExceptions = ( - Testing/Repository/FakeFolderRepository.swift, - Testing/Repository/FakeLinkRepository.swift, - Testing/Repository/MockFolderRepository.swift, - Testing/Repository/MockLinkRepository.swift, - ); - target = 57BFD8C22DA4E19600648AD4 /* Mark-In */; - }; - 576EF9392E00393F00CA1D3F /* Exceptions for "Mark-In-Tests" folder in "Mark-In-Tests" target */ = { - isa = PBXFileSystemSynchronizedBuildFileExceptionSet; - membershipExceptions = ( - Testing/Repository/FakeFolderRepository.swift, - Testing/Repository/FakeLinkRepository.swift, - ); - target = 57664F672DF03A9000CA8D68 /* Mark-In-Tests */; - }; 57B7E0242DC3D44100D9225A /* Exceptions for "Mark-In" folder in "Mark-In" target */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( @@ -140,10 +122,6 @@ /* Begin PBXFileSystemSynchronizedRootGroup section */ 57664F692DF03A9000CA8D68 /* Mark-In-Tests */ = { isa = PBXFileSystemSynchronizedRootGroup; - exceptions = ( - 576EF9382E00393F00CA1D3F /* Exceptions for "Mark-In-Tests" folder in "Mark-In" target */, - 576EF9392E00393F00CA1D3F /* Exceptions for "Mark-In-Tests" folder in "Mark-In-Tests" target */, - ); path = "Mark-In-Tests"; sourceTree = ""; }; From 8bb5ff0217e95bb65cb384555422dfd1aa97be19 Mon Sep 17 00:00:00 2001 From: Jeong Dong Date: Tue, 17 Jun 2025 14:48:37 +0900 Subject: [PATCH 7/7] =?UTF-8?q?[#59]=20DeleteFolderUseCase=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UseCase/DeleteFolderUseCaseTests.swift | 153 ++++++++++++++++++ Mark-In/Sources/App/AuthUserManager.swift | 18 ++- 2 files changed, 164 insertions(+), 7 deletions(-) diff --git a/Mark-In-Tests/Test/UseCase/DeleteFolderUseCaseTests.swift b/Mark-In-Tests/Test/UseCase/DeleteFolderUseCaseTests.swift index 1699bb0..d734ba0 100644 --- a/Mark-In-Tests/Test/UseCase/DeleteFolderUseCaseTests.swift +++ b/Mark-In-Tests/Test/UseCase/DeleteFolderUseCaseTests.swift @@ -10,6 +10,159 @@ import Testing @testable import Mark_In struct DeleteFolderUseCaseTests { + + @Test + func test_인증된_유저_정보가_없을_때_unauthenticated_에러를_반환한다() async throws { + // Given: 준비 + let stubAuthUserManager = StubAuthUserManager(userID: nil) + let mockLinkRepository = MockLinkRepository() + let mockFolderRepository = MockFolderRepository() + + let sut = DeleteFolderUseCaseImpl( + authUserManager: stubAuthUserManager, + linkRepository: mockLinkRepository, + folderRepository: mockFolderRepository + ) + + // When: 실행 + var thrownError: Error? + do { + try await sut.execute(folderID: "", includingChildren: false) + } catch { + thrownError = error + } + + // Then: 검증 + #expect(thrownError as? AuthError == AuthError.unauthenticated) + + // TearDown: 해제 + + } + + @Test + func test_인증된_유저_정보가_있다면_에러를_반환하지_않는다() async throws { + // Given: 준비 + let stubAuthUserManager = StubAuthUserManager(userID: "testUser") + let mockLinkRepository = MockLinkRepository() + let mockFolderRepository = MockFolderRepository() + + let sut = DeleteFolderUseCaseImpl( + authUserManager: stubAuthUserManager, + linkRepository: mockLinkRepository, + folderRepository: mockFolderRepository + ) + + // When: 실행 + var thrownError: Error? + do { + try await sut.execute(folderID: "", includingChildren: false) + } catch { + thrownError = error + } + + // Then: 검증 + #expect(thrownError as? AuthError == nil) + + // TearDown: 해제 + + } + + @Test + func test_includeChildren이_true이면_linkRepository의_deleteAllInFolder를_호출한다() async throws { + // Given: 준비 + let stubAuthUserManager = StubAuthUserManager(userID: "testUser") + let mockLinkRepository = MockLinkRepository() + let mockFolderRepository = MockFolderRepository() + + let sut = DeleteFolderUseCaseImpl( + authUserManager: stubAuthUserManager, + linkRepository: mockLinkRepository, + folderRepository: mockFolderRepository + ) + + // When: 실행 + try await sut.execute(folderID: "", includingChildren: true) + + // Then: 검증 + let deleteAllInFolderCallCount = mockLinkRepository.deleteAllInFolderCallCount + #expect(deleteAllInFolderCallCount == 1) + + // TearDown: 해제 + + } + + @Test + func test_includeChildren이_false이면_linkRepository의_moveLinksInFolder를_호출한다() async throws { + // Given: 준비 + let stubAuthUserManager = StubAuthUserManager(userID: "testUser") + let mockLinkRepository = MockLinkRepository() + let mockFolderRepository = MockFolderRepository() + + let sut = DeleteFolderUseCaseImpl( + authUserManager: stubAuthUserManager, + linkRepository: mockLinkRepository, + folderRepository: mockFolderRepository + ) + + // When: 실행 + try await sut.execute(folderID: "", includingChildren: false) + + // Then: 검증 + let moveLinksInFolderCallCount = mockLinkRepository.moveLinksInFolderCallCount + #expect(moveLinksInFolderCallCount == 1) + + // TearDown: 해제 + + } + + @Test + func test_폴더ID가_존재할_경우_folderRepository의_delete를_호출한다() async throws { + // Given: 준비 + let stubAuthUserManager = StubAuthUserManager(userID: "testUser") + let mockLinkRepository = MockLinkRepository() + let mockFolderRepository = MockFolderRepository() + + let sut = DeleteFolderUseCaseImpl( + authUserManager: stubAuthUserManager, + linkRepository: mockLinkRepository, + folderRepository: mockFolderRepository + ) + + // When: 실행 + try await sut.execute(folderID: "testFolder", includingChildren: false) + + // Then: 검증 + let deleteCallCount = mockFolderRepository.deleteCallCount + #expect(deleteCallCount == 1) + + // TearDown: 해제 + + } + + @Test + func test_폴더ID가_존재하지_않을_경우_folderRepository의_delete를_호출하지_않는다() async throws { + // Given: 준비 + let stubAuthUserManager = StubAuthUserManager(userID: "testUser") + let mockLinkRepository = MockLinkRepository() + let mockFolderRepository = MockFolderRepository() + + let sut = DeleteFolderUseCaseImpl( + authUserManager: stubAuthUserManager, + linkRepository: mockLinkRepository, + folderRepository: mockFolderRepository + ) + + // When: 실행 + try await sut.execute(folderID: nil, includingChildren: false) + + // Then: 검증 + let deleteCallCount = mockFolderRepository.deleteCallCount + #expect(deleteCallCount == 0) + + // TearDown: 해제 + + } + @Test func test_includingChildren이_true면_링크들을_삭제한다() async throws { diff --git a/Mark-In/Sources/App/AuthUserManager.swift b/Mark-In/Sources/App/AuthUserManager.swift index 0bd820c..7803da6 100644 --- a/Mark-In/Sources/App/AuthUserManager.swift +++ b/Mark-In/Sources/App/AuthUserManager.swift @@ -61,13 +61,17 @@ final class AuthUserManagerImpl: AuthUserManager { final class StubAuthUserManager: AuthUserManager { var user: AuthUser? - init(userID: String) { - self.user = .init( - id: userID, - name: userID, - email: "\(userID)@test.com", - provider: .apple - ) + init(userID: String?) { + if let userID { + self.user = .init( + id: userID, + name: userID, + email: "\(userID)@test.com", + provider: .apple + ) + } else { + self.user = nil + } } func save(_ user: AuthUser) { }