From 1b0bdcf609f1654f0ee3b2e2ca9297bc73cbf4f7 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Fri, 7 Aug 2026 16:33:18 -0700 Subject: [PATCH] fix(accounts): restore AccountSecurity.CurrentAlgorithm after each test case AccountPasswordTests mutates the process-wide AccountSecurity.CurrentAlgorithm static in every test case and never restored it. The class shares the "Sequential UOContent Tests" collection with ~45 other test classes; DisableParallelization serialises them but gives no isolation between classes, so whichever algorithm a case last set leaked into whatever ran next. Nothing reads CurrentAlgorithm elsewhere today, but Tasks 6 and 7 add rehash tests to this same file that will. Capture the ambient algorithm in the constructor and restore it via IDisposable so xUnit's per-test-case construct/dispose cycle resets it automatically -- tests added later to this file inherit the restoration without having to opt in. Co-Authored-By: Claude Opus 5 (1M context) --- .../Tests/Accounting/AccountPasswordTests.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Projects/UOContent.Tests/Tests/Accounting/AccountPasswordTests.cs b/Projects/UOContent.Tests/Tests/Accounting/AccountPasswordTests.cs index b1659fa56..b41e876cd 100644 --- a/Projects/UOContent.Tests/Tests/Accounting/AccountPasswordTests.cs +++ b/Projects/UOContent.Tests/Tests/Accounting/AccountPasswordTests.cs @@ -1,3 +1,4 @@ +using System; using Server.Accounting; using Server.Accounting.Security; using Xunit; @@ -5,10 +6,19 @@ using Xunit; namespace Server.Tests.Accounting; [Collection("Sequential UOContent Tests")] -public class AccountPasswordTests +public class AccountPasswordTests : IDisposable { private const string Password = "hunter2"; + // AccountSecurity.CurrentAlgorithm is process-wide static state, shared with every other + // class in the "Sequential UOContent Tests" collection. xUnit constructs/disposes this class + // once per test case, so capturing and restoring it here means every case -- current and any + // added later to this file -- starts from and leaves behind the ambient value, instead of + // bleeding whatever algorithm it last set into the rest of the collection. + private readonly PasswordProtectionAlgorithm _originalAlgorithm = AccountSecurity.CurrentAlgorithm; + + public void Dispose() => AccountSecurity.CurrentAlgorithm = _originalAlgorithm; + [Theory] [InlineData(PasswordProtectionAlgorithm.SHA1)] [InlineData(PasswordProtectionAlgorithm.SHA2)]