Skip to content

Dependencies: Revert NUnit 4 upgrade to unblock integration tests#22562

Merged
andr317c merged 3 commits into
v18/devfrom
v18/task/revert-nunit-4-upgrade
Apr 22, 2026
Merged

Dependencies: Revert NUnit 4 upgrade to unblock integration tests#22562
andr317c merged 3 commits into
v18/devfrom
v18/task/revert-nunit-4-upgrade

Conversation

@andr317c

Copy link
Copy Markdown
Contributor

Reverts #22155 (NUnit 4 upgrade). Integration tests on Windows SQL Server LocalDB have been deterministically hanging on v18/dev. The hang dump points at a sync-over-async call in ContentCacheRefresher.HandleMemoryCache .GetAwaiter().GetResult() on an async cache method.

We don't fully understand why NUnit 4 makes this hang, only that it does, reverting to NUnit 3 unblocks CI. Best guess is something about how NUnit 4 awaits async tests changes the thread-pool timing enough that, under CI memory pressure, the inner continuation never resumes.

The real fix is removing that sync-over-async from ContentCacheRefresher, but it's a gnarly change and not something we want to tackle right now. Worth keeping in mind next time we look at NUnit 4 or that refresher.

andr317c and others added 2 commits April 21, 2026 14:10
…latest major versions (#22155)"

This reverts commit 7014f9a on v18/dev
to resolve the Part3Of4 SQL Server integration test nightly hangs that
started around 2026-04-10.

Root cause was confirmed by hang-dump analysis: a sync-over-async call
in ContentCacheRefresher.HandleMemoryCache
(.GetAwaiter().GetResult() on an async cache method) that NUnit 3's
pumping synchronization context had been quietly completing on the test
thread. NUnit 4 dropped that behaviour, so the continuation now requires
a free thread-pool thread; under CI conditions it deadlocks.

Reverting #22155 on a branch was verified to make the nightly pass.
This is a temporary rollback to unblock v18; the proper fix is to make
ContentCacheRefresher.Refresh async end-to-end, tracked separately.

Additional adjustments beyond the pure revert to keep the branch
compiling:

- CoreConfigurationHttpTests.cs: added `using Umbraco.Cms.Core.Services;`
  for IUserService (referenced by post-#22155 code unaffected by the
  revert).
- ContentVersionCleanupServiceTest.cs: merged imports so both
  AutoFixture.NUnit3 (from revert) and Microsoft.Extensions.Options
  (from unrelated later commit) stay.
- UdiTests.cs and ContentPermissionResourceTests.cs: removed unused
  `using NUnit.Framework.Legacy;` (namespace introduced in NUnit 4).
- BackOfficeAuthorizationInitializationMiddlewareTests.cs: replaced
  `[CancelAfter(5000)]` with its NUnit 3 equivalent `[Timeout(5000)]`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 22, 2026 07:22
@claude

claude Bot commented Apr 22, 2026

Copy link
Copy Markdown

Claude finished @andr317c's task in 3m 46s —— View job


Review complete

  • Read review skill and gather context
  • Analyze PR diff and changed files
  • Review package version changes
  • Review test file changes
  • Post summary review comment
  • Apply labels (area/test, area/backend, category/test-automation)

Review posted. One suggestion noted around the remaining sync-over-async in ClearHost(), but overall the revert is clean and correct. Verdict: Approved.

@claude claude 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.

PR Review

Target: origin/v18/dev · Based on commit: f4df7a78 · Classified as: Test-only

Reverts the NUnit 4 upgrade (PR #22155) back to NUnit 3.14.0 across all test projects to unblock deterministic Windows SQL Server LocalDB integration test hangs caused by a sync-over-async deadlock in ContentCacheRefresher.HandleMemoryCache that NUnit 4's thread-pool changes exposed.


Suggestions

  • tests/Umbraco.Tests.Integration/TestServerTest/UmbracoWebApplicationFactory.cs:30: ClearHost() uses _host.StopAsync().GetAwaiter().GetResult() — sync-over-async, but safe under NUnit 3 teardown (no problematic SynchronizationContext). The previous NUnit 4 workaround wrapped this in Task.Run(); reverting to a direct GetAwaiter().GetResult() is correct here. The underlying pattern remains technical debt, consistent with the PR description's note that the real fix is making ContentCacheRefresher async end-to-end.

Approved

Clean, mechanically consistent revert. All 44 files show the expected mechanical changes: AutoFixture.NUnit4AutoFixture.NUnit3 namespace swaps, [CancelAfter][Timeout] attribute replacements, CancellationToken parameter removals, and assertion style rollbacks from NUnit 4 fluent constraints to NUnit 3 CollectionAssert equivalents — all correctly applied and consistently ordered (expected-before-actual throughout). The UmbracoWebApplicationFactory simplification (dropping Task.Run wrapping in favour of _host.Start()) and the CoreConfigurationHttpTests factory refactor (removing NUnit 4-specific AddTestServices/ModelsBuilder workarounds) restore the known-good pre-upgrade state. Root cause is clearly documented in the PR description.

This looks good to be merged as-is, but please do a manual sanity check and testing before merging.

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 reverts the NUnit 4.x upgrade (and related test dependency changes) back to NUnit 3.x in order to unblock deterministically hanging Windows SQL Server LocalDB integration tests.

Changes:

  • Downgrade NUnit + NUnit3TestAdapter and switch AutoFixture integration back to AutoFixture.NUnit3.
  • Revert NUnit 4-only test APIs/usages (e.g., CancelAfter, constraint .AsCollection) back to NUnit 3-compatible assertions/attributes.
  • Adjust test-server bootstrapping to remove NUnit 4-specific deadlock workarounds.

Reviewed changes

Copilot reviewed 44 out of 44 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/Umbraco.Tests.UnitTests/Umbraco.Web.Common/FileNameTests.cs Switch AutoFixture integration back to NUnit3.
tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/UmbracoPasswordHasherTests.cs Switch AutoFixture integration back to NUnit3.
tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Runtime/DefaultMainDomKeyGeneratorTests.cs Switch AutoFixture integration back to NUnit3.
tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedContentQueryTests.cs Replace NUnit4 constraint collection assertions with NUnit3 CollectionAssert.
tests/Umbraco.Tests.UnitTests/Umbraco.Core/TaskHelperTests.cs Switch AutoFixture integration back to NUnit3.
tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/DefaultContentVersionCleanupPolicyTest.cs Switch AutoFixture integration back to NUnit3.
tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/ContentVersionCleanupServiceTest.cs Switch AutoFixture integration back to NUnit3 (and add required using).
tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/ContentNavigationServiceBaseTests.cs Replace NUnit4 collection constraints with NUnit3 CollectionAssert.
tests/Umbraco.Tests.UnitTests/Umbraco.Core/Security/UserGroupAssignmentAuthorizationTests.cs Replace NUnit4 equivalence constraint with CollectionAssert.AreEquivalent.
tests/Umbraco.Tests.UnitTests/Umbraco.Core/Security/ContentPermissionResourceTests.cs Remove NUnit legacy namespace usage; use CollectionAssert for collections.
tests/Umbraco.Tests.UnitTests/Umbraco.Core/Scheduling/ContentVersionCleanupTest.cs Switch AutoFixture integration back to NUnit3.
tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByPageIdQueryTests.cs Switch AutoFixture integration back to NUnit3.
tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByKeyTests.cs Switch AutoFixture integration back to NUnit3.
tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DateTimeDataValueEditorSortableTests.cs Replace NUnit4 .AsCollection assertions with CollectionAssert.AreEqual.
tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollectionTests.cs Replace NUnit4 collection constraints with CollectionAssert.AreEquivalent.
tests/Umbraco.Tests.UnitTests/Umbraco.Core/Extensions/UmbracoBuilderExtensionsTests.cs Switch AutoFixture integration back to NUnit3.
tests/Umbraco.Tests.UnitTests/Umbraco.Core/EnumerableExtensionsTests.cs Replace equivalence constraints with CollectionAssert.AreEquivalent.
tests/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/UdiTests.cs Remove NUnit legacy namespace usage.
tests/Umbraco.Tests.UnitTests/Umbraco.Cms.Api.Management/Middleware/BackOfficeAuthorizationInitializationMiddlewareTests.cs Replace CancelAfter with NUnit3 Timeout.
tests/Umbraco.Tests.UnitTests/AutoFixture/InlineAutoMoqDataAttribute.cs Switch AutoFixture integration back to NUnit3.
tests/Umbraco.Tests.UnitTests/AutoFixture/AutoMoqDataAttribute.cs Switch AutoFixture integration back to NUnit3.
tests/Umbraco.Tests.Integration/Umbraco.Persistence.EFCore/Scoping/EFCoreLockTests.cs Replace CancelAfter with Timeout and remove NUnit4 CancellationToken injection pattern.
tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/UserServiceTests.cs Replace Is.Empty constraint with CollectionAssert.IsEmpty.
tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MemberServiceTests.cs Replace equivalence constraints with CollectionAssert.AreEquivalent.
tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MediaServiceTests.cs Replace CancelAfter with Timeout; use NUnit3-friendly empty assertions and remove CancellationToken injection pattern.
tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MediaListViewServiceTests.cs Replace equivalence constraints with CollectionAssert.AreEquivalent.
tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ElementEditingServiceTests.Restore.cs Replace equivalence constraints with CollectionAssert.AreEquivalent.
tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ElementEditingServiceTests.Move.cs Replace equivalence constraints with CollectionAssert.AreEquivalent.
tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentSearchServiceTests.cs Replace .AsCollection constraints with CollectionAssert.AreEqual.
tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentListViewServiceTests.cs Replace collection constraints with CollectionAssert variants.
tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/AuditServiceTests.cs Replace item-not-null constraint with CollectionAssert.AllItemsAreNotNull.
tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/PublicAccessRepositoryTest.cs Replace equivalence constraint with CollectionAssert.AreEquivalent.
tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/LocksTests.cs Replace CancelAfter with Timeout and remove NUnit4 CancellationToken injection pattern.
tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Packaging/CreatedPackageSchemaTests.cs Replace Not.Empty/Unique constraints with CollectionAssert equivalents.
tests/Umbraco.Tests.Integration/Umbraco.Core/Services/UserGroupServiceTests.cs Add ShortStringHelper and replace equivalence constraints with CollectionAssert.AreEquivalent.
tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Update.cs Replace equivalence constraints with CollectionAssert.AreEquivalent.
tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Rebuild.cs Replace equivalence constraints with CollectionAssert.AreEquivalent.
tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DynamicRootServiceTests.cs Replace Has.Member constraints with CollectionAssert.Contains.
tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Update.cs Replace equivalence constraints with CollectionAssert.AreEquivalent.
tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Rebuild.cs Replace equivalence constraints with CollectionAssert.AreEquivalent.
tests/Umbraco.Tests.Integration/TestServerTest/UmbracoWebApplicationFactory.cs Remove NUnit4 deadlock workaround and revert to synchronous host start/stop patterns.
tests/Umbraco.Tests.Integration/TestServerTest/CoreConfigurationHttpTests.cs Rework factory setup to set content root via WithWebHostBuilder/ConfigureTestServices.
tests/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj Swap AutoFixture NUnit integration package back to NUnit3.
tests/Directory.Packages.props Downgrade NUnit + adapter versions and swap AutoFixture NUnit integration package back to NUnit3.

@nhudinh0309 nhudinh0309 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.

LGTM

@andr317c andr317c merged commit f981502 into v18/dev Apr 22, 2026
50 of 51 checks passed
@andr317c andr317c deleted the v18/task/revert-nunit-4-upgrade branch April 22, 2026 09:04
@lauraneto lauraneto added release/no-notes Not directly part of the release (updating README, build scripts, tests, etc.) release/18.0.0 labels May 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/backend area/test category/test-automation release/no-notes Not directly part of the release (updating README, build scripts, tests, etc.) release/18.0.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants