Skip to content

Flaky: WorkflowScaffolderTests.Scaffolded_workflow_objects_pass_the_pre_write_guards loses a written file under parallel run #158

Description

@dynamics365ninja

@

Symptom

On a full dotnet test run, D365FO.Core.Tests.WorkflowScaffolderTests.Scaffolded_workflow_objects_pass_the_pre_write_guards intermittently fails:

Assert.Equal() Failure: Values differ
Expected: 3
Actual:   2
   at D365FO.Core.Tests.WorkflowScaffolderTests.Scaffolded_workflow_objects_pass_the_pre_write_guards()
      in tests/D365FO.Core.Tests/WorkflowScaffolderTests.cs:line 189

The test writes three scaffolded workflow documents into a fresh GUID-named temp directory and asserts three *.xml files exist.

What is notable about the failure mode

It fails on the count, not on an exception. All three ScaffoldFileWriter.Write calls returned successfully — WriteCore either writes the file or throws, and nothing threw. So a file was written and then went missing, or one write landed somewhere other than its explicit path.

Reproduction

Not reliably reproducible. Observed once on a cold full-suite run (that run took 1m11s; the passing re-run took 18s, so the failing run was under noticeably more contention).

Does not reproduce:

  • in isolation: dotnet test --filter "FullyQualifiedName~WorkflowScaffolderTests" → 11/11 pass
  • on a full-suite re-run → 962/962 pass
  • via a standalone console repro driving the same three WorkflowScaffolder factories through ScaffoldFileWriter.Write — all three files land every time:
    --- WorkflowTemplate: root=<AxWorkflowTemplate>  WROTE ConVehicleReview.xml (460 bytes)
    --- WorkflowApproval: root=<AxWorkflowApproval>  WROTE ConVehicleApproval.xml (645 bytes)
    --- WorkflowTask:     root=<AxWorkflowTask>      WROTE ConVehicleTask.xml (576 bytes)
    *.xml count = 3
    

Suspected cause

ScaffoldFileWriter.WriteCore reads process-wide state on every call:

var cfg = D365FoSettings.FromEnvironment();
PathGuard.EnsureWithinBoundary(full, new[] { cfg.PackagesPath, cfg.WorkspacePath }
    .Concat(cfg.CustomPackagesPaths).ToArray());

Four Core test classes mutate environment variables and run in parallel with this one:

  • D365FoSettingsResolveTests
  • ObjectModifyEngineTests
  • ProvenanceStoreTests
  • ScaffoldFileWriterJournalTests

A race there is the most plausible explanation for the write path behaving differently under load. It does not fully explain a silent loss (a boundary rejection throws), so the journal path (RecordJournalEntry) and the atomic .tmp → target File.Move are also worth looking at — a File.Move that reports success while another process holds/reaps the target would fit the symptom.

Why this is being filed separately

Discovered while validating #157 (landing knowledge-audit Phase 2 on main). It is not a regression from that merge: neither side of the merge touches WorkflowScaffolderTests.cs, WorkflowScaffolder.cs, or the ScaffoldFileWriter write path, and CI is green on main.

Suggested fix direction

Give the env-mutating test classes a shared xUnit collection (or an IDisposable fixture that serialises them) so they cannot interleave with tests that call D365FoSettings.FromEnvironment(). If the loss turns out not to be env-related, add a post-write existence assertion inside WriteCore so the failure surfaces at the write rather than at a later count.

🤖 Generated with Claude Code
@

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions