Upgraded the tests to NUnit v5.0.0 and awaited their asynchronous assertions

This commit is contained in:
Thorsten Sommer committed 2026-11-01 14:36:56 +01:00
1 parent 8e8d1999d9
commit bc47b5c492
21 files changed
+51 -54

No files matched your search

@@ -65,12 +65,12 @@ public sealed class DataSourceERICancellationTests
}
[Test]
public void ACancelledSearchStops()
public async Task ACancelledSearchStops()
{
using var cancellation = new CancellationTokenSource();
cancellation.Cancel();
Assert.CatchAsync<OperationCanceledException>(async () => await UNREACHABLE.RetrieveDataAsync("query", 1, new ChatThread(), cancellation.Token), "Reporting the data source as not searched would carry on after the user cancelled.");
await Assert.CatchAsync<OperationCanceledException>(async () => await UNREACHABLE.RetrieveDataAsync("query", 1, new ChatThread(), cancellation.Token), "Reporting the data source as not searched would carry on after the user cancelled.");
}
[Test]
+9 -12
View File
@@ -10,29 +10,26 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="10.0.1" />
<PackageReference Include="coverlet.collector" Version="10.1.0" />
<!--
Held at the version the two analyzer projects build against, so that a test compiling a
snippet uses the same Roslyn the analyzers themselves were written for. Raise all three
together, and never above the Roslyn of the .NET SDK in metadata.txt (5.0 for 10.0.1xx).
-->
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="5.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.10.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.11.0" />
<!--
Pinned below 4.6.0 on purpose, verified by bisecting 4.4.0, 4.5.0, 4.5.1, 4.6.0 and 4.6.1
against the .NET 9 SDK (9.0.120, C# 13): from 4.6.0 on, the compiler no longer picks
Assert.That<TActual>(TActual, IResolveConstraint, ...) for a plain value. 4.6.0 added
Assert.That(Action, ...) and Assert.That<T>(Func<T>, ...) alongside it, and the value
overload drops out of the candidate set, so even Assert.That(1, Is.EqualTo(1)) fails with
"argument 1: cannot convert int to System.Action", a message that points nowhere near the
real cause. Re-test the newest version whenever the .NET SDK is raised.
NUnit 4.6 broke Assert.That for plain values under C# 13: with Assert.That(Action, ...) added,
even Assert.That(1, Is.EqualTo(1)) failed with "argument 1: cannot convert int to System.Action".
NUnit 5 with C# 14 picks the value overload again. Should that message come back after an update,
this is where it comes from.
-->
<PackageReference Include="NUnit" Version="4.5.1" />
<PackageReference Include="NUnit.Analyzers" Version="4.14.0">
<PackageReference Include="NUnit" Version="5.0.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.15.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="NUnit3TestAdapter" Version="6.3.0" />
<PackageReference Include="NUnit3TestAdapter" Version="6.3.1" />
</ItemGroup>
<ItemGroup>
@@ -233,13 +233,13 @@ public sealed class MailboxIndexStoreTests
{
var mailId = await this.StoreDocumentAsync(KEY_A);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.ThrowsAsync<ArgumentException>(() => this.Client.UpsertMailAsync(DATA_SOURCE_ID, Mail(mailId), TOKEN), "A mail which lies nowhere would be an orphan from the start.");
Assert.ThrowsAsync<ArgumentException>(() => this.Client.UpsertMailAsync(DATA_SOURCE_ID, Mail(mailId, At(INBOX, 7), At(INBOX, 7)), TOKEN));
Assert.ThrowsAsync<InvalidOperationException>(() => this.Client.UpsertMailAsync(DATA_SOURCE_ID, Mail(mailId, At("Unknown", 1)), TOKEN));
Assert.ThrowsAsync<ArgumentException>(() => this.Client.UpsertMailAsync(DATA_SOURCE_ID, Mail(mailId, At(INBOX, 7)) with { ReferenceMessageIds = ["one two@example.org"] }, TOKEN), "Stored separated by spaces, this one would come back as two.");
});
await Assert.ThrowsAsync<ArgumentException>(() => this.Client.UpsertMailAsync(DATA_SOURCE_ID, Mail(mailId), TOKEN), "A mail which lies nowhere would be an orphan from the start.");
await Assert.ThrowsAsync<ArgumentException>(() => this.Client.UpsertMailAsync(DATA_SOURCE_ID, Mail(mailId, At(INBOX, 7), At(INBOX, 7)), TOKEN));
await Assert.ThrowsAsync<InvalidOperationException>(() => this.Client.UpsertMailAsync(DATA_SOURCE_ID, Mail(mailId, At("Unknown", 1)), TOKEN));
await Assert.ThrowsAsync<ArgumentException>(() => this.Client.UpsertMailAsync(DATA_SOURCE_ID, Mail(mailId, At(INBOX, 7)) with { ReferenceMessageIds = ["one two@example.org"] }, TOKEN), "Stored separated by spaces, this one would come back as two.");
}
}
/// <summary>
@@ -91,9 +91,9 @@ public sealed class MailboxStateStoreTests
}
[Test]
public void ARemovalOfNoMailIsNothingToHoldBack()
public async Task ARemovalOfNoMailIsNothingToHoldBack()
{
Assert.ThrowsAsync<ArgumentOutOfRangeException>(() => this.Client.HoldBackMailRemovalAsync(DATA_SOURCE_ID, 0, TOKEN));
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(() => this.Client.HoldBackMailRemovalAsync(DATA_SOURCE_ID, 0, TOKEN));
}
[Test]
+2 -2
View File
@@ -65,10 +65,10 @@ public sealed class FileExportFeedbackTests
}
[Test]
public void AnExceptionRemovesTheRunningNotification()
public async Task AnExceptionRemovesTheRunningNotification()
{
var sent = new SentNotifications();
Assert.ThrowsAsync<IOException>(async () =>
await Assert.ThrowsAsync<IOException>(async () =>
{
await using var feedback = new FileExportFeedback(sent.Send, _ => Task.CompletedTask);
feedback.ShowRunning(FILE_PATH);
+1 -1
View File
@@ -59,7 +59,7 @@ public class FileTypesTests
.ToArray();
Assert.That(extensions, Is.Not.Empty);
Assert.That(extensions, Has.All.Matches<string>(extension => extension == extension.ToLowerInvariant() && !extension.Contains('.')));
Assert.That(extensions, Has.All.Matches<string>(extension => extension is not null && extension == extension.ToLowerInvariant() && !extension.Contains('.')));
}
[Test]
@@ -90,7 +90,7 @@ public sealed class ImapMailboxConnectorTests
// failure instead of this one.
//
await using var connector = new ImapMailboxConnector();
var exception = Assert.ThrowsAsync<MailboxConnectionException>(() => connector.ConnectAsync(mailbox, password, MailServerPolicy.ANY_SERVER, CancellationToken.None));
var exception = await Assert.ThrowsAsync<MailboxConnectionException>(() => connector.ConnectAsync(mailbox, password, MailServerPolicy.ANY_SERVER, CancellationToken.None));
Assert.That(exception?.Failure, Is.EqualTo(MailboxConnectionFailure.INVALID_SETTINGS));
}
@@ -103,7 +103,7 @@ public sealed class ImapMailboxConnectorTests
//
await using var connector = new ImapMailboxConnector();
var policy = new MailServerPolicy(true, ["imap.intra.example.org"]);
var exception = Assert.ThrowsAsync<MailboxConnectionException>(() => connector.ConnectAsync(MAILBOX, PASSWORD, policy, CancellationToken.None));
var exception = await Assert.ThrowsAsync<MailboxConnectionException>(() => connector.ConnectAsync(MAILBOX, PASSWORD, policy, CancellationToken.None));
Assert.That(exception?.Failure, Is.EqualTo(MailboxConnectionFailure.SERVER_NOT_ALLOWED));
}
@@ -61,22 +61,22 @@ public sealed class MailAttachmentPiecesTests
}
[Test]
public void AnUnknownEncodingFetchesNothing()
public async Task AnUnknownEncodingFetchesNothing()
{
var server = new PieceServer(Encoding.ASCII.GetBytes("Uryyb"));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.ThrowsAsync<InvalidDataException>(async () => await WriteDecodedAsync(server, "x-rot13"));
await Assert.ThrowsAsync<InvalidDataException>(async () => await WriteDecodedAsync(server, "x-rot13"));
Assert.That(server.RequestedOffsets, Is.Empty);
});
}
}
[Test]
public void APieceLargerThanAskedForIsRefused()
public async Task APieceLargerThanAskedForIsRefused()
{
// Taken as it came, the next piece would repeat what this one already delivered:
var server = new PieceServer(new byte[100], deliveredBytes: PIECE_BYTES + 1);
Assert.ThrowsAsync<InvalidDataException>(async () => await WriteDecodedAsync(server, "binary"));
await Assert.ThrowsAsync<InvalidDataException>(async () => await WriteDecodedAsync(server, "binary"));
}
private static async Task<byte[]> WriteDecodedAsync(PieceServer server, string? transferEncoding)
@@ -97,11 +97,11 @@ public sealed class TranscriptPromptInjectionTests
}
[Test]
public void OnlyAReadyTranscriptCanBeRead()
public async Task OnlyAReadyTranscriptCanBeRead()
{
var transcript = PersistentMediaTranscriptResult.Unavailable(PersistentMediaTranscriptStatus.NO_PROVIDER, null, "No provider.");
Assert.ThrowsAsync<ArgumentException>(() => PersistentMediaTranscript.ReadTranscriptAsync(this.rustService, transcript, this.mediaPath, reportingScope: null, CancellationToken.None));
await Assert.ThrowsAsync<ArgumentException>(() => PersistentMediaTranscript.ReadTranscriptAsync(this.rustService, transcript, this.mediaPath, reportingScope: null, CancellationToken.None));
}
[Test]
+2 -2
View File
@@ -117,12 +117,12 @@ public sealed class PassageProvenanceTests
}
[Test]
public void StoringNeverStopsTheAnswer()
public async Task StoringNeverStopsTheAnswer()
{
// A passage without its text, as an ERI server sent one before its fields were checked:
var answer = new ContentText { SourcePassages = [Passage(HANDBOOK_URL, null!)] };
Assert.DoesNotThrowAsync(() => answer.StoreReceivedContentAsync(Guid.NewGuid()), "Completing the answer waits for this, and a throw left its chat running until the app restarted.");
await Assert.DoesNotThrowAsync(() => answer.StoreReceivedContentAsync(Guid.NewGuid()), "Completing the answer waits for this, and a throw left its chat running until the app restarted.");
}
[Test]
+2 -2
View File
@@ -163,11 +163,11 @@ public sealed class RetrievalPagingTests
}
[Test]
public void AnERIDataSourceRefusesAFurtherPage()
public async Task AnERIDataSourceRefusesAFurtherPage()
{
var source = new DataSourceERI_V1 { Id = "33333333-3333-3333-3333-333333333333", Name = "Intranet", MaxMatches = 10 };
Assert.ThrowsAsync<ArgumentOutOfRangeException>(async () => await source.RetrieveDataAsync("query", 2, new AIStudio.Chat.ChatThread()));
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(async () => await source.RetrieveDataAsync("query", 2, new AIStudio.Chat.ChatThread()));
}
private static (IReadOnlyList<string> Matches, bool HasMore) Merge(string[] first, string[] second, int page)
@@ -158,5 +158,5 @@ public sealed class CountMailsToolTests : ToolRegistryTestBase
private static JsonElement Arguments(string json) => JsonSerializer.Deserialize<JsonElement>(json);
private static string Refusal(TestDelegate read) => Assert.Throws<ArgumentException>(read)!.Message;
private static string Refusal(Action read) => Assert.Throws<ArgumentException>(read)!.Message;
}
@@ -223,7 +223,7 @@ public sealed class MailToolArgumentsTests
private static JsonElement Arguments(string json) => JsonSerializer.Deserialize<JsonElement>(json);
private static string Refusal(TestDelegate read) => Assert.Throws<ArgumentException>(read)!.Message;
private static string Refusal(Action read) => Assert.Throws<ArgumentException>(read)!.Message;
/// <summary>
/// A time zone two hours ahead of UTC, all year round, so that no test depends on the computer or on daylight saving time.
@@ -194,5 +194,5 @@ public sealed class ReadMailToolTests : ToolRegistryTestBase
private static JsonElement Arguments(string json) => JsonSerializer.Deserialize<JsonElement>(json);
private static string Refusal(TestDelegate read) => Assert.Throws<ArgumentException>(read)!.Message;
private static string Refusal(Action read) => Assert.Throws<ArgumentException>(read)!.Message;
}
@@ -149,11 +149,11 @@ public sealed class ReadWebPageAllowedPrivateHostTests : ToolRegistryTestBase
}
[Test]
public void AnAllowedNameWhichResolvesToAPublicAddressIsRefused()
public async Task AnAllowedNameWhichResolvesToAPublicAddressIsRefused()
{
var service = new WebPageRetrievalService(new HTMLParser());
var exception = Assert.ThrowsAsync<WebPageAccessBlockedException>(() => service.RetrieveAsync(new Uri(PUBLIC_ADDRESS), new WebPageRetrievalOptions
var exception = await Assert.ThrowsAsync<WebPageAccessBlockedException>(() => service.RetrieveAsync(new Uri(PUBLIC_ADDRESS), new WebPageRetrievalOptions
{
TimeoutSeconds = 10,
ProviderConfidence = ConfidenceLevel.HIGH,
@@ -165,7 +165,7 @@ public sealed class ReadWebPageAllowedPrivateHostTests : ToolRegistryTestBase
}
[Test]
public void ARedirectFromAnAllowedPrivateHostToAPublicAddressIsRefused()
public async Task ARedirectFromAnAllowedPrivateHostToAPublicAddressIsRefused()
{
using var server = new LoopbackWebServer(redirectLocation: PUBLIC_ADDRESS);
var service = new WebPageRetrievalService(new HTMLParser());
@@ -175,7 +175,7 @@ public sealed class ReadWebPageAllowedPrivateHostTests : ToolRegistryTestBase
// chose only, which the limit holds for all the same, because it guards what the address
// may carry:
//
var exception = Assert.ThrowsAsync<WebPageAccessBlockedException>(() => service.RetrieveAsync(server.Address("/redirect"), new WebPageRetrievalOptions
var exception = await Assert.ThrowsAsync<WebPageAccessBlockedException>(() => service.RetrieveAsync(server.Address("/redirect"), new WebPageRetrievalOptions
{
TimeoutSeconds = 10,
TargetChosenByUser = true,
@@ -94,7 +94,7 @@ public sealed class ReadWebPageFreeAddressChoiceTests
}
[Test]
public void OffRefusesAMadeUpAddressWithoutRepeatingIt()
public async Task OffRefusesAMadeUpAddressWithoutRepeatingIt()
{
var tool = new ReadWebPageTool(null!, null!, null!, NullLogger<ReadWebPageTool>.Instance);
using var arguments = JsonDocument.Parse("""{"url":"https://made-up.example/?question=budget"}""");
@@ -109,7 +109,7 @@ public sealed class ReadWebPageFreeAddressChoiceTests
SettingsValues = new Dictionary<string, string>(),
};
var exception = Assert.ThrowsAsync<ToolExecutionBlockedException>(() => tool.ExecuteAsync(arguments.RootElement, context));
var exception = await Assert.ThrowsAsync<ToolExecutionBlockedException>(() => tool.ExecuteAsync(arguments.RootElement, context));
Assert.That(exception!.Message, Does.Contain("Free address choice is off").And.Not.Contain("made-up").And.Not.Contain("budget"), "Repeated in the result, the address would stand in the chat afterwards.");
}
@@ -87,7 +87,7 @@ public sealed class ReadWebPageOutboundDataRestrictionTests : ToolRegistryTestBa
Assert.That(ReadWebPageTool.IsAllowedByOutboundDataRestriction(new Uri(GIVEN), (OutboundDataRestriction)99, ChatWithTheUserWriting(GIVEN), WIKI_URL, NO_PRIVATE_HOSTS, out _), Is.False);
[Test]
public void ARefusalDoesNotRepeatTheAddress()
public async Task ARefusalDoesNotRepeatTheAddress()
{
//
// Repeated in the result of a call, the address would stand in the chat afterwards, and the
@@ -98,7 +98,7 @@ public sealed class ReadWebPageOutboundDataRestrictionTests : ToolRegistryTestBa
var chat = RestrictedChat(OutboundDataRestriction.ONLY_LINKS_FROM_CHAT);
using var arguments = JsonDocument.Parse("""{"url":"https://attacker.example/?mail=board-meeting"}""");
var exception = Assert.ThrowsAsync<ToolExecutionBlockedException>(() => tool.ExecuteAsync(arguments.RootElement, this.ExecutionContext(tool, chat)));
var exception = await Assert.ThrowsAsync<ToolExecutionBlockedException>(() => tool.ExecuteAsync(arguments.RootElement, this.ExecutionContext(tool, chat)));
Assert.That(exception!.Message, Does.Not.Contain("attacker").And.Not.Contain("board-meeting"));
}
@@ -324,5 +324,5 @@ public sealed class SearchMailsToolTests : ToolRegistryTestBase
private static JsonElement Arguments(string json) => JsonSerializer.Deserialize<JsonElement>(json);
private static string Refusal(TestDelegate read) => Assert.Throws<ArgumentException>(read)!.Message;
private static string Refusal(Action read) => Assert.Throws<ArgumentException>(read)!.Message;
}
@@ -115,5 +115,5 @@ public sealed class SemanticSearchToolRequestTests
/// <summary>
/// Reads a search which has to be refused and returns what the refusal said.
/// </summary>
private static string Refusal(TestDelegate read) => Assert.Throws<ArgumentException>(read)!.Message;
private static string Refusal(Action read) => Assert.Throws<ArgumentException>(read)!.Message;
}
@@ -163,5 +163,5 @@ public sealed class ToolArgumentReaderTests
/// <summary>
/// Runs a reader which has to refuse its argument and returns what it said.
/// </summary>
private static string Refusal(TestDelegate read) => Assert.Throws<ArgumentException>(read)!.Message;
private static string Refusal(Action read) => Assert.Throws<ArgumentException>(read)!.Message;
}
@@ -166,5 +166,5 @@ public sealed class WebSearchToolArgumentTests
/// <summary>
/// Runs a reader which has to refuse its argument and returns what it said.
/// </summary>
private static string Refusal(TestDelegate read) => Assert.Throws<ArgumentException>(read)!.Message;
private static string Refusal(Action read) => Assert.Throws<ArgumentException>(read)!.Message;
}