mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-03 22:07:21 +00:00
Switched the tests to Assert.EnterMultipleScope
This commit is contained in:
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>"));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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)] };
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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"));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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.");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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]
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
Reference in new issue
Block a user