diff --git a/backend/FwLite/FwLiteProjectSync.Tests/UpdateDiffTests.cs b/backend/FwLite/FwLiteProjectSync.Tests/UpdateDiffTests.cs index c3ed8aa1c..9d1feffa2 100644 --- a/backend/FwLite/FwLiteProjectSync.Tests/UpdateDiffTests.cs +++ b/backend/FwLite/FwLiteProjectSync.Tests/UpdateDiffTests.cs @@ -11,7 +11,7 @@ public class UpdateDiffTests { private readonly AutoFaker _autoFaker = new(new AutoFakerConfig() { - Overrides = [new MultiStringOverride()] + Overrides = [new MultiStringOverride(), new WritingSystemIdOverride()] }); [Fact] diff --git a/backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.cs b/backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.cs index 7118f61e0..c08c3812c 100644 --- a/backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.cs +++ b/backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.cs @@ -7,14 +7,21 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using MiniLcm.Tests.AutoFakerHelpers; using SIL.Harmony.Changes; using SIL.Harmony.Entities; using Soenneker.Utils.AutoBogus; +using Soenneker.Utils.AutoBogus.Config; namespace LcmCrdt.Tests; public class DataModelSnapshotTests : IAsyncLifetime { + private static AutoFaker _faker = new AutoFaker(new AutoFakerConfig() + { + Overrides = [new MultiStringOverride(), new WritingSystemIdOverride()] + }); + protected readonly AsyncServiceScope _services; private readonly LcmCrdtDbContext _crdtDbContext; private CrdtConfig _crdtConfig; @@ -76,7 +83,6 @@ public async Task VerifyIObjectWithIdModels() [Fact] public void VerifyIObjectWithIdsMatchAdapterGetObjectTypeName() { - var faker = new AutoFaker(); var jsonSerializerOptions = _crdtConfig.JsonSerializerOptions; var types = jsonSerializerOptions.GetTypeInfo(typeof(IObjectWithId)).PolymorphismOptions?.DerivedTypes ?? []; using (new AssertionScope()) @@ -84,7 +90,7 @@ public void VerifyIObjectWithIdsMatchAdapterGetObjectTypeName() foreach (var jsonDerivedType in types) { var typeDiscriminator = jsonDerivedType.TypeDiscriminator.Should().BeOfType().Subject; - var obj = faker.Generate(jsonDerivedType.DerivedType); + var obj = _faker.Generate(jsonDerivedType.DerivedType); new MiniLcmCrdtAdapter((IObjectWithId)obj).GetObjectTypeName().Should().Be(typeDiscriminator); } } diff --git a/backend/FwLite/LcmCrdt.Tests/EntityCopyMethodTests.cs b/backend/FwLite/LcmCrdt.Tests/EntityCopyMethodTests.cs index 22fb0cebf..395615d46 100644 --- a/backend/FwLite/LcmCrdt.Tests/EntityCopyMethodTests.cs +++ b/backend/FwLite/LcmCrdt.Tests/EntityCopyMethodTests.cs @@ -1,5 +1,6 @@ using SIL.Harmony.Entities; using LcmCrdt.Objects; +using MiniLcm.Tests.AutoFakerHelpers; using SIL.Harmony; using Soenneker.Utils.AutoBogus; using Soenneker.Utils.AutoBogus.Config; @@ -8,7 +9,11 @@ namespace LcmCrdt.Tests; public class EntityCopyMethodTests { - private readonly AutoFaker _autoFaker = new(new AutoFakerConfig()); + private readonly AutoFaker _autoFaker = new(new AutoFakerConfig() + { + Overrides = [new MultiStringOverride(), new WritingSystemIdOverride()] + }); + public static IEnumerable GetEntityTypes() { var crdtConfig = new CrdtConfig(); diff --git a/backend/FwLite/LcmCrdt.Tests/MultiStringOverride.cs b/backend/FwLite/LcmCrdt.Tests/MultiStringOverride.cs deleted file mode 100644 index ec6eb3752..000000000 --- a/backend/FwLite/LcmCrdt.Tests/MultiStringOverride.cs +++ /dev/null @@ -1,22 +0,0 @@ -using MiniLcm.Models; -using Soenneker.Utils.AutoBogus.Context; -using Soenneker.Utils.AutoBogus.Override; - -namespace LcmCrdt.Tests; - -public class MultiStringOverride: AutoFakerOverride -{ - public override void Generate(AutoFakerOverrideContext context) - { - var target = context.Instance as MultiString; - if (target is null) - { - context.Instance = target = new MultiString(); - } - var wordsArray = context.Faker.Random.WordsArray(1, 4); - foreach (var word in wordsArray) - { - target[context.Faker.Random.String(2, 'a', 'z')] = word; - } - } -} diff --git a/backend/FwLite/MiniLcm.Tests/AutoFakerHelpers/MultiStringOverride.cs b/backend/FwLite/MiniLcm.Tests/AutoFakerHelpers/MultiStringOverride.cs index b6818dd2f..678a6a153 100644 --- a/backend/FwLite/MiniLcm.Tests/AutoFakerHelpers/MultiStringOverride.cs +++ b/backend/FwLite/MiniLcm.Tests/AutoFakerHelpers/MultiStringOverride.cs @@ -16,9 +16,7 @@ public override void Generate(AutoFakerOverrideContext context) var wordsArray = context.Faker.Random.WordsArray(1, 4); foreach (var word in wordsArray) { - var writingSystemId = validWs is not null - ? context.Faker.Random.ArrayElement(validWs) - : context.Faker.Random.String(2, 'a', 'z'); + var writingSystemId = context.Faker.Random.ArrayElement(validWs ?? WritingSystemCodes.ValidTwoLetterCodes); target[writingSystemId] = word; } } diff --git a/backend/FwLite/MiniLcm.Tests/AutoFakerHelpers/WritingSystemIdOverride.cs b/backend/FwLite/MiniLcm.Tests/AutoFakerHelpers/WritingSystemIdOverride.cs new file mode 100644 index 000000000..2901dc454 --- /dev/null +++ b/backend/FwLite/MiniLcm.Tests/AutoFakerHelpers/WritingSystemIdOverride.cs @@ -0,0 +1,16 @@ +using MiniLcm.Models; +using Soenneker.Utils.AutoBogus.Context; +using Soenneker.Utils.AutoBogus.Override; + +namespace MiniLcm.Tests.AutoFakerHelpers; + +public class WritingSystemIdOverride: AutoFakerOverride +{ + public override bool Preinitialize => false; + + public override void Generate(AutoFakerOverrideContext context) + { + var ws = context.Faker.Random.ArrayElement(WritingSystemCodes.ValidTwoLetterCodes); + context.Instance = new WritingSystemId(ws); + } +} diff --git a/backend/FwLite/MiniLcm.Tests/WritingSystemCodes.cs b/backend/FwLite/MiniLcm.Tests/WritingSystemCodes.cs new file mode 100644 index 000000000..eda122ca2 --- /dev/null +++ b/backend/FwLite/MiniLcm.Tests/WritingSystemCodes.cs @@ -0,0 +1,8 @@ +using SIL.WritingSystems; + +namespace MiniLcm.Tests; + +public static class WritingSystemCodes +{ + public static readonly string[] ValidTwoLetterCodes = StandardSubtags.RegisteredLanguages.Select(lang => lang.Code).ToArray(); +} diff --git a/backend/FwLite/MiniLcm.Tests/WritingSystemIdTests.cs b/backend/FwLite/MiniLcm.Tests/WritingSystemIdTests.cs new file mode 100644 index 000000000..6448f77b0 --- /dev/null +++ b/backend/FwLite/MiniLcm.Tests/WritingSystemIdTests.cs @@ -0,0 +1,34 @@ +using MiniLcm.Models; + +namespace MiniLcm.Tests; + +public class WritingSystemIdTests +{ + [Theory] + [InlineData("en")] + [InlineData("th")] + [InlineData("en-Zxxx-x-audio")] + public void ValidWritingSystemId_ShouldNotThrow(string code) + { + var ws = new WritingSystemId(code); + ws.Should().NotBeNull(); + } + + [Theory] + [InlineData("gx")] + [InlineData("oo")] + [InlineData("eng")] // Three-letter codes not allowed when there's a valid two-letter code + [InlineData("eng-Zxxx-x-audio")] + [InlineData("nonsense")] + public void InvalidWritingSystemId_ShouldThrow(string code) + { + Assert.Throws(() => new WritingSystemId(code)); + } + + [Fact] + public void DefaultWritingSystemId_IsValid() + { + var ws = new WritingSystemId("default"); + ws.Should().NotBeNull(); + } +} diff --git a/backend/FwLite/MiniLcm/MiniLcm.csproj b/backend/FwLite/MiniLcm/MiniLcm.csproj index dbdbf9ad8..8f4dc0c48 100644 --- a/backend/FwLite/MiniLcm/MiniLcm.csproj +++ b/backend/FwLite/MiniLcm/MiniLcm.csproj @@ -9,6 +9,7 @@ + diff --git a/backend/FwLite/MiniLcm/Models/WritingSystemId.cs b/backend/FwLite/MiniLcm/Models/WritingSystemId.cs index 670b88753..7441fb99d 100644 --- a/backend/FwLite/MiniLcm/Models/WritingSystemId.cs +++ b/backend/FwLite/MiniLcm/Models/WritingSystemId.cs @@ -1,5 +1,6 @@ using System.Text.Json; using System.Text.Json.Serialization; +using SIL.WritingSystems; namespace MiniLcm.Models; @@ -26,11 +27,26 @@ public override void WriteAsPropertyName(Utf8JsonWriter writer, WritingSystemId } [JsonConverter(typeof(WritingSystemIdJsonConverter))] -public readonly record struct WritingSystemId(string Code): ISpanFormattable, ISpanParsable +public readonly record struct WritingSystemId: ISpanFormattable, ISpanParsable { + public string Code { get; init; } + + public WritingSystemId(string code) + { + if (code == "default" || IetfLanguageTag.IsValid(code)) + { + Code = code; + } + else + { + throw new ArgumentException($"Invalid writing system ID '{code}'", nameof(code)); + } + } + public static implicit operator string(WritingSystemId ws) => ws.Code; public static implicit operator WritingSystemId(string code) => new(code); public static implicit operator WritingSystemId(ReadOnlySpan code) => new(new string(code)); + public override string ToString() { return Code;