diff --git a/backend/FwLite/FwDataMiniLcmBridge.Tests/MiniLcmTests/SenseTests.cs b/backend/FwLite/FwDataMiniLcmBridge.Tests/MiniLcmTests/SenseTests.cs new file mode 100644 index 0000000000..520ab17c23 --- /dev/null +++ b/backend/FwLite/FwDataMiniLcmBridge.Tests/MiniLcmTests/SenseTests.cs @@ -0,0 +1,12 @@ +using FwDataMiniLcmBridge.Tests.Fixtures; + +namespace FwDataMiniLcmBridge.Tests.MiniLcmTests; + +[Collection(ProjectLoaderFixture.Name)] +public class SenseTests(ProjectLoaderFixture fixture) : SenseTestsBase +{ + protected override Task NewApi() + { + return Task.FromResult(fixture.NewProjectApi("SenseTests", "en", "en")); + } +} diff --git a/backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs b/backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs index e56ab38879..a1d5da2eef 100644 --- a/backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs +++ b/backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs @@ -953,7 +953,8 @@ public IAsyncEnumerable SearchEntries(string query, QueryOptions? options public Task GetEntry(Guid id) { - return Task.FromResult(FromLexEntry(EntriesRepository.GetObject(id))); + EntriesRepository.TryGetObject(id, out var lexEntry); + return Task.FromResult(lexEntry is null ? null : FromLexEntry(lexEntry)); } public async Task CreateEntry(Entry entry, CreateEntryOptions? options = null) @@ -1423,7 +1424,7 @@ private void ApplySenseToLexSense(Sense sense, ILexSense lexSense) public Task GetSense(Guid entryId, Guid id) { - var lcmSense = SenseRepository.GetObject(id); + SenseRepository.TryGetObject(id, out var lcmSense); return Task.FromResult(lcmSense is null ? null : FromLexSense(lcmSense)); } @@ -1550,7 +1551,7 @@ public Task DeleteSense(Guid entryId, Guid senseId) public Task GetExampleSentence(Guid entryId, Guid senseId, Guid id) { - var lcmExampleSentence = ExampleSentenceRepository.GetObject(id); + ExampleSentenceRepository.TryGetObject(id, out var lcmExampleSentence); return Task.FromResult(lcmExampleSentence is null ? null : FromLexExampleSentence(senseId, lcmExampleSentence)); } diff --git a/backend/FwLite/FwLiteProjectSync.Tests/CrdtRepairTests.cs b/backend/FwLite/FwLiteProjectSync.Tests/CrdtRepairTests.cs index 048cdbf788..acb72a9934 100644 --- a/backend/FwLite/FwLiteProjectSync.Tests/CrdtRepairTests.cs +++ b/backend/FwLite/FwLiteProjectSync.Tests/CrdtRepairTests.cs @@ -214,6 +214,25 @@ public async Task CrdtEntryMissingTranslationId_FwTranslationRemoved_FullSync() updatedCrdtEntry!.SingleExampleSentence().Translations.Should().BeEmpty(); } + [Fact] + public async Task CrdtEntryMissingTranslationId_FwExampleSentenceRemoved_FullSync() + { + // arrange + var (fwEntry, crdtEntry, _) = await CreateSyncedEntryMissingTranslationId(); + var entryId = fwEntry.Id; + var senseId = fwEntry.Senses.Single().Id; + var exampleSentenceId = fwEntry.SingleExampleSentence().Id; + + // act + await FwDataApi.DeleteExampleSentence(entryId, senseId, exampleSentenceId); + var result = await SyncService.Sync(CrdtApi, FwDataApi); + result.CrdtChanges.Should().Be(1, "the crdt example-sentence was removed"); + + // assert - the crdt translation was also removed + var updatedCrdtEntry = await CrdtApi.GetEntry(crdtEntry.Id); + updatedCrdtEntry!.Senses.Single().ExampleSentences.Should().BeEmpty(); + } + [Fact] public async Task CrdtEntryMissingTranslationId_CrdtTranslationRemoved_SyncMissingTranslationIds() { @@ -255,6 +274,24 @@ public async Task CrdtEntryMissingTranslationId_CrdtTranslationRemoved_FullSync( updatedFwEntry!.SingleExampleSentence().Translations.Should().BeEmpty(); } + [Fact] + public async Task CrdtEntryMissingTranslationId_CrdtExampleSentenceRemoved_FullSync() + { + // arrange + var (fwEntry, crdtEntry, _) = await CreateSyncedEntryMissingTranslationId(); + var entryId = fwEntry.Id; + var senseId = fwEntry.Senses.Single().Id; + var exampleSentenceId = fwEntry.SingleExampleSentence().Id; + + // act + await CrdtApi.DeleteExampleSentence(entryId, senseId, exampleSentenceId); + await SyncService.Sync(CrdtApi, FwDataApi); + + // assert - the fw translation was also removed + var updatedFwEntry = await FwDataApi.GetEntry(crdtEntry.Id); + updatedFwEntry!.Senses.Single().ExampleSentences.Should().BeEmpty(); + } + [Fact] public async Task CrdtEntryMissingTranslationId_NewCrdtEntry_SyncMissingTranslationIds() { diff --git a/backend/FwLite/LcmCrdt.Tests/MiniLcmTests/SenseTests.cs b/backend/FwLite/LcmCrdt.Tests/MiniLcmTests/SenseTests.cs new file mode 100644 index 0000000000..f91c08f6a5 --- /dev/null +++ b/backend/FwLite/LcmCrdt.Tests/MiniLcmTests/SenseTests.cs @@ -0,0 +1,19 @@ +namespace LcmCrdt.Tests.MiniLcmTests; + +public class SenseTests : SenseTestsBase +{ + private readonly MiniLcmApiFixture _fixture = new(); + + protected override async Task NewApi() + { + await _fixture.InitializeAsync(); + var api = _fixture.Api; + return api; + } + + public override async Task DisposeAsync() + { + await base.DisposeAsync(); + await _fixture.DisposeAsync(); + } +} diff --git a/backend/FwLite/MiniLcm.Tests/ExampleSentenceTestsBase.cs b/backend/FwLite/MiniLcm.Tests/ExampleSentenceTestsBase.cs index 8b41a77855..0cd3d00ada 100644 --- a/backend/FwLite/MiniLcm.Tests/ExampleSentenceTestsBase.cs +++ b/backend/FwLite/MiniLcm.Tests/ExampleSentenceTestsBase.cs @@ -4,6 +4,7 @@ public abstract class ExampleSentenceTestsBase : MiniLcmTestBase { private readonly Guid _entryId = Guid.NewGuid(); private readonly Guid _senseId = Guid.NewGuid(); + private readonly Guid _exampleSentenceId = Guid.NewGuid(); public override async Task InitializeAsync() { @@ -14,11 +15,35 @@ await Api.CreateEntry(new Entry() LexemeForm = { { "en", "new-lexeme-form" } }, Senses = [ - new Sense() { Id = _senseId, Gloss = { { "en", "new-sense-gloss" } } } + new Sense() + { + Id = _senseId, + Gloss = { { "en", "new-sense-gloss" } }, + ExampleSentences = [ new() + { + Id = _exampleSentenceId, + Sentence = { { "en", new("new-example-sentence") } }, + }] + } ] }); } + [Fact] + public async Task Get_MissingExampleSentence_ReturnsNull() + { + var exampleSentence = await Api.GetExampleSentence(_entryId, _senseId, Guid.NewGuid()); + exampleSentence.Should().BeNull(); + } + + [Fact] + public async Task Get_ExistingExampleSentence_ReturnsExampleSentence() + { + var exampleSentence = await Api.GetExampleSentence(_entryId, _senseId, _exampleSentenceId); + exampleSentence.Should().NotBeNull(); + exampleSentence.Sentence["en"].Should().BeEquivalentTo(new RichString("new-example-sentence", "en")); + } + [Fact] public async Task CanCreateExampleSentence() { diff --git a/backend/FwLite/MiniLcm.Tests/QueryEntryTestsBase.cs b/backend/FwLite/MiniLcm.Tests/QueryEntryTestsBase.cs index 28078d9ebc..ce4f0fb8f9 100644 --- a/backend/FwLite/MiniLcm.Tests/QueryEntryTestsBase.cs +++ b/backend/FwLite/MiniLcm.Tests/QueryEntryTestsBase.cs @@ -6,6 +6,7 @@ namespace MiniLcm.Tests; public abstract class QueryEntryTestsBase : MiniLcmTestBase { + private readonly Guid appleId = Guid.NewGuid(); private readonly string Apple = "Apple"; private readonly string Peach = "Peach"; private readonly string Banana = "Banana"; @@ -23,7 +24,11 @@ public override async Task InitializeAsync() await Api.CreateSemanticDomain(semanticDomain); var complexFormType = new ComplexFormType() { Id = Guid.NewGuid(), Name = new() { { "en", "Very complex" } } }; await Api.CreateComplexFormType(complexFormType); - await Api.CreateEntry(new Entry() { LexemeForm = { { "en", Apple } } }); + await Api.CreateEntry(new Entry() + { + Id = appleId, + LexemeForm = { { "en", Apple } } + }); await Api.CreateEntry(new Entry() { LexemeForm = { { "en", Peach } }, @@ -92,6 +97,21 @@ await Api.CreateEntry(new Entry() await Api.CreateEntry(new Entry()); } + [Fact] + public async Task Get_MissingEntry_ReturnsNull() + { + var entry = await Api.GetEntry(Guid.NewGuid()); + entry.Should().BeNull(); + } + + [Fact] + public async Task Get_ExistingEntry_ReturnsEntry() + { + var entry = await Api.GetEntry(appleId); + entry.Should().NotBeNull(); + entry.LexemeForm["en"].Should().Be(Apple); + } + [Fact] public async Task CanFilterToMissingSenses() { diff --git a/backend/FwLite/MiniLcm.Tests/SenseTestsBase.cs b/backend/FwLite/MiniLcm.Tests/SenseTestsBase.cs new file mode 100644 index 0000000000..83df2a1ad5 --- /dev/null +++ b/backend/FwLite/MiniLcm.Tests/SenseTestsBase.cs @@ -0,0 +1,37 @@ +namespace MiniLcm.Tests; + +public abstract class SenseTestsBase : MiniLcmTestBase +{ + private static readonly Guid _entryId = Guid.NewGuid(); + private static readonly Guid _senseId = Guid.NewGuid(); + + public override async Task InitializeAsync() + { + await base.InitializeAsync(); + await Api.CreateEntry(new Entry() + { + Id = _entryId, + LexemeForm = { { "en", "new-lexeme-form" } }, + Senses = [new() + { + Id = _senseId, + Gloss = { { "en", "new-sense-gloss" } } + }] + }); + } + + [Fact] + public async Task Get_MissingSense_ReturnsNull() + { + var sense = await Api.GetSense(_entryId, Guid.NewGuid()); + sense.Should().BeNull(); + } + + [Fact] + public async Task Get_ExistingSense_ReturnsSense() + { + var sense = await Api.GetSense(_entryId, _senseId); + sense.Should().NotBeNull(); + sense.Gloss["en"].Should().Be("new-sense-gloss"); + } +}