Sitelet https://github.com/Altinn/app-lib-dotnet/pull/1875
Skip to content

test(app-api): Fix api tests fixture race - #1875

Open
ivarne wants to merge 4 commits into
Altinn:mainfrom
ivarne-agent:fix/api-tests-fixture-race
Open

ivarne wants to merge 4 commits into
Altinn:mainfrom
ivarne-agent:fix/api-tests-fixture-race

Conversation

@ivarne

@ivarne ivarne commented Oct 2, 2026

Copy link
Copy Markdown
Member

Description

Altinn.App.Api.Tests has been failing intermittently in CI. The two failure modes seen in recent runs are both races between test classes that xUnit runs in parallel inside one test process, on the shared on-disk instance fixtures under Data/Instances. They are not related to which target framework runs the tests: each test process already gets its own copy of the fixtures, and the failures happened inside that private copy.

  • Shared instance between two test classes. OptionsControllerTests (11 tests) and DataController_LayoutEvaluatorTests both used instance 500600/cff1cb24-5bc1-4888-8e06-c634753c5144 in tdd/contributer-restriction. TestData.PrepareInstance deletes and recreates the instance's data files, so while an OptionsControllerTests test prepared the instance, the layout evaluator test's PUT to the data element failed with FileNotFoundException (run, run). The fixture is copied to a new instance GUID (and data element GUID) that only DataController_LayoutEvaluatorTests uses.
  • Recursive enumeration of the instances tree in the mock. InstanceClientMockSi.GetInstancePath(partyId, guid) located the instance file with Directory.GetFiles(..., SearchOption.AllDirectories) over the whole instances directory. That descends into the data folders of every other instance, including one that a parallel test is deleting through TestData.DeleteInstanceAndData. On Windows a directory in pending-delete state is reported as access denied, so the enumeration threw and e.g. InstancesController_PostNewInstanceTests.PostNewInstanceWithContent_ValidFilename_Succeeds got a 500 (run). The lookup now probes the fixed org/app/partyId/guid.json layout, walking only the org and app directories, which no test deletes. GetInstances is changed the same way: it walks the remaining directory levels and lists only the *.json files at the instance level, so it reads the same files as before without enumerating into the instance folders.

The old party filter in GetInstancePath was a substring match on the path, so party 1337 also matched 501337. The new lookup compares the directory name exactly.

Verification

  • Your code builds clean without any errors or warnings
  • Manual testing done (required)
  • Relevant automated test added (if you find this hard, leave it and we'll help out)
  • All tests run green

Altinn.App.Api.Tests was run locally on both target frameworks after each commit: 467 tests pass on net10.0 and 466 on net8.0. Three further net10.0 runs only failed in AddRuntimeConfigFiles_Production_ReloadsWhenKubernetesDataSymlinkChanges, which times out waiting for a file-watcher event in the sandbox used for testing, fails the same way on unmodified main there, and is skipped on Windows in CI.

The Windows race can only be reproduced on Windows, so that part is verified by reasoning about the code and by the test run, not by reproducing the original failure.

Documentation

  • User documentation is updated with a separate linked PR in altinn-studio-docs. (if applicable)

ivarne-agent added 2 commits October 2, 2026 22:48
OptionsControllerTests and DataController_LayoutEvaluatorTests both used
instance 500600/cff1cb24-5bc1-4888-8e06-c634753c5144. xUnit runs the two
classes in parallel, and TestData.PrepareInstance in OptionsControllerTests
deletes and recreates the instance's data files while the layout evaluator
test is PUTting to the same data element, which intermittently fails with
FileNotFoundException.

Copy the fixture to a new instance and data element GUID that only
DataController_LayoutEvaluatorTests uses.
…tree

GetInstancePath(partyId, guid) searched for the instance file with a
recursive Directory.GetFiles over the whole instances directory. That
descends into the data folders of every other instance, including ones a
parallel test is deleting via TestData.DeleteInstanceAndData, which on
Windows fails with UnauthorizedAccessException and turns e.g.
InstancesController_PostNewInstanceTests.PostNewInstanceWithContent_ValidFilename_Succeeds
into an intermittent 500.

Probe the fixed org/app/partyId/instance.json layout instead, and make
GetInstances walk directory levels so it only lists files at the instance
level. The party filter was also a substring match (1337 matched 501337).
Copilot AI balanced review requested due to automatic review settings October 2, 2026 21:13
@coderabbitai

coderabbitai Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: a67c9e02-3148-4d15-9153-506c02f4e304

  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

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 review overview

🟡 Changes recommended

The exact party-directory lookup fix lacks deterministic regression coverage.

Review effort: Balanced
Findings: 1 Low severity

Open (1)
What changed in this PR

Fixes intermittent API test races caused by shared fixtures and recursive instance-directory enumeration.

Changes:

  • Gives layout evaluator tests dedicated instance/data fixtures.
  • Limits instance discovery to the fixed directory structure.
  • Uses exact party-directory matching.
File Description
Mocks/​InstanceClientMockSi.cs Avoids recursive instance-data traversal.
Data/​.../​blob/​a2e9d1b4-...pretest Adds isolated XML fixture.
Data/​.../​a2e9d1b4-...pretest.json Adds isolated data metadata.
Data/​.../​7f1a3c0e-...pretest.json Adds isolated instance metadata.
Controllers/​DataController_LayoutEvaluatorTests.cs Uses the isolated fixture identifiers.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread test/Altinn.App.Api.Tests/Mocks/InstanceClientMockSi.cs
Comment thread test/Altinn.App.Api.Tests/Mocks/InstanceClientMockSi.cs Dismissed

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants