Switched the tests to Assert.EnterMultipleScope

This commit is contained in:
Thorsten Sommer committed 2026-11-01 14:55:07 +01:00
1 parent bc47b5c492
commit 809259c1e1
290 files changed
+2548 -2542

No files matched your search

@@ -26,12 +26,12 @@ public sealed class MissingInterpolationAnalyzerTests
{
var reported = await AnalyzeAsync(expression);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(reported, Has.Count.EqualTo(1));
Assert.That(reported.FirstOrDefault()?.Id, Is.EqualTo("MWAIS0014"));
Assert.That(reported.FirstOrDefault()?.GetMessage(), Does.Contain("\"{this.location}\""));
});
}
}
[Test]
@@ -25,13 +25,13 @@ public sealed class BatchProcessingOutputFolderTests
{
var outputFolder = new BatchProcessingOutputFolder(INPUT_FOLDER, PROJECT_FOLDER, RESULTS_FILE_NAME);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(INPUT_FOLDER, "report.pdf")), Is.False);
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(INPUT_FOLDER, "notes", "minutes.md")), Is.False);
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(INPUT_FOLDER, "log.csv")), Is.False, "The log of a run lies in the output folder, so a file of this name in the input folder is a document.");
Assert.That(outputFolder.Enters(Path.Combine(INPUT_FOLDER, "notes")), Is.True);
});
}
}
[Test]
@@ -39,12 +39,12 @@ public sealed class BatchProcessingOutputFolderTests
{
var outputFolder = new BatchProcessingOutputFolder(INPUT_FOLDER, DESKTOP, RESULTS_FILE_NAME);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(INPUT_FOLDER, "report.pdf")), Is.False);
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(INPUT_FOLDER, "notes", "minutes.md")), Is.False);
Assert.That(outputFolder.Enters(Path.Combine(INPUT_FOLDER, "notes")), Is.True);
});
}
}
[Test]
@@ -53,13 +53,13 @@ public sealed class BatchProcessingOutputFolderTests
var resultsFolder = Path.Combine(INPUT_FOLDER, "ai-results");
var outputFolder = new BatchProcessingOutputFolder(INPUT_FOLDER, resultsFolder, RESULTS_FILE_NAME);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(resultsFolder, "report_result.md")), Is.True);
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(INPUT_FOLDER, "report.pdf")), Is.False);
Assert.That(outputFolder.Enters(resultsFolder), Is.False);
Assert.That(outputFolder.Enters(Path.Combine(INPUT_FOLDER, "notes")), Is.True);
});
}
}
[Test]
@@ -69,14 +69,14 @@ public sealed class BatchProcessingOutputFolderTests
var resultsFolder = Path.Combine(parentFolder, "results");
var outputFolder = new BatchProcessingOutputFolder(INPUT_FOLDER, resultsFolder, RESULTS_FILE_NAME);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(resultsFolder, "report_result.md")), Is.True);
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(parentFolder, "report.pdf")), Is.False);
Assert.That(outputFolder.Enters(Path.Combine(INPUT_FOLDER, "2026")), Is.True);
Assert.That(outputFolder.Enters(parentFolder), Is.True);
Assert.That(outputFolder.Enters(resultsFolder), Is.False);
});
}
}
[Test]
@@ -84,11 +84,11 @@ public sealed class BatchProcessingOutputFolderTests
{
var outputFolder = new BatchProcessingOutputFolder(INPUT_FOLDER, Path.Combine(INPUT_FOLDER, "out"), RESULTS_FILE_NAME);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(INPUT_FOLDER, "output-notes.md")), Is.False);
Assert.That(outputFolder.Enters(Path.Combine(INPUT_FOLDER, "output")), Is.True);
});
}
}
[Test]
@@ -96,12 +96,12 @@ public sealed class BatchProcessingOutputFolderTests
{
var outputFolder = new BatchProcessingOutputFolder(INPUT_FOLDER, $"{INPUT_FOLDER}-results", RESULTS_FILE_NAME);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(INPUT_FOLDER, "report.pdf")), Is.False);
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(INPUT_FOLDER, "log.csv")), Is.False);
Assert.That(outputFolder.Enters(Path.Combine(INPUT_FOLDER, "notes")), Is.True);
});
}
}
[TestCase(false)]
@@ -111,13 +111,13 @@ public sealed class BatchProcessingOutputFolderTests
var outputDirectory = withTrailingSeparator ? INPUT_FOLDER + Path.DirectorySeparatorChar : INPUT_FOLDER;
var outputFolder = new BatchProcessingOutputFolder(INPUT_FOLDER, outputDirectory, RESULTS_FILE_NAME);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(INPUT_FOLDER, "log.csv")), Is.True);
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(INPUT_FOLDER, RESULTS_FILE_NAME)), Is.True);
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(INPUT_FOLDER, "report.pdf")), Is.False);
Assert.That(outputFolder.Enters(Path.Combine(INPUT_FOLDER, "notes")), Is.True, "Leaving out the output folder would leave out the input folder, too.");
});
}
}
[Test]
@@ -125,13 +125,13 @@ public sealed class BatchProcessingOutputFolderTests
{
var outputFolder = new BatchProcessingOutputFolder(INPUT_FOLDER, INPUT_FOLDER, RESULTS_FILE_NAME);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(INPUT_FOLDER, "report_result.md")), Is.True);
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(INPUT_FOLDER, "report_result_2.docx")), Is.True, "A second result of the same name gets a counter.");
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(INPUT_FOLDER, "Report_Result.HTML")), Is.True);
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(INPUT_FOLDER, "report.v2_result.md")), Is.True, "Only the last extension of the document makes way for the suffix.");
});
}
}
[Test]
@@ -139,14 +139,14 @@ public sealed class BatchProcessingOutputFolderTests
{
var outputFolder = new BatchProcessingOutputFolder(INPUT_FOLDER, INPUT_FOLDER, RESULTS_FILE_NAME);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(INPUT_FOLDER, "report.md")), Is.False);
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(INPUT_FOLDER, "result.md")), Is.False);
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(INPUT_FOLDER, "my_results.md")), Is.False);
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(INPUT_FOLDER, "report_result_draft.md")), Is.False);
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(INPUT_FOLDER, "report_result_.md")), Is.False);
});
}
}
[Test]
@@ -155,11 +155,11 @@ public sealed class BatchProcessingOutputFolderTests
var outputFolder = new BatchProcessingOutputFolder(INPUT_FOLDER, INPUT_FOLDER, RESULTS_FILE_NAME);
var subfolder = Path.Combine(INPUT_FOLDER, "notes");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(subfolder, "log.csv")), Is.False, "A run writes into the output folder itself, never into one of its subfolders.");
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(subfolder, RESULTS_FILE_NAME)), Is.False);
Assert.That(outputFolder.IsOutputOfRun(Path.Combine(subfolder, "report_result.md")), Is.False);
});
}
}
}
@@ -32,11 +32,11 @@ public sealed class ERIServerFileListTests
var isRead = ERIServerFileList.TryParse(ANSWER, out var files);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(isRead, Is.True);
Assert.That(files, Is.EqualTo(FILES));
});
}
}
[Test]
@@ -44,11 +44,11 @@ public sealed class ERIServerFileListTests
{
var isRead = ERIServerFileList.TryParse("""{ "files": [ "src\\main.py" ] }""", out var files);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(isRead, Is.True);
Assert.That(files, Is.EqualTo(PATH_WITH_BACKSLASH));
});
}
}
[Test]
@@ -56,11 +56,11 @@ public sealed class ERIServerFileListTests
{
var isRead = ERIServerFileList.TryParse("""{ "files": [ "main.py", "", " " ] }""", out var files);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(isRead, Is.True);
Assert.That(files, Is.EqualTo(MAIN_PY));
});
}
}
[TestCase("""[ "main.py" ]""", TestName = "A list without its object")]
@@ -73,10 +73,10 @@ public sealed class ERIServerFileListTests
{
var isRead = ERIServerFileList.TryParse(answer, out var files);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(isRead, Is.False);
Assert.That(files, Is.Empty);
});
}
}
}
@@ -45,11 +45,11 @@ public sealed class ERIServerFileWritingTests
{
var outcome = await ERIServerFiles.WriteAsync(this.baseDirectory, "src/my-server/main.py", ANSWER, NullLogger.Instance);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(outcome, Is.EqualTo(ERIServerFileOutcome.WRITTEN));
Assert.That(File.ReadAllText(Path.Join(this.baseDirectory, "src", "my-server", "main.py")), Is.EqualTo(MAIN_PY));
});
}
}
[Test]
@@ -58,11 +58,11 @@ public sealed class ERIServerFileWritingTests
var file = $"../outside-{Guid.NewGuid():N}.py";
var outcome = await ERIServerFiles.WriteAsync(this.baseDirectory, file, ANSWER, NullLogger.Instance);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(outcome, Is.EqualTo(ERIServerFileOutcome.OUTSIDE_BASE_DIRECTORY));
Assert.That(File.Exists(Path.GetFullPath(Path.Join(this.baseDirectory, file))), Is.False);
});
}
}
[Test]
@@ -70,11 +70,11 @@ public sealed class ERIServerFileWritingTests
{
var outcome = await ERIServerFiles.WriteAsync(this.baseDirectory, "main.py", "## main.py\n\n```python\nprint(", NullLogger.Instance);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(outcome, Is.EqualTo(ERIServerFileOutcome.NO_COMPLETE_CODE_BLOCK));
Assert.That(File.Exists(Path.Join(this.baseDirectory, "main.py")), Is.False);
});
}
}
[Test]
@@ -51,11 +51,11 @@ public sealed class ERIServerPromptTests
{
var lines = Lines(new ERIServerPrompt { AuthenticationMethods = [Auth.NONE, Auth.USERNAME_PASSWORD], AuthDescription = AUTH_DESCRIPTION });
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(lines, Does.Contain("The authentication process is planned as follows:"));
Assert.That(lines, Does.Contain(AUTH_DESCRIPTION));
});
}
}
[Test]
@@ -36,7 +36,7 @@ public sealed class SwotAnalysisContentPresentationTests
var restoredContent = (ContentText)restoredThread!.Blocks.Single().Content!;
var presentationWasRead = SwotAnalysisContentPresentation.TryRead(restoredContent.Presentation, out var restoredResult);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(clone.Presentation, Is.Not.SameAs(content.Presentation));
Assert.That(clone.Text, Is.EqualTo(content.Text));
@@ -45,7 +45,7 @@ public sealed class SwotAnalysisContentPresentationTests
Assert.That(restoredResult.MatrixHeading, Is.EqualTo("SWOT Matrix"));
Assert.That(restoredResult.Strengths.Findings.Single().Summary, Is.EqualTo("Experienced team"));
Assert.That(restoredResult.PrioritizedActions.Items.Single().Action, Is.EqualTo("Prepare expansion"));
});
}
}
[Test]
@@ -59,11 +59,11 @@ public sealed class SwotAnalysisContentPresentationTests
var presentationWasRead = SwotAnalysisContentPresentation.TryRead(content.Presentation, out _);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(presentationWasRead, Is.False);
Assert.That(content.Text, Is.EqualTo("Fallback Markdown"));
});
}
}
[Test]
@@ -90,12 +90,12 @@ public sealed class SwotAnalysisContentPresentationTests
var presentationWasRead = SwotAnalysisContentPresentation.TryRead(content.Presentation, out var readResult);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(presentationWasRead, Is.True);
Assert.That(readResult.Categories.All(category => category.Findings.Count == 0), Is.True);
Assert.That(readResult.PrioritizedActions.Items, Is.Empty);
});
}
}
[Test]
@@ -16,7 +16,7 @@ public sealed class SwotAnalysisMarkdownFormatterTests
var markdown = SwotAnalysisMarkdownFormatter.Format(result);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(markdown, Does.Contain("| | Positive | Negative |"));
Assert.That(Count(markdown, "Experienced team"), Is.GreaterThanOrEqualTo(2));
@@ -26,7 +26,7 @@ public sealed class SwotAnalysisMarkdownFormatterTests
Assert.That(markdown.IndexOf("## SWOT Matrix", StringComparison.Ordinal), Is.LessThan(markdown.IndexOf("## Strengths", StringComparison.Ordinal)));
Assert.That(markdown.IndexOf("## SWOT Matrix", StringComparison.Ordinal), Is.LessThan(markdown.IndexOf("## Prioritized Actions", StringComparison.Ordinal)));
Assert.That(markdown, Does.Contain("| **External** | **Opportunities:** Growing market | **Threats:** New competitor |"));
});
}
}
[Test]
@@ -45,13 +45,13 @@ public sealed class SwotAnalysisMarkdownFormatterTests
.Select(heading => markdown.IndexOf(heading, StringComparison.Ordinal))
.ToList();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(positions, Is.All.GreaterThanOrEqualTo(0));
Assert.That(positions, Is.Ordered);
Assert.That(markdown, Does.Contain("The position is solid\\.\n\nThe competitor is the main risk\\."));
Assert.That(markdown, Does.EndWith("- **Moved the new competitor to the threats\\.** — It is external and negative\\."));
});
}
}
[Test]
@@ -13,7 +13,7 @@ public sealed class SwotAnalysisModerationTests
var parsed = SwotAnalysisModerationResponseParser.TryParse(JsonSerializer.Serialize(CreateModeration()), out var moderation, out var issue);
var result = SwotAnalysisModeration.Assemble(moderation);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(parsed, Is.True);
Assert.That(issue, Is.Empty);
@@ -23,7 +23,7 @@ public sealed class SwotAnalysisModerationTests
Assert.That(result.OverallAssessment.Paragraphs, Is.EqualTo(new[] { "The position is solid.", "The competitor is the main risk." }));
Assert.That(result.ModerationNotes.Items.Single().Change, Is.EqualTo("Merged two strengths about the team."));
Assert.That(result.PrioritizedActions.Items.Single().AddressedFactors, Is.EqualTo(new[] { "Experienced team", "Growing market" }));
});
}
}
[Test]
@@ -34,11 +34,11 @@ public sealed class SwotAnalysisModerationTests
var parsed = SwotAnalysisModerationResponseParser.TryParse(JsonSerializer.Serialize(moderation), out var parsedModeration, out _);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(parsed, Is.True);
Assert.That(SwotAnalysisModeration.Assemble(parsedModeration).PrioritizedActions.Items.Single().AddressedFactors, Is.EqualTo(new[] { "Experienced team", "Growing market" }));
});
}
}
[Test]
@@ -47,11 +47,11 @@ public sealed class SwotAnalysisModerationTests
var moderation = CreateModeration();
moderation.Strengths.Findings.Single().FactorId = "W2";
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(SwotAnalysisModerationResponseParser.TryParse(JsonSerializer.Serialize(moderation), out _, out var issue), Is.False);
Assert.That(issue, Is.EqualTo("Finding 1 of strengths has the factor_id \"W2\", which does not start with \"S\"."));
});
}
}
[Test]
@@ -60,11 +60,11 @@ public sealed class SwotAnalysisModerationTests
var moderation = CreateModeration();
moderation.Strengths.Findings.Add(Finding("s1", "Loyal customers", "The survey shows long customer relationships."));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(SwotAnalysisModerationResponseParser.TryParse(JsonSerializer.Serialize(moderation), out _, out var issue), Is.False);
Assert.That(issue, Does.Contain("\"s1\" is used more than once"));
});
}
}
[Test]
@@ -73,11 +73,11 @@ public sealed class SwotAnalysisModerationTests
var moderation = CreateModeration();
moderation.PrioritizedActions.Items.Single().AddressedFactorIds = ["S1", "X9"];
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(SwotAnalysisModerationResponseParser.TryParse(JsonSerializer.Serialize(moderation), out _, out var issue), Is.False);
Assert.That(issue, Does.Contain("\"X9\""));
});
}
}
[Test]
@@ -86,11 +86,11 @@ public sealed class SwotAnalysisModerationTests
var moderation = CreateModeration();
moderation.OverallAssessment.Text = " ";
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(SwotAnalysisModerationResponseParser.TryParse(JsonSerializer.Serialize(moderation), out _, out var issue), Is.False);
Assert.That(issue, Is.EqualTo("The field overall_assessment.text is empty."));
});
}
}
[Test]
@@ -102,22 +102,22 @@ public sealed class SwotAnalysisModerationTests
withReason.ModerationNotes.Items.Clear();
withReason.ModerationNotes.EmptyMessage = "The four categories needed no changes.";
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(SwotAnalysisModerationResponseParser.TryParse(JsonSerializer.Serialize(withoutReason), out _, out var issue), Is.False);
Assert.That(issue, Does.StartWith("moderation_notes has no items"));
Assert.That(SwotAnalysisModerationResponseParser.TryParse(JsonSerializer.Serialize(withReason), out _, out _), Is.True);
});
}
}
[Test]
public void AnAnswerWithoutJsonNamesTheProblemForTheModel()
{
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(SwotAnalysisModerationResponseParser.TryParse("Here is my assessment.", out _, out var issue), Is.False);
Assert.That(issue, Does.Contain("no complete JSON object"));
});
}
}
[Test]
@@ -125,7 +125,7 @@ public sealed class SwotAnalysisModerationTests
{
var request = SwotAnalysisModeration.BuildUserRequest(CreateCategories(), "Support the investment decision", string.Empty, "A market report documents increasing demand.");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
foreach (var category in new[] { "strengths", "weaknesses", "opportunities", "threats" })
Assert.That(request, Does.Contain($"\"{category}\""));
@@ -137,7 +137,7 @@ public sealed class SwotAnalysisModerationTests
Assert.That(request, Does.Match("(?s)<analysis-focus>\\s*No additional analysis focus was provided\\.\\s*</analysis-focus>"));
Assert.That(request, Does.Match("(?s)<shared-research>\\s*A market report documents increasing demand\\.\\s*</shared-research>"));
Assert.That(request, Does.Not.Contain("<analysis-subject>"), "The moderator works on the results, not on the material.");
});
}
}
[Test]
@@ -149,13 +149,13 @@ public sealed class SwotAnalysisModerationTests
{
var prompt = SwotAnalysisModeration.BuildSystemPrompt("Answer in German.");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
foreach (var key in new[] { "\"moderation_notes\"", "\"overall_assessment\"", "\"prioritized_actions\"", "\"addressed_factor_ids\"", "\"factor_id\"" })
Assert.That(prompt, Does.Contain(key));
Assert.That(prompt, Does.EndWith("Answer in German."));
});
}
}
private static Dictionary<SwotAnalysisCategory, SwotCategoryAnalysisResult> CreateCategories() => new()
@@ -18,7 +18,7 @@ public sealed class SwotAnalysisSendToChatTests
var chatThread = AssistantSWOTAnalysis.CreateChatThreadForResult(assistantThread, visiblePrompt, result);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(chatThread.Blocks, Has.Count.EqualTo(2));
Assert.That(chatThread.Blocks[0], Is.SameAs(visiblePrompt));
@@ -27,7 +27,7 @@ public sealed class SwotAnalysisSendToChatTests
Assert.That(((ContentText)chatThread.Blocks[1].Content!).Text, Is.EqualTo("## SWOT Matrix"));
Assert.That(chatThread.Blocks[1].Content, Is.Not.SameAs(result.Content));
Assert.That(chatThread.SystemPrompt, Is.EqualTo(SystemPrompts.DEFAULT));
});
}
}
[Test]
@@ -38,11 +38,11 @@ public sealed class SwotAnalysisSendToChatTests
var chatThread = AssistantSWOTAnalysis.CreateChatThreadForResult(assistantThread, VisiblePrompt(result.Time.AddTicks(-1)), result);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(assistantThread.IsWebAddressGivenToTheModel(new Uri(ADDRESS_A_MODEL_CHOSE)), Is.True, "The hidden request counts as a message of the user.");
Assert.That(chatThread.IsWebAddressGivenToTheModel(new Uri(ADDRESS_A_MODEL_CHOSE)), Is.False);
});
}
}
[Test]
@@ -54,11 +54,11 @@ public sealed class SwotAnalysisSendToChatTests
var chatThread = AssistantSWOTAnalysis.CreateChatThreadForResult(assistantThread, VisiblePrompt(result.Time.AddTicks(-1)), result);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(chatThread.ChatId, Is.EqualTo(assistantThread.ChatId));
Assert.That(chatThread.RequiredProviderConfidence, Is.EqualTo(ConfidenceLevel.HIGH));
});
}
}
[Test]
@@ -21,12 +21,12 @@ public sealed class SwotAnalysisSessionStateTests
var reader = new AssistantSessionStateReader(writer.ToDictionary(), "SWOT Analysis");
reader.Restore(key, value => restored = value);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(restored, Is.Not.Null);
Assert.That(restored!.MatrixHeading, Is.EqualTo("SWOT Matrix"));
Assert.That(restored.Strengths.Findings.Single().Summary, Is.EqualTo("Experienced team"));
});
}
}
[Test]
@@ -44,12 +44,12 @@ public sealed class SwotAnalysisSessionStateTests
var reader = new AssistantSessionStateReader(writer.ToDictionary(), "SWOT Analysis");
reader.RestoreDictionary(key, restored);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(restored[SwotAnalysisCategory.STRENGTHS].Status, Is.EqualTo(SwotCategoryAnalysisStatus.COMPLETED));
Assert.That(restored[SwotAnalysisCategory.STRENGTHS].Result!.Findings.Single().Summary, Is.EqualTo("Experienced team"));
Assert.That(restored[SwotAnalysisCategory.WEAKNESSES].Status, Is.EqualTo(SwotCategoryAnalysisStatus.RUNNING));
});
}
}
private static SwotFinding Finding() => new()
@@ -15,13 +15,13 @@ public sealed class SwotCategoryAnalysisResponseTests
var parsed = SwotCategoryAnalysisResponseParser.TryParse(JsonSerializer.Serialize(result), out var parsedResult, out var issue);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(parsed, Is.True);
Assert.That(issue, Is.Empty);
Assert.That(parsedResult.EmptyMessage, Is.Empty);
Assert.That(parsedResult.Findings.Single().Summary, Is.EqualTo("Experienced team"));
});
}
}
[Test]
@@ -29,11 +29,11 @@ public sealed class SwotCategoryAnalysisResponseTests
{
var parsed = SwotCategoryAnalysisResponseParser.TryParse("I cannot analyze this category.", out _, out var issue);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(parsed, Is.False);
Assert.That(issue, Does.Contain("no complete JSON object"));
});
}
}
[Test]
@@ -41,11 +41,11 @@ public sealed class SwotCategoryAnalysisResponseTests
{
var parsed = SwotCategoryAnalysisResponseParser.TryParse("""{"findings": [{"summary": "Experienced team",}""", out _, out var issue);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(parsed, Is.False);
Assert.That(issue, Is.Not.Empty);
});
}
}
[Test]
@@ -53,11 +53,11 @@ public sealed class SwotCategoryAnalysisResponseTests
{
var parsed = SwotCategoryAnalysisResponseParser.TryParse("""{"empty_message": null, "findings": []}""", out _, out var issue);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(parsed, Is.False);
Assert.That(issue, Does.StartWith("The JSON object cannot be read:"));
});
}
}
[Test]
@@ -65,11 +65,11 @@ public sealed class SwotCategoryAnalysisResponseTests
{
var parsed = SwotCategoryAnalysisResponseParser.TryParse("""{"empty_message": "", "findings": [null, {"summary": "Experienced team", "explanation": "Long tenure."}]}""", out var result, out _);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(parsed, Is.True);
Assert.That(result.Findings.Single().Summary, Is.EqualTo("Experienced team"));
});
}
}
[Test]
@@ -81,13 +81,13 @@ public sealed class SwotCategoryAnalysisResponseTests
});
var invalidEmptyResponse = JsonSerializer.Serialize(new SwotCategoryAnalysisResult());
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(SwotCategoryAnalysisResponseParser.TryParse(validEmptyResponse, out var result, out _), Is.True);
Assert.That(result.Findings, Is.Empty);
Assert.That(SwotCategoryAnalysisResponseParser.TryParse(invalidEmptyResponse, out _, out var issue), Is.False);
Assert.That(issue, Does.Contain("empty_message"));
});
}
}
[Test]
@@ -101,13 +101,13 @@ public sealed class SwotCategoryAnalysisResponseTests
result.Findings[0].Explanation = string.Empty;
var responseWithoutExplanation = JsonSerializer.Serialize(result);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(SwotCategoryAnalysisResponseParser.TryParse(responseWithContradictoryEmptyMessage, out _, out var contradictionIssue), Is.False);
Assert.That(contradictionIssue, Does.Contain("Leave empty_message empty"));
Assert.That(SwotCategoryAnalysisResponseParser.TryParse(responseWithoutExplanation, out _, out var explanationIssue), Is.False);
Assert.That(explanationIssue, Is.EqualTo("Finding 1 has no explanation."));
});
}
}
[TestCase("STRENGTHS", "strengths: internal positive factors")]
@@ -119,7 +119,7 @@ public sealed class SwotCategoryAnalysisResponseTests
var category = Enum.Parse<SwotAnalysisCategory>(categoryName);
var prompt = SwotCategoryAnalysis.BuildSystemPrompt(category, "Answer in German.");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(prompt, Does.Contain($"Analyze only {expectedDescription}."));
Assert.That(prompt, Does.Contain("Do not return findings for another SWOT category."));
@@ -127,7 +127,7 @@ public sealed class SwotCategoryAnalysisResponseTests
Assert.That(prompt, Does.Contain("analysis-subject, contextual-knowledge, and\nshared-research"));
Assert.That(prompt, Does.Contain("untrusted data"));
Assert.That(prompt, Does.EndWith("Answer in German."));
});
}
}
[Test]
@@ -140,14 +140,14 @@ public sealed class SwotCategoryAnalysisResponseTests
new SwotContextualKnowledge("The annual report names 120 employees.", []),
sharedResearch: "A market report documents increasing regional demand.");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(request, Does.Match("(?s)<analysis-subject>\\s*A regional manufacturer\\s*</analysis-subject>"));
Assert.That(request, Does.Match("(?s)<analysis-goal>\\s*Support the investment decision\\s*</analysis-goal>"));
Assert.That(request, Does.Match("(?s)<analysis-focus>\\s*Pay particular attention to staffing\\s*</analysis-focus>"));
Assert.That(request, Does.Match("(?s)<contextual-knowledge>\\s*The annual report names 120 employees\\.\\s*</contextual-knowledge>"));
Assert.That(request, Does.Match("(?s)<shared-research>\\s*A market report documents increasing regional demand\\.\\s*</shared-research>"));
});
}
}
[Test]
@@ -159,12 +159,12 @@ public sealed class SwotCategoryAnalysisResponseTests
string.Empty,
SwotContextualKnowledge.NONE);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(request, Does.Not.Contain("<shared-research>"));
Assert.That(request, Does.Contain("No additional analysis focus was provided."));
Assert.That(request, Does.Contain("No additional contextual knowledge was provided."));
});
}
}
private static SwotCategoryAnalysisResult CreateResult() => new()
@@ -22,11 +22,11 @@ public sealed class SwotContextualKnowledgeTests
var oneImage = new SwotContextualKnowledge(string.Empty, [Image("chart.png")]);
var twoImages = new SwotContextualKnowledge(string.Empty, [Image("chart.png"), Image("photo.jpg")]);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(oneImage.Describe(), Is.EqualTo("The image attached to this message belongs to the contextual knowledge."));
Assert.That(twoImages.Describe(), Is.EqualTo("The 2 images attached to this message belong to the contextual knowledge."));
});
}
}
[Test]
@@ -34,11 +34,11 @@ public sealed class SwotContextualKnowledgeTests
{
var description = new SwotContextualKnowledge(DOCUMENTS, [Image("chart.png")]).Describe();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(description, Does.StartWith(DOCUMENTS));
Assert.That(description, Does.EndWith("The image attached to this message belongs to the contextual knowledge."));
});
}
}
private static FileAttachmentImage Image(string fileName) => new(fileName, $"/tmp/{fileName}", 1024);
@@ -13,10 +13,10 @@ public sealed class SwotQuadrantLabelsTests
[TestCase(SwotAnalysisCategory.THREATS, "Risiken", "Extern · Negativ")]
public void EveryQuadrantNamesItselfAndWhereItLies(SwotAnalysisCategory category, string expectedName, string expectedAxes)
{
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(LABELS.NameOf(category), Is.EqualTo(expectedName));
Assert.That(LABELS.AxesOf(category), Is.EqualTo(expectedAxes));
});
}
}
}
@@ -14,11 +14,11 @@ public sealed class SwotRepairRoundTests
{
var request = SwotModelAnswer.RepairRequestOf("Finding 1 has no explanation.");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(request, Does.Contain("Finding 1 has no explanation."));
Assert.That(request, Does.Contain("complete corrected JSON object"));
});
}
}
[Test]
@@ -28,12 +28,12 @@ public sealed class SwotRepairRoundTests
var read = await ReadAsync(VALID_ANSWER, repairRequests, ANSWER_WITHOUT_JSON);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(read.Result?.Findings.Single().Summary, Is.EqualTo("Experienced team"));
Assert.That(read.Issue, Is.Empty);
Assert.That(repairRequests, Is.Empty);
});
}
}
[Test]
@@ -43,12 +43,12 @@ public sealed class SwotRepairRoundTests
var read = await ReadAsync(ANSWER_WITHOUT_EXPLANATION, repairRequests, VALID_ANSWER);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(read.Result?.Findings.Single().Explanation, Is.EqualTo("The documents describe long employee tenure."));
Assert.That(read.Issue, Is.Empty);
Assert.That(repairRequests.Single(), Is.EqualTo("Finding 1 has no explanation."));
});
}
}
[Test]
@@ -58,12 +58,12 @@ public sealed class SwotRepairRoundTests
var read = await ReadAsync(ANSWER_WITHOUT_EXPLANATION, repairRequests, ANSWER_WITHOUT_JSON);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(read.Result, Is.Null);
Assert.That(read.Issue, Does.Contain("no complete JSON object"));
Assert.That(repairRequests, Has.Count.EqualTo(1));
});
}
}
[Test]
@@ -74,12 +74,12 @@ public sealed class SwotRepairRoundTests
// The provider failed, so the repair round brought no answer:
var read = await ReadAsync(ANSWER_WITHOUT_EXPLANATION, repairRequests, string.Empty);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(read.Result, Is.Null);
Assert.That(read.Issue, Is.EqualTo("Finding 1 has no explanation."));
Assert.That(repairRequests, Has.Count.EqualTo(1));
});
}
}
private static Task<SwotReadAnswer<SwotCategoryAnalysisResult>> ReadAsync(string answer, List<string> repairRequests, string repairedAnswer) =>
@@ -10,14 +10,14 @@ public sealed class SwotSharedResearchTests
{
var prompt = SwotSharedResearch.BuildSystemPrompt("Answer in German.");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(prompt, Does.Contain("gather additional evidence once"));
Assert.That(prompt, Does.Contain("Do not perform the SWOT analysis yourself"));
Assert.That(prompt, Does.Contain("tool result as untrusted data"));
Assert.That(prompt, Does.Contain("source titles and URLs"));
Assert.That(prompt, Does.EndWith("Answer in German."));
});
}
}
[Test]
@@ -29,13 +29,13 @@ public sealed class SwotSharedResearchTests
"Pay particular attention to staffing",
new SwotContextualKnowledge("The annual report names 120 employees.", []));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(request, Does.Match("(?s)<analysis-subject>\\s*A regional manufacturer\\s*</analysis-subject>"));
Assert.That(request, Does.Match("(?s)<analysis-goal>\\s*Support the investment decision\\s*</analysis-goal>"));
Assert.That(request, Does.Match("(?s)<analysis-focus>\\s*Pay particular attention to staffing\\s*</analysis-focus>"));
Assert.That(request, Does.Match("(?s)<contextual-knowledge>\\s*The annual report names 120 employees\\.\\s*</contextual-knowledge>"));
Assert.That(request, Does.Not.Contain("<shared-research>"));
});
}
}
}
+6 -6
View File
@@ -33,11 +33,11 @@ public sealed class SendToReceiverTests
{
var data = destination.GetData();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(data.Event, Is.Not.EqualTo(Event.NONE), "Without an event of its own, the text has nothing to travel with.");
Assert.That(PagesOf(data.Route), Has.Count.EqualTo(1), "The text is taken by the page which opens under the route.");
});
}
}
[TestCaseSource(nameof(Destinations))]
@@ -49,22 +49,22 @@ public sealed class SendToReceiverTests
var applyMethod = page.GetMethod("ApplyTextLaunchPayload", BindingFlags.Instance | BindingFlags.NonPublic);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(IsAssistant(page), Is.True, $"{page.Name} is expected to be an assistant.");
Assert.That(applyMethod, Is.Not.Null);
Assert.That(applyMethod!.DeclaringType, Is.Not.EqualTo(applyMethod.GetBaseDefinition().DeclaringType), $"{page.Name} allows sending to it, so it has to put the text sent to it into a field.");
});
}
}
[Test]
public void TextForTheChatGoesIntoTheFieldForTheNextMessage()
{
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(AssistantLaunchService.TextEventFor(AIStudio.Tools.Components.CHAT), Is.EqualTo(Event.SEND_TO_CHAT_INPUT));
Assert.That(AssistantLaunchService.TextEventFor(AIStudio.Tools.Components.SLIDE_BUILDER_ASSISTANT), Is.EqualTo(Event.SEND_TO_SLIDE_BUILDER_ASSISTANT));
});
}
}
private static List<Type> PagesOf(string route) => APP.GetTypes()
@@ -67,11 +67,11 @@ public sealed class VisualBriefingRestrictionsTests
var loaded = await this.CreateStore().LoadAsync(created.BriefingId);
Assert.That(loaded, Is.Not.Null);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(loaded!.Settings.Instruction, Is.EqualTo("Summarize the minutes."), "The save did not happen.");
AssertSame(loaded.Restrictions, FROM_MAILBOX);
});
}
}
[Test]
@@ -127,23 +127,26 @@ public sealed class VisualBriefingRestrictionsTests
var exception = Assert.Throws<VisualBriefingBuildException>(() => VisualBriefingBuildOrchestrator.ValidateProviderMaySeeContent(restricted, cloudProvider));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(exception!.Code, Is.EqualTo(VisualBriefingFailureCode.PROVIDER_NOT_ALLOWED));
Assert.DoesNotThrow(() => VisualBriefingBuildOrchestrator.ValidateProviderMaySeeContent(restricted, selfHostedProvider));
Assert.DoesNotThrow(() => VisualBriefingBuildOrchestrator.ValidateProviderMaySeeContent(new VisualBriefingManifest(), cloudProvider), "A briefing nothing was sent to may use any provider.");
});
}
}
private VisualBriefingStore CreateStore() => new(null!, NullLogger<VisualBriefingStore>.Instance, new VisualBriefingStorageOptions { DataDirectory = this.dataDirectory });
private static void AssertSame(ChatRestrictions actual, ChatRestrictions expected) => Assert.Multiple(() =>
private static void AssertSame(ChatRestrictions actual, ChatRestrictions expected)
{
Assert.That(actual.ProviderConfidence, Is.EqualTo(expected.ProviderConfidence));
Assert.That(actual.DataSecurity, Is.EqualTo(expected.DataSecurity));
Assert.That(actual.OutboundData, Is.EqualTo(expected.OutboundData));
Assert.That(actual.WebAddressesWrittenByModels, Is.EquivalentTo(expected.WebAddressesWrittenByModels));
});
using (Assert.EnterMultipleScope())
{
Assert.That(actual.ProviderConfidence, Is.EqualTo(expected.ProviderConfidence));
Assert.That(actual.DataSecurity, Is.EqualTo(expected.DataSecurity));
Assert.That(actual.OutboundData, Is.EqualTo(expected.OutboundData));
Assert.That(actual.WebAddressesWrittenByModels, Is.EquivalentTo(expected.WebAddressesWrittenByModels));
}
}
private AIStudio.Settings.Provider AddProvider(string id, LLMProviders llmProvider, bool isSelfHosted)
{
+2 -2
View File
@@ -36,12 +36,12 @@ public sealed class AttachingAFileAgainTests
File.WriteAllText(path, "The board met on Monday and on Tuesday.");
attachments.AttachInPlaceOfSameFile(FileAttachment.FromPath(path));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(attachments.Count(attachment => attachment.FilePath == path), Is.EqualTo(1), "The message would go with the old and the new text of the file.");
Assert.That(attachments.Single(attachment => attachment.FilePath == path).ProvenanceId, Is.Null, "Attaching a file again means the file as it is now.");
Assert.That(attachments, Does.Contain(otherFile));
});
}
}
[Test]
+24 -24
View File
@@ -81,13 +81,13 @@ public sealed class AttachmentProvenanceTests
var firstRequest = await this.PrepareAsync(message);
var secondRequest = await this.PrepareAsync(message);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(message.FileAttachments.Single().ProvenanceId, Is.Not.Null);
Assert.That(firstRequest, Does.Contain("The board met on Monday."));
Assert.That(secondRequest, Is.EqualTo(firstRequest));
Assert.That(this.runtime.ReadFiles, Has.Count.EqualTo(1), "Every request read every document anew, which a long chat with large documents paid for with every message.");
});
}
}
[Test]
@@ -100,11 +100,11 @@ public sealed class AttachmentProvenanceTests
await message.StoreAttachedDocumentsAsync(this.store.Client, Guid.NewGuid(), this.rustService, null, readChangedAgain: false);
var prepared = await this.PrepareAsync(message);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(prepared, Does.Contain("The board met on Monday."), "Every request of the chat sends the same text, which is what the user can look at.");
Assert.That(prepared, Does.Not.Contain("never met"));
});
}
}
[Test]
@@ -119,13 +119,13 @@ public sealed class AttachmentProvenanceTests
await message.StoreAttachedDocumentsAsync(this.store.Client, chatId, this.rustService, null, readChangedAgain: true);
var prepared = await this.PrepareAsync(message);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(prepared, Does.Contain("and on Tuesday"), "The user chose that the AI works with the files as they are now.");
Assert.That(message.FileAttachments.Single().ProvenanceId, Is.Not.EqualTo(firstId), "The text sent before belongs to what the AI received then.");
Assert.That(message.FileAttachments.Single().Version, Is.EqualTo(FileVersion.Of(this.documentPath)));
Assert.That(this.runtime.ReadFiles, Has.Count.EqualTo(2));
});
}
}
[Test]
@@ -138,12 +138,12 @@ public sealed class AttachmentProvenanceTests
await message.StoreAttachedDocumentsAsync(this.store.Client, chatId, this.rustService, null, readChangedAgain: true);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(message.HasDocumentsToStore(readChangedAgain: true), Is.False);
Assert.That(message.FileAttachments.Single().ProvenanceId, Is.EqualTo(firstId));
Assert.That(this.runtime.ReadFiles, Has.Count.EqualTo(1));
});
}
}
[Test]
@@ -155,11 +155,11 @@ public sealed class AttachmentProvenanceTests
File.Delete(this.documentPath);
var prepared = await this.PrepareAsync(message);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(message.HasDocumentsToStore(readChangedAgain: true), Is.False, "A file which is gone did not change: its stored text is all there is.");
Assert.That(prepared, Does.Contain("The board met on Monday."));
});
}
}
[Test]
@@ -184,12 +184,12 @@ public sealed class AttachmentProvenanceTests
var provenanceId = message.FileAttachments.Single().ProvenanceId!.Value;
var entries = await this.store.Client.GetEntriesAsync([provenanceId], CancellationToken.None);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(entries[provenanceId].Content, Does.Contain(FakeRuntime.REDACTION_MARKER), "The store keeps what the AI received, not what the file holds.");
Assert.That(entries[provenanceId].Content, Does.Not.Contain(SUSPICIOUS_PASSAGE));
Assert.That(entries[provenanceId].Kind, Is.EqualTo(ProvenanceKind.ATTACHMENT));
});
}
}
[Test]
@@ -202,13 +202,13 @@ public sealed class AttachmentProvenanceTests
var prepared = await this.PrepareAsync(message);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(prepared, Does.Contain("The board met on Monday."));
Assert.That(prepared, Does.Not.Contain(SUSPICIOUS_PASSAGE), "Every way of content to the AI goes through the filter, with the rules of today.");
Assert.That(prepared, Does.Contain(FakeRuntime.REDACTION_MARKER));
Assert.That(this.runtime.ReadFiles, Is.Empty, "The stored text is filtered, not the file read again.");
});
}
}
[Test]
@@ -222,13 +222,13 @@ public sealed class AttachmentProvenanceTests
await message.StoreAttachedDocumentsAsync(this.store.Client, chatId, this.rustService, null, readChangedAgain: false);
await using var context = this.store.CreateContext();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(message.HasDocumentsToStore(readChangedAgain: false), Is.False);
Assert.That(message.FileAttachments.Single().ProvenanceId, Is.EqualTo(firstId));
Assert.That(context.Entries.Count(), Is.EqualTo(1));
Assert.That(this.runtime.ReadFiles, Has.Count.EqualTo(1));
});
}
}
[Test]
@@ -239,11 +239,11 @@ public sealed class AttachmentProvenanceTests
await message.StoreAttachedDocumentsAsync(new NoProvenanceStoreClient("SQLite", "not available"), Guid.NewGuid(), this.rustService, null, readChangedAgain: false);
var prepared = await this.PrepareAsync(message);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(message.FileAttachments.Single().ProvenanceId, Is.Null);
Assert.That(prepared, Does.Contain("The board met on Monday."), "Without the store, a chat works as it always did.");
});
}
}
[Test]
@@ -253,11 +253,11 @@ public sealed class AttachmentProvenanceTests
var prepared = await this.PrepareAsync(message);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(prepared, Does.Contain("The board met on Monday."), "A store which lost the entry, e.g., one restored from an older backup, must not cost the document.");
Assert.That(this.runtime.ReadFiles, Has.Count.EqualTo(1));
});
}
}
[Test]
@@ -270,12 +270,12 @@ public sealed class AttachmentProvenanceTests
await message.StoreAttachedDocumentsAsync(this.store.Client, Guid.NewGuid(), this.rustService, null, readChangedAgain: false);
var chatsWithEntries = await this.store.Client.GetChatIdsAsync(CancellationToken.None);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(message.HasDocumentsToStore(readChangedAgain: false), Is.False, "An image goes to the AI as an image, not as a text to keep.");
Assert.That(message.FileAttachments.Single().ProvenanceId, Is.Null);
Assert.That(chatsWithEntries, Is.Empty);
});
}
}
[Test]
@@ -289,7 +289,7 @@ public sealed class AttachmentProvenanceTests
using var document = JsonDocument.Parse(json);
var storedAttachments = document.RootElement.GetProperty("file_attachments");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(attachment.Normalize().ProvenanceId, Is.EqualTo(attachment.ProvenanceId), "Every request normalizes its attachments first.");
Assert.That(attachment.Normalize().Version, Is.EqualTo(attachment.Version), "Otherwise, a chat which reads changed files again would read every file again.");
@@ -300,7 +300,7 @@ public sealed class AttachmentProvenanceTests
Assert.That(transcript.WithFilePath(Path.Combine(this.directoryPath, "moved.md")).ProvenanceId, Is.EqualTo(transcript.ProvenanceId));
Assert.That(loaded.FileAttachments[0].ProvenanceId, Is.EqualTo(attachment.ProvenanceId));
Assert.That(storedAttachments[1].TryGetProperty("provenance_id", out _), Is.False, "An attachment never sent is stored as before, also in a chat template.");
});
}
}
private ContentText MessageWithDocument() => new() { Text = "Summarize the minutes.", FileAttachments = [FileAttachment.FromPath(this.documentPath)] };
+26 -26
View File
@@ -25,11 +25,11 @@ public sealed class ChatBlockActionsTests
var removed = WhileWriting(chat).TryRemove(chat, answer.Content!);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(removed, Is.False);
Assert.That(chat.Blocks.Select(x => x.Content), Is.EqualTo(new[] { question.Content, answer.Content }), "The answer stays, since the job goes on writing into it.");
});
}
}
[Test]
@@ -41,13 +41,13 @@ public sealed class ChatBlockActionsTests
var blocks = chat.Blocks.ToList();
var actions = WhileWriting(chat);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(actions.TryRemove(chat, firstQuestion.Content!), Is.False);
Assert.That(actions.TryRemove(chat, firstAnswer.Content!), Is.False);
Assert.That(actions.TryRemoveForRegenerate(chat, firstAnswer.Content!), Is.False);
Assert.That(chat.Blocks, Is.EqualTo(blocks), "The job stores the conversation meanwhile, so no message may change, not even an older one.");
});
}
}
[Test]
@@ -58,13 +58,13 @@ public sealed class ChatBlockActionsTests
var chat = Chat(question, answer);
var actions = WhileWriting(chat);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(actions.TryEditLastUserBlock(chat, question.Content!), Is.False);
Assert.That(actions.TryEditLastBlock(chat, question.Content!), Is.False);
Assert.That(chat.Blocks, Has.Count.EqualTo(2), "Both the question and the answer being written stay.");
Assert.That(chat.Draft.IsEmpty, Is.True, "The question does not become the draft, since it could not be sent anyway.");
});
}
}
[Test]
@@ -75,11 +75,11 @@ public sealed class ChatBlockActionsTests
var removed = WhileWriting(chat).TryRemoveForRegenerate(chat, answer.Content!);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(removed, Is.False, "Regenerating would leave a second answer next to the one being written.");
Assert.That(chat.Blocks, Has.Count.EqualTo(2));
});
}
}
[Test]
@@ -90,11 +90,11 @@ public sealed class ChatBlockActionsTests
var rolledBack = WhileWriting(chat).TryRollBack(chat, firstAnswer.Content!);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(rolledBack, Is.False);
Assert.That(chat.Blocks, Has.Count.EqualTo(4));
});
}
}
[Test]
@@ -106,11 +106,11 @@ public sealed class ChatBlockActionsTests
var removed = Done().TryRemove(chat, answer.Content!);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(removed, Is.True);
Assert.That(chat.Blocks.Select(x => x.Content), Is.EqualTo(new[] { question.Content }));
});
}
}
[Test]
@@ -122,11 +122,11 @@ public sealed class ChatBlockActionsTests
var removed = Done().TryRemoveForRegenerate(chat, answer.Content!);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(removed, Is.True);
Assert.That(chat.Blocks.Select(x => x.Content), Is.EqualTo(new[] { question.Content }), "The question stays, to be answered anew.");
});
}
}
[Test]
@@ -138,11 +138,11 @@ public sealed class ChatBlockActionsTests
var rolledBack = Done().TryRollBack(chat, firstAnswer.Content!);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(rolledBack, Is.True);
Assert.That(chat.Blocks.Select(x => x.Content), Is.EqualTo(new[] { firstQuestion.Content, firstAnswer.Content }));
});
}
}
[Test]
@@ -153,12 +153,12 @@ public sealed class ChatBlockActionsTests
var edited = Done().TryEditLastUserBlock(chat, question.Content!);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(edited, Is.True);
Assert.That(chat.Blocks, Is.Empty, "The question goes back into the composer, and the answer to it goes.");
Assert.That(chat.Draft.Text, Is.EqualTo("Question"));
});
}
}
[Test]
@@ -169,12 +169,12 @@ public sealed class ChatBlockActionsTests
var edited = Done().TryEditLastBlock(chat, question.Content!);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(edited, Is.True);
Assert.That(chat.Blocks, Is.Empty);
Assert.That(chat.Draft.Text, Is.EqualTo("Question"));
});
}
}
[Test]
@@ -185,12 +185,12 @@ public sealed class ChatBlockActionsTests
var edited = Done().TryEditLastUserBlock(chat, new ContentText { Text = "Question of another chat" });
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(edited, Is.False);
Assert.That(chat.Blocks, Has.Count.EqualTo(2), "Removing the last answer only makes sense together with the question to it.");
Assert.That(chat.Draft.IsEmpty, Is.True);
});
}
}
[Test]
@@ -208,11 +208,11 @@ public sealed class ChatBlockActionsTests
var removed = actions.TryRemove(chat, answer.Content!);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(removed, Is.True);
Assert.That(askedChatIds, Is.EqualTo(new[] { chat.ChatId }), "Only the chat being changed decides.");
});
}
}
[Test]
@@ -221,7 +221,7 @@ public sealed class ChatBlockActionsTests
var actions = Done();
var content = new ContentText { Text = "Question" };
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(actions.AreAllowed(null), Is.False);
Assert.That(actions.TryRemove(null, content), Is.False);
@@ -229,7 +229,7 @@ public sealed class ChatBlockActionsTests
Assert.That(actions.TryRollBack(null, content), Is.False);
Assert.That(actions.TryEditLastUserBlock(null, content), Is.False);
Assert.That(actions.TryEditLastBlock(null, content), Is.False);
});
}
}
/// <summary>
+2 -2
View File
@@ -62,11 +62,11 @@ public sealed class ChatDraftAutosaveTests
storage.IsBusy = false;
await storage.WaitForStoreAsync();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(storedWhileBusy, Is.Zero, "The AI job changes the conversation meanwhile, so storing it from elsewhere could read it in the middle of a change.");
Assert.That(storage.StoredChats, Is.EqualTo(new[] { chat }), "Once the answer is done, the change must still reach the disk.");
});
}
}
[Test]
+16 -16
View File
@@ -46,12 +46,12 @@ public sealed class ChatDraftTests
var added = draft.AddAttachments([TRANSCRIPT]);
var addedAgain = draft.AddAttachments([TRANSCRIPT with { FileSizeBytes = 1024 }]);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(added, Is.True);
Assert.That(addedAgain, Is.False, "The same file counts once, even in a newer state.");
Assert.That(draft.FileAttachments, Is.EqualTo(new FileAttachment[] { TRANSCRIPT }), "The composer would show the transcript twice otherwise.");
});
}
}
[Test]
@@ -74,11 +74,11 @@ public sealed class ChatDraftTests
draft.ReplaceAttachments(attachment => attachment with { FilePath = $"/moved{attachment.FilePath}" });
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(draft.FileAttachments.Select(attachment => attachment.FilePath), Is.EqualTo(new[] { "/moved/documents/agenda.pdf", "/moved/data/tempChats/meeting-transcript-0001.md" }));
Assert.That(draft.MediaTranscripts, Has.Count.EqualTo(1), "A transcript stays a transcript, so removing it from the composer still deletes its file.");
});
}
}
[Test]
@@ -90,11 +90,11 @@ public sealed class ChatDraftTests
draft.RestoreFromMessage(new ContentText { Text = "Summarize the minutes", FileAttachments = [MINUTES] });
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(draft.Text, Is.EqualTo("Summarize the minutes"), "The message to edit replaces the older draft.");
Assert.That(draft.FileAttachments, Is.EqualTo(new[] { MINUTES, TRANSCRIPT }), "The draft is the only owner of its transcripts, so dropping them would leave their files behind.");
});
}
}
[Test]
@@ -107,11 +107,11 @@ public sealed class ChatDraftTests
draft.Clear();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(draft.IsEmpty, Is.True, "The attachments belong to the sent message now, and a transcript has only one owner.");
Assert.That(draft.PreviewedTexts, Is.Empty, "A text the preview read went with the message, or is of no use to the next one.");
});
}
}
[Test]
@@ -126,13 +126,13 @@ public sealed class ChatDraftTests
draft.NotePreviewedText(otherFile);
var replacedBySecond = draft.NotePreviewedText(secondPreview);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(replacedByFirst, Is.Null);
Assert.That(replacedBySecond, Is.EqualTo(firstPreview.ProvenanceId), "Nothing refers to the text of the first preview any longer, so the caller removes it from the store.");
Assert.That(draft.PreviewedTexts, Is.EquivalentTo(new[] { otherFile, secondPreview }));
Assert.That(draft.GetPreviewedText(AGENDA.FilePath), Is.EqualTo(secondPreview));
});
}
}
[Test]
@@ -148,12 +148,12 @@ public sealed class ChatDraftTests
draft.MirrorAttachments([MINUTES]);
var forgotten = draft.ForgetPreviewsOfDetachedFiles();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(forgotten, Is.EqualTo(new[] { agendaPreview.ProvenanceId }), "Nothing refers to the text any longer, so the caller removes it from the store.");
Assert.That(draft.PreviewedTexts, Is.EqualTo(new[] { minutesPreview }));
Assert.That(draft.ForgetPreviewsOfDetachedFiles(), Is.Empty);
});
}
}
[Test]
@@ -178,12 +178,12 @@ public sealed class ChatDraftTests
var loaded = JsonSerializer.Deserialize<ChatThread>(JsonSerializer.Serialize(thread, WorkspaceBehaviour.JSON_OPTIONS), WorkspaceBehaviour.JSON_OPTIONS)!;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(loaded.Draft.Text, Is.EqualTo("Summarize the meeting"));
Assert.That(loaded.Draft.FileAttachments, Is.EqualTo(new[] { AGENDA, CHART, STAGED_TRANSCRIPT }), "A transcript loaded as a plain document would no longer be managed, and its file would never be deleted.");
Assert.That(loaded.Draft.MediaTranscripts.Single().IsStaged, Is.True, "A staged transcript has to move into the chat directory once the chat is stored.");
});
}
}
[Test]
@@ -216,12 +216,12 @@ public sealed class ChatDraftTests
var loaded = JsonSerializer.Deserialize<ChatThread>(CHAT_WITH_PENDING_TRANSCRIPTS, WorkspaceBehaviour.JSON_OPTIONS)!;
var stored = JsonSerializer.Serialize(loaded, WorkspaceBehaviour.JSON_OPTIONS);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(loaded.Draft.FileAttachments, Is.EqualTo(new FileAttachment[] { TRANSCRIPT }), "Opening the chat has to bring the transcript back into the composer.");
Assert.That(stored, Does.Not.Contain("pending_media_transcripts"), "The draft holds the transcript now, so storing the chat must not write the former list again.");
Assert.That(stored, Does.Contain("\"draft\""));
});
}
}
[Test]
+25 -22
View File
@@ -56,14 +56,14 @@ public sealed class ChatRestrictionsTests
foreach (var combined in new[] { fromDataSource.StricterOf(fromMailbox), fromMailbox.StricterOf(fromDataSource) })
{
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(combined.ProviderConfidence, Is.EqualTo(ConfidenceLevel.HIGH));
Assert.That(combined.DataSecurity, Is.EqualTo(DataSourceSecurity.SELF_HOSTED), "Data for self-hosted providers only keeps them as the only choice, whichever side brings it.");
Assert.That(combined.OutboundData.Restriction, Is.EqualTo(OutboundDataRestriction.ONLY_CONFIGURED_SERVICES));
Assert.That(combined.OutboundData.Reference, Is.EqualTo(MAILBOX_ID), "The source which demands the strictest restriction is the one to name.");
Assert.That(combined.WebAddressesWrittenByModels, Is.EquivalentTo(new[] { RequestKey("https://example.org/report"), RequestKey("https://example.org/minutes") }), "What models wrote on either side never counts as given.");
});
}
}
}
@@ -72,11 +72,11 @@ public sealed class ChatRestrictionsTests
{
var restrictions = ChatRestrictions.NONE.WithWebAddressesWrittenByModelIn("The report is on https://EXAMPLE.org/report#summary, and the minutes are on http://10.20.30.40:8000/minutes/. Write to ftp://example.org/upload.");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(restrictions.WebAddressesWrittenByModels, Is.EquivalentTo(new[] { RequestKey("https://example.org/report"), RequestKey("http://10.20.30.40:8000/minutes/") }), "Only addresses a web page could be read from count, compared as the server gets asked for them.");
Assert.That(ChatRestrictions.NONE.WebAddressesWrittenByModels, Is.Empty, "Adding addresses leaves NONE as it is.");
});
}
}
[Test]
@@ -84,11 +84,11 @@ public sealed class ChatRestrictionsTests
{
var restrictions = new ChatRestrictions(ConfidenceLevel.MEDIUM, DataSourceSecurity.ALLOW_ANY, new(OutboundDataRestriction.ONLY_LINKS_FROM_CHAT, OutboundDataOrigin.DATA_SOURCE, DATA_SOURCE_ID), []).WithWebAddressesWrittenByModelIn("See https://example.org/report.");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
AssertSame(restrictions.StricterOf(ChatRestrictions.NONE), restrictions);
AssertSame(ChatRestrictions.NONE.StricterOf(restrictions), restrictions);
});
}
}
[Test]
@@ -131,11 +131,11 @@ public sealed class ChatRestrictionsTests
chatToStart.RequireRestrictions(new(ConfidenceLevel.HIGH, DataSourceSecurity.SELF_HOSTED, new(OutboundDataRestriction.ONLY_CONFIGURED_SERVICES, OutboundDataOrigin.MAILBOX, MAILBOX_ID), []));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
AssertSame(chatToStart.CaptureRestrictions(), new(ConfidenceLevel.HIGH, DataSourceSecurity.SELF_HOSTED, new(OutboundDataRestriction.ONLY_CONFIGURED_SERVICES, OutboundDataOrigin.MAILBOX, MAILBOX_ID), []));
AssertSame(assistantThread.CaptureRestrictions(), ChatRestrictions.NONE);
});
}
}
[Test]
@@ -148,11 +148,11 @@ public sealed class ChatRestrictionsTests
chat.RequireProviderConfidence(ConfidenceLevel.HIGH);
chat.RequireDataSecurity(DataSourceSecurity.SELF_HOSTED);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(snapshot.ProviderConfidence, Is.EqualTo(ConfidenceLevel.LOW), "Content which left the chat does not hold what the chat read afterward.");
Assert.That(snapshot.DataSecurity, Is.EqualTo(DataSourceSecurity.NOT_SPECIFIED));
});
}
}
[Test]
@@ -162,12 +162,12 @@ public sealed class ChatRestrictionsTests
var selfHostedProvider = this.AddProvider(SELF_HOSTED_PROVIDER_ID, LLMProviders.SELF_HOSTED, isSelfHosted: true);
var restrictions = ChatRestrictions.NONE with { DataSecurity = DataSourceSecurity.SELF_HOSTED };
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(restrictions.IsLLMProviderAllowed(cloudProvider), Is.False);
Assert.That(restrictions.IsLLMProviderAllowed(selfHostedProvider), Is.True);
Assert.That(ChatRestrictions.NONE.IsLLMProviderAllowed(cloudProvider), Is.True, "Content which demands nothing may go to any provider.");
});
}
}
[Test]
@@ -178,11 +178,11 @@ public sealed class ChatRestrictionsTests
var higherLevels = Enum.GetValues<ConfidenceLevel>().Where(candidate => candidate > level).ToList();
Assume.That(higherLevels, Is.Not.Empty, "The provider has the highest level there is, so no content can demand more of it.");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That((ChatRestrictions.NONE with { ProviderConfidence = level }).IsLLMProviderAllowed(provider), Is.True);
Assert.That((ChatRestrictions.NONE with { ProviderConfidence = higherLevels.Min() }).IsLLMProviderAllowed(provider), Is.False);
});
}
}
[Test]
@@ -194,21 +194,24 @@ public sealed class ChatRestrictionsTests
chat.RequireDataSecurity(DataSourceSecurity.SELF_HOSTED);
ChatThread? newChat = null;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(chat.IsLLMProviderAllowed(cloudProvider), Is.EqualTo(chat.CaptureRestrictions().IsLLMProviderAllowed(cloudProvider)));
Assert.That(chat.IsLLMProviderAllowed(selfHostedProvider), Is.EqualTo(chat.CaptureRestrictions().IsLLMProviderAllowed(selfHostedProvider)));
Assert.That(newChat.IsLLMProviderAllowed(cloudProvider), Is.True, "A new chat demands nothing yet.");
});
}
}
private static void AssertSame(ChatRestrictions actual, ChatRestrictions expected) => Assert.Multiple(() =>
private static void AssertSame(ChatRestrictions actual, ChatRestrictions expected)
{
Assert.That(actual.ProviderConfidence, Is.EqualTo(expected.ProviderConfidence));
Assert.That(actual.DataSecurity, Is.EqualTo(expected.DataSecurity));
Assert.That(actual.OutboundData, Is.EqualTo(expected.OutboundData));
Assert.That(actual.WebAddressesWrittenByModels, Is.EquivalentTo(expected.WebAddressesWrittenByModels));
});
using (Assert.EnterMultipleScope())
{
Assert.That(actual.ProviderConfidence, Is.EqualTo(expected.ProviderConfidence));
Assert.That(actual.DataSecurity, Is.EqualTo(expected.DataSecurity));
Assert.That(actual.OutboundData, Is.EqualTo(expected.OutboundData));
Assert.That(actual.WebAddressesWrittenByModels, Is.EquivalentTo(expected.WebAddressesWrittenByModels));
}
}
private static string RequestKey(string address) => WebAddresses.CreateRequestKey(new Uri(address));
@@ -41,11 +41,11 @@ public sealed class ChatThreadAugmentedDataTests
var loaded = JsonSerializer.Deserialize<ChatThread>(CHAT_WITH_AUGMENTED_DATA, WorkspaceBehaviour.JSON_OPTIONS);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(loaded, Is.Not.Null, "An older chat file must still load.");
Assert.That(loaded!.AugmentedData, Is.Empty, "It belonged to a request long over; the next message retrieves anew.");
Assert.That(loaded.Name, Is.EqualTo("An older chat"));
});
}
}
}
+6 -6
View File
@@ -67,14 +67,14 @@ public sealed class ChatThreadEditTests
thread.Remove(answer.Content!);
composer.RestoreFromDraft(thread.Draft);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(thread.Blocks, Is.Empty, "Both the question and the stopped answer leave the chat.");
Assert.That(File.Exists(transcript.FilePath), Is.True, "The composer still carries the transcript, so its file has to stay.");
Assert.That(composer.FileAttachments.Single().Exists, Is.True, "The edited message goes out with the transcript, not with a reference to a deleted file.");
Assert.That(thread.Draft.MediaTranscripts.Select(x => x.FilePath), Is.EqualTo(new[] { transcript.FilePath }), "The draft owns the transcript now: opening the chat again brings it back, and removing it from the composer deletes the file.");
Assert.That(thread.Draft.Text, Is.EqualTo("Summarize the meeting"), "The message being edited is the draft now, its text included.");
});
}
}
[Test]
@@ -101,11 +101,11 @@ public sealed class ChatThreadEditTests
thread.Remove(question.Content!);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(File.Exists(transcript.FilePath), Is.False, "Nothing refers to the transcript of a deleted message any longer, so its file has to go.");
Assert.That(thread.Draft.IsEmpty, Is.True, "A deleted message hands nothing over to the draft.");
});
}
}
[Test]
@@ -121,12 +121,12 @@ public sealed class ChatThreadEditTests
thread.RemoveForEditing(new ContentText { Text = "Question of another chat", FileAttachments = [transcript] });
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(thread.Blocks, Has.Count.EqualTo(2), "The message to edit is not part of this chat, so no block goes.");
Assert.That(thread.Draft.IsEmpty, Is.True, "A message this chat does not hold never becomes its draft.");
Assert.That(File.Exists(transcript.FilePath), Is.True);
});
}
}
/// <summary>
+6 -6
View File
@@ -38,12 +38,12 @@ public sealed class ChatThreadFilePathTests
chat.RewriteFilePaths(Move);
var message = (ContentText)chat.Blocks[0].Content!;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(message.FileAttachments.Select(attachment => attachment.FilePath), Is.EqualTo(new[] { New("report.pdf"), New("meeting.md"), New("chart.png") }));
Assert.That(photo.Source, Is.EqualTo(New("photo.jpg")), "The model gets such an image from its path.");
Assert.That(chat.Draft.FileAttachments.Select(attachment => attachment.FilePath), Is.EqualTo(new[] { New("call.md") }), "The draft is the one place every path rewrite once forgot.");
});
}
}
[Test]
@@ -60,11 +60,11 @@ public sealed class ChatThreadFilePathTests
chat.RewriteFilePaths(Move);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(((ContentText)chat.Blocks[0].Content!).FileAttachments.Single(), Is.SameAs(elsewhere));
Assert.That(linked.Source, Is.EqualTo("https://example.org/chart.png"), "Only an image held by a local path has a path to rewrite.");
});
}
}
[Test]
@@ -78,14 +78,14 @@ public sealed class ChatThreadFilePathTests
var movedPicture = picture.WithFilePath(New("chart.png"));
var movedDocument = document.WithFilePath(New("report.pdf"));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(movedTranscript, Is.EqualTo(new ManagedTranscriptAttachment("meeting.md", New("meeting.md"), 512, "meeting.mp3", true)), "A transcript has to stay a transcript, or deleting the chat leaves its file behind.");
Assert.That(movedPicture, Is.TypeOf<FileAttachmentImage>());
Assert.That(((FileAttachmentImage)movedPicture).Source, Is.EqualTo(New("chart.png")), "The model gets the image from its source, not from its path.");
Assert.That(movedPicture.FilePathAsUrl, Is.EqualTo(new FileAttachmentImage("chart.png", New("chart.png"), 2048).FilePathAsUrl), "The preview of the image loads it from there.");
Assert.That(movedDocument, Is.EqualTo(new FileAttachment(FileAttachmentType.DOCUMENT, "report.pdf", New("report.pdf"), 1024)));
});
}
}
private static string Old(string fileName) => Path.Combine(OLD_DIRECTORY, fileName);
@@ -100,12 +100,12 @@ public sealed class ChatThreadOutboundDataRestrictionTests
[TestCase(OutboundDataRestriction.UNRESTRICTED)]
public void ARestrictionThisVersionDoesNotKnowCountsAsTheStrictest(OutboundDataRestriction known)
{
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(UNKNOWN_RESTRICTION.StricterOf(known), Is.EqualTo(OutboundDataRestriction.ONLY_CONFIGURED_SERVICES));
Assert.That(known.StricterOf(UNKNOWN_RESTRICTION), Is.EqualTo(OutboundDataRestriction.ONLY_CONFIGURED_SERVICES));
Assert.That(UNKNOWN_RESTRICTION.StricterOf(UNKNOWN_RESTRICTION), Is.EqualTo(OutboundDataRestriction.ONLY_CONFIGURED_SERVICES));
});
}
}
[Test]
@@ -28,7 +28,7 @@ public sealed class ChatThreadReportedHistoryTests
var history = thread.ReportedHistoryFor(MODEL);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(history.IsKnown, Is.True);
Assert.That(history.PromptTokens, Is.EqualTo(1200));
@@ -38,7 +38,7 @@ public sealed class ChatThreadReportedHistoryTests
// would include the reasoning the next request never carries:
//
Assert.That(history.LastAnswer, Is.EqualTo("Answer 2"));
});
}
}
[Test]
@@ -54,12 +54,12 @@ public sealed class ChatThreadReportedHistoryTests
var thread = Thread(Question(1), Block(ChatRole.AI, reasoningOnly, 2));
var history = thread.ReportedHistoryFor(MODEL);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(history.IsKnown, Is.True);
Assert.That(history.PromptTokens, Is.EqualTo(1200));
Assert.That(history.LastAnswer, Is.Empty);
});
}
}
[Test]
@@ -135,12 +135,12 @@ public sealed class ChatThreadReportedHistoryTests
thread.Remove(lastAnswer.Content!);
var history = thread.ReportedHistoryFor(MODEL);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(history.IsKnown, Is.True, "The first answer is the last block again, with exactly the blocks it was reported for.");
Assert.That(history.PromptTokens, Is.EqualTo(1200));
Assert.That(history.LastAnswer, Is.EqualTo("Answer 2"));
});
}
}
[Test]
@@ -152,12 +152,12 @@ public sealed class ChatThreadReportedHistoryTests
thread.RollBackTo(firstAnswer.Content!);
var history = thread.ReportedHistoryFor(MODEL);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(history.IsKnown, Is.True);
Assert.That(history.PromptTokens, Is.EqualTo(1200));
Assert.That(history.LastAnswer, Is.EqualTo("Answer 2"));
});
}
}
private static ChatThread Thread(params ContentBlock[] blocks) => new()
@@ -24,13 +24,13 @@ public sealed class ChatThreadRestrictionsOfTests
assistantThread.RequireRestrictionsOf(researchThread);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(assistantThread.RequiredProviderConfidence, Is.EqualTo(ConfidenceLevel.HIGH));
Assert.That(assistantThread.DataSecurity, Is.EqualTo(DataSourceSecurity.SELF_HOSTED));
Assert.That(assistantThread.RequiredOutboundDataRestriction.Restriction, Is.EqualTo(OutboundDataRestriction.ONLY_LINKS_FROM_CHAT));
Assert.That(assistantThread.RequiredOutboundDataRestriction.Reference, Is.EqualTo(DATA_SOURCE_ID));
});
}
}
[Test]
@@ -60,12 +60,12 @@ public sealed class ChatThreadRestrictionsOfTests
assistantThread.RequireRestrictionsOf(researchThread);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(assistantThread.RequiredProviderConfidence, Is.EqualTo(ConfidenceLevel.HIGH));
Assert.That(assistantThread.DataSecurity, Is.EqualTo(DataSourceSecurity.SELF_HOSTED));
Assert.That(assistantThread.RequiredOutboundDataRestriction.Restriction, Is.EqualTo(OutboundDataRestriction.ONLY_CONFIGURED_SERVICES));
});
}
}
[Test]
@@ -75,12 +75,12 @@ public sealed class ChatThreadRestrictionsOfTests
assistantThread.RequireRestrictionsOf(new ChatThread());
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(assistantThread.RequiredProviderConfidence, Is.EqualTo(ConfidenceLevel.NONE));
Assert.That(assistantThread.DataSecurity, Is.EqualTo(DataSourceSecurity.NOT_SPECIFIED));
Assert.That(assistantThread.RequiredOutboundDataRestriction, Is.EqualTo(OutboundDataRequirement.NONE));
Assert.That(assistantThread.WebAddressesWrittenByModels, Is.Empty);
});
}
}
}
+8 -8
View File
@@ -41,11 +41,11 @@ public sealed class ChatThreadRollbackTests
var rolledBack = thread.RollBackTo(answer.Content!);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(rolledBack, Is.True, "Two blocks came after the answer, so the rollback removed something.");
Assert.That(Texts(thread), Is.EqualTo(new[] { "First question", "First answer" }), "The hidden prompt goes with the answer to it, although the user never saw it.");
});
}
}
[Test]
@@ -117,7 +117,7 @@ public sealed class ChatThreadRollbackTests
thread.RollBackTo(answer.Content!);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(thread.AugmentedData, Is.Empty, "Nobody knows whether the retrieved data belongs to a kept or to a removed message, so it must not reach the next system prompt.");
Assert.That(thread.AISelectedDataSources, Is.Empty, "The data sources an agent picked belong to the same retrieval as the data.");
@@ -125,7 +125,7 @@ public sealed class ChatThreadRollbackTests
Assert.That(options.DisableDataSources, Is.False);
Assert.That(options.AutomaticDataSourceSelection, Is.True);
Assert.That(options.PreselectedDataSourceIds, Is.EqualTo(new[] { "handbook" }));
});
}
}
[Test]
@@ -150,12 +150,12 @@ public sealed class ChatThreadRollbackTests
thread.RollBackTo(answer.Content!);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(thread.DataSecurity, Is.EqualTo(DataSourceSecurity.SELF_HOSTED), "The confidential data was seen by this chat, so no cloud provider may continue it.");
Assert.That(thread.RequiredProviderConfidence, Is.EqualTo(ConfidenceLevel.HIGH), "The confidence the data demanded stays, although the message which brought it in is gone.");
Assert.That(thread.RequiredOutboundDataRestriction, Is.EqualTo(restriction), "The mail content was read by this chat, so it may still carry it out no further than before.");
});
}
}
[Test]
@@ -194,13 +194,13 @@ public sealed class ChatThreadRollbackTests
private static void AssertUnchanged(ChatThread thread, bool rolledBack, params string[] expectedTexts)
{
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(rolledBack, Is.False, "Nothing came after the content, so there was nothing to roll back.");
Assert.That(Texts(thread), Is.EqualTo(expectedTexts), "A rollback without anything to remove leaves the blocks alone.");
Assert.That(thread.AugmentedData, Is.EqualTo("A chunk from the handbook"), "A rollback without anything to remove keeps the last retrieval, because it still belongs to the last message.");
Assert.That(thread.AISelectedDataSources, Has.Count.EqualTo(1));
});
}
}
private static ContentBlock Block(ChatRole role, string text, int minute, bool hidden = false) => new()
+12 -12
View File
@@ -29,12 +29,12 @@ public sealed class ChatThreadWebAddressTests
{
var thread = Thread(Block(ChatRole.USER, "Please summarize https://example.org/report for me.", 1));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("https://example.org/report")), Is.True);
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("https://EXAMPLE.org/report#summary")), Is.True, "The server gets the same request.");
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("https://example.org/report?budget=2026")), Is.False, "The query would carry something the user never wrote.");
});
}
}
[Test]
@@ -43,12 +43,12 @@ public sealed class ChatThreadWebAddressTests
// An internal service such as a simple Python web server for the results of a data science run:
var thread = Thread(Block(ChatRole.USER, "The results of run 17 are on http://10.20.30.40:8000/run-17/, please compare them with run 16.", 1));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("http://10.20.30.40:8000/run-17/")), Is.True);
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("http://10.20.30.40:8000/run-16/")), Is.False, "The user named run 16, but not its address.");
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("https://10.20.30.40:8000/run-17/")), Is.False, "Another scheme is another request.");
});
}
}
[Test]
@@ -89,11 +89,11 @@ public sealed class ChatThreadWebAddressTests
using var rustService = new RustService("1", "unused");
thread.PrepareSystemPrompt(new SettingsManager(NullLogger<SettingsManager>.Instance, rustService));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("https://handbook.example.org/")), Is.True);
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("https://intranet.example.org/travel")), Is.True, "The RAG process found it in a data source, the model did not choose it.");
});
}
}
[Test]
@@ -131,11 +131,11 @@ public sealed class ChatThreadWebAddressTests
var thread = Thread(Block(ChatRole.USER, "Make slides of this: the details are on https://example.org/chosen, and our handbook is on https://example.org/handbook.", 1));
thread.RequireRestrictions(ChatRestrictions.NONE.WithWebAddressesWrittenByModelIn("The details are on https://example.org/chosen."));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("https://example.org/chosen")), Is.False, "A model wrote it, so it could carry anything of the chat it came from.");
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("https://example.org/handbook")), Is.True, "Another address in the same message still counts.");
});
}
}
[Test]
@@ -151,11 +151,11 @@ public sealed class ChatThreadWebAddressTests
thread.RequireRestrictions(ChatRestrictions.NONE.WithWebAddressesWrittenByModelIn("See https://example.org/chosen and https://example.org/sources/2026."));
thread.RuntimeWebAddressesFromTools.Add(WebAddresses.CreateRequestKey(new Uri("https://example.org/chosen")));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("https://example.org/chosen")), Is.True, "A tool returned it in this chat, so it was not only a model which wrote it.");
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("https://example.org/sources/2026")), Is.True, "A document the user attached holds it.");
});
}
}
[Test]
@@ -177,11 +177,11 @@ public sealed class ChatThreadWebAddressTests
copy.RequireRestrictions(ChatRestrictions.NONE.WithWebAddressesWrittenByModelIn("See https://example.org/chosen."));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(copy.IsWebAddressGivenToTheModel(new Uri("https://example.org/chosen")), Is.False);
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("https://example.org/chosen")), Is.True, "What the copy took over must not reach the chat it was copied from.");
});
}
}
private static ChatThread Thread(params ContentBlock[] blocks) => new() { Blocks = [..blocks] };
@@ -21,13 +21,13 @@ public sealed class ContentBlockApartFromChatTests
var template = new ChatThread { ChatId = Guid.NewGuid(), Blocks = chat.Blocks.Select(block => block.CloneApartFromChat()).ToList() };
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(template.CollectProvenanceIds(), Is.Empty, "The entries belong to the chat and are deleted with it.");
Assert.That(template.Blocks.Select(block => ((ContentText)block.Content!).Text), Is.EqualTo(new[] { "Summarize the minutes.", "The board met on Monday." }));
Assert.That(template.Blocks[0].Content!.FileAttachments.Select(attachment => attachment.FilePath), Is.EqualTo(new[] { "/tmp/minutes.txt" }), "The file is read anew by every chat it reaches.");
Assert.That(chat.CollectProvenanceIds(), Has.Count.EqualTo(2), "The chat itself keeps what it received.");
});
}
}
[Test]
+2 -2
View File
@@ -15,11 +15,11 @@ public sealed class ContentPresentationTests
presentation.Data = JsonSerializer.SerializeToElement(new { heading = "Second" });
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(firstHashCode, Is.Not.Zero);
Assert.That(presentation.GetDataHashCode(), Is.Not.EqualTo(firstHashCode));
});
}
}
[Test]
+24 -24
View File
@@ -53,12 +53,12 @@ public sealed class ConversationPartsTests
var parts = ConversationParts.Of(thread, "You are helpful.", "And of Italy?", null, imagesAreSent: true, toolDefinitions: null);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(parts.Texts, Is.EqualTo(new[] { "You are helpful.", "What is the capital of France?", "Paris." }));
Assert.That(parts.DraftText, Is.EqualTo("And of Italy?"));
Assert.That(parts.GrowingTexts, Is.Empty, "The draft is a part of its own, and it stands nowhere else.");
});
}
}
[Test]
@@ -75,12 +75,12 @@ public sealed class ConversationPartsTests
var parts = ConversationParts.Of(thread, string.Empty, "a draft", null, imagesAreSent: true, toolDefinitions: null);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(parts.Texts, Is.EqualTo(new[] { "A question." }));
Assert.That(parts.GrowingTexts, Is.EqualTo(new[] { "The answer so far" }));
Assert.That(parts.DraftText, Is.EqualTo("a draft"));
});
}
}
[Test]
@@ -91,11 +91,11 @@ public sealed class ConversationPartsTests
var parts = ConversationParts.Of(new() { Blocks = [finished] }, string.Empty, string.Empty, null, imagesAreSent: true, toolDefinitions: null);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(parts.Texts, Is.EqualTo(new[] { "The whole answer." }));
Assert.That(parts.GrowingTexts, Is.Empty);
});
}
}
[Test]
@@ -134,11 +134,11 @@ public sealed class ConversationPartsTests
{
var parts = ConversationParts.Of(null, string.Empty, "Hello", null, imagesAreSent: true, toolDefinitions: null);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(parts.Texts, Is.Empty);
Assert.That(parts.DraftText, Is.EqualTo("Hello"));
});
}
}
[TestCase("")]
@@ -147,12 +147,12 @@ public sealed class ConversationPartsTests
{
var parts = ConversationParts.Of(null, string.Empty, draft, null, imagesAreSent: true, toolDefinitions: null);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(parts.Texts, Is.Empty);
Assert.That(parts.GrowingTexts, Is.Empty);
Assert.That(parts.DraftText, Is.Empty);
});
}
}
[Test]
@@ -169,11 +169,11 @@ public sealed class ConversationPartsTests
var parts = ConversationParts.Of(new() { Blocks = [empty] }, string.Empty, string.Empty, null, imagesAreSent: true, toolDefinitions: null);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(parts.Texts, Is.Empty);
Assert.That(parts.Documents, Is.Empty);
});
}
}
[Test]
@@ -191,11 +191,11 @@ public sealed class ConversationPartsTests
var parts = ConversationParts.Of(new() { Blocks = [block] }, string.Empty, "And this one.", [FileAttachment.FromPath(draft)], imagesAreSent: true, toolDefinitions: null);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(parts.Documents.Select(document => document.FileName), Is.EqualTo(new[] { "older.txt" }));
Assert.That(parts.DraftDocuments.Select(document => document.FileName), Is.EqualTo(new[] { "draft.txt" }), "Both count, each in the part it belongs to.");
});
}
}
[Test]
@@ -219,11 +219,11 @@ public sealed class ConversationPartsTests
var parts = ConversationParts.Of(null, string.Empty, "Look", [FileAttachment.FromPath(document), FileAttachment.FromPath(image)], imagesAreSent: true, toolDefinitions: null);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(parts.DraftDocuments.Select(entry => entry.FileName), Is.EqualTo(new[] { "notes.txt" }));
Assert.That(parts.DraftImages, Is.EqualTo(1));
});
}
}
[Test]
@@ -240,11 +240,11 @@ public sealed class ConversationPartsTests
var parts = ConversationParts.Of(new() { Blocks = [block] }, string.Empty, "And at this.", [FileAttachment.FromPath(waiting)], imagesAreSent: true, toolDefinitions: null);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(parts.Images, Is.EqualTo(1));
Assert.That(parts.DraftImages, Is.EqualTo(1));
});
}
}
[Test]
@@ -275,12 +275,12 @@ public sealed class ConversationPartsTests
var parts = ConversationParts.Of(new() { Blocks = [running] }, string.Empty, string.Empty, null, imagesAreSent: true, toolDefinitions: null);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(parts.Texts, Is.Empty);
Assert.That(parts.ToolConversation, Is.EqualTo(new[] { "What the web search found.", "What the page said." }));
Assert.That(parts.GrowingTexts, Is.Empty, "The tool conversation is a part of its own, so that its share can be named.");
});
}
}
[Test]
@@ -314,11 +314,11 @@ public sealed class ConversationPartsTests
var parts = ConversationParts.Of(new() { Blocks = [answered] }, string.Empty, string.Empty, null, imagesAreSent: true, toolDefinitions: null);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(parts.Texts, Is.EqualTo(new[] { "Here is what I found." }));
Assert.That(parts.ToolConversation, Is.Empty);
});
}
}
[Test]
@@ -335,7 +335,7 @@ public sealed class ConversationPartsTests
Tool("read_web_page", "Reads one page.", """{"type":"string"}"""),
]);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(parts.Texts, Is.EqualTo(new[]
{
@@ -344,7 +344,7 @@ public sealed class ConversationPartsTests
}));
Assert.That(parts.GrowingTexts, Is.Empty);
});
}
}
[Test]
@@ -131,11 +131,11 @@ public sealed class ConversationTokenTrackerTests
tracker.Start();
await Task.Delay(1_000);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(runs, Is.GreaterThanOrEqualTo(2));
Assert.That(asked, Is.EqualTo(runs));
});
}
}
[Test]
@@ -155,11 +155,11 @@ public sealed class ConversationTokenTrackerTests
var afterDisposal = runs;
await Task.Delay(400);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(afterDisposal, Is.GreaterThan(0), "The tracker never ran, so this proves nothing about stopping it.");
Assert.That(runs, Is.EqualTo(afterDisposal));
});
}
}
[Test]
+6 -6
View File
@@ -64,11 +64,11 @@ public sealed class ConversationTokensTests
ImageLimits = new ImageLimits(8, 100),
};
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(counted.TooManyImages, Is.True);
Assert.That(counted.ImageLimits.MaxInOneMessage, Is.EqualTo(8));
});
}
}
[Test]
@@ -111,11 +111,11 @@ public sealed class ConversationTokensTests
var estimated = reported with { HistoryIsReported = false };
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(reported.UncountedImages, Is.EqualTo(1), "The provider counted the two which were sent.");
Assert.That(estimated.UncountedImages, Is.EqualTo(3), "Without a report, nobody counted any of them.");
});
}
}
[Test]
@@ -133,11 +133,11 @@ public sealed class ConversationTokensTests
ImageLimits = new ImageLimits(8, null),
};
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(counted.UncountedImages, Is.Zero);
Assert.That(counted.TooManyImages, Is.True);
});
}
}
[Test]
@@ -43,12 +43,12 @@ public sealed class FileAttachmentSerializationTests
var json = JsonSerializer.Serialize(template, SettingsManager.JSON_OPTIONS);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(json, Does.Contain(FILE_PATH.Replace(@"\", @"\\")), "The attachment has to reach the JSON, or this test checks nothing.");
Assert.That(json, Does.Contain("chart.png"), "The attachment of the example conversation has to reach the JSON, or this test checks nothing.");
Assert.That(json, Does.Not.Contain("\"Exists\""));
});
}
}
[Test]
@@ -67,11 +67,11 @@ public sealed class FileAttachmentSerializationTests
var read = JsonSerializer.Deserialize<FileAttachment>(LEGACY_JSON, SettingsManager.JSON_OPTIONS)!;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(read.FileName, Is.EqualTo("report.pdf"));
Assert.That(read.FilePath, Is.EqualTo("/tmp/report.pdf"));
Assert.That(read.FileSizeBytes, Is.EqualTo(1024));
});
}
}
}
+22 -22
View File
@@ -27,12 +27,12 @@ public sealed class IContentExtensionsTests
var found = content.TryGetExportMarkdown(out var markdown);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(found, Is.True);
Assert.That(markdown, Does.EndWith(content.Sources.ToExportMarkdown()), "What the chat shows below the answer is what the file holds below it.");
Assert.That(TopLevelBlocksOf(markdown), Is.EqualTo(new[] { "ParagraphBlock", "h1", "h2", "ListBlock" }), "The answer stays a paragraph of its own; the source list starts under its own heading.");
});
}
}
[Test]
@@ -47,11 +47,11 @@ public sealed class IContentExtensionsTests
var found = content.TryGetExportMarkdown(out var markdown);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(found, Is.True);
Assert.That(TopLevelBlocksOf(markdown), Is.EqualTo(new[] { "ParagraphBlock", "Table", "h1", "h2", "ListBlock" }), "The table ends where it ended; the headings below it are not two more rows.");
});
}
}
[Test]
@@ -66,11 +66,11 @@ public sealed class IContentExtensionsTests
var found = content.TryGetExportMarkdown(out var markdown);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(found, Is.True);
Assert.That(TopLevelBlocksOf(markdown), Is.EqualTo(new[] { "ParagraphBlock", "ListBlock", "h1", "h2", "ListBlock" }), "Two lists, not one: the sources do not become the fourth point of the answer.");
});
}
}
[Test]
@@ -86,11 +86,11 @@ public sealed class IContentExtensionsTests
var found = content.TryGetExportMarkdown(out var markdown);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(found, Is.True);
Assert.That(TopLevelBlocksOf(markdown), Is.EqualTo(new[] { "ParagraphBlock", "FencedCodeBlock", "h1", "h2", "ListBlock" }), "The code block is closed for the model, so the sources stand below it instead of inside it.");
});
}
}
[Test]
@@ -101,11 +101,11 @@ public sealed class IContentExtensionsTests
var found = content.TryGetExportMarkdown(out var markdown);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(found, Is.True);
Assert.That(markdown, Is.EqualTo(ANSWER.Trim()), "The everyday case: no heading, no empty line, nothing anybody has to explain.");
});
}
}
[Test]
@@ -115,11 +115,11 @@ public sealed class IContentExtensionsTests
var found = content.TryGetExportMarkdown(out var markdown);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(found, Is.True);
Assert.That(markdown, Is.EqualTo(content.Sources.ToExportMarkdown()), "Nothing above the heading means no empty line above it either.");
});
}
}
[Test]
@@ -132,11 +132,11 @@ public sealed class IContentExtensionsTests
Sources = [TOOL_SOURCE],
};
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(picture.TryGetExportMarkdown(out var markdown), Is.False, "There is no text document for a picture, so the caller hears no and says so.");
Assert.That(markdown, Is.Empty, "A file writer must not put an excuse into the file it writes.");
});
}
}
[Test]
@@ -147,11 +147,11 @@ public sealed class IContentExtensionsTests
var found = content.TryGetMarkdownText(out var markdown);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(found, Is.True);
Assert.That(markdown, Is.EqualTo(ANSWER), "Neither trimmed nor extended: whoever reads a table out of a message wants what the model wrote and nothing else.");
});
}
}
[Test]
@@ -164,11 +164,11 @@ public sealed class IContentExtensionsTests
var tables = FilesOf(content);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(tables, Has.Count.EqualTo(1), "One table in the message, one table offered for it.");
Assert.That(content.ToExportContent(tables[0]), Is.EqualTo(tables[0].Content), "A data table has no column a link list would fit into.");
});
}
}
[Test]
@@ -201,12 +201,12 @@ public sealed class IContentExtensionsTests
var exported = content.ToExportContent(file);
var appended = exported[file.Content.Length..].Trim();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(exported, Does.StartWith(file.Content), "The page stays as the model wrote it.");
Assert.That(appended, Does.StartWith("<!--").And.EndWith("-->"), "Below the page stands one comment and nothing a browser would show.");
Assert.That(appended, Does.Contain(TOOL_SOURCE.URL));
});
}
}
[Test]
@@ -221,12 +221,12 @@ public sealed class IContentExtensionsTests
var exported = content.ToExportContent(file);
var appended = exported[file.Content.Length..].Trim();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(exported, Does.StartWith(file.Content), "The document stays as the model wrote it.");
Assert.That(appended.Split(Environment.NewLine), Has.All.StartWith("%"), "A line LaTeX would read could stop the whole run.");
Assert.That(appended, Does.Contain(TOOL_SOURCE.URL));
});
}
}
[Test]
@@ -92,11 +92,11 @@ public sealed class ListContentBlockExtensionsTests
var messages = await BlocksWithAPicture(this.picturePath).BuildMessagesUsingDirectImageUrlAsync(PictureReadingProvider());
var picture = (messages.Single() as MultimodalMessage)!.Content.OfType<SubContentImageUrl>().Single();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(picture.ImageUrl, Is.EqualTo($"data:image/jpeg;base64,{FakeImageRuntime.IMAGE_BASE64}"));
Assert.That(this.runtime.RequestedPaths, Is.EqualTo(new[] { new FileInfo(this.picturePath).FullName }));
});
}
}
[Test]
@@ -49,11 +49,11 @@ public sealed class ManagedTranscriptAttachmentTests
var chatDirectory = Path.Combine(this.testDirectory, "workspaces", Guid.NewGuid().ToString(), Guid.NewGuid().ToString());
var transcript = WriteTranscript(ManagedTranscriptAttachment.GetTranscriptDirectory(chatDirectory));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(ManagedTranscriptAttachment.TryDeleteOwnedFile(transcript), Is.True);
Assert.That(File.Exists(transcript.FilePath), Is.False);
});
}
}
[Test]
@@ -62,11 +62,11 @@ public sealed class ManagedTranscriptAttachmentTests
var chatDirectory = Path.Combine(this.testDirectory, "workspaces", Guid.NewGuid().ToString(), Guid.NewGuid().ToString());
var file = WriteTranscript(Path.Combine(chatDirectory, "attachments", "images"));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(ManagedTranscriptAttachment.TryDeleteOwnedFile(file), Is.False, "Only the transcript folder belongs to the transcripts.");
Assert.That(File.Exists(file.FilePath), Is.True);
});
}
}
[Test]
@@ -77,11 +77,11 @@ public sealed class ManagedTranscriptAttachmentTests
{
var file = WriteTranscript(ManagedTranscriptAttachment.GetTranscriptDirectory(Path.Combine(outsideDirectory, "tempChats", Guid.NewGuid().ToString())));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(ManagedTranscriptAttachment.TryDeleteOwnedFile(file), Is.False, "The same folders elsewhere belong to somebody else.");
Assert.That(File.Exists(file.FilePath), Is.True);
});
}
}
finally
{
+14 -14
View File
@@ -50,12 +50,12 @@ public sealed class ProvenanceCleanupTests
var result = await CleanUpAsync(store, Locations(keptChatId), LoaderOf(chat), DateTimeOffset.UtcNow.AddMinutes(1));
var entries = await store.Client.GetEntriesAsync([referenced.Value, ofRemovedMessage!.Value, ofDeletedChat!.Value], TOKEN);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(entries.Keys, Is.EquivalentTo(new[] { referenced.Value }));
Assert.That(result.Orphaned, Is.EqualTo(1));
Assert.That(result.Unreferenced, Is.EqualTo(1));
});
}
}
[Test]
@@ -83,11 +83,11 @@ public sealed class ProvenanceCleanupTests
var result = await CleanUpAsync(store, Locations(Guid.NewGuid()), UNREADABLE, DateTimeOffset.UtcNow.AddMinutes(1));
var entries = await store.Client.GetEntriesAsync([entry!.Value], TOKEN);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(entries, Is.Empty, "No chat on the disk has no id, so nothing would ever remove such an entry.");
Assert.That(result.Orphaned, Is.EqualTo(1));
});
}
}
[Test]
@@ -100,11 +100,11 @@ public sealed class ProvenanceCleanupTests
var result = await CleanUpAsync(store, Locations(chatId), UNREADABLE, DateTimeOffset.UtcNow.AddMinutes(1));
var entries = await store.Client.GetEntriesAsync([entry!.Value], TOKEN);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(entries.Keys, Is.EquivalentTo(new[] { entry.Value }), "Nothing tells what an unreadable chat refers to, e.g., one written by a newer version.");
Assert.That(result.Unreadable, Is.EqualTo(1), "The next start has to read it again.");
});
}
}
[Test]
@@ -119,12 +119,12 @@ public sealed class ProvenanceCleanupTests
var result = await CleanUpAsync(store, Locations(chatId), LoaderOf(ChatWith(passageId: null, attachmentId: null, chatId), reads), lastCleanup.AddMinutes(1), lastCleanup, writtenAt: lastCleanup.AddHours(-1));
var entries = await store.Client.GetEntriesAsync([entry!.Value], TOKEN);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(reads, Is.Empty, "Reading every chat at every start costs much on a small computer.");
Assert.That(result.Skipped, Is.EqualTo(1));
Assert.That(entries.Keys, Is.EquivalentTo(new[] { entry.Value }), "Skipping a chat never removes anything.");
});
}
}
[Test]
@@ -139,11 +139,11 @@ public sealed class ProvenanceCleanupTests
await CleanUpAsync(store, Locations(chatId), LoaderOf(ChatWith(passageId: null, attachmentId: null, chatId), reads), DateTimeOffset.UtcNow.AddMinutes(1), lastCleanup, writtenAt: lastCleanup.AddHours(-1));
var entries = await store.Client.GetEntriesAsync([previewed!.Value], TOKEN);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(reads, Is.EquivalentTo(new[] { chatId }), "The last cleanup had to leave alone what was stored after it began.");
Assert.That(entries, Is.Empty);
});
}
}
[TestCase(60, TestName = "AChatWrittenSinceTheLastCleanupIsRead")]
@@ -160,11 +160,11 @@ public sealed class ProvenanceCleanupTests
await CleanUpAsync(store, Locations(chatId), LoaderOf(ChatWith(passageId: null, attachmentId: null, chatId), reads), lastCleanup.AddMinutes(1), lastCleanup, writtenAt: lastCleanup.AddSeconds(secondsAfterTheLastCleanup));
var entries = await store.Client.GetEntriesAsync([ofRemovedMessage!.Value], TOKEN);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(reads, Is.EquivalentTo(new[] { chatId }));
Assert.That(entries, Is.Empty);
});
}
}
[Test]
@@ -192,12 +192,12 @@ public sealed class ProvenanceCleanupTests
var result = await CleanUpAsync(store, Locations(), LoaderOf(ChatWith(passageId: null, attachmentId: null), reads), lastCleanup.AddMinutes(1), lastCleanup, writtenAt: lastCleanup.AddHours(-1));
var entries = await store.Client.GetEntriesAsync([entry!.Value], TOKEN);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(entries, Is.Empty, "Telling that a chat no longer exists costs no reading.");
Assert.That(result.Orphaned, Is.EqualTo(1));
Assert.That(reads, Is.Empty);
});
}
}
private static ChatThread ChatWith(Guid? passageId, Guid? attachmentId, Guid? chatId = null) => new()
@@ -25,12 +25,12 @@ public sealed class ChatComposerStateTests
composer.RestoreFromDraft(draft);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(composer.UserInput, Is.EqualTo("Summarize the meeting"));
Assert.That(composer.FileAttachments.Select(attachment => attachment.FilePath), Is.EqualTo(new[] { "/documents/agenda.pdf" }));
Assert.That(composer.HasUserDraft, Is.True, "A chat template must not replace the message the user came back to.");
});
}
}
[Test]
@@ -41,11 +41,11 @@ public sealed class ChatComposerStateTests
composer.RestoreFromDraft(new ChatDraft());
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(composer.UserInput, Is.Empty, "The message of the chat before belongs to that chat.");
Assert.That(composer.HasUserDraft, Is.False, "Nobody started a message in this chat, so a chat template may still fill the composer.");
});
}
}
[Test]
@@ -55,10 +55,10 @@ public sealed class ChatComposerStateTests
composer.ApplyTemplate(new ChatTemplate { PredefinedUserPrompt = "Translate the following text:" });
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(composer.UserInput, Is.EqualTo("Translate the following text:"));
Assert.That(composer.HasUserDraft, Is.False, "Leaving the chat must not warn about a message the user never wrote.");
});
}
}
}
@@ -132,11 +132,11 @@ public sealed class ConfigurationFileExclusionsTests
component.LockField = locked;
await component.ReplaceAsync("bin/;*.tmp");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(component.Draft, Is.EqualTo("*.original"));
Assert.That(component.StoredText, Is.EqualTo("*.original"));
});
}
}
finally
{
@@ -155,11 +155,11 @@ public sealed class ConfigurationFileExclusionsTests
component.Edit("old/;:text:");
await component.ReplaceAsync("bin/;:text:");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(component.Draft, Is.EqualTo("bin/;:text:"));
Assert.That(component.StoredText, Is.EqualTo("*.original"));
});
}
}
finally
{
+10 -10
View File
@@ -24,11 +24,11 @@ public sealed class ContentViewerTests
var shown = ContentViewer.CutAtLineBreak(TEXT, 100, out var cutOffCharacters);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(shown, Is.SameAs(TEXT));
Assert.That(cutOffCharacters, Is.Zero);
});
}
}
[Test]
@@ -38,11 +38,11 @@ public sealed class ContentViewerTests
var shown = ContentViewer.CutAtLineBreak(text, 50, out var cutOffCharacters);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(shown, Is.EqualTo(text));
Assert.That(cutOffCharacters, Is.Zero);
});
}
}
[Test]
@@ -52,11 +52,11 @@ public sealed class ContentViewerTests
var shown = ContentViewer.CutAtLineBreak(TEXT, 25, out var cutOffCharacters);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(shown, Is.EqualTo("| a | b |\n| 1 | 2 |\n"), "The last table row would be torn apart, so it goes as a whole.");
Assert.That(cutOffCharacters, Is.EqualTo(TEXT.Length - shown.Length));
});
}
}
[Test]
@@ -69,11 +69,11 @@ public sealed class ContentViewerTests
const string TEXT = "The budget $\\rho$ is spent on the hardest tokens.";
static bool HasMath(Markdig.MarkdownPipeline pipeline) => Markdig.Markdown.Parse(TEXT, pipeline).Descendants<MathInline>().Any();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(HasMath(Markdown.SAFE_MARKDOWN_PIPELINE), Is.True, "The safe pipeline reads this as math.");
Assert.That(HasMath(Markdown.CONTENT_MARKDOWN_PIPELINE), Is.False, "What AI Studio read from a document stays text.");
});
}
}
[Test]
@@ -83,10 +83,10 @@ public sealed class ContentViewerTests
var shown = ContentViewer.CutAtLineBreak(text, 20, out var cutOffCharacters);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(shown, Has.Length.EqualTo(20));
Assert.That(cutOffCharacters, Is.EqualTo(10));
});
}
}
}
@@ -24,12 +24,12 @@ public sealed class FileExclusionEditorStateTests
{
var state = Create(text);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(state.FolderText, Is.EqualTo("node_modules/; bin/"));
Assert.That(state.FileText, Is.EqualTo("*.min.js; draft-*"));
Assert.That(state.Text, Is.EqualTo(text));
});
}
}
[Test]
@@ -38,12 +38,12 @@ public sealed class FileExclusionEditorStateTests
var state = Create("bin/;*.min.js;draft-*");
state.EditFolders("obj/\nbuild/");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(FileExclusions.Parse(state.Text), Is.EqualTo(new[] { "obj/", "build/", "*.min.js", "draft-*" }));
Assert.That(state.FileText, Is.EqualTo("*.min.js; draft-*"));
Assert.That(state.Text, Is.EqualTo("obj/; build/; *.min.js; draft-*"));
});
}
}
[Test]
@@ -52,12 +52,12 @@ public sealed class FileExclusionEditorStateTests
var state = Create("bin/;node_modules/;*.min.js");
state.EditFiles("*.wav;*.md");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(FileExclusions.Parse(state.Text), Is.EqualTo(new[] { "bin/", "node_modules/", "*.wav", "*.md" }));
Assert.That(state.FolderText, Is.EqualTo("bin/; node_modules/"));
Assert.That(state.Text, Is.EqualTo("bin/; node_modules/; *.wav; *.md"));
});
}
}
[TestCase("")]
@@ -80,7 +80,7 @@ public sealed class FileExclusionEditorStateTests
state.EditFolders("node_modules/;cmake-build-*/");
var patterns = FileExclusions.Parse(state.Text);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(patterns, Is.EqualTo(new[] { "node_modules/", "cmake-build-*/" }));
Assert.That(FileExclusions.IsValid(state.Text), Is.True);
@@ -88,7 +88,7 @@ public sealed class FileExclusionEditorStateTests
Assert.That(FileExclusions.ExcludesFolder("node_modules", patterns), Is.True);
Assert.That(FileExclusions.ExcludesFolder("cmake-build-debug", patterns), Is.True);
Assert.That(FileExclusions.ExcludesFile("node_modules", patterns), Is.False);
});
}
}
[TestCase(":text:")]
@@ -103,11 +103,11 @@ public sealed class FileExclusionEditorStateTests
else
state.EditFolders("bin/");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(FileExclusions.Parse(state.Text), Does.Contain(invalidRule));
Assert.That(FileExclusions.IsValid(state.Text), Is.False);
});
}
}
[TestCase(":text:")]
@@ -117,11 +117,11 @@ public sealed class FileExclusionEditorStateTests
var state = Create(string.Empty);
state.EditFolders(selector);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(state.Text, Is.EqualTo(selector));
Assert.That(FileExclusions.IsValid(state.Text), Is.False);
});
}
}
[Test]
@@ -156,12 +156,12 @@ public sealed class FileExclusionEditorStateTests
else
state.RestoreFiles(DEFAULTS);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(state.FolderText, Is.EqualTo(folders ? "bin/; node_modules/" : "latest/; "));
Assert.That(state.FileText, Is.EqualTo(folders ? "*.latest; " : "*.tmp; *.bak"));
Assert.That(state.Text, Is.EqualTo(folders ? "bin/; node_modules/; *.latest" : "latest/; *.tmp; *.bak"));
});
}
}
[TestCase(true)]
@@ -187,11 +187,11 @@ public sealed class FileExclusionEditorStateTests
else
state.RestoreFiles(FileExclusions.DEFAULT_PATTERNS);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(FileExclusions.Parse(state.Text), Does.Contain(invalidRule));
Assert.That(FileExclusions.IsValid(state.Text), Is.False);
});
}
}
[Test]
@@ -199,13 +199,13 @@ public sealed class FileExclusionEditorStateTests
{
var state = Create("bin/;*.min.js");
state.EditFiles("obj/;*.wav;draft-*");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(state.IsValid, Is.False);
Assert.That(state.Text, Is.EqualTo("bin/; obj/; *.wav; draft-*"));
Assert.That(state.FolderText, Is.EqualTo("bin/"));
Assert.That(state.FileText, Is.EqualTo("obj/;*.wav;draft-*"));
});
}
}
[TestCase(true)]
@@ -230,12 +230,12 @@ public sealed class FileExclusionEditorStateTests
state.Receive(earlierValue);
state.Receive(state.Text);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(state.FolderText, Is.EqualTo("obj/; build/; "));
Assert.That(state.FileText, Is.EqualTo("*.wav; draft-*; "));
Assert.That(state.Text, Is.EqualTo("obj/; build/; *.wav; draft-*"));
});
}
}
[Test]
@@ -247,11 +247,11 @@ public sealed class FileExclusionEditorStateTests
state.EditFiles("*.latest");
state.Receive(earlierValue);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(state.Text, Is.EqualTo("*.latest"));
Assert.That(state.FileText, Is.EqualTo("*.latest"));
});
}
}
[Test]
@@ -271,11 +271,11 @@ public sealed class FileExclusionEditorStateTests
state.EditFiles("*.latest");
state.Receive("policy/;*.secret");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(state.FolderText, Is.EqualTo("policy/"));
Assert.That(state.FileText, Is.EqualTo("*.secret"));
});
}
}
[Test]
@@ -311,7 +311,7 @@ public sealed class FileExclusionEditorStateTests
state.EditFolders(":text:");
state.Reset(FileExclusions.DEFAULT_PATTERNS);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(state.Text, Is.EqualTo(FileExclusions.DEFAULT_PATTERNS));
Assert.That(FileExclusions.IsValid(state.Text), Is.True);
@@ -319,7 +319,7 @@ public sealed class FileExclusionEditorStateTests
Assert.That(state.FileText, Does.Contain("*.exe"));
Assert.That(state.FolderText, Does.Not.Contain("\n").And.Not.Contain("\r"));
Assert.That(state.FileText, Does.Not.Contain("\n").And.Not.Contain("\r"));
});
}
}
[Test]
@@ -341,14 +341,14 @@ public sealed class FileExclusionEditorStateTests
var state = Create("bin/;*.tmp");
state.EditFolders(pattern);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(state.IsValid, Is.False);
Assert.That(state.FolderText, Is.EqualTo(pattern));
Assert.That(FileExclusions.Parse(state.Text), Does.Contain(pattern));
Assert.That(FileExclusions.Parse(state.Text), Does.Not.Contain(pattern + "/"));
Assert.That(state.FileText, Is.EqualTo("*.tmp"));
});
}
}
[Test]
@@ -360,12 +360,12 @@ public sealed class FileExclusionEditorStateTests
state.EditFiles("*.bak;obj/");
state.Receive(earlierValue);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(state.IsValid, Is.False);
Assert.That(state.FileText, Is.EqualTo("*.bak;obj/"));
Assert.That(state.FolderText, Is.EqualTo("bin/"));
});
}
}
private static FileExclusionEditorState Create(string text)
@@ -68,11 +68,11 @@ public sealed class FileExclusionEditorTests
await editor.EditAsync(folders ? "*.keep;wrong/" : "*.wrong", folders: !folders);
await editor.ResetAsync(folders);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(published, Is.Empty);
Assert.That(resets, Is.Empty);
});
}
await editor.EditAsync(folders ? "*.keep" : "keep/", folders: !folders);
Assert.That(published, Is.EqualTo(new[] { folders ? "bin/; *.keep" : "keep/; *.tmp" }));
@@ -90,11 +90,11 @@ public sealed class FileExclusionEditorTests
await editor.EditAsync(folders ? "*.wrong" : "wrong/", folders);
await editor.ResetAsync(folders);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(published, Is.Empty);
Assert.That(resets, Is.EqualTo(new[] { folders ? "bin/; *.custom" : "custom/; *.tmp" }));
});
}
}
[Test]
@@ -114,11 +114,11 @@ public sealed class FileExclusionEditorTests
await editor.ResetAsync(folders: true);
await editor.ResetAsync(folders: false);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(published, Is.Empty);
Assert.That(resets, Is.Empty);
});
}
editor.SetParameters(new() { [nameof(FileExclusionEditor.ResetRequested)] = default(EventCallback<string>) });
await editor.ResetAsync(folders: true);
@@ -140,11 +140,11 @@ public sealed class FileExclusionEditorTests
await editor.EditAsync("*.wrong", folders: true);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(editor.IsValid, Is.False);
Assert.That(published, Is.Empty);
});
}
await editor.EditAsync("custom/", folders: true);
Assert.That(editor.IsValid, Is.True);
@@ -27,13 +27,13 @@ public sealed class MailFolderPickerTests
Folder("Archive", specialUse: MailFolderSpecialUse.ARCHIVE),
Folder("INBOX", isInbox: true));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(root.Value, Is.Empty, "The root does not stand for the whole mailbox.");
Assert.That(root.Text, Is.EqualTo(WHOLE_MAILBOX));
Assert.That(ValuesOf(root), Is.EqualTo(new[] { "INBOX", "Archive", "Projects" }), "The top level is not ordered with the inbox first.");
Assert.That(ValuesOf(Child(root, "Projects")), Is.EqualTo(new[] { "Projects/Alpha" }), "The subfolder did not land below its parent.");
});
}
}
[Test]
@@ -44,11 +44,11 @@ public sealed class MailFolderPickerTests
Folder("INBOX.Sent", "INBOX", separator: '.', specialUse: MailFolderSpecialUse.SENT),
Folder("INBOX.Drafts", "INBOX", separator: '.', specialUse: MailFolderSpecialUse.DRAFTS));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(ValuesOf(root), Is.EqualTo(new[] { "INBOX" }));
Assert.That(ValuesOf(Child(root, "INBOX")), Is.EqualTo(new[] { "INBOX.Drafts", "INBOX.Sent" }));
});
}
}
[Test]
@@ -85,13 +85,13 @@ public sealed class MailFolderPickerTests
};
var root = MailFolderPicker.BuildTree(folders, "Projects/Alpha/Reports", WHOLE_MAILBOX).Single();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(root.Expanded, Is.True, "The whole mailbox is collapsed.");
Assert.That(Child(root, "Projects").Expanded, Is.True, "The way to the picked folder is collapsed.");
Assert.That(Child(Child(root, "Projects"), "Projects/Alpha").Expanded, Is.True, "The way to the picked folder is collapsed.");
Assert.That(Child(root, "Private").Expanded, Is.False, "A folder off the way to the picked folder is expanded.");
});
}
}
[TestCase(MailFolderSpecialUse.NONE, ExpectedResult = true)]
+2 -2
View File
@@ -37,7 +37,7 @@ public sealed class CapabilityTests
{
var bits = new Dictionary<ulong, Capability>();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
foreach (var capability in Enum.GetValues<Capability>())
{
@@ -52,7 +52,7 @@ public sealed class CapabilityTests
bits[value] = capability;
}
});
}
}
[Test]
+6 -6
View File
@@ -53,11 +53,11 @@ public sealed class ContextWindowRuleTests
{
var window = provider.GetModelProfile(new Model(modelId, null)).Context;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(window.IsKnown, Is.True);
Assert.That(window.DefaultTokens, Is.EqualTo(tokens));
});
}
}
[Test]
@@ -70,11 +70,11 @@ public sealed class ContextWindowRuleTests
//
var profile = ModelRegistry.Shared.Profile(LLMProviders.OPEN_AI, "gpt-5.3");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(profile.Context.IsKnown, Is.False);
Assert.That(profile.Has(Capability.FUNCTION_CALLING), Is.True, "Everything else it does inherit.");
});
}
}
[Test]
@@ -100,10 +100,10 @@ public sealed class ContextWindowRuleTests
//
var profile = ModelRegistry.Shared.Profile(LLMProviders.SELF_HOSTED, "some-model-nobody-wrote-a-rule-for");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(profile.Context.IsKnown, Is.False);
Assert.That(profile.Context.DefaultTokens, Is.Zero, "And the number next to it is meaningless, which is why nothing may read it without asking first.");
});
}
}
}
+2 -2
View File
@@ -41,7 +41,7 @@ public sealed class CorpusTests
[Test]
public void EveryKnownWrongAnswerSaysWhyAndWhereThatCanBeChecked()
{
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
foreach (var change in ExpectedChanges.ENTRIES)
{
@@ -49,7 +49,7 @@ public sealed class CorpusTests
Assert.That(change.Source, Is.Not.Empty, $"{change.Provider} {change.ModelId} does not say where that can be checked.");
Assert.That(change.AnswerWanted, Is.Not.Empty, $"{change.Provider} {change.ModelId} does not say what the answer should be.");
}
});
}
}
[Test]
@@ -51,12 +51,12 @@ public sealed class ModelPatternLiteralAnalyzerTests
{
var reported = await AnalyzeAsync(statements);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(reported, Has.Count.EqualTo(1));
Assert.That(reported.FirstOrDefault()?.Id, Is.EqualTo("MWAIS0013"));
Assert.That(reported.FirstOrDefault()?.GetMessage(), Does.Contain($"write it as \"{expectedSpelling}\""));
});
}
}
[Test]
@@ -64,11 +64,11 @@ public sealed class ModelPatternLiteralAnalyzerTests
{
var reported = await AnalyzeAsync("""builder.Rule("___");""");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(reported, Has.Count.EqualTo(1));
Assert.That(reported.FirstOrDefault()?.GetMessage(), Does.Contain("nothing of it survives"));
});
}
}
[Test]
@@ -97,12 +97,12 @@ public sealed class ModelRegistryGeneratorTests
{
var generated = Generate(TWO_FAMILIES_AND_A_HOST, out _, out _);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(generated, Does.Contain("new global::Sample.FirstFamily()"));
Assert.That(generated, Does.Contain("new global::Sample.SecondFamily()"), "A family which inherits through another class is still a family.");
Assert.That(generated, Does.Contain("new global::Sample.SampleHost()"));
});
}
}
[Test]
@@ -142,11 +142,11 @@ public sealed class ModelRegistryGeneratorTests
//
var generated = Generate("namespace Sample;\n\npublic sealed class NothingToDoWithModels;", out var updated, out _);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(generated, Does.Contain("public static class ModelRegistrations"));
Assert.That(CompilationHarness.ErrorsOf(updated), Is.Empty);
});
}
}
[Test]
@@ -155,12 +155,12 @@ public sealed class ModelRegistryGeneratorTests
var generated = Generate(A_FAMILY_NEEDING_AN_ARGUMENT, out _, out var diagnostics);
var reported = diagnostics.Where(diagnostic => diagnostic.Id is "MDR001").ToList();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(reported, Has.Count.EqualTo(1));
Assert.That(reported.FirstOrDefault()?.GetMessage(), Does.Contain("DemandingFamily"));
Assert.That(generated, Does.Not.Contain("DemandingFamily"));
});
}
}
[Test]
@@ -170,11 +170,11 @@ public sealed class ModelRegistryGeneratorTests
// The tests above run the generator by hand. This one asks whether it also ran while the app
// was built, which is a different question and the one that actually matters.
//
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(ModelRegistrations.CreateFamilies(), Is.Not.Null);
Assert.That(ModelRegistrations.CreateHosts(), Is.Not.Null);
});
}
}
private static string Generate(string source, out Compilation updated, out IReadOnlyList<Diagnostic> diagnostics)
+22 -22
View File
@@ -20,12 +20,12 @@ public sealed class HostNamingTests
{
var taken = HostNaming.TrySplitOrganization(new ModelId("anthropic/claude-opus-5"), out var inner, out var vendor);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(taken, Is.True);
Assert.That(inner.Original, Is.EqualTo("claude-opus-5"));
Assert.That(vendor, Is.EqualTo(ModelVendor.ANTHROPIC));
});
}
}
[Test]
@@ -37,18 +37,18 @@ public sealed class HostNamingTests
//
var taken = HostNaming.TrySplitOrganization(new ModelId("azure/gpt-5.6"), out var inner, out var vendor);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(taken, Is.True);
Assert.That(inner.Original, Is.EqualTo("gpt-5.6"));
Assert.That(vendor, Is.Null);
});
}
}
[Test]
public void AnOrganizationIsRecognizedWhicheverWayTheHostSpellsIt()
{
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(HostNaming.VendorOfOrganization("meta-llama"), Is.EqualTo(ModelVendor.META));
Assert.That(HostNaming.VendorOfOrganization("Qwen"), Is.EqualTo(ModelVendor.ALIBABA));
@@ -57,7 +57,7 @@ public sealed class HostNamingTests
Assert.That(HostNaming.VendorOfOrganization("zai"), Is.EqualTo(ModelVendor.Z_AI));
Assert.That(HostNaming.VendorOfOrganization("minimaxi"), Is.EqualTo(ModelVendor.MINIMAX));
Assert.That(HostNaming.VendorOfOrganization("somebody-else"), Is.EqualTo(ModelVendor.UNKNOWN));
});
}
}
[Test]
@@ -65,12 +65,12 @@ public sealed class HostNamingTests
{
var taken = HostNaming.TrySplitOrganization(new ModelId("llama-3.3-70b-versatile"), out var inner, out var vendor);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(taken, Is.False);
Assert.That(inner.Original, Is.EqualTo("llama-3.3-70b-versatile"));
Assert.That(vendor, Is.Null);
});
}
}
[Test]
@@ -82,11 +82,11 @@ public sealed class HostNamingTests
//
var taken = HostNaming.TrySplitOrganization(new ModelId("accounts/fireworks/models/llama-v3p1-405b-instruct"), out var inner, out _);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(taken, Is.True);
Assert.That(inner.Original, Is.EqualTo("fireworks/models/llama-v3p1-405b-instruct"));
});
}
}
[Test]
@@ -94,11 +94,11 @@ public sealed class HostNamingTests
{
var taken = HostNaming.TrySplitOrganization(new ModelId("openai/"), out var inner, out _);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(taken, Is.False);
Assert.That(inner.Original, Is.EqualTo("openai/"));
});
}
}
[Test]
@@ -106,11 +106,11 @@ public sealed class HostNamingTests
{
var taken = HostNaming.TryStripRoutingSuffix(new ModelId("google/gemma-4-31B-it:novita"), out var inner);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(taken, Is.True);
Assert.That(inner.Original, Is.EqualTo("google/gemma-4-31B-it"));
});
}
}
[Test]
@@ -118,11 +118,11 @@ public sealed class HostNamingTests
{
var taken = HostNaming.TryStripRegionSuffix(new ModelId("bedrock/claude-opus-5@eu-central-1"), out var inner);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(taken, Is.True);
Assert.That(inner.Original, Is.EqualTo("bedrock/claude-opus-5"));
});
}
}
[TestCase("bedrock/claude-opus-5", TestName = "A name without an at sign has no region to take off")]
@@ -131,11 +131,11 @@ public sealed class HostNamingTests
{
var taken = HostNaming.TryStripRegionSuffix(new ModelId(modelId), out var inner);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(taken, Is.False);
Assert.That(inner.Original, Is.EqualTo(modelId));
});
}
}
[Test]
@@ -143,11 +143,11 @@ public sealed class HostNamingTests
{
var taken = HostNaming.TryStripMenuPosition(new ModelId("10 - Muse Glimmer 30b - the newest META model"), out var inner);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(taken, Is.True);
Assert.That(inner.Original, Is.EqualTo("Muse Glimmer 30b - the newest META model"));
});
}
}
[TestCase("70b-instruct", TestName = "A number the model is named after is not a menu position")]
@@ -158,10 +158,10 @@ public sealed class HostNamingTests
{
var taken = HostNaming.TryStripMenuPosition(new ModelId(modelId), out var inner);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(taken, Is.False);
Assert.That(inner.Original, Is.EqualTo(modelId));
});
}
}
}
@@ -49,12 +49,12 @@ public sealed class ModelHostIndexTests
{
var index = ModelHostIndex.Build([new SplittingHost()]);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(index.ProvidersWithoutAHost, Does.Contain(LLMProviders.ANTHROPIC));
Assert.That(index.ProvidersWithoutAHost, Does.Not.Contain(LLMProviders.OPEN_ROUTER));
Assert.That(index.ProvidersWithoutAHost, Does.Not.Contain(LLMProviders.NONE), "Nobody can configure it, so nobody has to write a host for it.");
});
}
}
[Test]
@@ -63,12 +63,12 @@ public sealed class ModelHostIndexTests
var index = ModelHostIndex.Build([new SplittingHost()]);
var unwrapped = index.Unwrap(new ModelId("anthropic/claude-opus-5"), LLMProviders.ANTHROPIC, out var vendor);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(index.Of(LLMProviders.ANTHROPIC), Is.Null);
Assert.That(unwrapped.Original, Is.EqualTo("anthropic/claude-opus-5"));
Assert.That(vendor, Is.Null);
});
}
}
[Test]
@@ -82,11 +82,11 @@ public sealed class ModelHostIndexTests
var profile = new ModelProfile { Capabilities = Capability.TEXT_INPUT | Capability.RESPONSES_API };
var throughTheProvider = index.ApplyTransport(profile, LLMProviders.ANTHROPIC);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(throughTheProvider.Has(Capability.RESPONSES_API), Is.False);
Assert.That(throughTheProvider.Has(Capability.CHAT_COMPLETION_API), Is.True);
});
}
}
[Test]
+28 -28
View File
@@ -47,11 +47,11 @@ public sealed class ModelHostTests
{
var unwrapped = Unwrap(LLMProviders.OPEN_ROUTER, "anthropic/claude-opus-5", out var vendor);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(unwrapped.Original, Is.EqualTo("claude-opus-5"));
Assert.That(vendor, Is.EqualTo(ModelVendor.ANTHROPIC));
});
}
}
[Test]
@@ -59,11 +59,11 @@ public sealed class ModelHostTests
{
var unwrapped = Unwrap(LLMProviders.HUGGINGFACE, "openai/gpt-oss-120b:fireworks-ai", out var vendor);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(unwrapped.Original, Is.EqualTo("gpt-oss-120b"));
Assert.That(vendor, Is.EqualTo(ModelVendor.OPEN_AI), "OpenAI published the weights, whoever is serving them today.");
});
}
}
[Test]
@@ -71,11 +71,11 @@ public sealed class ModelHostTests
{
var unwrapped = Unwrap(LLMProviders.HUGGINGFACE, "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B", out var vendor);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(unwrapped.Original, Is.EqualTo("DeepSeek-R1-Distill-Qwen-32B"));
Assert.That(vendor, Is.EqualTo(ModelVendor.DEEP_SEEK));
});
}
}
[Test]
@@ -83,11 +83,11 @@ public sealed class ModelHostTests
{
var unwrapped = Unwrap(LLMProviders.REQUESTY, "bedrock/claude-opus-5@eu-central-1", out var vendor);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(unwrapped.Original, Is.EqualTo("claude-opus-5"));
Assert.That(vendor, Is.Null, "Bedrock runs the model; it did not build it.");
});
}
}
[Test]
@@ -95,11 +95,11 @@ public sealed class ModelHostTests
{
var unwrapped = Unwrap(LLMProviders.REQUESTY, "claude-opus-5@eu", out var vendor);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(unwrapped.Original, Is.EqualTo("claude-opus-5"));
Assert.That(vendor, Is.Null);
});
}
}
[Test]
@@ -107,11 +107,11 @@ public sealed class ModelHostTests
{
var unwrapped = Unwrap(LLMProviders.REQUESTY, "openai/gpt-6-sol:flex", out var vendor);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(unwrapped.Original, Is.EqualTo("gpt-6-sol"));
Assert.That(vendor, Is.EqualTo(ModelVendor.OPEN_AI));
});
}
}
[TestCase("novita/qwen/qwen3.8-max", "qwen3.8-max", ModelVendor.ALIBABA, TestName = "Behind the cloud, the organization from the hub names the vendor")]
@@ -120,11 +120,11 @@ public sealed class ModelHostTests
{
var unwrapped = Unwrap(LLMProviders.REQUESTY, modelId, out var vendor);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(unwrapped.Original, Is.EqualTo(expectedModel));
Assert.That(vendor, Is.EqualTo(expectedVendor));
});
}
}
[Test]
@@ -132,11 +132,11 @@ public sealed class ModelHostTests
{
var unwrapped = Unwrap(LLMProviders.FIREWORKS, "accounts/fireworks/models/llama-v3p1-405b-instruct", out var vendor);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(unwrapped.Original, Is.EqualTo("llama-v3p1-405b-instruct"));
Assert.That(vendor, Is.Null, "None of the three path segments names a vendor.");
});
}
}
[Test]
@@ -152,11 +152,11 @@ public sealed class ModelHostTests
{
var unwrapped = Unwrap(LLMProviders.SELF_HOSTED, "meta-llama/Llama-3.3-70B-Instruct", out var vendor);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(unwrapped.Original, Is.EqualTo("Llama-3.3-70B-Instruct"));
Assert.That(vendor, Is.EqualTo(ModelVendor.META));
});
}
}
[Test]
@@ -184,11 +184,11 @@ public sealed class ModelHostTests
//
var unwrapped = Unwrap(LLMProviders.SELF_HOSTED, modelId, out var vendor);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(unwrapped.Original, Is.EqualTo(expectedModel));
Assert.That(vendor, Is.EqualTo(expectedVendor));
});
}
}
[Test]
@@ -203,13 +203,13 @@ public sealed class ModelHostTests
var atItsVendor = new ModelProfile { Capabilities = Capability.TEXT_INPUT | Capability.FUNCTION_CALLING | Capability.RESPONSES_API };
var throughTheReseller = Transport(LLMProviders.GWDG, atItsVendor);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(Unwrap(LLMProviders.GWDG, "claude-sonnet-5", out _).Original, Is.EqualTo("claude-sonnet-5"));
Assert.That(throughTheReseller.Has(Capability.FUNCTION_CALLING), Is.True);
Assert.That(throughTheReseller.Has(Capability.RESPONSES_API), Is.False);
Assert.That(throughTheReseller.Has(Capability.CHAT_COMPLETION_API), Is.True);
});
}
}
[Test]
@@ -221,11 +221,11 @@ public sealed class ModelHostTests
.Where(host => host.ApplyTransport(withBothApis).Has(Capability.RESPONSES_API))
.Select(host => host.GetType().Name);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(Transport(LLMProviders.OPEN_AI, withBothApis).Has(Capability.RESPONSES_API), Is.True);
Assert.That(elsewhere, Is.Empty, "The app sends a Responses API request from exactly one place.");
});
}
}
[Test]
@@ -238,22 +238,22 @@ public sealed class ModelHostTests
var embedding = new ModelProfile { Capabilities = Capability.EMBEDDING };
var throughAGateway = Transport(LLMProviders.OPEN_ROUTER, embedding);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(throughAGateway.Has(Capability.EMBEDDING), Is.True);
Assert.That(throughAGateway.HasAny(Capability.CHAT_COMPLETION_API | Capability.RESPONSES_API), Is.False);
});
}
}
[Test]
public void ANameWithoutAWrappingComesBackAsItWas()
{
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(Unwrap(LLMProviders.OPEN_AI, "gpt-5.6", out _).Original, Is.EqualTo("gpt-5.6"));
Assert.That(Unwrap(LLMProviders.GROQ, "llama-3.3-70b-versatile", out _).Original, Is.EqualTo("llama-3.3-70b-versatile"));
Assert.That(Unwrap(LLMProviders.LITE_LLM, "the-fast-one", out _).Original, Is.EqualTo("the-fast-one"));
});
}
}
private static ModelId Unwrap(LLMProviders provider, string modelId, out ModelVendor? declaredVendor) => INDEX.Unwrap(new ModelId(modelId), provider, out declaredVendor);
+8 -8
View File
@@ -37,12 +37,12 @@ public sealed class ImageLimitRuleTests
{
var limits = provider.GetModelProfile(new Model(modelId, null)).Images;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(limits.IsKnown, Is.True);
Assert.That(limits.MaxPerRequest, Is.EqualTo(perRequest));
Assert.That(limits.MaxPerMessage, Is.Null, "Neither vendor states a per-message limit, and inventing one would be a ceiling nobody wrote.");
});
}
}
[Test]
@@ -57,13 +57,13 @@ public sealed class ImageLimitRuleTests
var smallWindow = ModelRegistry.Shared.Profile(LLMProviders.ANTHROPIC, "claude-opus-4-1");
var largeWindow = ModelRegistry.Shared.Profile(LLMProviders.ANTHROPIC, "claude-opus-4-6");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(smallWindow.Context.DefaultTokens, Is.EqualTo(200_000));
Assert.That(smallWindow.Images.MaxPerRequest, Is.EqualTo(100));
Assert.That(largeWindow.Context.DefaultTokens, Is.EqualTo(1_000_000));
Assert.That(largeWindow.Images.MaxPerRequest, Is.EqualTo(600));
});
}
}
[Test]
@@ -76,11 +76,11 @@ public sealed class ImageLimitRuleTests
//
var profile = ModelRegistry.Shared.Profile(LLMProviders.SELF_HOSTED, "some-model-nobody-wrote-a-rule-for");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(profile.Images.IsKnown, Is.False);
Assert.That(profile.Images.MaxInOneMessage, Is.Null);
});
}
}
[Test]
@@ -93,11 +93,11 @@ public sealed class ImageLimitRuleTests
//
var profile = ModelRegistry.Shared.Profile(LLMProviders.OPEN_AI, "gpt-5.1");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(profile.Has(Capability.MULTIPLE_IMAGE_INPUT), Is.True, "It does read several images.");
Assert.That(profile.Images.IsKnown, Is.False, "How many, nobody said.");
});
}
}
[Test]
+12 -12
View File
@@ -35,11 +35,11 @@ public sealed class ListedModelsTests
{
var listed = new ListedModels();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(listed.Of(ONE_MACHINE, MODEL), Is.EqualTo(ModelListing.NOTHING));
Assert.That(listed.Of(ONE_MACHINE, MODEL).IsKnown, Is.False);
});
}
}
[Test]
@@ -53,11 +53,11 @@ public sealed class ListedModelsTests
listed.Report(ONE_MACHINE, [new(MODEL, ContextWindow.Of(32_768))]);
listed.Report(ANOTHER_MACHINE, [new(MODEL, ContextWindow.Of(8_192))]);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(listed.Of(ONE_MACHINE, MODEL).Context.DefaultTokens, Is.EqualTo(32_768));
Assert.That(listed.Of(ANOTHER_MACHINE, MODEL).Context.DefaultTokens, Is.EqualTo(8_192));
});
}
}
[Test]
@@ -67,11 +67,11 @@ public sealed class ListedModelsTests
listed.Report(ONE_MACHINE, [new(MODEL, ContextWindow.Of(32_768)), new("gemma3-27b", ContextWindow.Of(16_384))]);
listed.Report(ONE_MACHINE, [new("gemma3-27b", ContextWindow.Of(16_384))]);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(listed.Of(ONE_MACHINE, MODEL), Is.EqualTo(ModelListing.NOTHING), "The engine was restarted without it, so nothing is known about it any more.");
Assert.That(listed.Of(ONE_MACHINE, "gemma3-27b").Context.DefaultTokens, Is.EqualTo(16_384));
});
}
}
[Test]
@@ -105,11 +105,11 @@ public sealed class ListedModelsTests
var listed = new ListedModels();
listed.Report(string.Empty, [new(MODEL, ContextWindow.Of(32_768))]);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(listed.Of(string.Empty, MODEL), Is.EqualTo(ModelListing.NOTHING));
Assert.That(listed.Of(ONE_MACHINE, MODEL), Is.EqualTo(ModelListing.NOTHING), "One nameless report does not become every machine's answer.");
});
}
}
[Test]
@@ -126,11 +126,11 @@ public sealed class ListedModelsTests
{
var after = new ModelListing(MODEL, ContextWindow.Of(32_768)).ApplyTo(WHAT_THE_RULES_SAY);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(after.Context.DefaultTokens, Is.EqualTo(32_768));
Assert.That(after.Context.RaisableToTokens, Is.Null, "What the weights could be raised to is not a number anybody reaches without restarting this engine.");
});
}
}
[Test]
@@ -138,12 +138,12 @@ public sealed class ListedModelsTests
{
var after = new ModelListing(MODEL, ContextWindow.Of(32_768)).ApplyTo(WHAT_THE_RULES_SAY);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(after.Capabilities, Is.EqualTo(WHAT_THE_RULES_SAY.Capabilities));
Assert.That(after.Images, Is.EqualTo(WHAT_THE_RULES_SAY.Images));
Assert.That(after.Kind, Is.EqualTo(WHAT_THE_RULES_SAY.Kind));
});
}
}
[Test]
@@ -21,11 +21,11 @@ public sealed class MatchPatternTests
var onAlibaba = new MatchPattern { Kind = MatchKind.SEGMENT, Text = "qwq", OnlyOn = LLMProviders.ALIBABA_CLOUD };
var name = new ModelId("qwq-32b");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(onAlibaba.Matches(name, LLMProviders.ALIBABA_CLOUD, ModelVendor.UNKNOWN), Is.True);
Assert.That(onAlibaba.Matches(name, LLMProviders.SELF_HOSTED, ModelVendor.UNKNOWN), Is.False);
});
}
}
[Test]
@@ -34,11 +34,11 @@ public sealed class MatchPatternTests
var fromAnthropic = new MatchPattern { Kind = MatchKind.SEGMENT, Text = "claude", OnlyFrom = ModelVendor.ANTHROPIC };
var name = new ModelId("claude-sonnet-4-0");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(fromAnthropic.Matches(name, LLMProviders.LITE_LLM, ModelVendor.ANTHROPIC), Is.True);
Assert.That(fromAnthropic.Matches(name, LLMProviders.LITE_LLM, ModelVendor.UNKNOWN), Is.False);
});
}
}
[Test]
@@ -46,12 +46,12 @@ public sealed class MatchPatternTests
{
var withVision = new MatchPattern { Kind = MatchKind.SEGMENT, Text = "qwen3.8", AlsoContains = ["vl"] };
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(withVision.Matches(new ModelId("qwen3.8-27b-vl"), LLMProviders.SELF_HOSTED, ModelVendor.UNKNOWN), Is.True);
Assert.That(withVision.Matches(new ModelId("qwen3.8-27b"), LLMProviders.SELF_HOSTED, ModelVendor.UNKNOWN), Is.False);
Assert.That(withVision.Matches(new ModelId("qwen3.8-27b-vllm"), LLMProviders.SELF_HOSTED, ModelVendor.UNKNOWN), Is.False, "\"vl\" inside another name part is not the vision variant.");
});
}
}
[Test]
@@ -62,11 +62,11 @@ public sealed class MatchPatternTests
//
var withoutTools = new MatchPattern { Kind = MatchKind.SEGMENT, Text = "salamandra", NotContains = ["tools"] };
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(withoutTools.Matches(new ModelId("salamandra-7b-instruct"), LLMProviders.SELF_HOSTED, ModelVendor.UNKNOWN), Is.True);
Assert.That(withoutTools.Matches(new ModelId("salamandra-7b-instruct-tools"), LLMProviders.SELF_HOSTED, ModelVendor.UNKNOWN), Is.False);
});
}
}
[TestCase("gpt-5.1", true)]
@@ -29,11 +29,11 @@ public sealed class ModelFamilyIndexTests
var distill = Selector("deepseek-r1", MatchKind.SEGMENT, new() { Adds = Capability.TEXT_INPUT | Capability.FUNCTION_CALLING, Reasoning = ReasoningSupport.ALWAYS });
var name = new ModelId("deepseek-r1-distill-llama-70b");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(ModelFamilyIndex.Build([llama, distill]).Explain(name, ANY_PROVIDER, ModelVendor.UNKNOWN).Selector, Is.SameAs(distill));
Assert.That(ModelFamilyIndex.Build([distill, llama]).Explain(name, ANY_PROVIDER, ModelVendor.UNKNOWN).Selector, Is.SameAs(distill), "The order the rules arrive in must not change the answer.");
});
}
}
[Test]
@@ -47,11 +47,11 @@ public sealed class ModelFamilyIndexTests
var chat = Selector("gpt-5-chat", MatchKind.PREFIX, new() { Adds = Capability.TEXT_INPUT, Reasoning = ReasoningSupport.NONE });
var index = ModelFamilyIndex.Build([reasoning, chat]);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(index.Resolve(new ModelId("gpt-5-chat-latest"), ANY_PROVIDER, ModelVendor.UNKNOWN).Reasoning, Is.EqualTo(ReasoningSupport.NONE));
Assert.That(index.Resolve(new ModelId("gpt-5-pro"), ANY_PROVIDER, ModelVendor.UNKNOWN).Reasoning, Is.EqualTo(ReasoningSupport.ALWAYS));
});
}
}
[Test]
@@ -79,11 +79,11 @@ public sealed class ModelFamilyIndexTests
var index = ModelFamilyIndex.Build([openWeights, commercial]);
var name = new ModelId("qwq-32b");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(index.Explain(name, LLMProviders.ALIBABA_CLOUD, ModelVendor.UNKNOWN).Selector, Is.SameAs(commercial));
Assert.That(index.Explain(name, LLMProviders.SELF_HOSTED, ModelVendor.UNKNOWN).Selector, Is.SameAs(openWeights));
});
}
}
[Test]
@@ -98,11 +98,11 @@ public sealed class ModelFamilyIndexTests
var baseCheckpoint = Modifier("base", MatchKind.SEGMENT, new() { Removes = Capability.FUNCTION_CALLING });
var index = ModelFamilyIndex.Build([family, baseCheckpoint]);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(index.Resolve(new ModelId("llama-3.3-70b"), ANY_PROVIDER, ModelVendor.UNKNOWN).Has(Capability.FUNCTION_CALLING), Is.True);
Assert.That(index.Resolve(new ModelId("llama-3.3-70b-base"), ANY_PROVIDER, ModelVendor.UNKNOWN).Has(Capability.FUNCTION_CALLING), Is.False);
});
}
}
[Test]
@@ -113,11 +113,11 @@ public sealed class ModelFamilyIndexTests
var narrow = Modifier("instruct-nano", MatchKind.SEGMENT, new() { Removes = Capability.FUNCTION_CALLING });
var resolution = ModelFamilyIndex.Build([narrow, broad, family]).Explain(new ModelId("llama-3.3-instruct-nano"), ANY_PROVIDER, ModelVendor.UNKNOWN);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(resolution.Modifiers.Select(modifier => modifier.Pattern.Text), Is.EqualTo(new[] { "instruct", "instruct-nano" }));
Assert.That(resolution.Profile.Has(Capability.FUNCTION_CALLING), Is.False);
});
}
}
[Test]
@@ -154,12 +154,12 @@ public sealed class ModelFamilyIndexTests
var oneWay = ModelFamilyIndex.Build([one, other]).Explain(name, ANY_PROVIDER, ModelVendor.UNKNOWN);
var otherWay = ModelFamilyIndex.Build([other, one]).Explain(name, ANY_PROVIDER, ModelVendor.UNKNOWN);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(oneWay.IsAmbiguous, Is.True);
Assert.That(otherWay.IsAmbiguous, Is.True);
Assert.That(oneWay.Selector, Is.SameAs(otherWay.Selector), "Which of the two answers must not depend on the order they arrived in.");
});
}
}
[Test]
@@ -190,11 +190,11 @@ public sealed class ModelFamilyIndexTests
var index = ModelFamilyIndex.Build([Selector("llama", MatchKind.SEGMENT, new() { Adds = Capability.TEXT_INPUT })]);
var resolution = index.Explain(new ModelId("something-nobody-wrote-a-rule-for"), ANY_PROVIDER, ModelVendor.UNKNOWN);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(resolution.IsKnown, Is.False);
Assert.That(resolution.Profile, Is.EqualTo(ModelProfile.UNKNOWN));
});
}
}
[Test]
@@ -227,11 +227,11 @@ public sealed class ModelFamilyIndexTests
//
var index = ModelFamilyIndex.Build([Selector("GPT-5", MatchKind.PREFIX, new() { Adds = Capability.TEXT_INPUT })]);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(index.Rules.Where(rule => !rule.Pattern.IsWellFormed), Is.Not.Empty);
Assert.That(index.Explain(new ModelId("gpt-5.1"), ANY_PROVIDER, ModelVendor.UNKNOWN).IsKnown, Is.False);
});
}
}
private static ModelRule Selector(string text, MatchKind kind, ModelProfileChange change, string origin = "test") => new(new() { Kind = kind, Text = text }, ModelRuleKind.SELECTOR, change, origin);
+8 -8
View File
@@ -30,11 +30,11 @@ public sealed class ModelIdTests
{
var id = new ModelId(reported);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(id.IsEmpty, Is.True);
Assert.That(id.Normalized, Is.Empty);
});
}
}
[Test]
@@ -42,11 +42,11 @@ public sealed class ModelIdTests
{
var id = new ModelId("Qwen3.8:27B-MLX");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(id.Original, Is.EqualTo("Qwen3.8:27B-MLX"));
Assert.That(id.ToString(), Is.EqualTo("Qwen3.8:27B-MLX"));
});
}
}
[Test]
@@ -54,13 +54,13 @@ public sealed class ModelIdTests
{
ModelId untouched = default;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(untouched.IsEmpty, Is.True);
Assert.That(untouched.Original, Is.Empty);
Assert.That(untouched.Normalized, Is.Empty);
Assert.That(untouched.Segments.GetEnumerator().MoveNext(), Is.False);
});
}
}
[Test]
@@ -69,11 +69,11 @@ public sealed class ModelIdTests
var fromOllama = new ModelId("Qwen3.8:27b");
var fromHub = new ModelId("qwen3.8-27b");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(fromOllama, Is.EqualTo(fromHub));
Assert.That(fromOllama.GetHashCode(), Is.EqualTo(fromHub.GetHashCode()));
});
}
}
[Test]
@@ -82,10 +82,10 @@ public sealed class RuleSpecificityTests
var winner = RuleSpecificity.Of(expectedWinner);
var loser = RuleSpecificity.Of(expectedLoser);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(winner.CompareTo(loser), Is.GreaterThan(0));
Assert.That(loser.CompareTo(winner), Is.LessThan(0), "The comparison has to say the same thing in both directions.");
});
}
}
}
@@ -53,14 +53,14 @@ public sealed class MistralReleasesTests
// for "mistral-medium-3" would otherwise answer for "mistral-medium-3.5" as well and place
// it eleven months too early, before the release which gave it reasoning.
//
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(MistralReleases.Of(new ModelId("mistral-medium-3"), SOME_LATEST_RELEASE), Is.EqualTo(2505));
Assert.That(MistralReleases.Of(new ModelId("mistral-medium-3.5"), SOME_LATEST_RELEASE), Is.EqualTo(2604));
Assert.That(MistralReleases.Of(new ModelId("mistral-medium-3-5"), SOME_LATEST_RELEASE), Is.EqualTo(2604), "Mistral writes the version separator both ways for the same model.");
Assert.That(MistralReleases.Of(new ModelId("mistral-large-4"), SOME_LATEST_RELEASE), Is.EqualTo(2610));
Assert.That(MistralReleases.Of(new ModelId("mistral-large-4-0"), SOME_LATEST_RELEASE), Is.EqualTo(2610), "The model card names it with a minor version, which changes nothing about the release.");
});
}
}
[Test]
@@ -73,14 +73,14 @@ public sealed class MistralReleasesTests
var beforeItCouldSee = ModelRegistry.Shared.Profile(LLMProviders.MISTRAL, "mistral-large-2411");
var afterwards = ModelRegistry.Shared.Profile(LLMProviders.MISTRAL, "mistral-large-2512");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(beforeItCouldSee.Has(Capability.MULTIPLE_IMAGE_INPUT), Is.False);
Assert.That(beforeItCouldSee.Reasoning, Is.EqualTo(ReasoningSupport.NONE));
Assert.That(afterwards.Has(Capability.MULTIPLE_IMAGE_INPUT), Is.True);
Assert.That(afterwards.Reasoning, Is.EqualTo(ReasoningSupport.OPTIONAL));
});
}
}
[TestCase("mistral-large-4", TestName = "Mistral Large 4 under the name of Mistral's changelog")]
@@ -93,12 +93,12 @@ public sealed class MistralReleasesTests
//
var profile = ModelRegistry.Shared.Profile(LLMProviders.MISTRAL, modelId);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(profile.Has(Capability.MULTIPLE_IMAGE_INPUT), Is.True);
Assert.That(profile.Reasoning, Is.EqualTo(ReasoningSupport.OPTIONAL));
Assert.That(profile.Context.DefaultTokens, Is.EqualTo(1_000_000));
});
}
}
[TestCase("mistral-large-2512", TestName = "Mistral Large 3 keeps its own window")]
@@ -119,12 +119,12 @@ public sealed class MistralReleasesTests
//
var profile = ModelRegistry.Shared.Profile(LLMProviders.MISTRAL, "mistral-large-whenever");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(profile.Has(Capability.FUNCTION_CALLING), Is.True, "What the family could always do is still stated.");
Assert.That(profile.Has(Capability.MULTIPLE_IMAGE_INPUT), Is.False);
Assert.That(profile.Reasoning, Is.EqualTo(ReasoningSupport.NONE));
});
}
}
[Test]
@@ -132,10 +132,10 @@ public sealed class MistralReleasesTests
{
var newest = ModelRegistry.Shared.Profile(LLMProviders.MISTRAL, "ministral-3b-latest");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(newest.Has(Capability.MULTIPLE_IMAGE_INPUT), Is.True, "Ministral 3 reads images.");
Assert.That(newest.Reasoning, Is.EqualTo(ReasoningSupport.NONE), "No Ministral reasons, whatever its release.");
});
}
}
}
+10 -10
View File
@@ -20,11 +20,11 @@ public sealed class ModelFactsTests
{
ContextWindow untouched = default;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(untouched.IsKnown, Is.False);
Assert.That(untouched, Is.EqualTo(ContextWindow.UNKNOWN));
});
}
}
[Test]
@@ -32,12 +32,12 @@ public sealed class ModelFactsTests
{
var window = ContextWindow.Of(128_000, 1_000_000);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(window.IsKnown, Is.True);
Assert.That(window.DefaultTokens, Is.EqualTo(128_000));
Assert.That(window.RaisableToTokens, Is.EqualTo(1_000_000));
});
}
}
[Test]
@@ -59,11 +59,11 @@ public sealed class ModelFactsTests
{
TokenizerRef untouched = default;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(untouched.IsKnown, Is.False);
Assert.That(untouched.Kind, Is.EqualTo(TokenizerKind.UNKNOWN));
});
}
}
[Test]
@@ -79,12 +79,12 @@ public sealed class ModelFactsTests
{
ImageLimits untouched = default;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(untouched.IsKnown, Is.False);
Assert.That(untouched.MaxPerMessage, Is.Null);
Assert.That(untouched.MaxPerRequest, Is.Null);
});
}
}
[Test]
@@ -92,11 +92,11 @@ public sealed class ModelFactsTests
{
var noImages = new ImageLimits(MaxPerMessage: 0, MaxPerRequest: null);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(noImages.IsKnown, Is.True, "Zero images is a statement an operator can make.");
Assert.That(noImages.MaxPerMessage, Is.EqualTo(0));
});
}
}
[Test]
+10 -10
View File
@@ -21,11 +21,11 @@ public sealed class ModelFamilyTests
{
var family = new SampleFamily();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(family.Name, Is.EqualTo(nameof(SampleFamily)));
Assert.That(family.Rules.Select(rule => rule.Origin), Is.All.EqualTo(nameof(SampleFamily)));
});
}
}
[Test]
@@ -52,7 +52,7 @@ public sealed class ModelFamilyTests
var index = ModelFamilyIndex.Build(new SampleFamily().Rules);
var codex = index.Resolve(new ModelId("gpt-5.1-codex-max"), LLMProviders.OPEN_AI, ModelVendor.OPEN_AI);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(codex.Has(Capability.WEB_SEARCH), Is.False, "This is the one thing the variant takes away.");
Assert.That(codex.Has(Capability.TEXT_INPUT | Capability.MULTIPLE_IMAGE_INPUT | Capability.FUNCTION_CALLING), Is.True);
@@ -60,7 +60,7 @@ public sealed class ModelFamilyTests
Assert.That(codex.Context.DefaultTokens, Is.EqualTo(400_000));
Assert.That(codex.Tokenizer.Id, Is.EqualTo("o200k_base"));
Assert.That(codex.SystemPromptRole, Is.EqualTo(SystemPromptRole.DEVELOPER));
});
}
}
[Test]
@@ -69,11 +69,11 @@ public sealed class ModelFamilyTests
var index = ModelFamilyIndex.Build(new SampleFamily().Rules.Concat(new FamilyWithAModifier().Rules));
var adjusted = index.Resolve(new ModelId("gpt-5.1-base"), LLMProviders.OPEN_AI, ModelVendor.OPEN_AI);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(adjusted.Has(Capability.FUNCTION_CALLING), Is.False, "The modifier did apply.");
Assert.That(adjusted.SystemPromptRole, Is.EqualTo(SystemPromptRole.DEVELOPER));
});
}
}
[Test]
@@ -99,11 +99,11 @@ public sealed class ModelFamilyTests
{
var index = ModelFamilyIndex.Build(new FamilyWithTwoGenerations().Rules);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(index.Resolve(new ModelId("thing3-mini"), LLMProviders.SELF_HOSTED, ModelVendor.UNKNOWN).Reasoning, Is.EqualTo(ReasoningSupport.ALWAYS));
Assert.That(index.Resolve(new ModelId("thing4"), LLMProviders.SELF_HOSTED, ModelVendor.UNKNOWN).Reasoning, Is.EqualTo(ReasoningSupport.NONE));
});
}
}
[Test]
@@ -174,12 +174,12 @@ public sealed class ModelFamilyTests
[Test]
public void ASourceWithoutAPageOrADayIsNotAStatement()
{
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(new SampleFamily().Source.IsStated, Is.True);
Assert.That(new ModelSource(string.Empty, new DateOnly(2026, 9, 11), "a note").IsStated, Is.False);
Assert.That(new ModelSource("https://example.invalid", default, "a note").IsStated, Is.False);
});
}
}
/// <summary>
+6 -6
View File
@@ -24,7 +24,7 @@ public sealed class ModelKindTests
[Test]
public void EveryExampleIsRecognizedAsWhatItIsMadeFor()
{
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
foreach (var example in ModelKindCorpus.ENTRIES)
{
@@ -32,7 +32,7 @@ public sealed class ModelKindTests
Assert.That(profile.Kind, Is.EqualTo(example.Kind), $"{example.Provider} \"{example.ModelId}\"");
}
});
}
}
[Test]
@@ -59,11 +59,11 @@ public sealed class ModelKindTests
//
var profile = ModelRegistry.Shared.Profile(LLMProviders.SELF_HOSTED, "llama-guard-3-8b");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(profile.Kind, Is.EqualTo(ModelKind.MODERATION));
Assert.That(profile.Has(Capability.TEXT_INPUT), Is.True, "The family which chose the model still speaks for it.");
});
}
}
[Test]
@@ -71,10 +71,10 @@ public sealed class ModelKindTests
{
var resolution = ModelRegistry.Shared.Explain(LLMProviders.SELF_HOSTED, "bge-reranker-v2-m3");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(resolution.Profile.Kind, Is.EqualTo(ModelKind.RERANKING));
Assert.That(resolution.Modifiers.Select(modifier => modifier.Pattern.Text), Does.Contain("bge"), "Both words match; the ranked one has to be the one which gets the last word.");
});
}
}
}
+8 -8
View File
@@ -14,7 +14,7 @@ public sealed class ModelProfileTests
{
var untouched = ModelProfile.UNKNOWN;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(untouched.Capabilities, Is.EqualTo(Capability.NONE));
Assert.That(untouched.Reasoning, Is.EqualTo(ReasoningSupport.NONE));
@@ -27,7 +27,7 @@ public sealed class ModelProfileTests
// meaning "no idea".
//
Assert.That(untouched.Kind, Is.EqualTo(ModelKind.CHAT));
});
}
}
[Test]
@@ -35,13 +35,13 @@ public sealed class ModelProfileTests
{
var profile = new ModelProfile { Capabilities = Capability.TEXT_INPUT | Capability.TEXT_OUTPUT };
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(profile.Has(Capability.TEXT_INPUT | Capability.TEXT_OUTPUT), Is.True);
Assert.That(profile.Has(Capability.TEXT_INPUT | Capability.WEB_SEARCH), Is.False);
Assert.That(profile.HasAny(Capability.TEXT_INPUT | Capability.WEB_SEARCH), Is.True);
Assert.That(profile.HasAny(Capability.WEB_SEARCH | Capability.EMBEDDING), Is.False);
});
}
}
[Test]
@@ -69,13 +69,13 @@ public sealed class ModelProfileTests
var after = new ModelProfileChange { Removes = Capability.WEB_SEARCH }.ApplyTo(before);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(after.Capabilities, Is.EqualTo(Capability.TEXT_INPUT));
Assert.That(after.Reasoning, Is.EqualTo(ReasoningSupport.OPTIONAL), "A change saying nothing about reasoning must not reset it.");
Assert.That(after.Context, Is.EqualTo(before.Context), "A change saying nothing about the context window must not reset it.");
Assert.That(after.SystemPromptRole, Is.EqualTo(SystemPromptRole.DEVELOPER), "A change saying nothing about the system prompt role must not reset it.");
});
}
}
[Test]
@@ -106,11 +106,11 @@ public sealed class ModelProfileTests
var profile = change.ApplyTo(ModelProfile.UNKNOWN);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(profile.Capabilities, Is.EqualTo(Capability.TEXT_INPUT));
Assert.That(profile.Has(Capability.ALWAYS_REASONING), Is.False);
Assert.That(profile.Reasoning, Is.EqualTo(ReasoningSupport.ALWAYS));
});
}
}
}
+2 -2
View File
@@ -27,7 +27,7 @@ public sealed class Gpt6FamilyTests
{
var profile = LLMProviders.OPEN_AI.GetModelProfile(new Model(modelId, null));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(profile.Has(Capability.RESPONSES_API), Is.True);
Assert.That(profile.Has(Capability.MULTIPLE_IMAGE_INPUT), Is.True);
@@ -35,7 +35,7 @@ public sealed class Gpt6FamilyTests
Assert.That(profile.Has(Capability.WEB_SEARCH), Is.True);
Assert.That(profile.Context.IsKnown, Is.True);
Assert.That(profile.Context.DefaultTokens, Is.EqualTo(1_050_000));
});
}
}
[TestCase("gpt-6-astra", ReasoningSupport.ALWAYS, TestName = "Astra reasons on every request")]
@@ -27,13 +27,13 @@ public sealed class NewOpenAIModelTests
{
var profile = LLMProviders.OPEN_AI.GetModelProfile(new Model(modelId, null));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(profile.Has(Capability.RESPONSES_API), Is.True);
Assert.That(profile.Has(Capability.FUNCTION_CALLING), Is.True);
Assert.That(profile.Has(Capability.MULTIPLE_IMAGE_INPUT), Is.True);
Assert.That(profile.Reasoning, Is.Not.EqualTo(ReasoningSupport.NONE));
});
}
}
[Test]
@@ -46,12 +46,12 @@ public sealed class NewOpenAIModelTests
//
var profile = LLMProviders.OPEN_AI.GetModelProfile(new Model("gpt-oss-120b", null));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(profile.Has(Capability.RESPONSES_API), Is.False);
Assert.That(profile.Reasoning, Is.EqualTo(ReasoningSupport.NONE));
Assert.That(profile.Context.DefaultTokens, Is.EqualTo(131_072));
});
}
}
[Test]
@@ -59,11 +59,11 @@ public sealed class NewOpenAIModelTests
{
var profile = LLMProviders.SELF_HOSTED.GetModelProfile(new Model("gpt-j-6b", null));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(profile.Has(Capability.RESPONSES_API), Is.False);
Assert.That(profile.Has(Capability.MULTIPLE_IMAGE_INPUT), Is.False);
Assert.That(profile.Reasoning, Is.EqualTo(ReasoningSupport.NONE));
});
}
}
}
@@ -49,11 +49,11 @@ public sealed class DeclaredModelsTests
var profile = registry.Profile(LLMProviders.SELF_HOSTED, "acme-assistant-7b");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(profile.Has(Capability.FUNCTION_CALLING), Is.False, "The built-in rule grants this one, and the declaration does not.");
Assert.That(profile.Reasoning, Is.EqualTo(ReasoningSupport.NONE), "Nor does it reason, whatever the built-in rule says.");
});
}
}
[Test]
@@ -83,11 +83,11 @@ public sealed class DeclaredModelsTests
registry.Declare([]);
var afterwards = registry.Profile(LLMProviders.SELF_HOSTED, "acme-assistant-7b");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(whileDeclared.Has(Capability.FUNCTION_CALLING), Is.False);
Assert.That(afterwards.Has(Capability.FUNCTION_CALLING), Is.True);
});
}
}
[Test]
@@ -105,11 +105,11 @@ public sealed class DeclaredModelsTests
Declaring("acme-assistant-7b", MatchKind.EXACT, Capability.TEXT_INPUT | Capability.TEXT_OUTPUT | Capability.MULTIPLE_IMAGE_INPUT),
]);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(registry.Profile(LLMProviders.SELF_HOSTED, "acme-assistant-7b").Has(Capability.MULTIPLE_IMAGE_INPUT), Is.True);
Assert.That(registry.Profile(LLMProviders.SELF_HOSTED, "acme-assistant-3b").Has(Capability.MULTIPLE_IMAGE_INPUT), Is.False);
});
}
}
[Test]
@@ -127,11 +127,11 @@ public sealed class DeclaredModelsTests
{
registry.Declare([declaration]);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(registry.Profile(LLMProviders.OPEN_AI, "gpt-5.1").Has(Capability.VIDEO_INPUT), Is.True);
Assert.That(registry.Profile(LLMProviders.OPEN_ROUTER, "openai/gpt-5.1").Has(Capability.VIDEO_INPUT), Is.True, "The same model, reached through a gateway which wraps the name.");
});
}
}
finally
{
@@ -56,7 +56,7 @@ public sealed class ModelDeclarationTests
var declaration = await ReadAsync(A_COMPLETE_DECLARATION);
Assert.That(declaration, Is.Not.Null);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(declaration!.Pattern.Text, Is.EqualTo("acme-assistant"));
Assert.That(declaration.Pattern.Kind, Is.EqualTo(MatchKind.PREFIX));
@@ -68,7 +68,7 @@ public sealed class ModelDeclarationTests
Assert.That(declaration.Change.Images, Is.EqualTo(new ImageLimits(1, 8)));
Assert.That(declaration.Source.CheckedOn, Is.EqualTo(new DateOnly(2026, 9, 12)));
Assert.That(declaration.EnterpriseConfigurationPluginId, Is.EqualTo(PLUGIN_ID));
});
}
}
[Test]
@@ -77,7 +77,7 @@ public sealed class ModelDeclarationTests
var declaration = await ReadAsync(THE_LEAST_A_DECLARATION_CAN_SAY);
Assert.That(declaration, Is.Not.Null);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(declaration!.Pattern.Kind, Is.EqualTo(MatchKind.SEGMENT), "The kind to reach for by default, here as everywhere else.");
Assert.That(declaration.Change.Reasoning, Is.EqualTo(ReasoningSupport.NONE));
@@ -85,7 +85,7 @@ public sealed class ModelDeclarationTests
Assert.That(declaration.Change.Context, Is.Null);
Assert.That(declaration.Change.Tokenizer, Is.Null);
Assert.That(declaration.Change.Images, Is.Null);
});
}
}
[Test]
@@ -199,11 +199,11 @@ public sealed class ModelDeclarationTests
var theSameNames = await ReadAsync(A_COMPLETE_DECLARATION.Replace("""["CONTEXT_WINDOW"] = 131072,""", """["CONTEXT_WINDOW"] = 65536,""", StringComparison.Ordinal));
var otherNames = await ReadAsync(A_COMPLETE_DECLARATION.Replace("""["MATCH"] = "PREFIX",""", """["MATCH"] = "SEGMENT",""", StringComparison.Ordinal));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(theSameNames?.Id, Is.EqualTo(declaration?.Id), "The same pattern, so one of the two has to win.");
Assert.That(otherNames?.Id, Is.Not.EqualTo(declaration?.Id), "Bound to the name differently, so they claim different sets of names.");
});
}
}
private static async Task<ModelDeclaration?> ReadAsync(string entry)
+4 -4
View File
@@ -27,7 +27,7 @@ public sealed class PortingDifferenceTests
{
var corrected = ExpectedChanges.ENTRIES.Where(change => IsAnswered(change.Provider, change.ModelId)).ToList();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(corrected, Is.Not.Empty, "Nothing the audit found wrong is answered by a rule at all, which would make this test green for the wrong reason.");
@@ -38,7 +38,7 @@ public sealed class PortingDifferenceTests
Assert.That(rebuilt, Is.EqualTo(CapabilitySnapshot.Describe(change.AnswerWanted)), $"{change.Provider} \"{change.ModelId}\": {change.Reason}");
}
});
}
}
[Test]
@@ -80,7 +80,7 @@ public sealed class PortingDifferenceTests
// toss. Reading the rules alone cannot find it -- the two patterns are written differently
// and only meet on a real name, which is what the corpus is full of.
//
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
foreach (var entry in ModelCorpus.ENTRIES)
{
@@ -88,7 +88,7 @@ public sealed class PortingDifferenceTests
Assert.That(resolution.IsAmbiguous, Is.False, $"{entry.Provider} \"{entry.ModelId}\" is claimed by {resolution.Selector} and, just as strongly, by {string.Join(", ", resolution.TiedSelectors)}.");
}
});
}
}
/// <summary>
@@ -114,12 +114,12 @@ public sealed class ModelRegistryTests
var atOpenAI = ModelRegistry.Shared.Profile(LLMProviders.OPEN_AI, "gpt-5.1");
var throughAGateway = ModelRegistry.Shared.Profile(LLMProviders.OPEN_ROUTER, "openai/gpt-5.1");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(atOpenAI.Has(Capability.RESPONSES_API), Is.True);
Assert.That(throughAGateway.Has(Capability.RESPONSES_API), Is.False);
Assert.That(throughAGateway.Has(Capability.FUNCTION_CALLING), Is.True, "Everything but the API survives the trip through a gateway.");
});
}
}
[Test]
+8 -8
View File
@@ -40,11 +40,11 @@ public sealed class TokenizerRuleTests
{
var tokenizer = provider.GetModelProfile(new Model(modelId, null)).Tokenizer;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(tokenizer.Kind, Is.EqualTo(TokenizerKind.TIKTOKEN));
Assert.That(tokenizer.Id, Is.EqualTo(encoding));
});
}
}
[TestCase(LLMProviders.ANTHROPIC, "claude-opus-5", "/v1/messages/count_tokens")]
@@ -55,11 +55,11 @@ public sealed class TokenizerRuleTests
{
var tokenizer = provider.GetModelProfile(new Model(modelId, null)).Tokenizer;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(tokenizer.Kind, Is.EqualTo(TokenizerKind.PROVIDER_API));
Assert.That(tokenizer.Id, Is.EqualTo(endpoint));
});
}
}
[TestCase(LLMProviders.OPEN_AI, "gpt-6-astra", Description = "Newer than the mapping OpenAI publishes, so nothing is claimed for it.")]
@@ -70,11 +70,11 @@ public sealed class TokenizerRuleTests
{
var tokenizer = provider.GetModelProfile(new Model(modelId, null)).Tokenizer;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(tokenizer.IsKnown, Is.False);
Assert.That(tokenizer.Kind, Is.EqualTo(TokenizerKind.UNKNOWN), "Which means the built-in tokenizer, the same as today.");
});
}
}
[Test]
@@ -98,12 +98,12 @@ public sealed class TokenizerRuleTests
// without a kind cannot be told apart from any other string -- whether it is a repository,
// an encoding or an endpoint decides what a person can do with it.
//
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(new TokenizerRef(TokenizerKind.HUGGING_FACE, string.Empty).IsKnown, Is.False);
Assert.That(new TokenizerRef(TokenizerKind.UNKNOWN, "o200k_base").IsKnown, Is.False);
Assert.That(TokenizerRef.UNKNOWN.IsKnown, Is.False);
Assert.That(new TokenizerRef(TokenizerKind.TIKTOKEN, "o200k_base").IsKnown, Is.True);
});
}
}
}
+4 -4
View File
@@ -30,14 +30,14 @@ public sealed class AnthropicUsageTests
{
var line = Read(MESSAGE_START);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(line.GetUsage().IsKnown, Is.True);
Assert.That(line.GetUsage().PromptTokens, Is.EqualTo(25));
// It carries no answer, which is why it has to be read before the content check drops it:
Assert.That(line.ContainsContent(), Is.False);
});
}
}
[Test]
@@ -105,11 +105,11 @@ public sealed class AnthropicUsageTests
{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hi"}}
""");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(line.GetUsage().IsKnown, Is.False);
Assert.That(line.ContainsContent(), Is.True);
});
}
}
private static ResponseStreamLine Read(string data) => JsonSerializer.Deserialize<ResponseStreamLine>(data, ProviderJsonOptions.OPTIONS);
@@ -39,7 +39,7 @@ public sealed class ChatCompletionUsageTests
{
var line = JsonSerializer.Deserialize<ChatCompletionDeltaStreamLine>(USAGE_LINE, ProviderJsonOptions.OPTIONS);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(line!.GetUsage().IsKnown, Is.True);
Assert.That(line.GetUsage().PromptTokens, Is.EqualTo(1200));
@@ -49,7 +49,7 @@ public sealed class ChatCompletionUsageTests
// before the content check drops it:
//
Assert.That(line.ContainsContent(), Is.False);
});
}
}
[Test]
@@ -57,11 +57,11 @@ public sealed class ChatCompletionUsageTests
{
var line = JsonSerializer.Deserialize<ChatCompletionDeltaStreamLine>(CONTENT_LINE, ProviderJsonOptions.OPTIONS);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(line!.GetUsage().IsKnown, Is.False);
Assert.That(line.ContainsContent(), Is.True);
});
}
}
[Test]
@@ -120,10 +120,10 @@ public sealed class ChatCompletionUsageTests
{"id":"chatcmpl-xb8mn282eff3tiu46xz8t3","object":"chat.completion.chunk","created":1789917744,"model":"google/gemma-4-12b-qat","system_fingerprint":"google/gemma-4-12b-qat","choices":[],"usage":{"prompt_tokens":17,"completion_tokens":116,"total_tokens":133,"completion_tokens_details":{"reasoning_tokens":102}}}
""", ProviderJsonOptions.OPTIONS);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(line!.GetUsage().IsKnown, Is.True);
Assert.That(line.GetUsage().PromptTokens, Is.EqualTo(17));
});
}
}
}
+6 -6
View File
@@ -25,11 +25,11 @@ public sealed class HFModelTests
[Test]
public void AChosenProviderAnswersForItself()
{
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(SERVED_BY_THREE.ContextWindowTokens("novita"), Is.EqualTo(64_000));
Assert.That(SERVED_BY_THREE.ContextWindowTokens("together"), Is.EqualTo(128_000));
});
}
}
[Test]
@@ -58,11 +58,11 @@ public sealed class HFModelTests
new("together", "live", 128_000),
]);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(oneIsDown.ContextWindowTokens(AUTOMATIC), Is.EqualTo(128_000), "The small window belongs to a provider nobody can reach.");
Assert.That(oneIsDown.ContextWindowTokens("novita"), Is.Null, "And asking for that provider by name does not bring it back either.");
});
}
}
[Test]
@@ -86,10 +86,10 @@ public sealed class HFModelTests
[Test]
public void AModelNobodyServesHasNoWindow()
{
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(new HFModel("org/model", null).ContextWindowTokens(AUTOMATIC), Is.Null);
Assert.That(new HFModel("org/model", []).ContextWindowTokens(AUTOMATIC), Is.Null);
});
}
}
}
+8 -8
View File
@@ -17,32 +17,32 @@ public sealed class ImageSizeLimitTests
[Test]
public void AnthropicLeavesRoomForTheBase64OfTheImage()
{
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(LLMProviders.ANTHROPIC.GetMaxImageSizeBytes(), Is.EqualTo(3_932_160));
Assert.That(IImageSourceExtensions.MaxImageSizeBytesFor(ProviderOf(LLMProviders.ANTHROPIC)), Is.EqualTo(3_932_160));
});
}
}
[Test]
public void AProviderWithoutAStatedLimitGetsOurOwn()
{
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(LLMProviders.OPEN_AI.GetMaxImageSizeBytes(), Is.Null, "Nobody stated a limit, and no number stands in for that.");
Assert.That(IImageSourceExtensions.MaxImageSizeBytesFor(ProviderOf(LLMProviders.OPEN_AI)), Is.EqualTo(IImageSourceExtensions.MAX_IMAGE_SIZE_BYTES));
});
}
}
[Test]
public void LlmmanLeavesRoomForTheTextOfTheConversation()
{
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(Host.LLMMAN.GetMaxImageSizeBytes(), Is.EqualTo(1_048_576));
Assert.That(IImageSourceExtensions.MaxImageSizeBytesFor(ProviderOf(LLMProviders.SELF_HOSTED, Host.LLMMAN)), Is.EqualTo(1_048_576));
Assert.That(IImageSourceExtensions.MaxImageSizeBytesFor(ProviderOf(LLMProviders.SELF_HOSTED, Host.OLLAMA)), Is.EqualTo(IImageSourceExtensions.MAX_IMAGE_SIZE_BYTES), "Ollama states no limit we know of.");
});
}
}
[Test]
@@ -58,12 +58,12 @@ public sealed class ImageSizeLimitTests
[Test]
public void NoProviderGetsMoreThanOurOwnLimit()
{
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
foreach (var llmProvider in Enum.GetValues<LLMProviders>())
foreach (var host in Enum.GetValues<Host>())
Assert.That(IImageSourceExtensions.MaxImageSizeBytesFor(ProviderOf(llmProvider, host)), Is.LessThanOrEqualTo(IImageSourceExtensions.MAX_IMAGE_SIZE_BYTES), $"{llmProvider} with {host}");
});
}
}
private static AIStudio.Settings.Provider ProviderOf(LLMProviders llmProvider, Host host = Host.NONE) => new(0, "test", "Test", llmProvider, new AIStudio.Provider.Model("model", null), Host: host);
+4 -4
View File
@@ -44,11 +44,11 @@ public sealed class ModelListMetadataTests
""", AS_THE_PROVIDERS_READ_IT);
Assert.That(response.Data, Is.Not.Null);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(response.Data![0].ContextWindowTokens, Is.EqualTo(32_768));
Assert.That(response.Data[0].OwnedBy, Is.EqualTo("vllm"), "Read with the shared options, this one arrives too -- it did not before.");
});
}
}
[Test]
@@ -86,11 +86,11 @@ public sealed class ModelListMetadataTests
}
""", AS_THE_PROVIDERS_READ_IT);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(response.Data[0].ContextWindowTokens, Is.EqualTo(1_000_000));
Assert.That(response.Data[0].Name, Is.EqualTo("Anthropic: Claude Sonnet 4.5"), "The fields we do read keep working next to the fields we deliberately do not.");
});
}
}
[Test]
@@ -62,11 +62,11 @@ public sealed class ReasoningDispatcherTests
//
var llmman = ReasoningDispatcher.DialectsOf(LLMProviders.SELF_HOSTED, Host.LLMMAN);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(llmman, Is.SupersetOf(ReasoningDispatcher.DialectsOf(LLMProviders.SELF_HOSTED, Host.LLAMA_CPP)));
Assert.That(llmman, Is.SupersetOf(ReasoningDispatcher.DialectsOf(LLMProviders.SELF_HOSTED, Host.VLLM)));
});
}
}
[Test]
@@ -77,12 +77,12 @@ public sealed class ReasoningDispatcherTests
// switch into a Mistral provider says nothing -- and claiming it did would light an indicator
// for a request which will never carry that parameter anywhere.
//
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(ReasoningDispatcher.WhatTheParametersSay(LLMProviders.MISTRAL, Host.NONE, """ "enable_thinking": true """), Is.EqualTo(ReasoningConfigurationState.NOT_CONFIGURED));
Assert.That(ReasoningDispatcher.WhatTheParametersSay(LLMProviders.ANTHROPIC, Host.NONE, """ "reasoning_effort": "high" """), Is.EqualTo(ReasoningConfigurationState.NOT_CONFIGURED));
Assert.That(ReasoningDispatcher.WhatTheParametersSay(LLMProviders.MISTRAL, Host.NONE, """ "reasoning_effort": "high" """), Is.EqualTo(ReasoningConfigurationState.EXPLICITLY_ENABLED), "And the one it does speak still counts.");
});
}
}
[Test]
@@ -131,7 +131,7 @@ public sealed class ReasoningDispatcherTests
{
var known = ReasoningDispatcher.Dialects.Select(dialect => dialect.Dialect).ToHashSet();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
foreach (var provider in Enum.GetValues<LLMProviders>())
foreach (var host in Enum.GetValues<Host>())
@@ -141,6 +141,6 @@ public sealed class ReasoningDispatcherTests
Assert.That(named, Is.SubsetOf(known), $"{provider} on {host} names a dialect nothing answers to.");
Assert.That(named, Is.Unique, $"{provider} on {host} names a dialect twice.");
}
});
}
}
}
+2 -2
View File
@@ -31,11 +31,11 @@ public sealed class ResponsesUsageTests
//
var line = Read(COMPLETED_PREFIX + MESSAGE_ITEM + "]" + USAGE_SUFFIX);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(line.GetUsage().IsKnown, Is.True);
Assert.That(line.GetUsage().PromptTokens, Is.EqualTo(2006));
});
}
}
[Test]
@@ -58,12 +58,12 @@ public sealed class HostExtensionsTests
[Test]
public void TheHostNoneLoadsNoModels()
{
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(Host.NONE.IsModelListSupported(), Is.False);
Assert.That(Host.NONE.IsEmbeddingSupported(), Is.False);
Assert.That(Host.NONE.IsTranscriptionSupported(), Is.False);
Assert.That(LLMProviders.SELF_HOSTED.CanLoadModels(Host.NONE, "a key"), Is.False);
});
}
}
}
@@ -79,12 +79,12 @@ public sealed class SelfHostedModelListTests
.Select(model => model.Id)
.ToList();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(embeddingModels, Does.Contain("bge-m3:latest"), "Named after the family which built it, with no word about what it does.");
Assert.That(embeddingModels, Does.Contain("all-minilm:latest"), "The same, and without the organization which used to be the only marker.");
Assert.That(embeddingModels, Has.Count.EqualTo(5), "The three Qwen embedding tags belong here as well, and nothing else does.");
});
}
}
[Test]
@@ -79,12 +79,12 @@ public sealed class AnthropicMessageStreamAccumulatorTests
"""{"type":"message_stop"}""");
var block = response!.Content.Single();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(ReadString(block, "type"), Is.EqualTo("thinking"), "The block goes back as the kind it was.");
Assert.That(ReadString(block, "thinking"), Is.EqualTo("Weighing the options."), "With the thinking it carried.");
Assert.That(ReadString(block, "signature"), Is.EqualTo("EqQBCgIYAhIM+abc/DEF=="), "And with the signature that was made for exactly that text.");
});
}
}
[Test]
@@ -112,12 +112,12 @@ public sealed class AnthropicMessageStreamAccumulatorTests
"""{"type":"message_stop"}""");
var toolUse = response!.GetToolUses().Single();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(toolUse.Id, Is.EqualTo("toolu_1"), "The ID comes from the block as it opened.");
Assert.That(toolUse.Name, Is.EqualTo("web_search"), "So does the name.");
Assert.That(toolUse.Arguments, Is.EqualTo("""{"query":"weather"}"""), "And the arguments are the fragments joined back together.");
});
}
}
[Test]
@@ -149,11 +149,11 @@ public sealed class AnthropicMessageStreamAccumulatorTests
"""{"type":"message_stop"}""");
var toolUse = response!.GetToolUses().Single();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(toolUse.Arguments, Is.EqualTo("""{"query":"wea"""), "The call carries what actually arrived, which no tool executor will accept.");
Assert.That(ReadRawText(response.Content.Single(), "input"), Is.EqualTo("{}"), "While the block going back to Anthropic carries an object, because anything else would be refused.");
});
}
}
[Test]
@@ -198,11 +198,11 @@ public sealed class AnthropicMessageStreamAccumulatorTests
"""{"type":"content_block_stop","index":0}""",
"""{"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{"output_tokens":5}}""");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(response, Is.Not.Null, "A message with a stop reason is a message which ended.");
Assert.That(response!.StopReason, Is.EqualTo("end_turn"), "And the reason it ended travels with it.");
});
}
}
[Test]
@@ -232,12 +232,12 @@ public sealed class AnthropicMessageStreamAccumulatorTests
"""{"type":"content_block_stop","index":0}""",
"""{"type":"message_stop"}""");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(part.Usage.PromptTokens, Is.EqualTo(2679), "The start of the message states what the request carried.");
Assert.That(part.HasContent, Is.False, "And it has nothing to show.");
Assert.That(response!.Content, Has.Count.EqualTo(1), "Nor does it become a block of the message.");
});
}
}
[Test]
@@ -49,11 +49,11 @@ public sealed class AnthropicToolCallingAdapterTests
var secondRound = await Usages(adapter);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(firstRound, Is.EqualTo(new[] { 1200 }), "The first round states the conversation up to the question.");
Assert.That(secondRound, Is.EqualTo(new[] { 9800 }), "The second round states its own request, and the cumulative number at its end stays behind.");
});
}
}
[Test]
@@ -28,12 +28,12 @@ public sealed class ChatCompletionToolCallAccumulatorTests
"[DONE]");
var call = message!.ToolCalls!.Single()!;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(call.Id, Is.EqualTo("call_1"), "The ID arrived with the first fragment and belongs to the whole call.");
Assert.That(call.Function!.Name, Is.EqualTo("web_search"), "So does the name.");
Assert.That(call.Function!.Arguments, Is.EqualTo("""{"query":"weather"}"""), "And the arguments are the fragments in the order they came, joined without anything in between.");
});
}
}
[Test]
@@ -68,11 +68,11 @@ public sealed class ChatCompletionToolCallAccumulatorTests
"""{"choices":[{"index":0,"delta":{"tool_calls":[{"function":{"arguments":"\"weather\"}"}}]}}]}""");
var call = message!.ToolCalls!.Single()!;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(call.Id, Is.EqualTo("call_1"), "One call, not two: a fragment without an index belongs to the one being written.");
Assert.That(call.Function!.Arguments, Is.EqualTo("""{"query":"weather"}"""), "And its arguments are complete.");
});
}
}
[Test]
@@ -100,12 +100,12 @@ public sealed class ChatCompletionToolCallAccumulatorTests
.Select(part => part.TextDelta));
var message = accumulator.Build();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(shown, Is.EqualTo("Let me look that up."), "The text goes out while it is being written, in the pieces it arrives in.");
Assert.That(message!.Content, Is.EqualTo("Let me look that up."), "And the same text goes back to the provider as what the model said.");
Assert.That(message.ToolCalls!.Single()!.Id, Is.EqualTo("call_1"), "The tool call of that round is there as well.");
});
}
}
[Test]
@@ -124,11 +124,11 @@ public sealed class ChatCompletionToolCallAccumulatorTests
"""{"choices":[{"index":0,"delta":{"reasoning_content":"Thinking about it."}}]}""",
"""{"choices":[{"index":0,"delta":{"content":"The answer."}}]}""");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(message!.ReasoningContent, Is.EqualTo("Thinking about it."), "Reasoning is kept, because the next request is charged for it.");
Assert.That(message.Content, Is.EqualTo("The answer."), "And it is not mixed into the answer.");
});
}
}
[Test]
@@ -142,11 +142,11 @@ public sealed class ChatCompletionToolCallAccumulatorTests
var withoutAnyFragment = Read("""{"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_1","function":{"name":"get_time"}}]}}]}""");
var withAnEmptyFragment = Read("""{"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_1","function":{"name":"get_time","arguments":""}}]}}]}""");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(withoutAnyFragment!.ToolCalls!.Single()!.Function!.Arguments, Is.EqualTo("{}"), "No fragment at all is a call without arguments, not a broken one.");
Assert.That(withAnEmptyFragment!.ToolCalls!.Single()!.Function!.Arguments, Is.EqualTo("{}"), "And neither is the empty fragment some providers send instead.");
});
}
}
[Test]
@@ -158,11 +158,11 @@ public sealed class ChatCompletionToolCallAccumulatorTests
//
var message = Read("""{"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_1","function":{"name":"web_search","arguments":"{}"}}]}}]}""");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(message!.RawContent, Is.Null, "No text means no content field.");
Assert.That(message.Content, Is.Null, "Which is what the adapter reads as an answer without words.");
});
}
}
[Test]
@@ -190,12 +190,12 @@ public sealed class ChatCompletionToolCallAccumulatorTests
var accumulator = new ChatCompletionToolCallAccumulator();
var part = accumulator.Process(Event("""{"choices":[],"usage":{"prompt_tokens":1200,"completion_tokens":345,"total_tokens":1545}}"""));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(part.Usage.IsKnown, Is.True, "The last line of the stream has no choices, and it must not be dropped for that.");
Assert.That(part.Usage.PromptTokens, Is.EqualTo(1200));
Assert.That(part.HasContent, Is.False, "It has nothing to show, though.");
});
}
}
[Test]
@@ -208,11 +208,11 @@ public sealed class ChatCompletionToolCallAccumulatorTests
var accumulator = new ChatCompletionToolCallAccumulator();
var part = accumulator.Process(Event("""{"choices":[{"index":0,"delta":{"content":"Bye"}}],"usage":{"prompt_tokens":1200,"completion_tokens":2}}"""));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(part.TextDelta, Is.EqualTo("Bye"));
Assert.That(part.Usage.PromptTokens, Is.EqualTo(1200));
});
}
}
private static ChatCompletionResponseMessage? Read(params string[] data)
@@ -59,11 +59,11 @@ public sealed class ChatCompletionToolCallingAdapterTests
var secondRound = await Usages(adapter);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(firstRound, Is.EqualTo(new[] { 1200 }), "The first round reports the conversation up to the question.");
Assert.That(secondRound, Is.EqualTo(new[] { 9800 }), "The second round reports its own request, tool result included, and leaves it to the loop to drop.");
});
}
}
[Test]
@@ -116,12 +116,12 @@ public sealed class ChatCompletionToolCallingAdapterTests
var (call, nextRequest) = await AnswerOneCall(MALFORMED_NAME, "{}", "The tool call was invalid.");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(call.IsValid, Is.False, "The call is answered as invalid instead of being run.");
Assert.That(nextRequest, Does.Not.Contain(MALFORMED_NAME), "The provider would reject the whole request over this name.");
Assert.That(nextRequest, Does.Contain("\"name\":\"invalid_tool_call\""), "The call itself still goes back, or its result would answer a call the provider does not know.");
});
}
}
[Test]
@@ -134,12 +134,12 @@ public sealed class ChatCompletionToolCallingAdapterTests
//
var (call, nextRequest) = await AnswerOneCall("web_lookup", "{}", "Tool 'web_lookup' is not available.");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(call.IsValid, Is.True);
Assert.That(call.ToolName, Is.EqualTo("web_lookup"));
Assert.That(nextRequest, Does.Contain("\"name\":\"web_lookup\""));
});
}
}
[Test]
@@ -153,13 +153,13 @@ public sealed class ChatCompletionToolCallingAdapterTests
var (call, nextRequest) = await AnswerOneCall("web_search", BROKEN_ARGUMENTS, "The tool call was invalid.");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(call.IsValid, Is.False, "The call is answered as invalid instead of being run.");
Assert.That(nextRequest, Does.Not.Contain("weather in Berl"), "The provider would reject the whole request over these arguments.");
Assert.That(nextRequest, Does.Contain("\"arguments\":\"{}\""), "The call itself still goes back, with arguments the provider can read.");
Assert.That(nextRequest, Does.Contain("\"name\":\"web_search\""), "A valid name goes back as it came, even when the arguments next to it are broken.");
});
}
}
/// <summary>
@@ -24,11 +24,11 @@ public sealed class OpenAIStrictToolSchemaTests
.OptionalString("language", "The language.")
.OptionalInteger("page", "The page."))["properties"]!;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(Types(properties["language"]!), Is.EqualTo(new[] { "string", "null" }));
Assert.That(Types(properties["page"]!), Is.EqualTo(new[] { "integer", "null" }));
});
}
}
[Test]
@@ -38,11 +38,11 @@ public sealed class OpenAIStrictToolSchemaTests
.RequiredString("query", "The search query.")
.OptionalEnum("time_range", "The time range.", "day", "month", "year"))["properties"]!["time_range"]!;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(Types(timeRange), Is.EqualTo(new[] { "string", "null" }));
Assert.That(timeRange["enum"]!.AsArray().Select(value => value?.GetValue<string>()), Is.EqualTo(new[] { null, "day", "month", "year" }));
});
}
}
[Test]
@@ -52,12 +52,12 @@ public sealed class OpenAIStrictToolSchemaTests
.RequiredString("query", "The search query.")
.OptionalStringArray("data_source_ids", "The data sources.", "first", "second"))["properties"]!["data_source_ids"]!;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(Types(dataSourceIds), Is.EqualTo(["array", "null"]), "Leaving the list out is said by allowing null for the list itself.");
Assert.That(dataSourceIds["items"]!["enum"]!.AsArray().Select(value => value?.GetValue<string>()), Is.EqualTo(["first", "second"]), "Null is a way to leave the list out, not an entry it may hold.");
Assert.That(dataSourceIds["enum"], Is.Null, "The list itself names no values of its own.");
});
}
}
[Test]
@@ -24,11 +24,11 @@ public sealed class ProviderToolAdaptersTests
var definition = WebSearchLike();
var function = Sent(definition, hostEnforcesStrict: false);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(function["strict"]!.GetValue<bool>(), Is.False);
Assert.That(JsonNode.DeepEquals(function["parameters"], JsonNode.Parse(definition.Function.Parameters.GetRawText())), Is.True);
});
}
}
[Test]
@@ -36,11 +36,11 @@ public sealed class ProviderToolAdaptersTests
{
var function = Sent(WebSearchLike(), hostEnforcesStrict: true);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(function["strict"]!.GetValue<bool>(), Is.True);
Assert.That(function["parameters"]!["required"]!.AsArray().Select(name => name!.GetValue<string>()), Is.EqualTo(new[] { "query", "page" }));
});
}
}
[Test]
@@ -49,11 +49,11 @@ public sealed class ProviderToolAdaptersTests
var definition = WebSearchLike(isStrict: false);
var function = Sent(definition, hostEnforcesStrict: true);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(function["strict"]!.GetValue<bool>(), Is.False);
Assert.That(JsonNode.DeepEquals(function["parameters"], JsonNode.Parse(definition.Function.Parameters.GetRawText())), Is.True);
});
}
}
/// <summary>
@@ -38,12 +38,12 @@ public sealed class ResponsesStreamAccumulatorTests
"""{"type":"function_call","call_id":"call_1","name":"web_search","arguments":"{\"query\":\"weather\"}"}""" +
"]}}");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(response!.GetTextOutput(), Is.EqualTo("Here is the answer."), "The text of the round is read out of the completed response.");
Assert.That(response.GetFunctionCalls().Single().CallId, Is.EqualTo("call_1"), "And so are the calls it asked for.");
Assert.That(response.Output, Has.Count.EqualTo(3), "Every output item is kept, because every one of them goes back.");
});
}
}
[Test]
@@ -65,12 +65,12 @@ public sealed class ResponsesStreamAccumulatorTests
{
var accumulator = new ResponsesStreamAccumulator();
var part = accumulator.Process(Event("""{"type":"response.output_text.annotation.added","annotation_index":0,"annotation":{"type":"url_citation","title":"Example","url":"https://example.org/"}}"""));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(part.Sources.Select(x => x.URL), Is.EqualTo(new[] { "https://example.org/" }), "A citation reaches the user as soon as the model makes it.");
Assert.That(part.TextDelta, Is.Empty, "A line which only announces a source carries no text.");
});
}
}
[Test]
@@ -84,12 +84,12 @@ public sealed class ResponsesStreamAccumulatorTests
"""{"type":"response.output_item.done","output_index":0,"item":""" + REASONING_ITEM + "}",
"""{"type":"response.output_item.done","output_index":1,"item":{"type":"function_call","call_id":"call_1","name":"web_search","arguments":"{}"}}""");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(response, Is.Not.Null, "A round built from its items is still a round.");
Assert.That(response!.Output.First().GetRawText(), Is.EqualTo(REASONING_ITEM), "And the reasoning item is as untouched as it would be in the completed event.");
Assert.That(response.GetFunctionCalls().Single().Name, Is.EqualTo("web_search"), "The call is there to be executed.");
});
}
}
[Test]
@@ -111,13 +111,13 @@ public sealed class ResponsesStreamAccumulatorTests
var accumulator = new ResponsesStreamAccumulator();
var part = accumulator.Process(Event(COMPLETED_PREFIX + REASONING_ITEM + """],"usage":{"input_tokens":2679,"input_tokens_details":{"cached_tokens":0},"output_tokens":510}}}"""));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(part.Usage.IsKnown, Is.True, "The closing event is the one line which states what the request cost.");
Assert.That(part.Usage.PromptTokens, Is.EqualTo(2679));
Assert.That(part.HasContent, Is.False, "And there is nothing on it to show.");
Assert.That(accumulator.Build()!.Output, Has.Count.EqualTo(1), "Reading the usage leaves the round as it was.");
});
}
}
[Test]
@@ -40,11 +40,11 @@ public sealed class ResponsesToolCallingAdapterTests
var secondRound = await Usages(adapter);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(firstRound, Is.EqualTo(new[] { 1200 }), "The first round states the conversation up to the question.");
Assert.That(secondRound, Is.EqualTo(new[] { 9800 }), "The second round states its own request.");
});
}
}
[Test]
@@ -70,11 +70,11 @@ public sealed class ResponsesToolCallingAdapterTests
written.Add(delta.Content);
}
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(usages, Is.Empty, "What the search found is part of the number, and no later request carries it.");
Assert.That(written, Is.EqualTo(new[] { "It is sunny." }), "The answer itself goes out as always.");
});
}
}
/// <summary>
@@ -54,11 +54,11 @@ public sealed class BatchProcessingResultFileFormatTests
{
var processed = await ProcessAsync("MICROSOFT_WORD");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(processed, Is.True);
Assert.That(this.ResultFileFormat, Is.EqualTo(FileExportFormat.MICROSOFT_WORD));
});
}
}
[TestCase("CSV", TestName = "A table, which holds one table out of an answer")]
@@ -70,12 +70,12 @@ public sealed class BatchProcessingResultFileFormatTests
var processed = await ProcessAsync(configuredFormat);
ManagedConfiguration.TryGet(x => x.BatchProcessing, x => x.ResultFileFormat, out var configMeta);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(processed, Is.False, "The result files would end up without an extension or as something other than an answer.");
Assert.That(this.ResultFileFormat, Is.EqualTo(FileExportFormat.HTML), "A rejected value leaves the choice of the user untouched.");
Assert.That(configMeta.IsLocked, Is.False, "A rejected value locks nothing.");
});
}
}
private FileExportFormat ResultFileFormat
@@ -45,10 +45,10 @@ public sealed class ChatDataSourceDefaultsTests
var read = JsonSerializer.Deserialize<DataChat>(LEGACY_JSON, SettingsManager.JSON_OPTIONS)!;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(read.PreselectedDataSourcesDisabled, Is.False, "The legacy nested object has to reach the individual fields, or this test checks nothing.");
Assert.That(read.PreselectedDataSourcesRetrievalMode, Is.EqualTo(DataSourceRetrievalMode.SEMANTIC_SEARCH));
});
}
}
}
@@ -42,7 +42,7 @@ public sealed class ChatTemplateConfigurationTests
var read = await ExportAndReadBackAsync(written);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(read.ToolIds, Is.EquivalentTo(written.ToolIds!));
Assert.That(read.DataSourceOptions, Is.Not.Null);
@@ -51,7 +51,7 @@ public sealed class ChatTemplateConfigurationTests
Assert.That(read.DataSourceOptions.AutomaticValidation, Is.True);
Assert.That(read.DataSourceOptions.RetrievalMode, Is.EqualTo(DataSourceRetrievalMode.EVERY_MESSAGE), "Semantic search is the default, so only the other choice shows that the export carries it.");
Assert.That(read.DataSourceOptions.PreselectedDataSourceIds, Is.EqualTo(written.DataSourceOptions!.PreselectedDataSourceIds));
});
}
}
[Test]
@@ -70,13 +70,13 @@ public sealed class ChatTemplateConfigurationTests
var read = await ExportAndReadBackAsync(written);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(read.DataSourceOptions, Is.Not.Null);
Assert.That(read.DataSourceOptions!.AutomaticDataSourceSelection, Is.True, "Letting an agent pick the sources is the one thing only a chat template can state, so it must not be lost on the way through Lua.");
Assert.That(read.DataSourceOptions.PreselectedDataSourceIds, Is.Empty);
Assert.That(read.ToolIds, Is.Null);
});
}
}
[Test]
@@ -85,22 +85,22 @@ public sealed class ChatTemplateConfigurationTests
var written = NewTemplate();
Assert.That(written.TryExportAsConfigurationSection(out var luaCode, out var issue), Is.True, issue);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(luaCode, Does.Not.Contain("ToolIds"));
Assert.That(luaCode, Does.Not.Contain("DataSourceOptions"));
Assert.That(luaCode, Does.Not.Contain("ProviderId"));
Assert.That(luaCode, Does.Not.Contain("ProfileId"));
Assert.That(luaCode, Does.Not.Contain("WorkspaceName"));
});
}
var read = await ParseAsync(luaCode);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(read.ToolIds, Is.Null, "A template without a tool selection must stay without one, so the chat keeps using its own default.");
Assert.That(read.DataSourceOptions, Is.Null);
});
}
}
[Test]
@@ -130,14 +130,14 @@ public sealed class ChatTemplateConfigurationTests
""");
Assert.That(read.DataSourceOptions, Is.Not.Null);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(read.DataSourceOptions!.DisableDataSources, Is.False, "Writing this table is already the statement that the template wants data sources, so an omitted switch must not turn them off again.");
Assert.That(read.DataSourceOptions.AutomaticDataSourceSelection, Is.False);
Assert.That(read.DataSourceOptions.AutomaticValidation, Is.False);
Assert.That(read.DataSourceOptions.RetrievalMode, Is.EqualTo(DataSourceRetrievalMode.SEMANTIC_SEARCH));
Assert.That(read.DataSourceOptions.PreselectedDataSourceIds, Is.EqualTo(new[] { "11111111-1111-1111-1111-111111111111" }));
});
}
}
[TestCase("'EVERY_MESSAGE'", DataSourceRetrievalMode.EVERY_MESSAGE)]
@@ -186,11 +186,11 @@ public sealed class ChatTemplateConfigurationTests
}
""");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(read.ToolIds, Is.EquivalentTo(new[] { "web_search", "read_web_page" }));
Assert.That(read.DataSourceOptions!.PreselectedDataSourceIds, Is.EqualTo(new[] { "11111111-1111-1111-1111-111111111111" }));
});
}
}
[Test]
@@ -204,11 +204,11 @@ public sealed class ChatTemplateConfigurationTests
var read = await ExportAndReadBackAsync(written);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(read.ProviderId, Is.EqualTo(written.ProviderId));
Assert.That(read.ProfileId, Is.EqualTo(written.ProfileId));
});
}
}
[Test]
@@ -217,12 +217,12 @@ public sealed class ChatTemplateConfigurationTests
var read = await ExportAndReadBackAsync(NewTemplate() with { ProfileId = Profile.NO_PROFILE.Id });
var silent = await ExportAndReadBackAsync(NewTemplate());
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(read.ProfileId, Is.EqualTo(Profile.NO_PROFILE.Id), "Reading this back as null would hand out the default profile the template ruled out.");
Assert.That(silent.ProfileId, Is.Null);
Assert.That(silent.ProviderId, Is.Null);
});
}
}
[Test]
@@ -239,11 +239,11 @@ public sealed class ChatTemplateConfigurationTests
}
""");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(read.ProviderId, Is.EqualTo("44444444-4444-4444-4444-444444444444"));
Assert.That(read.ProfileId, Is.EqualTo("55555555-5555-5555-5555-555555555555"));
});
}
}
[TestCase("\"\"")]
@@ -271,11 +271,11 @@ public sealed class ChatTemplateConfigurationTests
var read = await ExportAndReadBackAsync(written);
var silent = await ExportAndReadBackAsync(NewTemplate());
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(read.WorkspaceName, Is.EqualTo(written.WorkspaceName));
Assert.That(silent.WorkspaceName, Is.Null, "A template without a workspace must keep leaving the chat where it would go anyway.");
});
}
}
[TestCase("\"\"")]
@@ -320,11 +320,11 @@ public sealed class ChatTemplateConfigurationTests
Assert.That(withProvider.TryExportAsConfigurationSection(out var withProviderLua, out var issue), Is.True, issue);
Assert.That(withoutProfile.TryExportAsConfigurationSection(out var withoutProfileLua, out issue), Is.True, issue);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(withProviderLua, Does.StartWith("--"), "A provider of this machine is mostly the user's own, which no configuration plugin knows.");
Assert.That(withoutProfileLua, Does.Not.StartWith("--"), "Starting without a profile names nothing which could be missing.");
});
}
}
[Test]
@@ -343,11 +343,11 @@ public sealed class ChatTemplateConfigurationTests
Assert.That(withSources.TryExportAsConfigurationSection(out var withSourcesLua, out var issue), Is.True, issue);
Assert.That(agenticOnly.TryExportAsConfigurationSection(out var agenticOnlyLua, out issue), Is.True, issue);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(withSourcesLua, Does.StartWith("--"), "Whoever pastes this into a plugin cannot see from the IDs alone that they belong to another machine.");
Assert.That(agenticOnlyLua, Does.Not.StartWith("--"), "Without IDs there is nothing to check, so the note would only be noise.");
});
}
}
[Test]
@@ -380,12 +380,12 @@ public sealed class ChatTemplateConfigurationTests
DataSourceOptions = new() { DisableDataSources = false, AutomaticDataSourceSelection = true },
};
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(ChatTemplate.GetPreselectedLocalDataSourceNames(eriOnly, ConfiguredDataSources()), Is.Empty);
Assert.That(ChatTemplate.GetPreselectedLocalDataSourceNames(agentic, ConfiguredDataSources()), Is.Empty, "An agent picks the sources per message, so this template names none to begin with.");
Assert.That(ChatTemplate.GetPreselectedLocalDataSourceNames(NewTemplate(), ConfiguredDataSources()), Is.Empty);
});
}
}
[Test]
@@ -41,12 +41,12 @@ public sealed class ChatTemplateLauncherSelectionTests : ToolRegistryTestBase
var providerResult = service.ResolveProvider(PLUGIN_NAME, template, LAUNCHER_PROVIDER_ID);
var profileResult = service.ResolveProfile(PLUGIN_NAME, template, LAUNCHER_PROFILE_ID);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(providerResult.Provider, Is.EqualTo(templateProvider));
Assert.That(providerResult.IsExplicit, Is.True);
Assert.That(profileResult.Profile, Is.EqualTo(templateProfile));
});
}
}
[Test]
@@ -61,13 +61,13 @@ public sealed class ChatTemplateLauncherSelectionTests : ToolRegistryTestBase
var providerResult = service.ResolveProvider(PLUGIN_NAME, template, LAUNCHER_PROVIDER_ID);
var profileResult = service.ResolveProfile(PLUGIN_NAME, template, LAUNCHER_PROFILE_ID);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(providerResult.Provider, Is.EqualTo(launcherProvider));
Assert.That(providerResult.ErrorMessage, Is.Empty);
Assert.That(profileResult.Profile, Is.EqualTo(launcherProfile));
Assert.That(profileResult.ErrorMessage, Is.Empty);
});
}
}
[Test]
@@ -81,12 +81,12 @@ public sealed class ChatTemplateLauncherSelectionTests : ToolRegistryTestBase
var template = NewTemplate() with { ProviderId = templateProvider.Id };
var providerResult = this.CreateService().ResolveProvider(PLUGIN_NAME, template, null);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(providerResult.Provider, Is.Not.EqualTo(templateProvider), "A template must not open the way to a provider the user could not pick by hand.");
Assert.That(providerResult.IsExplicit, Is.False, "The launcher names no provider, so the chat starts with its defaults instead of failing.");
Assert.That(providerResult.ErrorMessage, Is.Empty);
});
}
}
[Test]
@@ -107,11 +107,11 @@ public sealed class ChatTemplateLauncherSelectionTests : ToolRegistryTestBase
var options = new DataSourceOptions { DisableDataSources = false, PreselectedDataSourceIds = ["99999999-9999-9999-9999-999999999999"] };
var result = await this.CreateService().CheckChosenDataSourcesAsync(ToolCapableProvider(), options, originChatTemplate: null);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(result.Options, Is.SameAs(options), "The chat uses no data sources without RAG, so the options travel along unchecked and count once RAG is back.");
Assert.That(result.ErrorMessage, Is.Empty);
});
}
}
[Test]
@@ -121,11 +121,11 @@ public sealed class ChatTemplateLauncherSelectionTests : ToolRegistryTestBase
var options = new DataSourceOptions { DisableDataSources = false, PreselectedDataSourceIds = ["99999999-9999-9999-9999-999999999999"] };
var result = await this.CreateService().CheckChosenDataSourcesAsync(ToolCapableProvider(), options, originChatTemplate: null);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(result.Options, Is.Null, "Opening a launcher is one click, so a missing source has to be said out loud.");
Assert.That(result.ErrorMessage, Is.Not.Empty);
});
}
}
// Data sources are not resolved by these tests beyond their lookup, so the service needs no data source service:
@@ -22,11 +22,11 @@ public sealed class ChatTemplatePrecedenceTests
var toolChoice = ChatTemplate.ChooseToolIds(NewTemplate(), null);
var optionsChoice = ChatTemplate.ChooseDataSourceOptions(NewTemplate(), null);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(toolChoice, Is.EqualTo(ChatTemplateToolChoice.NONE), "Nobody named any tool, so the chat has to keep using the tools of its own default.");
Assert.That(optionsChoice, Is.EqualTo(ChatTemplateDataSourceChoice.NONE), "Nobody named any data source, so the chat has to keep using its own default options.");
});
}
}
[Test]
@@ -38,13 +38,13 @@ public sealed class ChatTemplatePrecedenceTests
var toolChoice = ChatTemplate.ChooseToolIds(template, new[] { "web_search" });
var optionsChoice = ChatTemplate.ChooseDataSourceOptions(template, launcherOptions);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(toolChoice.ToolIds, Is.EquivalentTo(new[] { "web_search" }));
Assert.That(toolChoice.LauncherChoiceDropped, Is.False, "Nothing was dropped here, so nothing may be reported as dropped either.");
Assert.That(optionsChoice.Options, Is.SameAs(launcherOptions));
Assert.That(optionsChoice.LauncherChoiceDropped, Is.False);
});
}
}
[Test]
@@ -59,13 +59,13 @@ public sealed class ChatTemplatePrecedenceTests
var toolChoice = ChatTemplate.ChooseToolIds(template, null);
var optionsChoice = ChatTemplate.ChooseDataSourceOptions(template, null);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(toolChoice.ToolIds, Is.EquivalentTo(new[] { "read_web_page" }));
Assert.That(toolChoice.LauncherChoiceDropped, Is.False);
Assert.That(optionsChoice.Options!.PreselectedDataSourceIds, Is.EqualTo(new[] { "22222222-2222-2222-2222-222222222222" }));
Assert.That(optionsChoice.LauncherChoiceDropped, Is.False);
});
}
}
[Test]
@@ -80,13 +80,13 @@ public sealed class ChatTemplatePrecedenceTests
var toolChoice = ChatTemplate.ChooseToolIds(template, new[] { "web_search" });
var optionsChoice = ChatTemplate.ChooseDataSourceOptions(template, NewLauncherOptions("11111111-1111-1111-1111-111111111111"));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(toolChoice.ToolIds, Is.EquivalentTo(new[] { "read_web_page" }));
Assert.That(toolChoice.LauncherChoiceDropped, Is.True, "The tools of the launcher are gone, and only this flag can make the log say so.");
Assert.That(optionsChoice.Options!.PreselectedDataSourceIds, Is.EqualTo(new[] { "22222222-2222-2222-2222-222222222222" }));
Assert.That(optionsChoice.LauncherChoiceDropped, Is.True);
});
}
}
[Test]
@@ -96,11 +96,11 @@ public sealed class ChatTemplatePrecedenceTests
var toolChoice = ChatTemplate.ChooseToolIds(template, new[] { "web_search" });
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(toolChoice.ToolIds, Is.Empty, "An empty selection is the statement that this template wants no tools, which is as much of a statement as naming one.");
Assert.That(toolChoice.LauncherChoiceDropped, Is.True);
});
}
}
[Test]
@@ -113,12 +113,12 @@ public sealed class ChatTemplatePrecedenceTests
var optionsChoice = ChatTemplate.ChooseDataSourceOptions(template, NewLauncherOptions("11111111-1111-1111-1111-111111111111"));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(optionsChoice.Options!.AutomaticDataSourceSelection, Is.True, "Letting an agent pick the sources is the one thing a launcher cannot express, so it is exactly what must not be overwritten by one.");
Assert.That(optionsChoice.Options.PreselectedDataSourceIds, Is.Empty);
Assert.That(optionsChoice.LauncherChoiceDropped, Is.True);
});
}
}
[Test]
@@ -28,11 +28,11 @@ public sealed class ChatTemplateStartSelectionTests : ToolRegistryTestBase
this.AddProfile();
this.SettingsManager.ConfigurationData.Providers.Add(ToolCapableProvider());
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(this.SettingsManager.GetChatTemplateProfile(NewTemplate()), Is.Null);
Assert.That(this.SettingsManager.GetChatTemplateProvider(NewTemplate()), Is.EqualTo(AIStudio.Settings.Provider.NONE));
});
}
}
[Test]
@@ -43,11 +43,11 @@ public sealed class ChatTemplateStartSelectionTests : ToolRegistryTestBase
this.SettingsManager.ConfigurationData.Providers.Add(provider);
var template = NewTemplate() with { ProfileId = PROFILE_ID, ProviderId = provider.Id };
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(this.SettingsManager.GetChatTemplateProfile(template), Is.EqualTo(profile));
Assert.That(this.SettingsManager.GetChatTemplateProvider(template), Is.EqualTo(provider));
});
}
}
[Test]
@@ -63,11 +63,11 @@ public sealed class ChatTemplateStartSelectionTests : ToolRegistryTestBase
{
var template = NewTemplate() with { ProfileId = PROFILE_ID, ProviderId = "66666666-6666-6666-6666-666666666666" };
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(this.SettingsManager.GetChatTemplateProfile(template), Is.Null);
Assert.That(this.SettingsManager.GetChatTemplateProvider(template), Is.EqualTo(AIStudio.Settings.Provider.NONE));
});
}
}
[Test]
@@ -83,11 +83,11 @@ public sealed class ChatTemplateStartSelectionTests : ToolRegistryTestBase
this.SettingsManager.ConfigurationData.Chat.PreselectedProfile = defaultProfile.Id;
var withDefault = this.SettingsManager.GetChatTemplateStartProfile(template);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(withoutDefault, Is.EqualTo(Profile.NO_PROFILE), "Keeping the profile the chat had would hand out one the previous chat happened to use, a choice nobody made for this one.");
Assert.That(withDefault, Is.EqualTo(defaultProfile));
});
}
}
[Test]
@@ -95,12 +95,12 @@ public sealed class ChatTemplateStartSelectionTests : ToolRegistryTestBase
{
this.AddProfile();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(this.SettingsManager.GetChatTemplateStartProfile(NewTemplate()), Is.Null);
Assert.That(this.SettingsManager.GetChatTemplateStartProfile(NewTemplate() with { AllowProfileUsage = false, ProfileId = PROFILE_ID }), Is.Null);
Assert.That(this.SettingsManager.GetChatTemplateStartProfile(NewTemplate() with { ProfileId = PROFILE_ID })!.Id, Is.EqualTo(PROFILE_ID));
});
}
}
[Test]
@@ -135,13 +135,13 @@ public sealed class ChatTemplateStartSelectionTests : ToolRegistryTestBase
this.SettingsManager.ConfigurationData.Confidence.EnforceGlobalMinimumConfidence = true;
this.SettingsManager.ConfigurationData.Confidence.GlobalMinimumConfidence = ConfidenceLevel.HIGH;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(this.SettingsManager.GetChatTemplateProvider(trustedProvider.Id), Is.EqualTo(trustedProvider));
Assert.That(this.SettingsManager.GetChatTemplateProvider(lessTrustedProvider.Id), Is.EqualTo(AIStudio.Settings.Provider.NONE), "The chat template dialog and the import warn about exactly the providers a chat leaves out.");
Assert.That(this.SettingsManager.GetChatTemplateProvider("66666666-6666-6666-6666-666666666666"), Is.EqualTo(AIStudio.Settings.Provider.NONE));
Assert.That(this.SettingsManager.GetChatTemplateProvider((string?)null), Is.EqualTo(AIStudio.Settings.Provider.NONE));
});
}
}
[Test]
@@ -155,12 +155,12 @@ public sealed class ChatTemplateStartSelectionTests : ToolRegistryTestBase
this.SettingsManager.ConfigurationData.Workspace.StorageBehavior = WorkspaceStorageBehavior.DISABLE_WORKSPACES;
var whileSwitchedOff = this.SettingsManager.GetChatTemplateWorkspaceName(template);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(whileUsed, Is.EqualTo("Mails"));
Assert.That(whileSwitchedOff, Is.Null, "Without workspaces, a template must not bring one back for its chats.");
Assert.That(this.SettingsManager.GetChatTemplateWorkspaceName(NewTemplate()), Is.Null);
});
}
}
[Test]
@@ -177,11 +177,11 @@ public sealed class ChatTemplateStartSelectionTests : ToolRegistryTestBase
var previousTemplate = NewTemplate() with { ProfileId = PROFILE_ID, ProviderId = templateProvider.Id };
var nextTemplate = NewTemplate() with { Id = OTHER_TEMPLATE_ID };
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(this.SettingsManager.ChooseChatTemplateProfile(nextTemplate, previousTemplate, templateProfile), Is.EqualTo(defaultProfile), "The profile was the choice of another template, not one made for this chat.");
Assert.That(this.SettingsManager.ChooseChatTemplateProvider(nextTemplate, previousTemplate, templateProvider), Is.EqualTo(defaultProvider), "The provider was the choice of another template, not one made for this chat.");
});
}
}
[Test]
@@ -197,12 +197,12 @@ public sealed class ChatTemplateStartSelectionTests : ToolRegistryTestBase
var previousTemplate = NewTemplate() with { ProfileId = defaultProfile.Id, ProviderId = templateProvider.Id };
var nextTemplate = NewTemplate() with { Id = OTHER_TEMPLATE_ID };
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(this.SettingsManager.ChooseChatTemplateProfile(nextTemplate, previousTemplate, handPickedProfile), Is.EqualTo(handPickedProfile));
Assert.That(this.SettingsManager.ChooseChatTemplateProvider(nextTemplate, previousTemplate, handPickedProvider), Is.EqualTo(handPickedProvider));
Assert.That(this.SettingsManager.ChooseChatTemplateProfile(NewTemplate(), NewTemplate(), handPickedProfile), Is.EqualTo(handPickedProfile), "Without any preselection, nothing changes.");
});
}
}
[Test]
@@ -218,11 +218,11 @@ public sealed class ChatTemplateStartSelectionTests : ToolRegistryTestBase
var previousTemplate = NewTemplate() with { Id = OTHER_TEMPLATE_ID, ProfileId = otherProfile.Id, ProviderId = otherProvider.Id };
var nextTemplate = NewTemplate() with { ProfileId = PROFILE_ID, ProviderId = templateProvider.Id };
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(this.SettingsManager.ChooseChatTemplateProfile(nextTemplate, previousTemplate, otherProfile), Is.EqualTo(templateProfile));
Assert.That(this.SettingsManager.ChooseChatTemplateProvider(nextTemplate, previousTemplate, otherProvider), Is.EqualTo(templateProvider));
});
}
}
[Test]
@@ -37,11 +37,11 @@ public sealed class DataAppImportPermissionTests
var app = new DataApp();
SwitchOff(app, type);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
foreach (var other in IMPORTABLE_TYPES)
Assert.That(app.CanImportConfigurationSnippet(other), Is.EqualTo(other != type), $"Switched off: {type}, checked: {other}");
});
}
}
[TestCase(PluginConfigurationObjectType.LLM_PROVIDER)]
@@ -57,11 +57,11 @@ public sealed class DataAppImportPermissionTests
AllowUserToAddTranscriptionProvider = type is not PluginConfigurationObjectType.TRANSCRIPTION_PROVIDER,
};
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(withoutAnyProvider.CanImportConfigurationSnippet(type), Is.False, "No provider may be added at all.");
Assert.That(withoutThisProvider.CanImportConfigurationSnippet(type), Is.False, "This type of provider may not be added.");
});
}
}
[Test]
@@ -75,13 +75,13 @@ public sealed class DataAppImportPermissionTests
AllowUserToAddTranscriptionProvider = false,
};
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(app.CanImportConfigurationSnippet(PluginConfigurationObjectType.PROFILE), Is.True);
Assert.That(app.CanImportConfigurationSnippet(PluginConfigurationObjectType.CHAT_TEMPLATE), Is.True);
Assert.That(app.CanImportConfigurationSnippet(PluginConfigurationObjectType.DATA_SOURCE), Is.True);
Assert.That(app.CanImportConfigurationSnippet(PluginConfigurationObjectType.DOCUMENT_ANALYSIS_POLICY), Is.True);
});
}
}
[TestCase(PluginConfigurationObjectType.NONE)]
@@ -78,10 +78,10 @@ public sealed class DataSourceERICancellationTests
{
var page = await UNREACHABLE.RetrieveDataAsync("query", 1, new ChatThread());
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(page.Contexts, Is.Empty);
Assert.That(page.Gaps, Is.EqualTo(new[] { RetrievalGap.NOT_SEARCHED }), "Nobody cancelled, so the model learns that this data source is missing rather than empty.");
});
}
}
}
@@ -53,12 +53,12 @@ public sealed class DataSourceOutboundDataRestrictionTests
[Test]
public void ANewDataSourceAllowsOnlyLinksFromTheChat()
{
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(new DataSourceLocalFile().OutboundDataRestriction, Is.EqualTo(OutboundDataRestriction.ONLY_LINKS_FROM_CHAT));
Assert.That(new DataSourceLocalDirectory().OutboundDataRestriction, Is.EqualTo(OutboundDataRestriction.ONLY_LINKS_FROM_CHAT));
Assert.That(new DataSourceERI_V1().OutboundDataRestriction, Is.EqualTo(OutboundDataRestriction.ONLY_LINKS_FROM_CHAT));
});
}
}
[TestCase(nameof(DataSourceType.LOCAL_FILE))]
@@ -101,11 +101,11 @@ public sealed class DataSourceOutboundDataRestrictionTests
[Test]
public void WithoutAnOrganizationTheUserDecides()
{
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(new DataSourceSecuritySettings().MinimumOutboundDataRestriction, Is.EqualTo(OutboundDataRestriction.UNRESTRICTED));
Assert.That(OPEN_FOLDER.GetEffectiveOutboundDataRestriction(OutboundDataRestriction.UNRESTRICTED), Is.EqualTo(OutboundDataRestriction.UNRESTRICTED));
});
}
}
[Test]
@@ -119,11 +119,11 @@ public sealed class DataSourceOutboundDataRestrictionTests
[Test]
public void ADataSourceForSelfHostedProvidersAllowsOnlyTheConfiguredServices()
{
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(SELF_HOSTED_SERVER.GetEffectiveOutboundDataRestriction(OutboundDataRestriction.UNRESTRICTED), Is.EqualTo(OutboundDataRestriction.ONLY_CONFIGURED_SERVICES), "Stored before, or provided by a configuration plugin, its level never went through the dialog.");
Assert.That(OPEN_SERVER.GetEffectiveOutboundDataRestriction(OutboundDataRestriction.UNRESTRICTED), Is.EqualTo(OutboundDataRestriction.UNRESTRICTED));
});
}
}
[TestCase(DataSourceSecurity.NOT_SPECIFIED, OutboundDataRestriction.UNRESTRICTED, OutboundDataRestriction.UNRESTRICTED)]
@@ -69,11 +69,11 @@ public sealed class EnterpriseEncryptionExportFormatTests
var settingsManager = CreateSettingsManager();
await settingsManager.LoadSettings();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(File.Exists(this.SettingsPath), Is.False, "A new installation has no settings file yet.");
Assert.That(settingsManager.ConfigurationData.App.EnterpriseEncryptionExportFormat, Is.EqualTo(EnterpriseEncryptionFormat.V2));
});
}
}
[Test]
@@ -87,11 +87,11 @@ public sealed class EnterpriseEncryptionExportFormatTests
await settingsManager.LoadSettings();
await settingsManager.StoreSettings();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(settingsManager.ConfigurationData.App.EnterpriseEncryptionExportFormat, Is.EqualTo(EnterpriseEncryptionFormat.V1));
Assert.That(this.ReadStoredFormat(), Is.EqualTo(nameof(EnterpriseEncryptionFormat.V1)), "Once stored, the settings hold the format, and the shim has nothing left to do.");
});
}
}
[Test]
Loaded 100 of 290 files, more files were not shown because too many files have changed in this diff. Show more