diff --git a/RefillStation/RefillStation.xcodeproj/project.pbxproj b/RefillStation/RefillStation.xcodeproj/project.pbxproj index 69c857f0..8c282b38 100644 --- a/RefillStation/RefillStation.xcodeproj/project.pbxproj +++ b/RefillStation/RefillStation.xcodeproj/project.pbxproj @@ -101,7 +101,6 @@ 32DB626E2981689C008C2522 /* WithdrawUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB626D2981689C008C2522 /* WithdrawUseCase.swift */; }; 32DB6270298168C4008C2522 /* ValidNicknameUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB626F298168C4008C2522 /* ValidNicknameUseCase.swift */; }; 32DB627229816943008C2522 /* EditProfileUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB627129816943008C2522 /* EditProfileUseCase.swift */; }; - 32DB6274298169EB008C2522 /* CreateNicknameUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DB6273298169EB008C2522 /* CreateNicknameUseCase.swift */; }; 32DDE65F29866CCB008AFDB2 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DDE65E29866CCB008AFDB2 /* LoginViewController.swift */; }; 32DFDAE12976BAE400B6EDA3 /* FilteredProductCountCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DFDAE02976BAE400B6EDA3 /* FilteredProductCountCell.swift */; }; 32DFDAE32976C93500B6EDA3 /* DetailPhotoReviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DFDAE22976C93500B6EDA3 /* DetailPhotoReviewViewController.swift */; }; @@ -128,6 +127,14 @@ FC083C482992B4A4002DD943 /* AccountManagementViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC083C472992B4A4002DD943 /* AccountManagementViewController.swift */; }; FC083C4C2992B670002DD943 /* AccountManagementViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC083C4B2992B670002DD943 /* AccountManagementViewModel.swift */; }; FC1FD6A12962C4AF00869F5F /* RegisterReviewPopUpViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC1FD6A02962C4AF00869F5F /* RegisterReviewPopUpViewController.swift */; }; + FC27E05929A7592B002F00B3 /* SignUpTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC27E05829A7592B002F00B3 /* SignUpTest.swift */; }; + FC27E06229A78C73002F00B3 /* FetchProfileTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC27E06129A78C73002F00B3 /* FetchProfileTest.swift */; }; + FC27E06429A78C8A002F00B3 /* EditProfileTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC27E06329A78C8A002F00B3 /* EditProfileTest.swift */; }; + FC27E06629A78C9B002F00B3 /* ValidNicknameTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC27E06529A78C9B002F00B3 /* ValidNicknameTest.swift */; }; + FC27E06829A78CB4002F00B3 /* FetchUserReviewsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC27E06729A78CB4002F00B3 /* FetchUserReviewsTest.swift */; }; + FC27E06A29A9DA15002F00B3 /* SignOutTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC27E06929A9DA15002F00B3 /* SignOutTest.swift */; }; + FC27E06C29A9DA3B002F00B3 /* WithdrawTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC27E06B29A9DA3B002F00B3 /* WithdrawTest.swift */; }; + FC27E07029ABAFBE002F00B3 /* MockKeychainManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC27E06F29ABAFBE002F00B3 /* MockKeychainManager.swift */; }; FC3630172969316900532598 /* LevelHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC3630162969316900532598 /* LevelHeaderView.swift */; }; FC3E8CF82976ED6F00FE65F6 /* NicknameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC3E8CF72976ED6F00FE65F6 /* NicknameViewController.swift */; }; FC3EBDF32932872100A9C1F9 /* Product.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC3EBDF22932872100A9C1F9 /* Product.swift */; }; @@ -291,7 +298,6 @@ 32DB626D2981689C008C2522 /* WithdrawUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithdrawUseCase.swift; sourceTree = ""; }; 32DB626F298168C4008C2522 /* ValidNicknameUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidNicknameUseCase.swift; sourceTree = ""; }; 32DB627129816943008C2522 /* EditProfileUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileUseCase.swift; sourceTree = ""; }; - 32DB6273298169EB008C2522 /* CreateNicknameUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateNicknameUseCase.swift; sourceTree = ""; }; 32DDE65E29866CCB008AFDB2 /* LoginViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = ""; }; 32DFDAE02976BAE400B6EDA3 /* FilteredProductCountCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilteredProductCountCell.swift; sourceTree = ""; }; 32DFDAE22976C93500B6EDA3 /* DetailPhotoReviewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailPhotoReviewViewController.swift; sourceTree = ""; }; @@ -320,6 +326,14 @@ FC083C472992B4A4002DD943 /* AccountManagementViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountManagementViewController.swift; sourceTree = ""; }; FC083C4B2992B670002DD943 /* AccountManagementViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountManagementViewModel.swift; sourceTree = ""; }; FC1FD6A02962C4AF00869F5F /* RegisterReviewPopUpViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterReviewPopUpViewController.swift; sourceTree = ""; }; + FC27E05829A7592B002F00B3 /* SignUpTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpTest.swift; sourceTree = ""; }; + FC27E06129A78C73002F00B3 /* FetchProfileTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchProfileTest.swift; sourceTree = ""; }; + FC27E06329A78C8A002F00B3 /* EditProfileTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileTest.swift; sourceTree = ""; }; + FC27E06529A78C9B002F00B3 /* ValidNicknameTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidNicknameTest.swift; sourceTree = ""; }; + FC27E06729A78CB4002F00B3 /* FetchUserReviewsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchUserReviewsTest.swift; sourceTree = ""; }; + FC27E06929A9DA15002F00B3 /* SignOutTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignOutTest.swift; sourceTree = ""; }; + FC27E06B29A9DA3B002F00B3 /* WithdrawTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithdrawTest.swift; sourceTree = ""; }; + FC27E06F29ABAFBE002F00B3 /* MockKeychainManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = MockKeychainManager.swift; path = RefillStationTests/RepositoryTests/RegisterReview/MockKeychainManager.swift; sourceTree = SOURCE_ROOT; }; FC3630162969316900532598 /* LevelHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LevelHeaderView.swift; sourceTree = ""; }; FC3E8CF72976ED6F00FE65F6 /* NicknameViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NicknameViewController.swift; sourceTree = ""; }; FC3EBDF22932872100A9C1F9 /* Product.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Product.swift; sourceTree = ""; }; @@ -681,7 +695,6 @@ 32DB626B29816849008C2522 /* SignUpUseCase.swift */, 32A0F4F02981707100A04ECC /* SignOutUseCase.swift */, 32DB626D2981689C008C2522 /* WithdrawUseCase.swift */, - 32DB6273298169EB008C2522 /* CreateNicknameUseCase.swift */, ); path = Account; sourceTree = ""; @@ -822,6 +835,7 @@ 32EF052E29A4DA0A00AF4CA1 /* MockNetworkService.swift */, 32EF054429A6516E00AF4CA1 /* MockAWSS3Service.swift */, 32EF053C29A64D7700AF4CA1 /* Account */, + FC27E06029A765A1002F00B3 /* UserInfo */, 32EF053129A513E900AF4CA1 /* Store */, 32EF053F29A6504000AF4CA1 /* CustomerSatisfaction */, 32EF054629A6524B00AF4CA1 /* RegisterReview */, @@ -845,6 +859,10 @@ isa = PBXGroup; children = ( 32EF053A29A646F000AF4CA1 /* OAuthLoginTest.swift */, + FC27E05829A7592B002F00B3 /* SignUpTest.swift */, + FC27E06929A9DA15002F00B3 /* SignOutTest.swift */, + FC27E06B29A9DA3B002F00B3 /* WithdrawTest.swift */, + FC27E06F29ABAFBE002F00B3 /* MockKeychainManager.swift */, ); path = Account; sourceTree = ""; @@ -894,6 +912,17 @@ path = ViewModel; sourceTree = ""; }; + FC27E06029A765A1002F00B3 /* UserInfo */ = { + isa = PBXGroup; + children = ( + FC27E06129A78C73002F00B3 /* FetchProfileTest.swift */, + FC27E06329A78C8A002F00B3 /* EditProfileTest.swift */, + FC27E06529A78C9B002F00B3 /* ValidNicknameTest.swift */, + FC27E06729A78CB4002F00B3 /* FetchUserReviewsTest.swift */, + ); + path = UserInfo; + sourceTree = ""; + }; FC3E8CF62976EAE100FE65F6 /* NicknameScene */ = { isa = PBXGroup; children = ( @@ -1393,7 +1422,6 @@ 32E6AE38298E5629005AA542 /* StoreDTO.swift in Sources */, FCE00A67294C5ADF00B5C05F /* CoreDataStorage.swift in Sources */, 322E15E6297A0B6600FE2E2F /* MyPageCoordinator.swift in Sources */, - 32DB6274298169EB008C2522 /* CreateNicknameUseCase.swift in Sources */, 32A0F4F92981712D00A04ECC /* FetchUserReviewsUseCase.swift in Sources */, 32A0F5032981797800A04ECC /* CustomerSatisfactionUseCase.swift in Sources */, FCF0181A295F08E50027078A /* UserLevelInfo.swift in Sources */, @@ -1545,21 +1573,29 @@ buildActionMask = 2147483647; files = ( 32EF052F29A4DA0A00AF4CA1 /* MockNetworkService.swift in Sources */, + FC27E06429A78C8A002F00B3 /* EditProfileTest.swift in Sources */, 32BA4B4329A7249D00A155C5 /* StoreRecommendDTOTests.swift in Sources */, 32EF053729A51EB100AF4CA1 /* FetchStoreRecommendTest.swift in Sources */, 32EF054329A6515100AF4CA1 /* RegisterReviewTest.swift in Sources */, 32EF053929A5219F00AF4CA1 /* RecommendStoreTest.swift in Sources */, + FC27E06629A78C9B002F00B3 /* ValidNicknameTest.swift in Sources */, 32EF054129A6505700AF4CA1 /* MockUserInfoRepository.swift in Sources */, 32572AAF29A714760042F82A /* ProductDTOTests.swift in Sources */, 3244A216292DC13B00716A2B /* FetchStoresTest.swift in Sources */, + FC27E06829A78CB4002F00B3 /* FetchUserReviewsTest.swift in Sources */, 32EF054529A6516E00AF4CA1 /* MockAWSS3Service.swift in Sources */, 32EF053529A51D7F00AF4CA1 /* FetchStoreReviewsTest.swift in Sources */, + FC27E05929A7592B002F00B3 /* SignUpTest.swift in Sources */, 32BA4B4929A72A6400A155C5 /* UserDTOTests.swift in Sources */, + FC27E06229A78C73002F00B3 /* FetchProfileTest.swift in Sources */, 32EF053B29A646F000AF4CA1 /* OAuthLoginTest.swift in Sources */, 32EF053E29A64DA000AF4CA1 /* CustomerSatisfactionUploadTest.swift in Sources */, 32BA4B4729A7263C00A155C5 /* ReviewDTOTests.swift in Sources */, + FC27E07029ABAFBE002F00B3 /* MockKeychainManager.swift in Sources */, 32EF053329A516DE00AF4CA1 /* FetchProductsTest.swift in Sources */, 32BA4B4529A725D100A155C5 /* FetchStoreRecommendDTOTests.swift in Sources */, + FC27E06A29A9DA15002F00B3 /* SignOutTest.swift in Sources */, + FC27E06C29A9DA3B002F00B3 /* WithdrawTest.swift in Sources */, 32572AAC29A7128D0042F82A /* StoreDTOTests.swift in Sources */, 32BA4B4029A723DD00A155C5 /* LoginDTOTests.swift in Sources */, ); diff --git a/RefillStation/RefillStation/Data/KeychainManager.swift b/RefillStation/RefillStation/Data/KeychainManager.swift index 02c33b8d..9d4b78db 100644 --- a/RefillStation/RefillStation/Data/KeychainManager.swift +++ b/RefillStation/RefillStation/Data/KeychainManager.swift @@ -7,10 +7,18 @@ import Foundation -final class KeychainManager { +protocol KeychainManagerInterface { + func addItem(key: Any, value: Any) -> Bool + func getItem(key: Any) -> Any? + func updateItem(key: Any, value: Any) -> Bool + func deleteItem(key: String) -> Bool + func deleteUserToken() -> Result +} + +final class KeychainManager: KeychainManagerInterface { enum KeychainError: Error { - case noData + case noData } static let shared = KeychainManager() @@ -63,7 +71,7 @@ final class KeychainManager { func updateItem(key: Any, value: Any) -> Bool { let prevQuery: [CFString: Any] = [kSecClass: kSecClassGenericPassword, - kSecAttrAccount: key] + kSecAttrAccount: key] let updateQuery: [CFString: Any] = [ kSecValueData: (value as AnyObject).data(using: String.Encoding.utf8.rawValue) as Any ] @@ -81,7 +89,7 @@ final class KeychainManager { func deleteItem(key: String) -> Bool { let deleteQuery: [CFString: Any] = [kSecClass: kSecClassGenericPassword, - kSecAttrAccount: key] + kSecAttrAccount: key] let status = SecItemDelete(deleteQuery as CFDictionary) if status == errSecSuccess { return true } @@ -91,10 +99,10 @@ final class KeychainManager { func deleteUserToken() -> Result { let deleteQuery: [CFString: Any] = [kSecClass: kSecClassGenericPassword, - kSecAttrAccount: "token"] + kSecAttrAccount: "token"] let status = SecItemDelete(deleteQuery as CFDictionary) let lookAroundDeleteQuery: [CFString: Any] = [kSecClass: kSecClassGenericPassword, - kSecAttrAccount: "lookAroundToken"] + kSecAttrAccount: "lookAroundToken"] let lookAroundStatus = SecItemDelete(lookAroundDeleteQuery as CFDictionary) if status == errSecSuccess || lookAroundStatus == errSecSuccess { return .success(()) } return .failure(KeychainError.noData) diff --git a/RefillStation/RefillStation/Data/Repositories/AsyncAccountRepository.swift b/RefillStation/RefillStation/Data/Repositories/AsyncAccountRepository.swift index 7a1ea757..691c0300 100644 --- a/RefillStation/RefillStation/Data/Repositories/AsyncAccountRepository.swift +++ b/RefillStation/RefillStation/Data/Repositories/AsyncAccountRepository.swift @@ -10,9 +10,13 @@ import Foundation final class AsyncAccountRepository: AsyncAccountRepositoryInterface { private let networkService: NetworkServiceInterface + private let keychainManager: KeychainManagerInterface - init(networkService: NetworkServiceInterface = NetworkService.shared) { + init(networkService: NetworkServiceInterface = NetworkService.shared, + keychainManager: KeychainManagerInterface = KeychainManager.shared + ) { self.networkService = networkService + self.keychainManager = keychainManager } func OAuthLogin(loginType: OAuthType, requestValue: OAuthLoginRequestValue) async throws -> OAuthLoginResponseValue { @@ -27,12 +31,12 @@ final class AsyncAccountRepository: AsyncAccountRepositoryInterface { } let loginDTO: LoginDTO = try await networkService.dataTask(request: request) if let token = loginDTO.jwt { - _ = KeychainManager.shared.deleteUserToken() + _ = keychainManager.deleteUserToken() if loginType == .lookAround { - _ = KeychainManager.shared.addItem(key: "lookAroundToken", value: token) + _ = keychainManager.addItem(key: "lookAroundToken", value: token) UserDefaults.standard.setValue(true, forKey: "isLookAroundUser") } else { - _ = KeychainManager.shared.addItem(key: "token", value: token) + _ = keychainManager.addItem(key: "token", value: token) } } return loginDTO.toDomain() @@ -54,13 +58,13 @@ final class AsyncAccountRepository: AsyncAccountRepositoryInterface { throw RepositoryError.urlParseFailed } let token: String = try await networkService.dataTask(request: request) - _ = KeychainManager.shared.deleteUserToken() - _ = KeychainManager.shared.addItem(key: "token", value: token) + _ = keychainManager.deleteUserToken() + _ = keychainManager.addItem(key: "token", value: token) return token } func signOut() async throws { - let result = KeychainManager.shared.deleteUserToken() + let result = keychainManager.deleteUserToken() switch result { case .success: UserDefaults.standard.setValue(false, forKey: "isLookAroundUser") @@ -72,8 +76,8 @@ final class AsyncAccountRepository: AsyncAccountRepositoryInterface { } func withdraw() async throws { - _ = KeychainManager.shared.deleteItem(key: "lookAroundToken") - guard KeychainManager.shared.getItem(key: "token") != nil else { return } + _ = keychainManager.deleteItem(key: "lookAroundToken") + guard keychainManager.getItem(key: "token") != nil else { return } var urlComponents = URLComponents(string: networkService.baseURL) urlComponents?.path = "/api/user" guard let request = urlComponents?.toURLRequest(method: .delete) else { @@ -81,20 +85,9 @@ final class AsyncAccountRepository: AsyncAccountRepositoryInterface { } let _: WithdrawDTO = try await networkService.dataTask(request: request) - _ = KeychainManager.shared.deleteUserToken() + _ = keychainManager.deleteUserToken() UserDefaults.standard.setValue(false, forKey: "isLookAroundUser") UserDefaults.standard.setValue(false, forKey: "didLookAroundLoginStarted") UserDefaults.standard.setValue(false, forKey: "didRequestRegion") } - - func createNickname() async throws -> String { - var urlComponents = URLComponents(string: networkService.baseURL) - urlComponents?.path = "/api/user/random-nickname" - guard let request = urlComponents?.toURLRequest(method: .get) else { - throw RepositoryError.urlParseFailed - } - - let randomNicknameDTO: RandomNicknameDTO = try await networkService.dataTask(request: request) - return randomNicknameDTO.nickname - } } diff --git a/RefillStation/RefillStation/Domain/Interfaces/Repositories/AsyncAccountRepositoryInterface.swift b/RefillStation/RefillStation/Domain/Interfaces/Repositories/AsyncAccountRepositoryInterface.swift index 8950aee7..41a8841b 100644 --- a/RefillStation/RefillStation/Domain/Interfaces/Repositories/AsyncAccountRepositoryInterface.swift +++ b/RefillStation/RefillStation/Domain/Interfaces/Repositories/AsyncAccountRepositoryInterface.swift @@ -12,5 +12,4 @@ protocol AsyncAccountRepositoryInterface { func signUp(requestValue: SignUpRequestValue) async throws -> String func signOut() async throws func withdraw() async throws - func createNickname() async throws -> String } diff --git a/RefillStation/RefillStation/Domain/UseCases/Account/CreateNicknameUseCase.swift b/RefillStation/RefillStation/Domain/UseCases/Account/CreateNicknameUseCase.swift deleted file mode 100644 index 9d935a8f..00000000 --- a/RefillStation/RefillStation/Domain/UseCases/Account/CreateNicknameUseCase.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// CreateNicknameUseCase.swift -// RefillStation -// -// Created by 천수현 on 2023/01/25. -// - -import Foundation - -protocol CreateNicknameUseCaseInterface { - func execute() async throws -> String -} - -final class CreateNicknameUseCase: CreateNicknameUseCaseInterface { - private let accountRepository: AsyncAccountRepositoryInterface - - init(accountRepository: AsyncAccountRepositoryInterface = AsyncAccountRepository()) { - self.accountRepository = accountRepository - } - - func execute() async throws -> String { - return try await accountRepository.createNickname() - } -} diff --git a/RefillStation/RefillStationTests/RepositoryTests/Account/SignOutTest.swift b/RefillStation/RefillStationTests/RepositoryTests/Account/SignOutTest.swift new file mode 100644 index 00000000..4dcccc0e --- /dev/null +++ b/RefillStation/RefillStationTests/RepositoryTests/Account/SignOutTest.swift @@ -0,0 +1,33 @@ +// +// SignOutTest.swift +// RefillStationTests +// +// Created by kong on 2023/02/25. +// + +import XCTest +@testable import RefillStation + +final class SignOutTest: XCTestCase { + var testUnit: AsyncAccountRepository! + + override func setUpWithError() throws { + try super.setUpWithError() + let dummyURL = "www.pump.com" + testUnit = AsyncAccountRepository(networkService: MockNetworkService(baseURL: dummyURL, dataToReturn: nil), keychainManager: MockKeychainManager()) + } + override func tearDownWithError() throws { + testUnit = nil + } + + func test_signOut_호출시_에러가_발생하지_않는지() async throws { + // given + do { + // when + _ = try await testUnit.signOut() + // then + } catch { + XCTFail("failed: \(error)") + } + } +} diff --git a/RefillStation/RefillStationTests/RepositoryTests/Account/SignUpTest.swift b/RefillStation/RefillStationTests/RepositoryTests/Account/SignUpTest.swift new file mode 100644 index 00000000..eb507d56 --- /dev/null +++ b/RefillStation/RefillStationTests/RepositoryTests/Account/SignUpTest.swift @@ -0,0 +1,79 @@ +// +// SignUpTest.swift +// RefillStationTests +// +// Created by kong on 2023/02/23. +// + +import XCTest +@testable import RefillStation + +final class SignUpTest: XCTestCase { + var sucessTestUnit: AsyncAccountRepository! + var failureTestUnit: AsyncAccountRepository! + let dataToReturn = +""" +{ + "message": "회원가입 완료", + "status": 200, + "data": "jwtToken" +} +""" + .data(using: .utf8)! + + + override func setUpWithError() throws { + try super.setUpWithError() + let dummyURL = "www.pump.com" + + sucessTestUnit = AsyncAccountRepository( + networkService: MockNetworkService(baseURL: dummyURL, dataToReturn: dataToReturn) + ) + failureTestUnit = AsyncAccountRepository( + networkService: MockNetworkService(baseURL: "한글은 안되겠지", dataToReturn: dataToReturn) + ) + } + + override func tearDownWithError() throws { + sucessTestUnit = nil + failureTestUnit = nil + } + + func test_올바른_baseURL로_signUp_호출시_jwtToken을_반환하는지() async throws { + // given + let requestValue = SignUpRequestValue(name: nil, + email: nil, + imagePath: nil, + oauthType: "oauthType", + oauthIdentity: "oauthIdentity") + do { + // when + let jwtToken = try await sucessTestUnit.signUp( + requestValue: requestValue + ) + // then + let networkResult = try JSONDecoder().decode(NetworkResult.self, from: dataToReturn) + let signUpResult = networkResult.data + XCTAssertEqual(jwtToken, signUpResult) + } catch { + XCTFail("failed: \(error)") + } + } + + func test_잘못된_baseURL로_signUp_호출시_urlParseFailed를_throw_하는지() async throws { + // given + let requestValue = SignUpRequestValue(name: nil, + email: nil, + imagePath: nil, + oauthType: "oauthType", + oauthIdentity: "oauthIdentity") + do { + // when + _ = try await failureTestUnit.signUp(requestValue: requestValue) + XCTFail() + } catch { + // then + XCTAssertEqual(error as? RepositoryError, .urlParseFailed) + } + } +} diff --git a/RefillStation/RefillStationTests/RepositoryTests/Account/WithdrawTest.swift b/RefillStation/RefillStationTests/RepositoryTests/Account/WithdrawTest.swift new file mode 100644 index 00000000..a747ccf4 --- /dev/null +++ b/RefillStation/RefillStationTests/RepositoryTests/Account/WithdrawTest.swift @@ -0,0 +1,63 @@ +// +// WithdrawTest.swift +// RefillStationTests +// +// Created by kong on 2023/02/25. +// + +import XCTest +@testable import RefillStation + +final class WithdrawTest: XCTestCase { + var sucessTestUnit: AsyncAccountRepository! + var failureTestUnit: AsyncAccountRepository! + let dataToReturn = +""" +{ + "message": "유저 탈퇴 성공", + "status": 200, + "data":{ + "userId":Int + } +} +""" + .data(using: .utf8)! + + override func setUpWithError() throws { + try super.setUpWithError() + let dummyURL = "www.pump.com" + sucessTestUnit = AsyncAccountRepository(networkService: MockNetworkService(baseURL: dummyURL, dataToReturn: dataToReturn), + keychainManager: MockKeychainManager()) + failureTestUnit = AsyncAccountRepository( + networkService: MockNetworkService(baseURL: "한글은 안되겠지", dataToReturn: dataToReturn) + ) + } + + override func tearDownWithError() throws { + sucessTestUnit = nil + failureTestUnit = nil + } + + func test_올바른_baseURL로_withdraw_호출시_에러가_발생하지_않는지() async throws { + // given + do { + // when + _ = try await sucessTestUnit.withdraw() + // then + } catch { + XCTFail("failed: \(error)") + } + } + + func test_잘못된_baseURL로_withdraw_호출시_urlParseFailed를_throw_하는지() async throws { + // given + do { + // when + _ = try await failureTestUnit.withdraw() + XCTFail() + } catch { + // then + XCTAssertEqual(error as? RepositoryError, .urlParseFailed) + } + } +} diff --git a/RefillStation/RefillStationTests/RepositoryTests/RegisterReview/MockKeychainManager.swift b/RefillStation/RefillStationTests/RepositoryTests/RegisterReview/MockKeychainManager.swift new file mode 100644 index 00000000..5a0f5b32 --- /dev/null +++ b/RefillStation/RefillStationTests/RepositoryTests/RegisterReview/MockKeychainManager.swift @@ -0,0 +1,27 @@ +// +// MockKeychainManager.swift +// RefillStationTests +// +// Created by kong on 2023/02/27. +// + +import Foundation +@testable import RefillStation + +final class MockKeychainManager: KeychainManagerInterface { + func addItem(key: Any, value: Any) -> Bool { + true + } + func getItem(key: Any) -> Any? { + nil + } + func updateItem(key: Any, value: Any) -> Bool { + true + } + func deleteItem(key: String) -> Bool { + true + } + func deleteUserToken() -> Result { + .success(()) + } +} diff --git a/RefillStation/RefillStationTests/RepositoryTests/UserInfo/EditProfileTest.swift b/RefillStation/RefillStationTests/RepositoryTests/UserInfo/EditProfileTest.swift new file mode 100644 index 00000000..beaee64c --- /dev/null +++ b/RefillStation/RefillStationTests/RepositoryTests/UserInfo/EditProfileTest.swift @@ -0,0 +1,89 @@ +// +// EditProfileTest.swift +// RefillStationTests +// +// Created by kong on 2023/02/23. +// + +import XCTest +@testable import RefillStation + +final class EditProfileTest: XCTestCase { + var sucessTestUnit: AsyncUserInfoRepository! + var failureTestUnit: AsyncUserInfoRepository! + let dataToReturn = +""" +{ + "message": "유저 수정 성공", + "status": 200, + "data": { + "createdAt": "2023-01-23T09:15:07", + "modifiedAt": "2023-01-23T09:15:07", + "id": 8, + "name": "김주원", + "nickname": "클로이바보", + "email": "thd930308@naver.com", + "phoneNumber": "010-7607-8704", + "type": "BOSS", + "oauthType": "KAKAO", + "oauthIdentity": "2596749748", + "rating": 1, + "imgPath": null, + } +} +""" + .data(using: .utf8)! + + override func setUpWithError() throws { + try super.setUpWithError() + let dummyURL = "www.pump.com" + + sucessTestUnit = AsyncUserInfoRepository( + networkService: MockNetworkService(baseURL: dummyURL, dataToReturn: dataToReturn) + ) + failureTestUnit = AsyncUserInfoRepository( + networkService: MockNetworkService(baseURL: "한글은 안되겠지", dataToReturn: dataToReturn) + ) + } + + override func tearDownWithError() throws { + sucessTestUnit = nil + failureTestUnit = nil + } + + func test_올바른_baseURL로_editProfile_호출시_User를_반환하는지() async throws { + // given + let requestValue = EditProfileRequestValue(nickname: "", + rating: 1, + newImage: nil, + oldImagePath: nil, + didImageChanged: false) + do { + // when + let editProfileResponse = try await sucessTestUnit.editProfile(requestValue: requestValue) + // then + let networkResult = try JSONDecoder().decode(NetworkResult.self, from: dataToReturn) + let editProfileResult = networkResult.data.toDomain() + XCTAssertEqual(editProfileResponse, editProfileResult) + } catch { + XCTFail("failed: \(error)") + } + } + + func test_잘못된_baseURL로_editProfile_호출시_urlParseFailed를_throw_하는지() async throws { + // given + let requestValue = EditProfileRequestValue(nickname: "", + rating: 1, + newImage: nil, + oldImagePath: nil, + didImageChanged: false) + do { + // when + _ = try await failureTestUnit.editProfile(requestValue: requestValue) + // then + XCTFail() + } catch { + XCTAssertEqual(error as? RepositoryError, .urlParseFailed) + } + } +} diff --git a/RefillStation/RefillStationTests/RepositoryTests/UserInfo/FetchProfileTest.swift b/RefillStation/RefillStationTests/RepositoryTests/UserInfo/FetchProfileTest.swift new file mode 100644 index 00000000..643659d6 --- /dev/null +++ b/RefillStation/RefillStationTests/RepositoryTests/UserInfo/FetchProfileTest.swift @@ -0,0 +1,79 @@ +// +// FetchProfileTest.swift +// RefillStationTests +// +// Created by kong on 2023/02/23. +// + +import XCTest +@testable import RefillStation + +final class FetchProfileTest: XCTestCase { + var sucessTestUnit: AsyncUserInfoRepository! + var failureTestUnit: AsyncUserInfoRepository! + let dataToReturn = +""" +{ + "message": "유저 로딩 성공", + "status": 200, + "data": { + "createdAt": "2023-01-23T09:15:07", + "modifiedAt": "2023-01-23T09:15:07", + "id": 8, + "name": "김주원", + "nickname": "클로이바보", + "email": "thd930308@naver.com", + "phoneNumber": "010-7607-8704", + "type": "BOSS", + "oauthType": "KAKAO", + "oauthIdentity": "2596749748", + "rating": 1, + "imgPath": null, + } +} +""" + .data(using: .utf8)! + + override func setUpWithError() throws { + try super.setUpWithError() + let dummyURL = "www.pump.com" + + sucessTestUnit = AsyncUserInfoRepository( + networkService: MockNetworkService(baseURL: dummyURL, dataToReturn: dataToReturn) + ) + failureTestUnit = AsyncUserInfoRepository( + networkService: MockNetworkService(baseURL: "한글은 안되겠지", dataToReturn: dataToReturn) + ) + } + + override func tearDownWithError() throws { + sucessTestUnit = nil + failureTestUnit = nil + } + + func test_올바른_baseURL로_fetchProfile_호출시_User를_반환하는지() async throws { + // given + do { + // when + let fetchProfileResponse = try await sucessTestUnit.fetchProfile() + // then + let networkResult = try JSONDecoder().decode(NetworkResult.self, from: dataToReturn) + let fetchProfileResult = networkResult.data.toDomain() + XCTAssertEqual(fetchProfileResponse, fetchProfileResult) + } catch { + XCTFail("failed: \(error)") + } + } + + func test_잘못된_baseURL로_fetchProfile_호출시_urlParseFailed를_throw_하는지() async throws { + // given + do { + // when + _ = try await failureTestUnit.fetchProfile() + XCTFail() + } catch { + XCTAssertEqual(error as? RepositoryError, .urlParseFailed) + } + } + +} diff --git a/RefillStation/RefillStationTests/RepositoryTests/UserInfo/FetchUserReviewsTest.swift b/RefillStation/RefillStationTests/RepositoryTests/UserInfo/FetchUserReviewsTest.swift new file mode 100644 index 00000000..5e12068f --- /dev/null +++ b/RefillStation/RefillStationTests/RepositoryTests/UserInfo/FetchUserReviewsTest.swift @@ -0,0 +1,108 @@ +// +// FetchUserReviewsTest.swift +// RefillStationTests +// +// Created by kong on 2023/02/23. +// + +import XCTest +@testable import RefillStation + +final class FetchUserReviewsTest: XCTestCase { + var sucessTestUnit: AsyncUserInfoRepository! + var failureTestUnit: AsyncUserInfoRepository! + let dataToReturn = +""" +{ + "message": "유저 리뷰 가져오기 성공", + "status": 200, + "data": [ + { + "createdAt": "2023-01-29T18:29:31", + "modifiedAt": "2023-01-29T18:29:31", + "id": 1, + "storeId": 1, + "userId": 8, + "reviewText": "리뷰테스트 찐막" + }, + { + "createdAt": "2023-01-29T18:56:24", + "modifiedAt": "2023-01-29T18:56:24", + "id": 3, + "storeId": 1, + "userId": 8, + "reviewText": "리뷰테스트 찐찐막" + }, + { + "createdAt": "2023-01-29T18:56:43", + "modifiedAt": "2023-01-29T18:56:43", + "id": 4, + "storeId": 1, + "userId": 8, + "reviewText": "리뷰테스트 찐찐막" + }, + { + "createdAt": "2023-01-30T02:34:14", + "modifiedAt": "2023-01-30T02:34:14", + "id": 5, + "storeId": 103, + "userId": 8, + "reviewText": "리뷰테스트 찐찐막" + }, + { + "createdAt": "2023-01-30T02:34:15", + "modifiedAt": "2023-01-30T02:34:15", + "id": 6, + "storeId": 103, + "userId": 8, + "reviewText": "리뷰테스트 찐찐막" + } + ] +} +""" + .data(using: .utf8)! + + override func setUpWithError() throws { + try super.setUpWithError() + let dummyURL = "www.pump.com" + + sucessTestUnit = AsyncUserInfoRepository( + networkService: MockNetworkService(baseURL: dummyURL, dataToReturn: dataToReturn) + ) + failureTestUnit = AsyncUserInfoRepository( + networkService: MockNetworkService(baseURL: "한글은 안되겠지", dataToReturn: dataToReturn) + ) + } + + override func tearDownWithError() throws { + sucessTestUnit = nil + failureTestUnit = nil + } + + func test_올바른_baseURL로_fetchUserReviews_호출시_Review_배열을_반환하는지() async throws { + // given + do { + // when + let fetchUserReviewsResponse = try await sucessTestUnit.fetchUserReviews() + // then + let networkResult = try JSONDecoder().decode(NetworkResult<[ReviewDTO]>.self, from: dataToReturn) + let fetchUserReviewsResult = networkResult.data.map { $0.toDomain() } + XCTAssertEqual(fetchUserReviewsResponse, fetchUserReviewsResult) + } catch { + XCTFail("failed: \(error)") + } + } + + func test_잘못된_baseURL로_fetchUserReviews_호출시_urlParseFailed를_throw_하는지() async throws { + // given + do { + // when + _ = try await failureTestUnit.fetchUserReviews() + // Then + XCTFail() + } catch { + XCTAssertEqual(error as? RepositoryError, .urlParseFailed) + } + } + +} diff --git a/RefillStation/RefillStationTests/RepositoryTests/UserInfo/ValidNicknameTest.swift b/RefillStation/RefillStationTests/RepositoryTests/UserInfo/ValidNicknameTest.swift new file mode 100644 index 00000000..0dbc9fd2 --- /dev/null +++ b/RefillStation/RefillStationTests/RepositoryTests/UserInfo/ValidNicknameTest.swift @@ -0,0 +1,68 @@ +// +// ValidNicknameTest.swift +// RefillStationTests +// +// Created by kong on 2023/02/23. +// + +import XCTest +@testable import RefillStation + +final class ValidNicknameTest: XCTestCase { + var sucessTestUnit: AsyncUserInfoRepository! + var failureTestUnit: AsyncUserInfoRepository! + let dataToReturn = +""" +{ + "message": "닉네임 중복 확인 완료", + "status": 200, + "data": true +} +""" + .data(using: .utf8)! + + override func setUpWithError() throws { + try super.setUpWithError() + let dummyURL = "www.pump.com" + + sucessTestUnit = AsyncUserInfoRepository( + networkService: MockNetworkService(baseURL: dummyURL, dataToReturn: dataToReturn) + ) + failureTestUnit = AsyncUserInfoRepository( + networkService: MockNetworkService(baseURL: "한글은 안되겠지", dataToReturn: dataToReturn) + ) + } + + override func tearDownWithError() throws { + sucessTestUnit = nil + failureTestUnit = nil + } + + func test_올바른_baseURL로_validNickname_호출시_닉네임_중복여부를_반환하는지() async throws { + // given + let requestValue = ValidNicknameRequestValue(nickname: "nickname") + do { + // when + let validNicknameResponse = try await sucessTestUnit.validNickname(requestValue: requestValue) + // then + let networkResult = try JSONDecoder().decode(NetworkResult.self, from: dataToReturn) + let validNicknameResult = networkResult.data + XCTAssertEqual(validNicknameResponse, validNicknameResult) + } catch { + XCTFail("failed: \(error)") + } + } + + func test_잘못된_baseURL로_validNickname_호출시_urlParseFailed를_throw_하는지() async throws { + // given + let requestValue = ValidNicknameRequestValue(nickname: "nickname") + do { + // when + _ = try await failureTestUnit.validNickname(requestValue: requestValue) + // then + XCTFail() + } catch { + XCTAssertEqual(error as? RepositoryError, .urlParseFailed) + } + } +}