From 4c21ab99adc9174f8fa17f80cdce87700b0664e7 Mon Sep 17 00:00:00 2001 From: Lysann Tranvouez Date: Mon, 9 Mar 2026 13:36:19 +0100 Subject: [PATCH] add tests for AppKeychain --- pass.xcodeproj/project.pbxproj | 4 + passKitTests/Helpers/AppKeychainTest.swift | 101 +++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 passKitTests/Helpers/AppKeychainTest.swift diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index e3e63e8..3b42e4b 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -114,6 +114,7 @@ 5F9D7B0D27AF6F7500A8AB22 /* CryptoTokenKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F9D7B0C27AF6F7300A8AB22 /* CryptoTokenKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 5F9D7B0E27AF6FCA00A8AB22 /* CryptoTokenKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F9D7B0C27AF6F7300A8AB22 /* CryptoTokenKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 5F9D7B0F27AF6FD200A8AB22 /* CryptoTokenKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F9D7B0C27AF6F7300A8AB22 /* CryptoTokenKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + 8A4716692F5EF56900C7A64D /* AppKeychainTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4716682F5EF56900C7A64D /* AppKeychainTest.swift */; }; 8AD8EBF32F5E2723007475AB /* Fixtures in Resources */ = {isa = PBXBuildFile; fileRef = 8AD8EBF22F5E268D007475AB /* Fixtures */; }; 9A1D1CE526E5D1CE0052028E /* OneTimePassword in Frameworks */ = {isa = PBXBuildFile; productRef = 9A1D1CE426E5D1CE0052028E /* OneTimePassword */; }; 9A1D1CE726E5D2230052028E /* OneTimePassword in Frameworks */ = {isa = PBXBuildFile; productRef = 9A1D1CE626E5D2230052028E /* OneTimePassword */; }; @@ -423,6 +424,7 @@ 30F6C1B327664C7200BE5AB2 /* SVProgressHUD.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = SVProgressHUD.xcframework; path = Carthage/Build/SVProgressHUD.xcframework; sourceTree = ""; }; 30FD2F77214D9E0E005E0A92 /* ParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParserTest.swift; sourceTree = ""; }; 5F9D7B0C27AF6F7300A8AB22 /* CryptoTokenKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CryptoTokenKit.framework; path = System/Library/Frameworks/CryptoTokenKit.framework; sourceTree = SDKROOT; }; + 8A4716682F5EF56900C7A64D /* AppKeychainTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppKeychainTest.swift; sourceTree = ""; }; 8AD8EBF22F5E268D007475AB /* Fixtures */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Fixtures; sourceTree = ""; }; 9A1EF0B324C50DD80074FEAC /* passBeta.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = passBeta.entitlements; sourceTree = ""; }; 9A1EF0B424C50E780074FEAC /* passBetaAutoFillExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = passBetaAutoFillExtension.entitlements; sourceTree = ""; }; @@ -626,6 +628,7 @@ 301F6464216164670071A4CE /* Helpers */ = { isa = PBXGroup; children = ( + 8A4716682F5EF56900C7A64D /* AppKeychainTest.swift */, 3032328922C9FBA2009EBD9C /* KeyFileManagerTest.swift */, ); path = Helpers; @@ -1643,6 +1646,7 @@ A2699ACF24027D9500F36323 /* PasswordTableEntryTest.swift in Sources */, 30FD2F78214D9E0E005E0A92 /* ParserTest.swift in Sources */, A2AA934622DE3A8000D79A00 /* PGPAgentTest.swift in Sources */, + 8A4716692F5EF56900C7A64D /* AppKeychainTest.swift in Sources */, 30695E2524FAEF2600C9D46E /* GitCredentialTest.swift in Sources */, 30BAC8C622E3BAAF00438475 /* TestBase.swift in Sources */, 30B04860209A5141001013CA /* PasswordTest.swift in Sources */, diff --git a/passKitTests/Helpers/AppKeychainTest.swift b/passKitTests/Helpers/AppKeychainTest.swift new file mode 100644 index 0000000..9b420fc --- /dev/null +++ b/passKitTests/Helpers/AppKeychainTest.swift @@ -0,0 +1,101 @@ +// +// AppKeychainTest.swift +// passKitTests +// +// Created by Lysann Tranvouez on 9/3/26. +// Copyright © 2026 Bob Sun. All rights reserved. +// + +import XCTest + +@testable import passKit + +final class AppKeychainTest: XCTestCase { + private let keychain = AppKeychain.shared + private let testPrefix = "test.AppKeychainTest." + + override func tearDown() { + super.tearDown() + keychain.removeAllContent(withPrefix: testPrefix) + } + + private func key(_ name: String) -> String { + "\(testPrefix)\(name)" + } + + // MARK: - Basic round-trip + + func testAddAndGet() { + keychain.add(string: "hello", for: key("addGet")) + + XCTAssertEqual(keychain.get(for: key("addGet")), "hello") + } + + func testGetMissingKeyReturnsNil() { + XCTAssertNil(keychain.get(for: key("nonexistent"))) + } + + func testOverwriteValue() { + keychain.add(string: "first", for: key("overwrite")) + keychain.add(string: "second", for: key("overwrite")) + + XCTAssertEqual(keychain.get(for: key("overwrite")), "second") + } + + func testAddNilRemovesValue() { + keychain.add(string: "value", for: key("addNil")) + keychain.add(string: nil, for: key("addNil")) + + XCTAssertNil(keychain.get(for: key("addNil"))) + XCTAssertFalse(keychain.contains(key: key("addNil"))) + } + + // MARK: - contains + + func testContainsReturnsTrueForExistingKey() { + keychain.add(string: "value", for: key("exists")) + + XCTAssertTrue(keychain.contains(key: key("exists"))) + } + + func testContainsReturnsFalseForMissingKey() { + XCTAssertFalse(keychain.contains(key: key("missing"))) + } + + // MARK: - removeContent + + func testRemoveContent() { + keychain.add(string: "value", for: key("remove")) + keychain.removeContent(for: key("remove")) + + XCTAssertNil(keychain.get(for: key("remove"))) + XCTAssertFalse(keychain.contains(key: key("remove"))) + } + + func testRemoveContentForMissingKeyDoesNotThrow() { + keychain.removeContent(for: key("neverExisted")) + // No assertion needed — just verifying it doesn't crash + } + + // MARK: - removeAllContent(withPrefix:) + + func testRemoveAllContentWithPrefix() { + keychain.add(string: "1", for: key("prefixA.one")) + keychain.add(string: "2", for: key("prefixA.two")) + keychain.add(string: "3", for: key("prefixB.one")) + + keychain.removeAllContent(withPrefix: key("prefixA")) + + XCTAssertNil(keychain.get(for: key("prefixA.one"))) + XCTAssertNil(keychain.get(for: key("prefixA.two"))) + XCTAssertEqual(keychain.get(for: key("prefixB.one")), "3") + } + + func testRemoveAllContentWithPrefixNoMatches() { + keychain.add(string: "value", for: key("survivor")) + + keychain.removeAllContent(withPrefix: key("noMatch")) + + XCTAssertEqual(keychain.get(for: key("survivor")), "value") + } +}