Skip to content

test: pre-cache symkey for every account in setup (op-4b7) - #8

Merged
jeremyschlatter merged 1 commit into
mainfrom
polecat/jasper-op-4b7
Apr 13, 2026
Merged

test: pre-cache symkey for every account in setup (op-4b7)#8
jeremyschlatter merged 1 commit into
mainfrom
polecat/jasper-op-4b7

Conversation

@jeremyschlatter-intern

Copy link
Copy Markdown
Collaborator

Summary

Fixes a flake in TestE2E_Migrated where parallel subtests surfaced errors like Error: no account configured (run 'opcli signin' first) or Error: account not found: my, despite those credentials having been stored in setup.

Un-namespaced resource (the root cause)

All of TestE2E_Migrated's subtests share one env (one tmpDir, one keychainService = \"opcli-test-<tmpDir>\", one shared keychain credential entry). The keychain entry is namespaced per-env, but the entry itself is concurrently written to by parallel subtests — that's the un-namespaced concurrency pattern.

The concurrent writes come from SetCachedSymKey. Whenever a subtest is the first to touch an account, newAccountKeychain derives the symmetric key (PBKDF2, expensive) and writes the result back into the credential store so subsequent invocations can skip the derivation. Under the migrated path every subprocess is slow (per-invocation v5→v61 in-memory migration), so one writer overlaps with many concurrent readers. Against a legacy ACL-protected Apple Keychain item, those overlaps surface as transient OSStatus values — verified via stress-repro CI traces:

  • -25300 errSecItemNotFound on keychainGet (entry briefly "not found" during a peer's write)
  • -67701 on keychainGet (under write contention)
  • -25299 errSecDuplicateItem on keychainSet

Any non-zero status from the credential-store load propagates up as Error: account not found: <sh> (for OP_ACCOUNT branches) or Error: no account configured (for default-account branches) — exactly the flake symptom.

Fix

setupTestEnv already issued one verify read (op://Private/Test Login/password), which primed the my account's symkey. Iterate every testDB.Accounts and verify-read a field from the first readable vault item we find, so work (and any future account) is primed too. After this, the parallel subtest workload against the shared keychain entry is read-only — there is nothing for the reads to race against, and the OS-level transient-error surface disappears.

This is the minimal targeted fix the bead called for — no refactor of the test framework or of keychainSet itself. Related: op-xev.3 (PR #6) atomized writes; this PR eliminates writes during the stress window.

Test plan

  • Added temporary stress job (go test -run TestE2E_Migrated -count=100 ./...) and watched it pass on CI (3min, 0 failures). Before the fix, -count=30 and -count=50 stress runs failed within a few iterations. Temporary stress job removed from this PR before review.
  • make test in normal CI passes.

🤖 Generated with Claude Code

Fixes flake: TestE2E_Migrated's parallel subtests share one env, and
hence one keychain credential entry (namespaced per-env at
keychainService = "opcli-test-<tmpDir>"). If a subtest is the first to
touch an account, the CLI derives that account's symmetric key and
writes it back to the entry via SetCachedSymKey. Under the migrated
path each subprocess is slow (per-invocation v5->v61 in-memory
migration) so many parallel subtests overlap with that one writer; the
concurrent read/write pattern on the legacy ACL-protected keychain item
surfaces transient OSStatus values. Observed in stress repro:

  - -25300 errSecItemNotFound on keychainGet (briefly "not found")
  - -67701 on keychainGet (under write contention)
  - -25299 errSecDuplicateItem on keychainSet

Any of those propagates as `Error: account not found: <sh>` or
`Error: no account configured` from the top-level command, which is
what the flake reports.

setupTestEnv already issued one verify read
(op://Private/Test Login/password), which primed the 'my' account's
symkey. Extend it to iterate every testDB.Accounts entry and
verify-read a field from the first readable vault item we find, so
'work' (and any future account) is also primed before subtests start.
Subtests then hit the symkey cache and the keychain workload is
read-only against the shared entry — nothing to race against.

Verified via `go test -run TestE2E_Migrated -count=100 ./...` in CI
(3min, 0 failures). Pre-fix the same stress repro reliably failed
within 30 iterations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@jeremyschlatter
jeremyschlatter merged commit aab41a6 into main Apr 13, 2026
1 check passed
@jeremyschlatter
jeremyschlatter deleted the polecat/jasper-op-4b7 branch April 13, 2026 23:14
@jeremyschlatter-intern
jeremyschlatter-intern restored the polecat/jasper-op-4b7 branch April 13, 2026 23:42
@jeremyschlatter
jeremyschlatter deleted the polecat/jasper-op-4b7 branch July 2, 2026 19:55
@jeremyschlatter-intern
jeremyschlatter-intern restored the polecat/jasper-op-4b7 branch July 11, 2026 11:12
@jeremyschlatter-intern
jeremyschlatter-intern deleted the polecat/jasper-op-4b7 branch July 11, 2026 11:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants