From 9918697486a31c571fd32e015c7bb66021e5eda3 Mon Sep 17 00:00:00 2001 From: "Hosung.Kim" Date: Tue, 27 Jan 2026 23:05:45 +0900 Subject: [PATCH] =?UTF-8?q?[feat]=20KeyChain=20Test=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TokenStorage/TokenStorage.swift | 11 +- FLINT/FLINTTests/FLINTTests.swift | 183 ------------------ FLINT/FLINTTests/KeyChainTests.swift | 94 +++++++++ 3 files changed, 101 insertions(+), 187 deletions(-) delete mode 100644 FLINT/FLINTTests/FLINTTests.swift create mode 100644 FLINT/FLINTTests/KeyChainTests.swift diff --git a/FLINT/Data/Sources/Networking/TokenStorage/TokenStorage.swift b/FLINT/Data/Sources/Networking/TokenStorage/TokenStorage.swift index 2636b4f8..cbaac084 100644 --- a/FLINT/Data/Sources/Networking/TokenStorage/TokenStorage.swift +++ b/FLINT/Data/Sources/Networking/TokenStorage/TokenStorage.swift @@ -54,10 +54,11 @@ public final class DefaultTokenStorage: TokenStorage { // 기존 항목 삭제 후 새로운 항목 저장 SecItemDelete(query as CFDictionary) let status = SecItemAdd(query as CFDictionary, nil) - if status == errSecSuccess { - Log.d("Keychain \(type.rawValue) 저장 완료") + guard status == errSecSuccess else { + Log.e("Keychain \(type.rawValue) save failed: \(SecCopyErrorMessageString(status, nil) as String? ?? "unknown")") + return } - Log.e(status) + Log.d("Keychain \(type.rawValue) saved.") } public func load(type: TokenType) -> String? { @@ -76,6 +77,7 @@ public final class DefaultTokenStorage: TokenStorage { Log.e("Keychain \(type.rawValue) load failed. \(status)") return nil } + Log.e("Keychain \(type.rawValue) load failed: \(SecCopyErrorMessageString(status, nil) as String? ?? "unknown")") guard let data = item as? Data else { Log.e("Keychain \(type.rawValue) data invalid") return nil @@ -97,9 +99,10 @@ public final class DefaultTokenStorage: TokenStorage { let status = SecItemDelete(query as CFDictionary) guard status == errSecSuccess else { - Log.e("Keychain \(type.rawValue) delete failed. \(status)") + Log.e("Keychain \(type.rawValue) delete failed: \(SecCopyErrorMessageString(status, nil) as String? ?? "unknown")") return } + Log.d("Keychain \(type.rawValue) deleted.") } public func clearAll() { diff --git a/FLINT/FLINTTests/FLINTTests.swift b/FLINT/FLINTTests/FLINTTests.swift deleted file mode 100644 index 340b41c2..00000000 --- a/FLINT/FLINTTests/FLINTTests.swift +++ /dev/null @@ -1,183 +0,0 @@ -// -// FLINTTests.swift -// FLINTTests -// -// Created by 김호성 on 2026.01.19. -// - -import XCTest -import Combine -@testable import Data -@testable import Domain - -final class FLINTTests: XCTestCase { - - private var userService: UserService! - private var searchContentsService: SearchService! - private var collectionService: CollectionService! - - override func setUp() { - super.setUp() - - userService = DefaultUserService() - searchContentsService = DefaultSearchService() - collectionService = DefaultCollectionService() - } - - override func tearDown() { - userService = nil - - super.tearDown() - } - - func testCheckNickname() throws { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - // Any test you write for XCTest can be annotated as throws and async. - // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. - // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. - - let expectation = XCTestExpectation(description: "비동기 Test") - var receivedValue: Bool? - - let cancellable = userService.checkNickname("코코아") - .sink(receiveCompletion: { result in - Log.d(result) - }, receiveValue: { dto in - receivedValue = dto.available - expectation.fulfill() - }) - - wait(for: [expectation], timeout: 3.0) - XCTAssertEqual(receivedValue, false) - - cancellable.cancel() - } - - func testSearchService() throws { - let expectation = XCTestExpectation(description: "비동기 Test") - var receivedValue: [SearchContentsDTO.ContentDTO]? - - let cancellable = searchContentsService.searchContents("주토피아") - .sink(receiveCompletion: { result in - Log.d(result) - }, receiveValue: { dto in - receivedValue = dto.contents ?? [] - expectation.fulfill() - }) - - wait(for: [expectation], timeout: 3.0) - XCTAssertEqual(receivedValue?.isEmpty, false) - - cancellable.cancel() - } - - func testCollectionService() throws { - let expectation = XCTestExpectation(description: "비동기 Test") - let request = CreateCollectionEntity(imgaeUrl: "collection/image/abc123.jpg", title: "주말에 보기 좋은 영화", description: "주말에 편하게 볼 수 있는 영화들을 모았습니다.", isPublic: true, contentList: []) - - let cancellable = collectionService.createCollection(request) - .sink(receiveCompletion: { result in - switch result { - case .finished: - return - case .failure(let failure): - XCTFail(failure.localizedDescription) - } - Log.d(result) - }, receiveValue: { _ in - expectation.fulfill() - }) - - wait(for: [expectation], timeout: 3.0) - XCTAssert(true) - - cancellable.cancel() - } - - func testHomeRepository_fetchRecommendedCollections() throws { - let expectation = XCTestExpectation(description: "비동기 Test") - let homeRepository = DefaultHomeRepository(homeService: DefaultHomeService()) - var receivedValue: [HomeRecommendedCollectionsEntity.HomeRecommendedCollectionEntity]? - var receivedError: NetworkError? - - let cancellable = homeRepository.fetchRecommendedCollections() - .sink(receiveCompletion: { result in - if case .failure(let error) = result { - receivedError = error - } - expectation.fulfill() - }, receiveValue: { entity in - receivedValue = entity.collections - }) - - wait(for: [expectation], timeout: 3.0) - - if let error = receivedError { - XCTFail("Home API failed: \(error)") - cancellable.cancel() - return - } - - XCTAssertNotNil(receivedValue) - XCTAssertEqual(receivedValue?.isEmpty, false) - - cancellable.cancel() - } - - func testFetchOTTPlatformsRepository() throws { - let expectation = XCTestExpectation(description: "비동기 Test") - - let repository = DefaultContentRepository(contentService: DefaultContentService()) - var receivedValue: [FetchOTTPlatformsEntity.OTTPlatformEntity]? - var receivedError: NetworkError? - - let cancellable = repository.fetchOTTPlatforms(1073741824) // ⚠️ 실제 존재하는 contentId로 변경 - .sink(receiveCompletion: { result in - if case .failure(let error) = result { - receivedError = error - } - expectation.fulfill() - }, receiveValue: { entity in - receivedValue = entity.ottPlatforms - }) - - wait(for: [expectation], timeout: 3.0) - - if let error = receivedError { - XCTFail("fetchOTTPlatforms failed: \(error)") - cancellable.cancel() - return - } - - XCTAssertNotNil(receivedValue) - XCTAssertEqual(receivedValue?.isEmpty, false) - - cancellable.cancel() - } - func testToggleContentBookmark_Fails_WithoutToken() throws { - let expectation = XCTestExpectation(description: "Auth Required Test") - - let repository = DefaultBookmarkRepository( - bookmarkService: DefaultBookmarkService() - ) - - var didFail = false - - let cancellable = repository.toggleContentBookmark(1) - .sink(receiveCompletion: { result in - if case .failure = result { - didFail = true - } - expectation.fulfill() - }, receiveValue: { _ in - XCTFail("Expected failure, but received value") - }) - - wait(for: [expectation], timeout: 3.0) - XCTAssertTrue(didFail) - - cancellable.cancel() - } - -} diff --git a/FLINT/FLINTTests/KeyChainTests.swift b/FLINT/FLINTTests/KeyChainTests.swift new file mode 100644 index 00000000..fdfc647e --- /dev/null +++ b/FLINT/FLINTTests/KeyChainTests.swift @@ -0,0 +1,94 @@ +// +// KeyChainTests.swift +// FLINTTests +// +// Created by 김호성 on 2026.01.27. +// + +import XCTest +@testable import Data +@testable import FLINT + +final class KeyChainTests: XCTestCase { + + private var tokenStorage: TokenStorage! + + override func setUpWithError() throws { + try super.setUpWithError() + + tokenStorage = DefaultTokenStorage() + tokenStorage.clearAll() +// tokenStorage = TestTokenStorage() + } + + override func tearDownWithError() throws { + tokenStorage.clearAll() + tokenStorage = nil + + try super.tearDownWithError() + } + + func testSaveAndLoadAccessToken() throws { + let token = "access_token_00" + + tokenStorage.save(token, type: .accessToken) + let loadedToken = tokenStorage.load(type: .accessToken) + + XCTAssertEqual(token, loadedToken) + } + func testSaveAndLoadTempToken() throws { + let token = "temp_token_00" + + tokenStorage.save(token, type: .tempToken) + let loadedToken = tokenStorage.load(type: .tempToken) + + XCTAssertEqual(token, loadedToken) + } + func testSaveAndLoadRefreshToken() throws { + let token = "refresh_token_00" + + tokenStorage.save(token, type: .refreshToken) + let loadedToken = tokenStorage.load(type: .refreshToken) + + XCTAssertEqual(token, loadedToken) + } + + func testLoadWithoutSaving() { + let loadedToken = tokenStorage.load(type: .accessToken) + + XCTAssertNil(loadedToken) + } + + func testDelete() { + let token = "access_token_01" + tokenStorage.save(token, type: .accessToken) + + tokenStorage.delete(type: .accessToken) + + let loadedToken = tokenStorage.load(type: .accessToken) + XCTAssertNil(loadedToken) + } + + func testSaveOverridesExistingToken() { + let oldToken = "access_token_02" + let newToken = "access_token_03" + + tokenStorage.save(oldToken, type: .accessToken) + tokenStorage.save(newToken, type: .accessToken) + + let loadedToken = tokenStorage.load(type: .accessToken) + XCTAssertEqual(newToken, loadedToken) + } + + func testClearAll() { + tokenStorage.save("access_token_04", type: .accessToken) + tokenStorage.save("refresh_token_01", type: .refreshToken) + tokenStorage.save("temp_token_01", type: .tempToken) + + tokenStorage.clearAll() + + XCTAssertNil(tokenStorage.load(type: .accessToken)) + XCTAssertNil(tokenStorage.load(type: .refreshToken)) + XCTAssertNil(tokenStorage.load(type: .tempToken)) + } +}