mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-05 04:47:21 +00:00
Turned getter-like extension methods into extension properties (#1157)
This commit is contained in:
1 parent
0375318b1b
commit
f649febf88
286 files changed
+1379
-1109
No files matched your search
@@ -0,0 +1,192 @@
|
||||
using AIStudio.Tests.Models.Generation;
|
||||
|
||||
using SourceCodeRules.StyleAnalyzers;
|
||||
using SourceCodeRules.StyleCodeFixes;
|
||||
|
||||
namespace AIStudio.Tests.Analyzers;
|
||||
|
||||
/// <summary>
|
||||
/// Checks that methods and properties which only return a switch expression start it on the line of their arrow,
|
||||
/// and that the code fix of MWAIS0006 keeps everything else of the member.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Extension blocks turn many such methods into properties, so the rule has to know properties as well: a
|
||||
/// getter which does nothing but return a switch expression reads like the method it replaced.
|
||||
/// </remarks>
|
||||
[TestFixture]
|
||||
public sealed class SwitchExpressionMethodAnalyzerTests
|
||||
{
|
||||
private const string SWITCH = "this.Kind switch { Kind.CALL => \"Call\", _ => \"Meeting\" }";
|
||||
|
||||
[TestCase("public string Label() { return " + SWITCH + "; }", "Method", TestName = "A method which returns a switch expression from a block")]
|
||||
[TestCase("public string Label() =>\n" + SWITCH + ";", "Method", TestName = "A method whose switch expression starts on the next line")]
|
||||
[TestCase("public string Label =>\n" + SWITCH + ";", "Property", TestName = "A property whose switch expression starts on the next line")]
|
||||
[TestCase("public string Label { get { return " + SWITCH + "; } }", "Property", TestName = "A getter which returns a switch expression from a block")]
|
||||
[TestCase("public string Label { get => " + SWITCH + "; }", "Property", TestName = "A getter with a switch expression as its body")]
|
||||
public async Task AMemberOfAClassIsRefused(string member, string kind)
|
||||
{
|
||||
var reported = await CompilationHarness.AnalyzeAsync(ClassWith(member), new SwitchExpressionMethodAnalyzer());
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(reported, Has.Count.EqualTo(1));
|
||||
Assert.That(reported.FirstOrDefault()?.Id, Is.EqualTo("MWAIS0006"));
|
||||
Assert.That(reported.FirstOrDefault()?.GetMessage(), Does.StartWith($"{kind} with a switch expression"));
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase("public string Label =>\nkind switch { Kind.CALL => \"Call\", _ => \"Meeting\" };", TestName = "An extension property whose switch expression starts on the next line")]
|
||||
[TestCase("public string Label { get { return kind switch { Kind.CALL => \"Call\", _ => \"Meeting\" }; } }", TestName = "The getter of an extension property which returns a switch expression from a block")]
|
||||
[TestCase("public string Label() { return kind switch { Kind.CALL => \"Call\", _ => \"Meeting\" }; }", TestName = "An extension method which returns a switch expression from a block")]
|
||||
public async Task AMemberOfAnExtensionBlockIsRefused(string member)
|
||||
{
|
||||
var reported = await CompilationHarness.AnalyzeAsync(ExtensionBlockWith(member), new SwitchExpressionMethodAnalyzer());
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(reported, Has.Count.EqualTo(1));
|
||||
Assert.That(reported.FirstOrDefault()?.Id, Is.EqualTo("MWAIS0006"));
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase("public string Label() => " + SWITCH + ";", TestName = "A method with the switch expression on the line of its arrow")]
|
||||
[TestCase("public string Label => " + SWITCH + ";", TestName = "A property with the switch expression on the line of its arrow")]
|
||||
[TestCase("public string Label { get { return " + SWITCH + "; } set { } }", TestName = "A property with a getter and a setter")]
|
||||
[TestCase("public string Label { get { var kind = this.Kind; return kind switch { Kind.CALL => \"Call\", _ => \"Meeting\" }; } }", TestName = "A getter which does more than return a switch expression")]
|
||||
[TestCase("public string Label() { return \"Meeting\"; }", TestName = "A method which returns no switch expression")]
|
||||
public async Task AnythingElseIsLeftAlone(string member)
|
||||
{
|
||||
var reported = await CompilationHarness.AnalyzeAsync(ClassWith(member), new SwitchExpressionMethodAnalyzer());
|
||||
|
||||
Assert.That(reported, Is.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TheCodeFixKeepsTheAttributesTypeParametersAndConstraintsOfAMethod()
|
||||
{
|
||||
var source = ClassWith(
|
||||
"""
|
||||
/// <summary>Names the hash of a value.</summary>
|
||||
[System.Diagnostics.Contracts.Pure]
|
||||
public static string Label<T>(T value) where T : struct
|
||||
{
|
||||
return value.GetHashCode() switch
|
||||
{
|
||||
0 => "Zero",
|
||||
_ => "Other",
|
||||
};
|
||||
}
|
||||
""");
|
||||
|
||||
var expected = ClassWith(
|
||||
"""
|
||||
/// <summary>Names the hash of a value.</summary>
|
||||
[System.Diagnostics.Contracts.Pure]
|
||||
public static string Label<T>(T value) where T : struct => value.GetHashCode() switch
|
||||
{
|
||||
0 => "Zero",
|
||||
_ => "Other",
|
||||
};
|
||||
""");
|
||||
|
||||
var fixedSource = await CompilationHarness.FixAsync(source, new SwitchExpressionMethodAnalyzer(), new SwitchExpressionMethodCodeFixProvider());
|
||||
|
||||
Assert.That(fixedSource.ReplaceLineEndings("\n"), Is.EqualTo(expected.ReplaceLineEndings("\n")));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TheCodeFixTurnsAGetterIntoAnExpressionBody()
|
||||
{
|
||||
var source = ClassWith(
|
||||
"""
|
||||
public string Label
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.Kind switch
|
||||
{
|
||||
Kind.CALL => "Call",
|
||||
_ => "Meeting",
|
||||
};
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
var expected = ClassWith(
|
||||
"""
|
||||
public string Label => this.Kind switch
|
||||
{
|
||||
Kind.CALL => "Call",
|
||||
_ => "Meeting",
|
||||
};
|
||||
""");
|
||||
|
||||
var fixedSource = await CompilationHarness.FixAsync(source, new SwitchExpressionMethodAnalyzer(), new SwitchExpressionMethodCodeFixProvider());
|
||||
|
||||
Assert.That(fixedSource.ReplaceLineEndings("\n"), Is.EqualTo(expected.ReplaceLineEndings("\n")));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TheCodeFixMovesTheSwitchOfAnExtensionPropertyOntoTheLineOfItsArrow()
|
||||
{
|
||||
var source = ExtensionBlockWith(
|
||||
"""
|
||||
public string Label =>
|
||||
kind switch
|
||||
{
|
||||
Kind.CALL => "Call",
|
||||
_ => "Meeting",
|
||||
};
|
||||
""");
|
||||
|
||||
var expected = ExtensionBlockWith(
|
||||
"""
|
||||
public string Label => kind switch
|
||||
{
|
||||
Kind.CALL => "Call",
|
||||
_ => "Meeting",
|
||||
};
|
||||
""");
|
||||
|
||||
var fixedSource = await CompilationHarness.FixAsync(source, new SwitchExpressionMethodAnalyzer(), new SwitchExpressionMethodCodeFixProvider());
|
||||
|
||||
Assert.That(fixedSource.ReplaceLineEndings("\n"), Is.EqualTo(expected.ReplaceLineEndings("\n")));
|
||||
}
|
||||
|
||||
private static string ClassWith(string members) =>
|
||||
$$"""
|
||||
namespace Sample;
|
||||
|
||||
public enum Kind
|
||||
{
|
||||
CALL,
|
||||
MEETING,
|
||||
}
|
||||
|
||||
public sealed class Meeting
|
||||
{
|
||||
public Kind Kind { get; set; } = Kind.MEETING;
|
||||
|
||||
{{members}}
|
||||
}
|
||||
""";
|
||||
|
||||
private static string ExtensionBlockWith(string members) =>
|
||||
$$"""
|
||||
namespace Sample;
|
||||
|
||||
public enum Kind
|
||||
{
|
||||
CALL,
|
||||
MEETING,
|
||||
}
|
||||
|
||||
public static class KindExtensions
|
||||
{
|
||||
extension(Kind kind)
|
||||
{
|
||||
{{members}}
|
||||
}
|
||||
}
|
||||
""";
|
||||
}
|
||||
@@ -25,7 +25,7 @@ public sealed class SendToReceiverTests
|
||||
private static readonly Assembly APP = typeof(AssistantLaunchService).Assembly;
|
||||
|
||||
private static IEnumerable<AIStudio.Tools.Components> Destinations() => Enum.GetValues<AIStudio.Tools.Components>()
|
||||
.Where(component => component.AllowSendTo())
|
||||
.Where(component => component.AllowSendTo)
|
||||
.Where(component => component is not AIStudio.Tools.Components.CHAT);
|
||||
|
||||
[TestCaseSource(nameof(Destinations))]
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CodeActions;
|
||||
using Microsoft.CodeAnalysis.CodeFixes;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace AIStudio.Tests.Models.Generation;
|
||||
|
||||
@@ -70,6 +73,53 @@ public static class CompilationHarness
|
||||
return reported;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs one analyzer over a snippet and applies its code fix to the first diagnostic it reports.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The code fix runs in a workspace of its own, as it does in the IDE. The fixed source still has to
|
||||
/// compile, since a code fix which breaks the code is worse than none.
|
||||
/// </remarks>
|
||||
/// <param name="source">The C# source to fix.</param>
|
||||
/// <param name="analyzer">The analyzer which reports what to fix.</param>
|
||||
/// <param name="codeFix">The code fix to apply.</param>
|
||||
/// <returns>The fixed source.</returns>
|
||||
public static async Task<string> FixAsync(string source, DiagnosticAnalyzer analyzer, CodeFixProvider codeFix)
|
||||
{
|
||||
using var workspace = new AdhocWorkspace();
|
||||
var projectInfo = ProjectInfo.Create(
|
||||
ProjectId.CreateNewId(),
|
||||
VersionStamp.Default,
|
||||
"SnippetUnderTest",
|
||||
"SnippetUnderTest",
|
||||
LanguageNames.CSharp,
|
||||
compilationOptions: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, nullableContextOptions: NullableContextOptions.Enable),
|
||||
parseOptions: new CSharpParseOptions(LanguageVersion.Latest),
|
||||
metadataReferences: REFERENCES.Value);
|
||||
|
||||
var project = workspace.AddProject(projectInfo);
|
||||
var document = workspace.AddDocument(project.Id, "Snippet.cs", SourceText.From(source));
|
||||
|
||||
var compilation = await document.Project.GetCompilationAsync();
|
||||
Assert.That(ErrorsOf(compilation!), Is.Empty, "The snippet has to compile, or the code fix is being asked about code which does not exist.");
|
||||
|
||||
var reported = await compilation!.WithAnalyzers([analyzer]).GetAnalyzerDiagnosticsAsync();
|
||||
Assert.That(reported, Is.Not.Empty, "The analyzer has to report something, or there is nothing for the code fix to do.");
|
||||
|
||||
var actions = new List<CodeAction>();
|
||||
var context = new CodeFixContext(document, reported[0], (action, _) => actions.Add(action), CancellationToken.None);
|
||||
await codeFix.RegisterCodeFixesAsync(context);
|
||||
Assert.That(actions, Has.Count.EqualTo(1), "The code fix has to offer exactly one action.");
|
||||
|
||||
var operations = await actions[0].GetOperationsAsync(CancellationToken.None);
|
||||
var changedSolution = operations.OfType<ApplyChangesOperation>().Single().ChangedSolution;
|
||||
var fixedText = await changedSolution.GetDocument(document.Id)!.GetTextAsync();
|
||||
var fixedSource = fixedText.ToString();
|
||||
|
||||
Assert.That(ErrorsOf(Compile(fixedSource)), Is.Empty, "The fixed source has to compile.");
|
||||
return fixedSource;
|
||||
}
|
||||
|
||||
private static MetadataReference[] GatherReferences()
|
||||
{
|
||||
//
|
||||
|
||||
@@ -19,7 +19,7 @@ public sealed class ImageSizeLimitTests
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(LLMProviders.ANTHROPIC.GetMaxImageSizeBytes(), Is.EqualTo(3_932_160));
|
||||
Assert.That(LLMProviders.ANTHROPIC.MaxImageSizeBytes, Is.EqualTo(3_932_160));
|
||||
Assert.That(IImageSourceExtensions.MaxImageSizeBytesFor(ProviderOf(LLMProviders.ANTHROPIC)), Is.EqualTo(3_932_160));
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ public sealed class ImageSizeLimitTests
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(LLMProviders.OPEN_AI.GetMaxImageSizeBytes(), Is.Null, "Nobody stated a limit, and no number stands in for that.");
|
||||
Assert.That(LLMProviders.OPEN_AI.MaxImageSizeBytes, 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));
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ public sealed class ImageSizeLimitTests
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(Host.LLMMAN.GetMaxImageSizeBytes(), Is.EqualTo(1_048_576));
|
||||
Assert.That(Host.LLMMAN.MaxImageSizeBytes, 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.");
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ public sealed class SystemPromptRoleTests
|
||||
{
|
||||
var profile = LLMProviders.OPEN_AI.GetModelProfile(new Model(modelId, null));
|
||||
|
||||
Assert.That(profile.SystemPromptRole.ToOpenAIRole(), Is.EqualTo(role));
|
||||
Assert.That(profile.SystemPromptRole.OpenAIRole, Is.EqualTo(role));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -43,6 +43,6 @@ public sealed class SystemPromptRoleTests
|
||||
// A model the rules do not know is almost always a new one, and the older generations which
|
||||
// still want "system" say so in their families.
|
||||
//
|
||||
Assert.That(SystemPromptRole.UNKNOWN.ToOpenAIRole(), Is.EqualTo("developer"));
|
||||
Assert.That(SystemPromptRole.UNKNOWN.OpenAIRole, Is.EqualTo("developer"));
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ public sealed class HostExtensionsTests
|
||||
[TestCaseSource(nameof(ALL_HOSTS))]
|
||||
public void TheDialogsOfferToLoadModelsExactlyWhereTheEngineCanListThem(Host host)
|
||||
{
|
||||
Assert.That(LLMProviders.SELF_HOSTED.CanLoadModels(host, null), Is.EqualTo(host.IsModelListSupported()));
|
||||
Assert.That(LLMProviders.SELF_HOSTED.CanLoadModels(host, null), Is.EqualTo(host.IsModelListSupported));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -30,7 +30,7 @@ public sealed class HostExtensionsTests
|
||||
//
|
||||
// There is no fixed chat model to offer instead, so such an engine would offer an empty list:
|
||||
//
|
||||
var unlisted = ALL_HOSTS.Where(host => host is not Host.NONE && host.IsChatSupported() && !host.IsModelListSupported());
|
||||
var unlisted = ALL_HOSTS.Where(host => host is not Host.NONE && host.IsChatSupported && !host.IsModelListSupported);
|
||||
Assert.That(unlisted, Is.Empty);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public sealed class HostExtensionsTests
|
||||
//
|
||||
// There is no fixed embedding model to offer instead, so such an engine would offer an empty list:
|
||||
//
|
||||
var unlisted = ALL_HOSTS.Where(host => host.IsEmbeddingSupported() && !host.IsModelListSupported());
|
||||
var unlisted = ALL_HOSTS.Where(host => host.IsEmbeddingSupported && !host.IsModelListSupported);
|
||||
Assert.That(unlisted, Is.Empty);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public sealed class HostExtensionsTests
|
||||
// The provider offers the one model of whisper.cpp under a fixed name. Any other engine
|
||||
// which transcribes but cannot list its models would offer an empty list:
|
||||
//
|
||||
var unlisted = ALL_HOSTS.Where(host => host.IsTranscriptionSupported() && !host.IsModelListSupported());
|
||||
var unlisted = ALL_HOSTS.Where(host => host.IsTranscriptionSupported && !host.IsModelListSupported);
|
||||
Assert.That(unlisted, Is.EqualTo(new[] { Host.WHISPER_CPP }));
|
||||
}
|
||||
|
||||
@@ -60,9 +60,9 @@ public sealed class HostExtensionsTests
|
||||
{
|
||||
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(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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public sealed class EnumNamesTests
|
||||
[TestCase(" Every_Message ")]
|
||||
public void ANameCountsWhateverItsCase(string text)
|
||||
{
|
||||
Assert.That(EnumNames.TryParse<DataSourceRetrievalMode>(text, out var mode), Is.True);
|
||||
Assert.That(DataSourceRetrievalMode.TryParseName(text, out var mode), Is.True);
|
||||
Assert.That(mode, Is.EqualTo(DataSourceRetrievalMode.EVERY_MESSAGE));
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public sealed class EnumNamesTests
|
||||
[TestCase("0x1")]
|
||||
public void ANumberDoesNotCountEvenWhenAMemberHasIt(string text)
|
||||
{
|
||||
Assert.That(EnumNames.TryParse<DataSourceRetrievalMode>(text, out _), Is.False, "Enum.TryParse would read 1 as EVERY_MESSAGE.");
|
||||
Assert.That(DataSourceRetrievalMode.TryParseName(text, out _), Is.False, "Enum.TryParse would read 1 as EVERY_MESSAGE.");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -37,8 +37,8 @@ public sealed class EnumNamesTests
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(EnumNames.TryParse<DataSourceRetrievalMode>("SEMANTIC_SEARCH, EVERY_MESSAGE", out _), Is.False, "Enum.TryParse would combine them to EVERY_MESSAGE.");
|
||||
Assert.That(EnumNames.TryParse<ConfidenceLevel>("HIGH, MEDIUM", out _), Is.False, "Enum.TryParse would combine them to a confidence level above HIGH.");
|
||||
Assert.That(DataSourceRetrievalMode.TryParseName("SEMANTIC_SEARCH, EVERY_MESSAGE", out _), Is.False, "Enum.TryParse would combine them to EVERY_MESSAGE.");
|
||||
Assert.That(ConfidenceLevel.TryParseName("HIGH, MEDIUM", out _), Is.False, "Enum.TryParse would combine them to a confidence level above HIGH.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ public sealed class EnumNamesTests
|
||||
[TestCase("EVERY MESSAGE")]
|
||||
public void NoNameDoesNotCount(string? text)
|
||||
{
|
||||
Assert.That(EnumNames.TryParse<DataSourceRetrievalMode>(text, out var mode), Is.False);
|
||||
Assert.That(DataSourceRetrievalMode.TryParseName(text, out var mode), Is.False);
|
||||
Assert.That(mode, Is.EqualTo(default(DataSourceRetrievalMode)));
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ public sealed class TranscriptionOpusBitrateTests
|
||||
foreach (var bitrate in Enum.GetValues<TranscriptionOpusBitrate>())
|
||||
{
|
||||
Assert.That(EXPECTED_BITS_PER_SECOND.ContainsKey(bitrate), Is.True, $"The selection offers {bitrate}, so this test has to state what that is worth in bits per second.");
|
||||
Assert.That(bitrate.GetBitsPerSecond(), Is.EqualTo(EXPECTED_BITS_PER_SECOND[bitrate]), $"{bitrate} is what the user picked; anything else travels to the encoder behind their back.");
|
||||
Assert.That(bitrate.BitsPerSecond, Is.EqualTo(EXPECTED_BITS_PER_SECOND[bitrate]), $"{bitrate} is what the user picked; anything else travels to the encoder behind their back.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public sealed class TranscriptionOpusBitrateTests
|
||||
var undeclared = (TranscriptionOpusBitrate)999;
|
||||
|
||||
Assert.That(Enum.IsDefined(undeclared), Is.False, "The point of this test is a value outside the enum; a declared one would prove nothing.");
|
||||
Assert.That(undeclared.GetBitsPerSecond(), Is.EqualTo(128_000u), "Not knowing which quality was meant is no reason to pick the worst one available.");
|
||||
Assert.That(undeclared.BitsPerSecond, Is.EqualTo(128_000u), "Not knowing which quality was meant is no reason to pick the worst one available.");
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -24,7 +24,7 @@ public sealed class WebRootFilesTests
|
||||
{
|
||||
private static IEnumerable<string> ChangelogFiles() => Changelog.LOGS.Select(log => log.Filename);
|
||||
|
||||
private static IEnumerable<ERIVersion> SpecificationVersions() => Enum.GetValues<ERIVersion>().Where(version => version.WasSpecificationSelected());
|
||||
private static IEnumerable<ERIVersion> SpecificationVersions() => Enum.GetValues<ERIVersion>().Where(version => version.WasSpecificationSelected);
|
||||
|
||||
private static string ReadAppPageSource([CallerFilePath] string sourceFilePath = "") => File.ReadAllText(Path.Combine(Path.GetDirectoryName(sourceFilePath)!, "..", "..", "..", "MindWork AI Studio", "App.razor"));
|
||||
|
||||
@@ -78,7 +78,7 @@ public sealed class WebRootFilesTests
|
||||
var destinationPath = Path.Combine(Path.GetTempPath(), $"eri-specification-{Guid.NewGuid():N}.json");
|
||||
try
|
||||
{
|
||||
Assert.That(await webRootFiles.CopyToAsync(version.SpecificationURL(), destinationPath), Is.True);
|
||||
Assert.That(await webRootFiles.CopyToAsync(version.SpecificationURL, destinationPath), Is.True);
|
||||
Assert.That(await File.ReadAllTextAsync(destinationPath), Is.EqualTo(await version.ReadSpecification(webRootFiles)));
|
||||
}
|
||||
finally
|
||||
|
||||
@@ -42,7 +42,7 @@ public sealed class AssistantLaunchServiceTests
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(destinations, Is.Not.Empty);
|
||||
Assert.That(destinations.All(destination => destination.AllowSendTo()), Is.True);
|
||||
Assert.That(destinations.All(destination => destination.AllowSendTo), Is.True);
|
||||
Assert.That(destinations, Does.Not.Contain(AIStudio.Tools.Components.DYNAMIC_ASSISTANT), "Assistant plugins cannot take content yet.");
|
||||
Assert.That(destinations, Does.Not.Contain(AIStudio.Tools.Components.NONE));
|
||||
}
|
||||
@@ -58,7 +58,7 @@ public sealed class AssistantLaunchServiceTests
|
||||
[Test]
|
||||
public void ShorterNamesComeFirst()
|
||||
{
|
||||
var lengths = AssistantLaunchService.SelectDestinations(AIStudio.Tools.Components.CHAT, _ => true).Select(destination => destination.Name().Length).ToList();
|
||||
var lengths = AssistantLaunchService.SelectDestinations(AIStudio.Tools.Components.CHAT, _ => true).Select(destination => destination.Name.Length).ToList();
|
||||
Assert.That(lengths, Is.Ordered);
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,7 @@ public sealed class CodeLanguageDetectorTests
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
var isFormat = FileExportFormatExtensions.TryFromCodeFenceLanguage(fenceLanguage, out _);
|
||||
var isFormat = FileExportFormat.TryFromCodeFenceLanguage(fenceLanguage, out _);
|
||||
Assert.That(isFormat || SourceCodeLanguages.Aliases.Contains(fenceLanguage, StringComparer.OrdinalIgnoreCase), Is.True, "The export would save it as a text file.");
|
||||
Assert.That(CodeHighlighters.TryGetRules(fenceLanguage, out _) || LIBRARY_LANGUAGES.Contains(fenceLanguage), Is.True, "Nobody highlights it.");
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ public sealed class CodeTokenKindExtensionsTests
|
||||
public async Task EveryKindHasAClassWhichTheStyleSheetColors([Values] CodeTokenKind kind)
|
||||
{
|
||||
var styleSheet = await new WebRootFiles().ReadTextAsync("system/MudBlazor.Markdown/MudBlazor.Markdown.min.css");
|
||||
var cssClass = kind.ToCssClass().Split(' ')[0];
|
||||
var cssClass = kind.CssClass.Split(' ')[0];
|
||||
|
||||
Assert.That(styleSheet, Does.Contain($".{cssClass}"), "A library which renames its classes would leave this kind without color.");
|
||||
}
|
||||
|
||||
@@ -13,11 +13,11 @@ public sealed class FileExportFormatTests
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(FileExportFormat.MICROSOFT_WORD.FollowsPageAnchors(), Is.False, "Word looks for a file whose name ends in the fragment, finds none, and refuses the link.");
|
||||
Assert.That(FileExportFormat.OPEN_DOCUMENT_TEXT.FollowsPageAnchors(), Is.False, "LibreOffice does the same, verified on 2026-09-15 with an exported .odt.");
|
||||
Assert.That(FileExportFormat.HTML.FollowsPageAnchors(), Is.True, "A browser opens the document on the page the fragment names.");
|
||||
Assert.That(FileExportFormat.MARKDOWN.FollowsPageAnchors(), Is.True);
|
||||
Assert.That(FileExportFormat.LATEX.FollowsPageAnchors(), Is.True);
|
||||
Assert.That(FileExportFormat.MICROSOFT_WORD.FollowsPageAnchors, Is.False, "Word looks for a file whose name ends in the fragment, finds none, and refuses the link.");
|
||||
Assert.That(FileExportFormat.OPEN_DOCUMENT_TEXT.FollowsPageAnchors, Is.False, "LibreOffice does the same, verified on 2026-09-15 with an exported .odt.");
|
||||
Assert.That(FileExportFormat.HTML.FollowsPageAnchors, Is.True, "A browser opens the document on the page the fragment names.");
|
||||
Assert.That(FileExportFormat.MARKDOWN.FollowsPageAnchors, Is.True);
|
||||
Assert.That(FileExportFormat.LATEX.FollowsPageAnchors, Is.True);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public sealed class FileExportFormatTests
|
||||
// Whoever adds a format decides what its reader can follow, rather than inheriting an
|
||||
// assumption. This fails for a format which nobody thought about, because the list below
|
||||
// has to name it:
|
||||
Assert.That(FileExportFormatExtensions.ANSWER_FORMATS, Is.EquivalentTo(new[]
|
||||
Assert.That(FileExportFormat.AnswerFormats, Is.EquivalentTo(new[]
|
||||
{
|
||||
FileExportFormat.MICROSOFT_WORD,
|
||||
FileExportFormat.OPEN_DOCUMENT_TEXT,
|
||||
@@ -59,10 +59,10 @@ public sealed class FileExportFormatTests
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(FileExportFormat.HTML.NeedsPageTitle(), Is.True, "Without one, the browser tab shows the random name of the temporary file Pandoc read.");
|
||||
Assert.That(FileExportFormat.MICROSOFT_WORD.NeedsPageTitle(), Is.False, "Word would keep it as a document property nobody asked for.");
|
||||
Assert.That(FileExportFormat.OPEN_DOCUMENT_TEXT.NeedsPageTitle(), Is.False, "The same goes for an OpenDocument file.");
|
||||
Assert.That(FileExportFormat.LATEX.NeedsPageTitle(), Is.False);
|
||||
Assert.That(FileExportFormat.HTML.NeedsPageTitle, Is.True, "Without one, the browser tab shows the random name of the temporary file Pandoc read.");
|
||||
Assert.That(FileExportFormat.MICROSOFT_WORD.NeedsPageTitle, Is.False, "Word would keep it as a document property nobody asked for.");
|
||||
Assert.That(FileExportFormat.OPEN_DOCUMENT_TEXT.NeedsPageTitle, Is.False, "The same goes for an OpenDocument file.");
|
||||
Assert.That(FileExportFormat.LATEX.NeedsPageTitle, Is.False);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,9 +81,9 @@ public sealed class FileExportFormatTests
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(FileExportFormatExtensions.TryFromCodeFenceLanguage(language, out var format), Is.True);
|
||||
Assert.That(FileExportFormat.TryFromCodeFenceLanguage(language, out var format), Is.True);
|
||||
Assert.That(format, Is.EqualTo(expectedFormat));
|
||||
Assert.That(format.IsPlainText(), Is.True, "A code block holds text, so the export writes it as it is.");
|
||||
Assert.That(format.IsPlainText, Is.True, "A code block holds text, so the export writes it as it is.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,16 +92,16 @@ public sealed class FileExportFormatTests
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(FileExportFormat.MICROSOFT_WORD.IsPlainText(), Is.False, "A Word file is an archive, and writing text into one breaks it.");
|
||||
Assert.That(FileExportFormat.OPEN_DOCUMENT_TEXT.IsPlainText(), Is.False);
|
||||
Assert.That(FileExportFormat.NONE.IsPlainText(), Is.False, "No format means no file.");
|
||||
Assert.That(FileExportFormat.UNKNOWN.IsPlainText(), Is.False);
|
||||
Assert.That(FileExportFormat.HTML.IsPlainText(), Is.True, "A page the model wrote is a finished file, even though an entire answer needs Pandoc to become one.");
|
||||
Assert.That(FileExportFormat.LATEX.IsPlainText(), Is.True);
|
||||
Assert.That(FileExportFormat.MARKDOWN.IsPlainText(), Is.True);
|
||||
Assert.That(FileExportFormat.CSV.IsPlainText(), Is.True);
|
||||
Assert.That(FileExportFormat.TSV.IsPlainText(), Is.True);
|
||||
Assert.That(FileExportFormat.SOURCE_CODE.IsPlainText(), Is.True, "Code is text, whatever its language.");
|
||||
Assert.That(FileExportFormat.MICROSOFT_WORD.IsPlainText, Is.False, "A Word file is an archive, and writing text into one breaks it.");
|
||||
Assert.That(FileExportFormat.OPEN_DOCUMENT_TEXT.IsPlainText, Is.False);
|
||||
Assert.That(FileExportFormat.NONE.IsPlainText, Is.False, "No format means no file.");
|
||||
Assert.That(FileExportFormat.UNKNOWN.IsPlainText, Is.False);
|
||||
Assert.That(FileExportFormat.HTML.IsPlainText, Is.True, "A page the model wrote is a finished file, even though an entire answer needs Pandoc to become one.");
|
||||
Assert.That(FileExportFormat.LATEX.IsPlainText, Is.True);
|
||||
Assert.That(FileExportFormat.MARKDOWN.IsPlainText, Is.True);
|
||||
Assert.That(FileExportFormat.CSV.IsPlainText, Is.True);
|
||||
Assert.That(FileExportFormat.TSV.IsPlainText, Is.True);
|
||||
Assert.That(FileExportFormat.SOURCE_CODE.IsPlainText, Is.True, "Code is text, whatever its language.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,9 +110,9 @@ public sealed class FileExportFormatTests
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(FileExportFormat.SOURCE_CODE.ToFileExtension(), Is.Empty, "Python and Rust share the format, but not the extension.");
|
||||
Assert.That(FileExportFormat.SOURCE_CODE.ToFileTypeFilter(), Is.Null, "Without the language, the save dialog would offer the wrong files.");
|
||||
Assert.That(FileExportFormatExtensions.ANSWER_FORMATS, Does.Not.Contain(FileExportFormat.SOURCE_CODE), "Code is one block out of an answer, never the answer itself.");
|
||||
Assert.That(FileExportFormat.SOURCE_CODE.FileExtension, Is.Empty, "Python and Rust share the format, but not the extension.");
|
||||
Assert.That(FileExportFormat.SOURCE_CODE.FileTypeFilter, Is.Null, "Without the language, the save dialog would offer the wrong files.");
|
||||
Assert.That(FileExportFormat.AnswerFormats, Does.Not.Contain(FileExportFormat.SOURCE_CODE), "Code is one block out of an answer, never the answer itself.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ public sealed class FileExportFormatTests
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(FileExportFormatExtensions.TryFromCodeFenceLanguage(language, out var format), Is.False);
|
||||
Assert.That(FileExportFormat.TryFromCodeFenceLanguage(language, out var format), Is.False);
|
||||
Assert.That(format, Is.EqualTo(FileExportFormat.NONE));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,8 +63,8 @@ public sealed class MailboxProviderTemplatesTests
|
||||
[Test]
|
||||
public void EveryUsernameFormatHasItsOwnDescription()
|
||||
{
|
||||
var descriptions = Enum.GetValues<MailboxUsernameFormat>().Select(format => format.GetDescription()).ToList();
|
||||
var fallback = ((MailboxUsernameFormat)int.MaxValue).GetDescription();
|
||||
var descriptions = Enum.GetValues<MailboxUsernameFormat>().Select(format => format.Description).ToList();
|
||||
var fallback = ((MailboxUsernameFormat)int.MaxValue).Description;
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
|
||||
@@ -162,10 +162,10 @@ public sealed class PassageProvenanceTests
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(Passage(HANDBOOK_URL, "text").GetProvenanceKind(), Is.EqualTo(ProvenanceKind.DATA_SOURCE_PASSAGE));
|
||||
Assert.That((Passage(HANDBOOK_URL, "text") with { Type = RetrievalContentType.TEXT_TRANSCRIPT }).GetProvenanceKind(), Is.EqualTo(ProvenanceKind.DATA_SOURCE_PASSAGE));
|
||||
Assert.That((Passage(HANDBOOK_URL, "text") with { Type = RetrievalContentType.TEXT_EMAIL }).GetProvenanceKind(), Is.EqualTo(ProvenanceKind.MAIL), "A mail is a mail, whether a mailbox data source or a tool handed it over.");
|
||||
Assert.That((Passage(HANDBOOK_URL, "text") with { Type = RetrievalContentType.TEXT_WEBSITE }).GetProvenanceKind(), Is.EqualTo(ProvenanceKind.WEB_PAGE));
|
||||
Assert.That(Passage(HANDBOOK_URL, "text").ProvenanceKind, Is.EqualTo(ProvenanceKind.DATA_SOURCE_PASSAGE));
|
||||
Assert.That((Passage(HANDBOOK_URL, "text") with { Type = RetrievalContentType.TEXT_TRANSCRIPT }).ProvenanceKind, Is.EqualTo(ProvenanceKind.DATA_SOURCE_PASSAGE));
|
||||
Assert.That((Passage(HANDBOOK_URL, "text") with { Type = RetrievalContentType.TEXT_EMAIL }).ProvenanceKind, Is.EqualTo(ProvenanceKind.MAIL), "A mail is a mail, whether a mailbox data source or a tool handed it over.");
|
||||
Assert.That((Passage(HANDBOOK_URL, "text") with { Type = RetrievalContentType.TEXT_WEBSITE }).ProvenanceKind, Is.EqualTo(ProvenanceKind.WEB_PAGE));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,9 +15,9 @@ public sealed class LayoutPurposeTests
|
||||
{
|
||||
foreach (var purpose in Enum.GetValues<LayoutPurpose>())
|
||||
{
|
||||
Assert.That(purpose.Label(), Is.Not.Empty, $"The kind of slide {purpose} has no label.");
|
||||
Assert.That(purpose.Question(), Does.EndWith("?"), $"The kind of slide {purpose} has no question.");
|
||||
Assert.That(purpose.PromptDescription(), Is.Not.Empty.And.Not.EndWith("."), $"The kind of slide {purpose} has no description, or one which ends where the rule for the model goes on.");
|
||||
Assert.That(purpose.Label, Is.Not.Empty, $"The kind of slide {purpose} has no label.");
|
||||
Assert.That(purpose.Question, Does.EndWith("?"), $"The kind of slide {purpose} has no question.");
|
||||
Assert.That(purpose.PromptDescription, Is.Not.Empty.And.Not.EndWith("."), $"The kind of slide {purpose} has no description, or one which ends where the rule for the model goes on.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ public sealed class LayoutPurposeTests
|
||||
[Test]
|
||||
public void EveryKindOfSlideHasALabelOfItsOwn()
|
||||
{
|
||||
var labels = Enum.GetValues<LayoutPurpose>().Select(purpose => purpose.Label()).ToList();
|
||||
var labels = Enum.GetValues<LayoutPurpose>().Select(purpose => purpose.Label).ToList();
|
||||
Assert.That(labels, Is.Unique);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public sealed class LayoutPurposeTests
|
||||
[Test]
|
||||
public void TheKindsOfSlidesFollowTheOrderOfTheAreas()
|
||||
{
|
||||
var areas = Enum.GetValues<LayoutPurpose>().Select(purpose => purpose.Area()).ToList();
|
||||
var areas = Enum.GetValues<LayoutPurpose>().Select(purpose => purpose.Area).ToList();
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(areas, Is.Ordered, "The kinds of an area stand together, and the areas follow their own order.");
|
||||
|
||||
@@ -433,7 +433,7 @@ public sealed class PresentationTemplateTests
|
||||
(PlaceholderRole.TEXT, 3, StackedHeadings.THIRD_TEXT, 8U),
|
||||
}), "The copies are numbered after the slots of the layout, in reading order.");
|
||||
Assert.That(slide.SlotCopies.Select(copy => copy.Slot.Capacity), Is.EqualTo(new TextCapacity?[] { new TextCapacity(1, 14), new TextCapacity(3, 25), new TextCapacity(1, 18), new TextCapacity(3, 25) }), "A copy holds what its size allows, as a moved slot does.");
|
||||
Assert.That(slide.Slots.Select(slot => $"{slot.Role.CatalogName()} {slot.Ordinal}"), Is.EqualTo(new[] { "title 1", "label 1", "text 1", "label 2", "text 2", "label 3", "text 3" }));
|
||||
Assert.That(slide.Slots.Select(slot => $"{slot.Role.CatalogName} {slot.Ordinal}"), Is.EqualTo(new[] { "title 1", "label 1", "text 1", "label 2", "text 2", "label 3", "text 3" }));
|
||||
Assert.That(slide.Contents.Select(content => (content.Slot.Role, content.Text)), Is.EqualTo(new[]
|
||||
{
|
||||
(PlaceholderRole.TITLE, "Missions"),
|
||||
@@ -500,7 +500,7 @@ public sealed class PresentationTemplateTests
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(slide.SlotCopies.Select(copy => (copy.Slot.Ordinal, copy.Slot.Bounds)), Is.EqualTo(new[] { (2, upperRight), (3, lowerRight) }));
|
||||
Assert.That(slide.Slots.Select(slot => $"{slot.Role.CatalogName()} {slot.Ordinal}"), Is.EqualTo(new[] { "title 1", "label 1", "text 2", "text 1", "text 3" }), "The slots of the layout keep their numbers; a copy goes where a reader comes across it.");
|
||||
Assert.That(slide.Slots.Select(slot => $"{slot.Role.CatalogName} {slot.Ordinal}"), Is.EqualTo(new[] { "title 1", "label 1", "text 2", "text 1", "text 3" }), "The slots of the layout keep their numbers; a copy goes where a reader comes across it.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ public sealed class SourceCodeLanguagesTests
|
||||
[Test]
|
||||
public void NoAliasNamesAFormatOfItsOwn()
|
||||
{
|
||||
// FileExportFormatExtensions.TryFromCodeFenceLanguage is asked first, so such an entry would never be found:
|
||||
Assert.That(SourceCodeLanguages.Aliases.Where(alias => FileExportFormatExtensions.TryFromCodeFenceLanguage(alias, out _)), Is.Empty);
|
||||
// FileExportFormat.TryFromCodeFenceLanguage is asked first, so such an entry would never be found:
|
||||
Assert.That(SourceCodeLanguages.Aliases.Where(alias => FileExportFormat.TryFromCodeFenceLanguage(alias, out _)), Is.Empty);
|
||||
}
|
||||
}
|
||||
@@ -279,8 +279,8 @@ public sealed class SourcePassageTests
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That((Passage(HANDBOOK_URL, "Welcome.") with { Type = RetrievalContentType.TEXT_TRANSCRIPT }).IsTranscript(), Is.True);
|
||||
Assert.That(Passage(HANDBOOK_URL, "Welcome.").IsTranscript(), Is.False);
|
||||
Assert.That((Passage(HANDBOOK_URL, "Welcome.") with { Type = RetrievalContentType.TEXT_TRANSCRIPT }).IsTranscript, Is.True);
|
||||
Assert.That(Passage(HANDBOOK_URL, "Welcome.").IsTranscript, Is.False);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ public sealed class MailPassageTests
|
||||
Assert.That(new[] { passage }.GetPassagesOf(MAIL_SOURCE), Is.EqualTo(new[] { passage }), "Only a passage under its source gets the eye next to it.");
|
||||
Assert.That(passage.Path, Is.Empty, "The address of a mail names it for AI Studio only, so there is nothing to copy or open.");
|
||||
Assert.That(passage.DataSourceName, Is.EqualTo(MAIL_SOURCE.Title));
|
||||
Assert.That(passage.GetProvenanceKind(), Is.EqualTo(ProvenanceKind.MAIL));
|
||||
Assert.That(passage.ProvenanceKind, Is.EqualTo(ProvenanceKind.MAIL));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,7 +140,7 @@ public sealed class ToolOutboundDataTests : ToolRegistryTestBase
|
||||
Assert.That(hasRun, Is.False, "The mail would already have left AI Studio.");
|
||||
Assert.That(outcome.Trace.Status, Is.EqualTo(ToolInvocationTraceStatus.BLOCKED));
|
||||
Assert.That(outcome.Content, Does.Contain("Work mailbox"), "The user learns which mailbox stands in the way.");
|
||||
Assert.That(outcome.Content, Does.Contain(OutboundDataRestriction.ONLY_LINKS_FROM_CHAT.GetName()));
|
||||
Assert.That(outcome.Content, Does.Contain(OutboundDataRestriction.ONLY_LINKS_FROM_CHAT.Name));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ public sealed class ToolOutboundDataTests : ToolRegistryTestBase
|
||||
{
|
||||
Assert.That(message, Does.Contain("removed since"));
|
||||
Assert.That(message, Does.Not.Contain("Private mailbox"), "Another mailbox did not restrict the chat.");
|
||||
Assert.That(message, Does.Contain(OutboundDataRestriction.ONLY_CONFIGURED_SERVICES.GetName()), "The restriction stays, so the user still learns why.");
|
||||
Assert.That(message, Does.Contain(OutboundDataRestriction.ONLY_CONFIGURED_SERVICES.Name), "The restriction stays, so the user still learns why.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ public sealed class ToolOutboundDataTests : ToolRegistryTestBase
|
||||
{
|
||||
Assert.That(message, Does.Contain("Confluence wiki"), "The user learns that the wiki stands in the way.");
|
||||
Assert.That(message, Does.Not.Contain("mailbox"), "No mailbox restricted the chat.");
|
||||
Assert.That(message, Does.Contain(OutboundDataRestriction.ONLY_LINKS_FROM_CHAT.GetName()));
|
||||
Assert.That(message, Does.Contain(OutboundDataRestriction.ONLY_LINKS_FROM_CHAT.Name));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ public sealed class ToolOutboundDataTests : ToolRegistryTestBase
|
||||
{
|
||||
Assert.That(message, Does.Contain("intranet.example.org"), "The user learns which page stands in the way.");
|
||||
Assert.That(message, Does.Not.Contain("mailbox"), "No mailbox restricted the chat.");
|
||||
Assert.That(message, Does.Contain(OutboundDataRestriction.ONLY_CONFIGURED_SERVICES.GetName()));
|
||||
Assert.That(message, Does.Contain(OutboundDataRestriction.ONLY_CONFIGURED_SERVICES.Name));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ public sealed class ToolOutboundDataTests : ToolRegistryTestBase
|
||||
{
|
||||
Assert.That(message, Does.Contain("Project files"), "The user learns which data source stands in the way.");
|
||||
Assert.That(message, Does.Not.Contain("mailbox"), "No mailbox restricted the chat.");
|
||||
Assert.That(message, Does.Contain(OutboundDataRestriction.ONLY_LINKS_FROM_CHAT.GetName()));
|
||||
Assert.That(message, Does.Contain(OutboundDataRestriction.ONLY_LINKS_FROM_CHAT.Name));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ public sealed class ToolOutboundDataTests : ToolRegistryTestBase
|
||||
{
|
||||
Assert.That(message, Does.Contain("removed since"));
|
||||
Assert.That(message, Does.Not.Contain("Minutes"), "Another data source did not restrict the chat.");
|
||||
Assert.That(message, Does.Contain(OutboundDataRestriction.ONLY_CONFIGURED_SERVICES.GetName()), "The restriction stays, so the user still learns why.");
|
||||
Assert.That(message, Does.Contain(OutboundDataRestriction.ONLY_CONFIGURED_SERVICES.Name), "The restriction stays, so the user still learns why.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ public sealed class MailboxValidationTests
|
||||
[TestCase(MailboxTransportSecurity.SSL_ON_CONNECT, ExpectedResult = 993)]
|
||||
[TestCase(MailboxTransportSecurity.STARTTLS, ExpectedResult = 143)]
|
||||
[TestCase(MailboxTransportSecurity.UNKNOWN, ExpectedResult = null)]
|
||||
public int? EachEncryptionComesWithItsUsualPort(MailboxTransportSecurity transportSecurity) => transportSecurity.GetUsualPort();
|
||||
public int? EachEncryptionComesWithItsUsualPort(MailboxTransportSecurity transportSecurity) => transportSecurity.UsualPort;
|
||||
|
||||
[TestCase(0, ExpectedResult = false)]
|
||||
[TestCase(DataSourceValidation.MIN_ATTACHMENT_SIZE_MEGABYTES, ExpectedResult = true)]
|
||||
|
||||
Reference in new issue
Block a user