5.8 KiB
Improve Test Coverage Plan
Motivation
The passKit codebase has ~100 test methods but critical components that will be heavily refactored (for multi-store support and other changes) have little or no test coverage. Adding regression tests now prevents silent breakage during future work.
This is standalone — it should be done before any other refactoring.
Current Test Coverage
Well-tested areas
- Password parsing (
Password,Parser,AdditionField, OTP,TokenBuilder) — ~40 tests PGPAgent— 8 tests covering multiple key types, error cases, passphrase handlingPasswordGenerator— 8 testsGitRepository— 8 tests (uses real temp git repos on disk)GitCredential— 6 tests (SSH test is skipped/"failed in CI")PasswordEntityCore Data operations — 6 tests (uses in-memory store viaCoreDataTestCase)KeyFileManager— 7 testsQRKeyScanner— 6 tests- String/Array extensions — 6 tests
Critical gaps (zero tests)
| Component | Notes |
|---|---|
PasswordStore (36 methods) |
Only 1 integration test that clones from GitHub. No unit tests for pull, push, add, delete, edit, decrypt, encrypt, reset, erase, eraseStoreData, deleteCoreData, fetchPasswordEntityCoreData, initPasswordEntityCoreData. |
AppKeychain |
Zero tests. Only exercised indirectly via DictBasedKeychain mock. |
PersistenceController / Core Data stack |
Only the isUnitTest: true path is exercised. No tests for reinitializePersistentStore, deletePersistentStore, error recovery. |
Services (PasswordDecryptor, PasswordEncryptor, PasswordManager, PasswordNavigationDataSource) |
Zero tests. Core business logic that ties PasswordStore + PGPAgent together. |
| All view controllers (28+) | Zero tests. No UI test target exists. |
| AutoFill / Share / Shortcuts extensions | Zero tests. No test targets for extensions. |
PasscodeLock |
Zero tests. Security-critical. |
Test infrastructure that already exists
CoreDataTestCase— base class with in-memoryPersistenceController(reusable)DictBasedKeychain— in-memoryKeyStoremock (reusable)TestPGPKeys— PGP key fixtures for RSA2048, RSA4096, ED25519, NISTP384, multi-key sets
Implementation
1. Fixture password-store repo
A pre-built bare git repo checked into passKitTests/Fixtures/password-store.git/. Contains:
- A
.gpg-idfile with test key ID(s) - Several
.gpgfiles encrypted with the test keys fromTestPGPKeys(at various directory depths) - A subdirectory structure to exercise the BFS walk (nested folders, empty dirs)
- A git history with at least a couple of commits
Since it's a bare repo, its contents (HEAD, objects/, refs/, etc.) are just regular files from the outer repo's perspective — no submodule issues.
Xcode project setup: The fixture directory must be added to the Xcode project as a folder reference (blue folder) in the passKitTests target, and with "Build Rules" set to "Apply Once to Folder", so it's included in the "Copy Bundle Resources" build phase. In Xcode: drag the Fixtures/ directory into the passKitTests group → select "Create folder references" → check only the passKitTests target. Without this, the files won't be accessible from the test bundle at runtime.
To update the fixture, pull from origin (already set to https://github.com/mssun/passforios-password-store.git) or replace with any local bare repo:
# Update from origin
cd passKitTests/Fixtures/password-store.git
git fetch origin
git update-ref refs/heads/master origin/master
# Or replace with a custom local repo
git clone --bare /path/to/local/repo passKitTests/Fixtures/password-store.git
2. PasswordStore unit tests (highest priority)
- Test
initPasswordEntityCoreData: Clone the fixture repo → verify correctPasswordEntitytree in Core Data (names, paths, directories, parent-child relationships). - Test
deleteCoreData: Populate, then delete, verify empty. - Test
eraseStoreData: Verify repo directory deleted, Core Data cleared, git handle nil'd. - Test
erase: Verify full cleanup (keychain, defaults, passcode, PGP state). - Test
fetchPasswordEntityCoreData: Verify fetch with parent filter, withDir filter. - Test encrypt → save → decrypt round-trip: Using
DictBasedKeychain+ test PGP keys + local repo. - Test
add/delete/edit: Verify filesystem + Core Data + git commit. - Test
reset: Verify Core Data rebuilt to match filesystem after git reset.
3. PasswordEntity relationship tests
Extend PasswordEntityTest (already uses CoreDataTestCase):
- Test
initPasswordEntityCoreDataBFS walk: Create a temp directory tree with.gpgfiles, call the static method, verify entity tree matches filesystem. - Test that
.gpgextension is stripped from names but non-.gpgfiles keep their names. - Test hidden files are skipped.
- Test empty directories.
4. AppKeychain tests
Basic tests against the real Keychain API (or a test wrapper):
- Test
add/get/removeContentround-trip. - Test
removeAllContent. - Test
contains. - Test
removeAllContent(withPrefix:)— this method already exists and will be useful for per-store cleanup.
5. PersistenceController tests
- Test
reinitializePersistentStore— verify existing data is gone after reinit. - Test model loading — verify the
.momdloads correctly.
Implementation Order
| Step | Description |
|---|---|
| 1 | Fixture password-store bare repo |
| 2 | PasswordStore unit tests (uses fixture from step 1) |
| 3 | PasswordEntity BFS walk + relationship tests |
| 4 | AppKeychain tests |
| 5 | PersistenceController tests |
Steps 2–5 can be done in parallel once step 1 is complete. Steps 3–5 are also independent of step 1.