Skip to content

fix(auth-tests): serialize interop test TFMs to fix parallel credential-vault race (#1047)#1320

Merged
joshsmithxrm merged 1 commit into
mainfrom
claude/friendly-cray-c701ed
Jul 11, 2026
Merged

fix(auth-tests): serialize interop test TFMs to fix parallel credential-vault race (#1047)#1320
joshsmithxrm merged 1 commit into
mainfrom
claude/friendly-cray-c701ed

Conversation

@joshsmithxrm

@joshsmithxrm joshsmithxrm commented Jul 11, 2026

Copy link
Copy Markdown
Owner

Summary

The Credential Store Interop workflow fails intermittently on the windows-latest leg (and, less often, ubuntu-latest) whenever a PR touches its path filter (Directory.Packages.props, Directory.Build.props, src/PPDS.Auth/**). This makes unrelated dependency bumps get a spurious red X.

This serializes the per-TFM test hosts so the interop round-trip stops racing on the shared OS credential vault.

Fixes #1047

Root cause

NativeCredentialStoreInteropTests round-trips a secret through the real per-user OS credential vault (Windows Credential Manager / macOS Keychain / libsecret). The test project multi-targets net8.0;net9.0;net10.0.

The .NET 9+ SDK runs dotnet test on a multi-TFM project with all target frameworks in parallel by default (introduced in .NET 9 Preview 2). This workflow installs the 10.0.x SDK, so it silently opted into that behavior. Three TFM host processes then hit the same per-user vault concurrently. NativeCredentialStore.GetAsync/RemoveAsync resolve an entry via WindowsCredentialManager.GetEnumerateCredEnumerate(null, AllCredentials) — a full-vault enumeration filtered in-process. A CredRead/enumeration can observe null for a just-written entry while a concurrent CredWrite from another TFM host is in flight, so Windows_RoundTripsSecret fails at retrieved.Should().NotBeNull().

Evidence from run 29164790261 (windows-latest), where two TFM hosts finished ~milliseconds apart with interleaved output — one passing, one failing at NativeCredentialStoreInteropTests.cs:53:

Windows_RoundTripsSecret [FAIL]   ...NativeCredentialStoreInteropTests.cs:line 53
Test Run Failed. Total tests: 3  Failed: 1  Skipped: 2

Not the skip reporting

The MacOS_/Linux_RoundTripsSecret [SKIP] — Exception of type 'Xunit.SkipException' was thrown lines are a red herring: in the same logs those tests are correctly tallied as Skipped, and TFM hosts that don't hit the race pass green (Passed: 1, Skipped: 2). Xunit.SkippableFact is working. The only red is the parallel-vault race on Windows_RoundTripsSecret — exactly the bug #1047 documents.

Why not "namespace the appId per TFM" (issue's option 1)

The appId is already globally unique per test instance (ppds-interop-test-{Guid.NewGuid():N}). The contention is not a key collision — it's at the vault/enumeration level (CredEnumerate over the whole vault, plus a shared _manifest entry every write updates). Adding the TFM to an already-unique key would not reduce concurrent vault mutations, so it would not fix the race.

Fix

Set <TestTfmsInParallel>false</TestTfmsInParallel> on PPDS.Auth.IntegrationTests.csproj — the documented, first-class opt-out for the SDK's parallel multi-TFM behavior. The three TFM hosts now run one at a time, eliminating concurrent access to the shared vault. This fixes the Windows Credential Manager race and the identical libsecret race noted on the issue.

Placing it in the csproj (rather than the workflow command line) makes the constraint travel with the project across CI, local dotnet test, and IDE runs — so no future invocation can silently reintroduce the race.

Coverage is unchanged: every TFM still runs its platform-native round-trip; they just run sequentially.

Verification

  • dotnet build PPDS.sln — 0 errors.
  • dotnet test PPDS.sln --filter "Category!=Integration" — 0 failures (full unit suite via pre-commit hook).
  • Interop tests run the workflow's way on Windows (SDK 10.0.109, parallel-by-default regime):
    • All 3 TFM hosts run sequentially (74/64/52 ms, no interleaving), exit 0.
    • Windows_RoundTripsSecret runs and passes; MacOS_/Linux_RoundTripsSecret report Skipped, not Failed.
    • dotnet build ... -getProperty:TestTfmsInParallelfalse, confirming the property is honored.

Scope

Independent of #1305 (plugins-extract); this only changes the interop test project's build property. A separate latent concern — that two concurrent ppds processes could hit the same full-vault enumeration race in product code — is out of scope for this test-hardening fix.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests
    • Updated integration test execution to run target frameworks sequentially, improving reliability for credential store interoperability tests.

…credential-vault race

NativeCredentialStoreInteropTests round-trips a secret through the real
per-user OS credential vault. The .NET 9+ SDK runs `dotnet test` on this
multi-TFM project (net8.0;net9.0;net10.0) with all frameworks in parallel
by default, so three host processes concurrently mutate and enumerate the
same vault. Get/Remove enumerate the entire vault (CredEnumerate) before
filtering, so a CredRead can observe null while another TFM's CredWrite is
in flight — Windows_RoundTripsSecret then fails "expected retrieved not to
be null" (issue #1047; the Linux libsecret variant races identically).

Set TestTfmsInParallel=false on the test project to serialize the per-TFM
hosts. Coverage is unchanged: every TFM still runs its platform-native
round-trip, just one at a time. Per-appId GUIDs cannot fix this because the
contention is at the vault/enumeration level, not on the key.

Fixes #1047

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bd89cfc0-20de-400d-8db7-915866f8070d

📥 Commits

Reviewing files that changed from the base of the PR and between 6f4bc01 and d006114.

📒 Files selected for processing (1)
  • tests/PPDS.Auth.IntegrationTests/PPDS.Auth.IntegrationTests.csproj

📝 Walkthrough

Walkthrough

The integration test project disables parallel execution across target frameworks and documents that sequential execution prevents races in native credential store interop tests.

Changes

Test execution configuration

Layer / File(s) Summary
Disable parallel TFM execution
tests/PPDS.Auth.IntegrationTests/PPDS.Auth.IntegrationTests.csproj
Sets TestTfmsInParallel to false and documents the credential store race caused by concurrent multi-TFM test execution.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Possibly related issues

  • #1047 — Disabling parallel multi-TFM test execution directly mitigates the credential store race described by the issue.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/friendly-cray-c701ed

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request disables parallel execution of target framework (TFM) test hosts in the integration tests project by setting TestTfmsInParallel to false. This change prevents race conditions when multiple TFMs concurrently access and mutate the shared OS credential vault during tests. There are no review comments, so I have no feedback to provide.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@joshsmithxrm joshsmithxrm marked this pull request as ready for review July 11, 2026 19:58
@joshsmithxrm joshsmithxrm merged commit 3c01dc2 into main Jul 11, 2026
25 checks passed
@joshsmithxrm joshsmithxrm deleted the claude/friendly-cray-c701ed branch July 11, 2026 19:58
@github-project-automation github-project-automation Bot moved this from Todo to Done in PPDS Roadmap Jul 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant