Skip to content

Make the PR-finalize symlink tests self-sufficient and drop a localized assertion - #37039

Merged
kubaflo merged 4 commits into
mainfrom
PureWeen/pr-finalize-testquality
Aug 5, 2026
Merged

Make the PR-finalize symlink tests self-sufficient and drop a localized assertion#37039
kubaflo merged 4 commits into
mainfrom
PureWeen/pr-finalize-testquality

Conversation

@PureWeen

@PureWeen PureWeen commented Aug 3, 2026

Copy link
Copy Markdown
Member

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Description of Change

Follow-up to #36919. This PR closes the remaining test-contract gaps around New-ExclusiveTempFile; production behavior is unchanged.

Prove New-Item actually attempts the planted symlink

The first version asserted that the deterministic name generator advanced from forced0 to forced1. @kubaflo correctly demonstrated that this did not prove New-Item saw forced0: a mutant could consume that name, skip before New-Item, and create forced1. Both symlink tests still passed and relied on a separate test as a backstop.

The tests now install a call-through Pester Mock New-Item. It records each attempted -Path while invoking the real cmdlet, preserving the filesystem behavior under test. Each symlink test asserts:

  1. The planted link is the first path passed to New-Item.
  2. The next deterministic candidate is attempted second.
  3. The planted target remains untouched (or remains missing for a dangling link).
  4. The returned file is the second candidate.

The exact reported mutant now fails both symlink tests directly:

$candidate = Join-Path $baseDir "$Prefix-$(& $NameGenerator).md"
if ($attempt -eq 0) { continue } # consume forced0 without calling New-Item

Result: 37 passed, 3 failed, including both symlink tests. The real implementation remains 40/40.

Assert a culture-invariant exception contract

The missing-directory test previously matched the localized text Could not find a part of the path. It now captures the exception and asserts System.IO.DirectoryNotFoundException, which is both culture-invariant and more precise about the contract being tested.

Document the actual New-Item -Path invariant

New-Item has no -LiteralPath parameter, so replacing -Path would throw ParameterBindingException. For this invocation, the helper supplies a complete leaf path and does not pass -Name; that path is treated literally and cannot glob onto an existing file.

A three-reviewer adversarial pass found 2/3 consensus that the original comment stated this too broadly: New-Item -Path ... -Name ... can expand wildcards across matching directories. The comment now scopes the guarantee to this exact invocation and explicitly warns not to infer a general no-globbing guarantee.

Issues Fixed

Follow-up to #36919; no separate issue.

Testing

  • Apply-PRFinalize.Tests.ps1: 40/40
  • Apply-PRFinalize.Tests.ps1, Review-PR.Tests.ps1, and Post-AISummaryComment.Tests.ps1: 102/102
  • Both modified PowerShell scripts parse cleanly.
  • The skip-first mutant fails both symlink tests directly.

maui-pr is path-filtered for script-only changes, so the Pester suites are the meaningful validation for this PR.

Follow-up to #36919, which merged with three optional non-blocking review notes
outstanding. Two were real and are fixed here; the third turns out not to be
applicable, so it is documented in place rather than acted on.

1. The symlink tests leaned on backstops (kubaflo)

   He was right, and I reproduced it. A mutant that ignores the -NameGenerator
   seam and picks its own random name PASSED both symlink tests — only the
   exhaustion/attempt-count tests caught it. That means the planted symlink was
   never in the mutant's path, so the skip logic went unexercised: the tests
   guarding the symlink behaviour could not, on their own, tell a skipping
   implementation from one that never looked.

   Both tests now assert the generator advanced past the planted index and that
   the returned path is the *next* candidate. The seam-ignoring mutant now fails
   both symlink tests directly, and the original write-through mutant still
   fails them too.

2. An assertion on a localized .NET message (kubaflo)

   'Could not find a part of the path' is a localized resource string and would
   fail on a non-en-US agent. Now asserts the exception type
   (DirectoryNotFoundException), which is culture-invariant. Practical risk was
   low since AzDO agents are en-US, but the type assertion is also more precise
   about what the test actually cares about.

3. New-Item -Path -> -LiteralPath: not applicable

   Suggested by the Copilot reviewer (filed low-confidence) and passed through by
   kubaflo as cosmetic. It is neither: New-Item has no -LiteralPath parameter, so
   binding it throws ParameterBindingException. The premise is also wrong --
   -Path does not glob when creating. Verified both: New-Item -Path 't*rget.md'
   next to an existing target.md created a file literally named 't*rget.md' and
   left target.md untouched. Added a comment recording this so it stops being
   re-raised.

Tests: still 40 in Apply-PRFinalize.Tests.ps1 (strengthened, not added to);
102/102 across the three affected suites. Scripts parse clean and the
stubbed-gh end-to-end run still reaches --body-file with the right content and
cleans up.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 40f61a36-c005-42d8-af25-e1228194d196
Copilot AI review requested due to automatic review settings August 3, 2026 13:34
@PureWeen
PureWeen temporarily deployed to copilot-pat-pool August 3, 2026 13:34 — with GitHub Actions Inactive
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 37039

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 37039"

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
1 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR improves the reliability and portability of the PR-finalize temp-file hardening tests, and documents a prior review suggestion that is not applicable to New-Item.

Changes:

  • Strengthen the symlink-collision Pester tests to prove the -NameGenerator seam is actually exercised (and the planted symlink candidate is reached and skipped).
  • Replace a localized exception message assertion with a culture-invariant exception type assertion (DirectoryNotFoundException).
  • Add an in-code note explaining why New-Item -Path is used (and why -LiteralPath is not applicable).
Show a summary per file
File Description
.github/scripts/Apply-PRFinalize.Tests.ps1 Makes symlink tests self-sufficient by asserting the generator advanced past the planted candidate; switches to exception type assertion to avoid localization fragility.
.github/scripts/apply-pr-finalize.ps1 Adds a clarifying comment documenting why New-Item uses -Path (no -LiteralPath parameter) and why wildcard concerns don’t apply here.

Copilot's findings

  • Files reviewed: 2/2 changed files
  • Comments generated: 0

@PureWeen
PureWeen temporarily deployed to copilot-pat-pool August 3, 2026 13:38 — with GitHub Actions Inactive
@PureWeen
PureWeen temporarily deployed to copilot-pat-pool August 3, 2026 13:39 — with GitHub Actions Inactive
@github-actions github-actions Bot added the area-infrastructure CI, Maestro / Coherency, upstream dependencies/versions label Aug 3, 2026
@PureWeen
PureWeen temporarily deployed to copilot-pat-pool August 3, 2026 13:39 — with GitHub Actions Inactive

@kubaflo kubaflo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@PureWeen — AI-generated adversarial review: independent Claude Opus 5, GPT-5.6 Sol, and Gemini 3.1 Pro passes; cross-pollination; MAUI expert verification; final GPT-5.6 Terra arbitration.

NEEDS_DISCUSSION

At exact head 9ee6132a0a, the production change is comment-only, the typed exception assertion is sound, and the New-Item -Path explanation is correct. The new assertions genuinely kill the seam-ignoring mutant described in the PR.

One low-severity test-contract gap remains inline: the assertions prove generator consumption and the returned second name, but not that New-Item attempted the planted symlink. A skip-first mutant still passes both symlink tests and is caught only by a separate test, so the stated self-sufficiency is not yet complete.

Prior review reconciliation: the #36919 production/security fixes remain intact; this refines only its optional self-sufficiency follow-up. No prior current-PR finding is duplicated.

CI: exact-head Pester (.github/scripts) is green (1499/1499 overall; 40/40 in the changed suite). Required maui-pr was path-filtered/skipped and Build Analysis remains pending, so repository review rules cap confidence at low and prohibit LGTM despite the clean code assessment.

Title/description: the narrower mutation evidence is accurate, but “self-sufficient” overstates what the changed tests directly prove. Confidence: low for merge safety; high in the reproduced inline finding.

# name also passes — the planted symlink would never have been in its way, so the
# skip logic goes unexercised and the test leans on the exhaustion test to catch
# seam-ignoring. Asserting the generator advanced makes this test self-sufficient.
$script:ForcedIndex | Should -BeGreaterThan 1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ These assertions prove generator consumption and the returned second name, not that New-Item was attempted against $planted. I reproduced a mutant that generates forced0, continues before New-Item, then creates forced1: both symlink tests pass, and only the separate non-collision test fails (39/40). The tests therefore still rely on another backstop. Use a call-through Mock New-Item/spy to record attempted -Path values and assert the first is $planted (and dangle0), or narrow the self-sufficiency claim.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in f845978. You were right: generator consumption did not prove New-Item saw the planted path. Both symlink tests now install a call-through Pester Mock New-Item that records attempted paths while invoking the real cmdlet, then assert the planted link was attempt 1 and the next candidate was attempt 2. I replayed your exact skip-first mutant (forced0; continue before New-Item; create forced1): both symlink tests now fail directly (the real suite remains 40/40; all related suites 102/102).

Copilot AI added 2 commits August 5, 2026 10:22
The existing assertions proved that the name generator advanced past the
planted candidate, but not that New-Item actually attempted that candidate.
A mutant could consume forced0, continue before New-Item, then create forced1;
both symlink tests still passed and relied on an unrelated test as a backstop.

Use a call-through Pester mock for New-Item that records each attempted path
while preserving the real filesystem behavior. Both symlink tests now assert
that the planted link is the first path passed to New-Item and that the next
candidate is attempted second.

Replayed the reported skip-first mutant: both symlink tests now fail directly.
The real suite remains 40/40, and all three related suites pass 102/102.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 40f61a36-c005-42d8-af25-e1228194d196
Copilot AI review requested due to automatic review settings August 5, 2026 15:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot's findings

  • Files reviewed: 2/2 changed files
  • Comments generated: 0 new

The adversarial review found 2/3 consensus that the existing comment was too
broad. New-Item treats a complete leaf -Path literally for this call, but its
-Path parameter can expand wildcards when combined with -Name.

Document the actual invariant: this invocation passes a complete leaf path and
no -Name, so it cannot resolve onto an existing file. Also warn not to infer a
general no-globbing guarantee from the call.

Tests remain 102/102 across the three related suites.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 40f61a36-c005-42d8-af25-e1228194d196
Copilot AI review requested due to automatic review settings August 5, 2026 15:33

@PureWeen PureWeen left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Adversarial review — no remaining findings

Three independent reviewers evaluated the authoritative two-file diff, full source context, MAUI regression/testing rules, and the prior review thread.

  • Prior finding: The call-through New-Item spy now proves the planted symlink path is actually attempted. The exact skip-first mutant fails both symlink tests directly. ✅
  • Consensus finding: One reviewer flagged the New-Item -Path comment as broader than the verified invocation. After the required dispute round, this reached 2/3 consensus and was fixed in 91bc8d1e42f by scoping the guarantee to a complete leaf path without -Name. ✅
  • Coverage: The changed tests exercise existing and dangling symlinks, exact attempted paths, localized-error portability, exhaustion, attempt count, and fallback behavior.

No unresolved correctness, security, regression, or test-quality findings remain.

Methodology: 3 independent reviewers with adversarial consensus. Review event is COMMENT only; human approval remains required.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot's findings

  • Files reviewed: 2/2 changed files
  • Comments generated: 0 new

@kubaflo
kubaflo merged commit a6de97d into main Aug 5, 2026
6 of 7 checks passed
@kubaflo
kubaflo deleted the PureWeen/pr-finalize-testquality branch August 5, 2026 18:49
@github-actions github-actions Bot added this to the .NET 10 SR10 milestone Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-infrastructure CI, Maestro / Coherency, upstream dependencies/versions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants