mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-13 10:47:21 +00:00
Fixed plugin values which depended on the region settings of the computer (#1194)
This commit is contained in:
1 parent
65d50de033
commit
a1b493cb13
20 files changed
+397
-137
No files matched your search
@@ -0,0 +1,48 @@
|
||||
using System.Globalization;
|
||||
|
||||
using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Tests.Settings;
|
||||
|
||||
/// <summary>
|
||||
/// Checks that the stored form of a managed setting does not depend on the region settings of the computer.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The settings keep the last value an organization applied as an editable default in this form, and compare it with
|
||||
/// the current value to see whether the user changed it. A form which followed the culture would make the value look
|
||||
/// changed once the computer, or its region settings, changed. All cases run under de-DE, which writes a decimal comma.
|
||||
/// </remarks>
|
||||
[TestFixture]
|
||||
[SetCulture("de-DE")]
|
||||
public sealed class ManagedValueCultureTests
|
||||
{
|
||||
[Test]
|
||||
public void TheCultureWritesADecimalComma()
|
||||
{
|
||||
Assert.That(1.5.ToString(CultureInfo.CurrentCulture), Is.EqualTo("1,5"), "Without a decimal comma, the other cases check nothing.");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TheItemsOfAListAreWrittenTheSameOnEveryComputer()
|
||||
{
|
||||
Assert.That(ManagedConfiguration.SerializeManagedScalarValue(new List<double> { 2.25, 1.5 }), Is.EqualTo("1.5;2.25"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TheKeysAndValuesOfATableAreWrittenTheSameOnEveryComputer()
|
||||
{
|
||||
var table = new Dictionary<int, double> { [2] = 0.5, [10] = 1.25 };
|
||||
|
||||
Assert.That(ManagedConfiguration.SerializeManagedScalarValue(table), Is.EqualTo("10:1.25;2:0.5"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WholeNumbersKeepTheFormTheyWereStoredInBefore()
|
||||
{
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(ManagedConfiguration.SerializeManagedScalarValue(new List<int> { 12, 3 }), Is.EqualTo("12;3"));
|
||||
Assert.That(ManagedConfiguration.SerializeManagedScalarValue(new Dictionary<int, string> { [1] = "one" }), Is.EqualTo("1:one"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,6 +84,32 @@ public sealed class LuaTableTests
|
||||
""".ReplaceLineEndings("\n")));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The order of the texts must not depend on the region settings of the computer which writes them.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A comparison by culture sorts the underscore before the letters, the ordinal one after them. The
|
||||
/// language plugins were written in the first order before, so this test fails with it.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
[SetCulture("de-DE")]
|
||||
public void TheTextsAreOrderedOrdinallyWhateverTheCulture()
|
||||
{
|
||||
var code = Create(new Dictionary<string, string>
|
||||
{
|
||||
["UI_TEXT_CONTENT.AISTUDIO.DIALOGS.A_B.T1"] = "With an underscore",
|
||||
["UI_TEXT_CONTENT.AISTUDIO.DIALOGS.AB.T1"] = "Without an underscore"
|
||||
});
|
||||
|
||||
var withoutUnderscore = code.IndexOf("AISTUDIO::DIALOGS::AB::T1", StringComparison.Ordinal);
|
||||
var withUnderscore = code.IndexOf("AISTUDIO::DIALOGS::A_B::T1", StringComparison.Ordinal);
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(withoutUnderscore, Is.GreaterThanOrEqualTo(0), code);
|
||||
Assert.That(withUnderscore, Is.GreaterThan(withoutUnderscore), code);
|
||||
}
|
||||
}
|
||||
|
||||
private static string Create(IReadOnlyDictionary<string, string> texts, IReadOnlyDictionary<string, string>? comments = null)
|
||||
{
|
||||
// The Lua runtime has a type of the same name, which stands for a table in Lua:
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
using System.Globalization;
|
||||
|
||||
using AIStudio.Tools.PluginSystem.Assistants;
|
||||
using AIStudio.Tools.PluginSystem.Assistants.DataModel;
|
||||
|
||||
namespace AIStudio.Tests.Tools.PluginSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Checks that assistant plugins read numbers and write dates the same way on every computer.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The app sets no culture of its own, so the current culture is the one of the operating system. Each case runs
|
||||
/// under a culture which writes differently than the invariant one: de-DE with a decimal comma and German names of
|
||||
/// months, sv-SE with a minus sign of its own (U+2212), fi-FI with a period between hours and minutes, and th-TH
|
||||
/// with the years of the Buddhist era. Each case first checks that its culture really writes differently, so that
|
||||
/// a system without the data of the cultures fails instead of passing.
|
||||
/// </remarks>
|
||||
[TestFixture]
|
||||
public sealed class AssistantPluginCultureTests
|
||||
{
|
||||
private static readonly DateTime MOMENT = new(2026, 3, 11, 14, 3, 22, 500);
|
||||
|
||||
[TestCase("de-DE", 12.5, "12.5")]
|
||||
[TestCase("sv-SE", -3.5, "-3.5")]
|
||||
public void ANumberFromLuaBecomesTheSameTextOnEveryComputer(string culture, double number, string expected)
|
||||
{
|
||||
InCulture(culture, () =>
|
||||
{
|
||||
var props = new Dictionary<string, object> { ["PrefillText"] = number };
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(number.ToString(CultureInfo.CurrentCulture), Is.Not.EqualTo(expected), "The culture must write the number differently, or this case checks nothing.");
|
||||
Assert.That(AssistantComponentPropHelper.ReadString(props, "PrefillText"), Is.EqualTo(expected));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase("de-DE")]
|
||||
[TestCase("sv-SE")]
|
||||
public void AWholeNumberIsReadOnEveryComputer(string culture)
|
||||
{
|
||||
InCulture(culture, () =>
|
||||
{
|
||||
var props = new Dictionary<string, object>
|
||||
{
|
||||
["FromLua"] = -3.0,
|
||||
["FromASetter"] = 7,
|
||||
["AsText"] = "-12",
|
||||
["WithAFraction"] = 6.5,
|
||||
["TooLarge"] = 1e15
|
||||
};
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(AssistantComponentPropHelper.ReadInt(props, "FromLua"), Is.EqualTo(-3));
|
||||
Assert.That(AssistantComponentPropHelper.ReadInt(props, "FromASetter"), Is.EqualTo(7));
|
||||
Assert.That(AssistantComponentPropHelper.ReadInt(props, "AsText"), Is.EqualTo(-12));
|
||||
Assert.That(AssistantComponentPropHelper.ReadInt(props, "WithAFraction", fallback: 42), Is.EqualTo(42));
|
||||
Assert.That(AssistantComponentPropHelper.ReadNullableInt(props, "TooLarge"), Is.Null);
|
||||
Assert.That(AssistantComponentPropHelper.ReadNullableInt(props, "Missing"), Is.Null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase("fi-FI", "yyyy-MM-dd HH:mm:ss", "2026-03-11 14:03:22")]
|
||||
[TestCase("th-TH", "yyyy-MM-dd HH:mm:ss", "2026-03-11 14:03:22")]
|
||||
[TestCase("de-DE", "dd. MMMM yyyy", "11. March 2026")]
|
||||
public void TheDateTimeHelperFormatsTheSameOnEveryComputer(string culture, string format, string expected)
|
||||
{
|
||||
InCulture(culture, () =>
|
||||
{
|
||||
var table = PluginAssistants.CreateDateTimeTable(MOMENT, format);
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(MOMENT.ToString(format, CultureInfo.CurrentCulture), Is.Not.EqualTo(expected), "The culture must write the moment differently, or this case checks nothing.");
|
||||
Assert.That(table["formatted"].Read<string>(), Is.EqualTo(expected));
|
||||
Assert.That(table["year"].Read<double>(), Is.EqualTo(2026));
|
||||
Assert.That(table["millisecond"].Read<double>(), Is.EqualTo(500));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void InCulture(string name, Action check)
|
||||
{
|
||||
var previousCulture = CultureInfo.CurrentCulture;
|
||||
try
|
||||
{
|
||||
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo(name);
|
||||
check();
|
||||
}
|
||||
finally
|
||||
{
|
||||
CultureInfo.CurrentCulture = previousCulture;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,9 +24,12 @@ namespace AIStudio.Tests.Tools.PluginSystem;
|
||||
/// tables are compared value by value. A parser which read a single string differently than Lua
|
||||
/// would hand the import something the configuration plugin of the same snippet never contains.<br/><br/>
|
||||
/// Nothing here asserts on the wording of an issue: I18N is process-wide state without a reset, so
|
||||
/// an Init somewhere else would decide whether these pass.
|
||||
/// an Init somewhere else would decide whether these pass.<br/><br/>
|
||||
/// All cases run under de-DE, which writes a decimal comma: an export which wrote a number with the
|
||||
/// region settings of the computer would fail the comparison with Lua here.
|
||||
/// </remarks>
|
||||
[TestFixture]
|
||||
[SetCulture("de-DE")]
|
||||
public sealed class ConfigurationSnippetParserTests
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.Globalization;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
using AIStudio.Tools.ToolCallingSystem.ToolCallingImplementations.WebSearch.SearXNG;
|
||||
|
||||
namespace AIStudio.Tests.Tools.ToolCalling;
|
||||
|
||||
/// <summary>
|
||||
/// Checks that the score of a SearXNG result is read the same way on every computer.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The score decides the order of the results. An instance may send it as a number or as text, both in the notation
|
||||
/// of JSON. All cases run under de-DE, where the period separates thousands: read with that culture, "0.75" was 75.
|
||||
/// </remarks>
|
||||
[TestFixture]
|
||||
[SetCulture("de-DE")]
|
||||
public sealed class SearXNGScoreTests
|
||||
{
|
||||
[Test]
|
||||
public void TheCultureReadsThePeriodAsAThousandsSeparator()
|
||||
{
|
||||
Assert.That(double.Parse("0.75", CultureInfo.CurrentCulture), Is.EqualTo(75), "Without that, the other cases check nothing.");
|
||||
}
|
||||
|
||||
[TestCase("""{ "score": 0.75 }""")]
|
||||
[TestCase("""{ "score": "0.75" }""")]
|
||||
public void TheScoreIsReadTheSameOnEveryComputer(string json)
|
||||
{
|
||||
var result = JsonNode.Parse(json)!.AsObject();
|
||||
|
||||
var found = SearXNGSearchClient.TryGetScore(result, out var score);
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(found, Is.True);
|
||||
Assert.That(score, Is.EqualTo(0.75));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user