Skip to content

Commit f6dc847

Browse files
committed
Try fixing keychain prompt during tests
1 parent 613f231 commit f6dc847

File tree

10 files changed

+63
-21
lines changed

10 files changed

+63
-21
lines changed

TaskMenu.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
986954B711E286DA6CDC3353 /* GoogleAuthServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5360741190B5DF2436BE448 /* GoogleAuthServiceTests.swift */; };
2424
9ADC495EEB87994A08F73AB8 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12212B7B62ECCDD28979CA54 /* Constants.swift */; };
2525
9BE1233CADF8C3BFFC734F1D /* ListPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A7998A82BE9D4729E85B13 /* ListPickerView.swift */; };
26+
B0420689B1657699950213A7 /* InMemoryKeychainService.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE9F6BF8DFE197A82CE6A11 /* InMemoryKeychainService.swift */; };
2627
C169507407838BDD4C665CF1 /* KeychainService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAFA7FEDB3F9559D6C71C2C4 /* KeychainService.swift */; };
2728
C1AE79D6748EFE551C7E8F1B /* TaskMenuApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AF69FFA11009E78484C090A /* TaskMenuApp.swift */; };
2829
C1D9C85DFAE57179E9A438A1 /* TaskRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F4164166B2474EB88443EC /* TaskRowView.swift */; };
@@ -71,6 +72,7 @@
7172
A8F4164166B2474EB88443EC /* TaskRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskRowView.swift; sourceTree = "<group>"; };
7273
B01BFC3843C941C89C85576B /* QuickAddView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickAddView.swift; sourceTree = "<group>"; };
7374
BB598B8D34C0A014D7826A63 /* MockURLProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockURLProtocol.swift; sourceTree = "<group>"; };
75+
BFE9F6BF8DFE197A82CE6A11 /* InMemoryKeychainService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InMemoryKeychainService.swift; sourceTree = "<group>"; };
7476
C3377E75BA19027E464EAEB8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
7577
C5360741190B5DF2436BE448 /* GoogleAuthServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoogleAuthServiceTests.swift; sourceTree = "<group>"; };
7678
C79B44669D075555352A0BB4 /* GoogleTasksAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoogleTasksAPITests.swift; sourceTree = "<group>"; };
@@ -176,6 +178,7 @@
176178
C5360741190B5DF2436BE448 /* GoogleAuthServiceTests.swift */,
177179
8FE1F31A1E2F87FD9042F1B4 /* GoogleTasksAPIBehaviorTests.swift */,
178180
C79B44669D075555352A0BB4 /* GoogleTasksAPITests.swift */,
181+
BFE9F6BF8DFE197A82CE6A11 /* InMemoryKeychainService.swift */,
179182
058C00FB539FD9B1D05BCBFD /* KeychainServiceTests.swift */,
180183
BB598B8D34C0A014D7826A63 /* MockURLProtocol.swift */,
181184
6E4CD6808B2587AD2F8F9563 /* TaskItemModelTests.swift */,
@@ -317,6 +320,7 @@
317320
986954B711E286DA6CDC3353 /* GoogleAuthServiceTests.swift in Sources */,
318321
068F90845303418F7954D22E /* GoogleTasksAPIBehaviorTests.swift in Sources */,
319322
69765D5311C642C2DFADB0A0 /* GoogleTasksAPITests.swift in Sources */,
323+
B0420689B1657699950213A7 /* InMemoryKeychainService.swift in Sources */,
320324
3059000A80728B12B6989398 /* KeychainServiceTests.swift in Sources */,
321325
D321CE95DA3DC0D6090CD619 /* MockURLProtocol.swift in Sources */,
322326
0FD718B29CF8183561A6F550 /* TaskItemModelTests.swift in Sources */,

TaskMenu/Services/GoogleAuthService.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import Foundation
44

55
@MainActor
66
final class GoogleAuthService: Sendable {
7-
private let keychain: KeychainService
7+
private let keychain: any KeychainServiceProtocol
88
private let session: URLSession
99

1010
private(set) var accessToken: String?
@@ -20,7 +20,7 @@ final class GoogleAuthService: Sendable {
2020
return Date() >= expiration
2121
}
2222

23-
init(keychain: KeychainService = KeychainService(), session: URLSession = .shared) {
23+
init(keychain: any KeychainServiceProtocol = KeychainService(), session: URLSession = .shared) {
2424
self.keychain = keychain
2525
self.session = session
2626
loadTokens()

TaskMenu/Services/KeychainService.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,16 @@ enum KeychainError: Error, Sendable {
88
case unexpectedData
99
}
1010

11-
struct KeychainService: Sendable {
11+
protocol KeychainServiceProtocol: Sendable {
12+
func save(key: String, data: Data) throws
13+
func save(key: String, string: String) throws
14+
func read(key: String) throws -> Data?
15+
func readString(key: String) throws -> String?
16+
func delete(key: String) throws
17+
func deleteAll() throws
18+
}
19+
20+
struct KeychainService: KeychainServiceProtocol, Sendable {
1221
let service: String
1322

1423
init(service: String = Constants.Keychain.service) {

TaskMenuTests/AppStateBehaviorTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import XCTest
66
/// Uses MockURLProtocol.requestLog to inspect requests (avoids captured var issues with Swift 6 concurrency).
77
@MainActor
88
final class AppStateBehaviorTests: XCTestCase {
9-
private var keychain: KeychainService!
9+
private var keychain: InMemoryKeychainService!
1010
private var state: AppState!
1111

1212
override func setUp() async throws {
1313
MockURLProtocol.reset()
1414

15-
keychain = KeychainService(service: "com.taskmenu.behavior.\(UUID().uuidString)")
15+
keychain = InMemoryKeychainService()
1616
// Pre-load valid tokens so validAccessToken() returns immediately
1717
try? keychain.save(key: Constants.Keychain.accessTokenKey, string: "test-access-token")
1818
try? keychain.save(key: Constants.Keychain.refreshTokenKey, string: "test-refresh-token")

TaskMenuTests/AppStateTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import XCTest
33

44
@MainActor
55
final class AppStateTests: XCTestCase {
6-
private var keychain: KeychainService!
6+
private var keychain: InMemoryKeychainService!
77

88
override func setUp() async throws {
9-
keychain = KeychainService(service: "com.taskmenu.statetest.\(UUID().uuidString)")
9+
keychain = InMemoryKeychainService()
1010
}
1111

1212
override func tearDown() async throws {

TaskMenuTests/GoogleAuthServiceTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import XCTest
33

44
@MainActor
55
final class GoogleAuthServiceTests: XCTestCase {
6-
private var keychain: KeychainService!
6+
private var keychain: InMemoryKeychainService!
77

88
override func setUp() async throws {
9-
keychain = KeychainService(service: "com.taskmenu.authtest.\(UUID().uuidString)")
9+
keychain = InMemoryKeychainService()
1010
}
1111

1212
override func tearDown() async throws {

TaskMenuTests/GoogleTasksAPIBehaviorTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import XCTest
66
/// Uses MockURLProtocol.requestLog to inspect requests (avoids captured var issues with Swift 6 concurrency).
77
@MainActor
88
final class GoogleTasksAPIBehaviorTests: XCTestCase {
9-
private var keychain: KeychainService!
9+
private var keychain: InMemoryKeychainService!
1010
private var api: GoogleTasksAPI!
1111

1212
override func setUp() async throws {
1313
MockURLProtocol.reset()
1414

15-
keychain = KeychainService(service: "com.taskmenu.apitest.\(UUID().uuidString)")
15+
keychain = InMemoryKeychainService()
1616
// Pre-load valid tokens so validAccessToken() returns without refreshing
1717
try? keychain.save(key: Constants.Keychain.accessTokenKey, string: "test-token")
1818
try? keychain.save(key: Constants.Keychain.refreshTokenKey, string: "test-refresh")
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
@testable import TaskMenu
2+
import Foundation
3+
4+
/// In-memory keychain replacement for tests — avoids macOS Keychain prompts.
5+
final class InMemoryKeychainService: KeychainServiceProtocol, @unchecked Sendable {
6+
private var storage: [String: Data] = [:]
7+
8+
func save(key: String, data: Data) throws {
9+
storage[key] = data
10+
}
11+
12+
func save(key: String, string: String) throws {
13+
guard let data = string.data(using: .utf8) else {
14+
throw KeychainError.unexpectedData
15+
}
16+
storage[key] = data
17+
}
18+
19+
func read(key: String) throws -> Data? {
20+
storage[key]
21+
}
22+
23+
func readString(key: String) throws -> String? {
24+
guard let data = storage[key] else { return nil }
25+
return String(data: data, encoding: .utf8)
26+
}
27+
28+
func delete(key: String) throws {
29+
storage.removeValue(forKey: key)
30+
}
31+
32+
func deleteAll() throws {
33+
storage.removeAll()
34+
}
35+
}

TaskMenuTests/KeychainServiceTests.swift

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
import XCTest
22
@testable import TaskMenu
33

4+
/// Tests KeychainServiceProtocol contract using InMemoryKeychainService.
5+
/// Avoids real macOS Keychain access (and password prompts) during tests.
46
final class KeychainServiceTests: XCTestCase {
5-
private var keychain: KeychainService!
7+
private var keychain: InMemoryKeychainService!
68

79
override func setUp() {
810
super.setUp()
9-
keychain = KeychainService(service: "com.taskmenu.test.\(UUID().uuidString)")
10-
}
11-
12-
override func tearDown() {
13-
try? keychain.delete(key: "token")
14-
try? keychain.delete(key: "data")
15-
super.tearDown()
11+
keychain = InMemoryKeychainService()
1612
}
1713

1814
func testSaveAndReadString() throws {

project.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,3 @@ targets:
5050
CODE_SIGN_IDENTITY: "47081BEFF0F575643E99369B44CBAC87BBCC85E6" # Apple Development: Jia Tan
5151
CODE_SIGN_STYLE: Manual
5252
DEVELOPMENT_TEAM: ZW5U6862Q8
53-
TEST_HOST: "$(BUILT_PRODUCTS_DIR)/TaskMenu.app/Contents/MacOS/TaskMenu"
54-
BUNDLE_LOADER: "$(TEST_HOST)"

0 commit comments

Comments
 (0)