Skip to content

Commit

Permalink
Fix some Crdt semantic domain bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
myieye committed Oct 4, 2024
1 parent 10ab90a commit 6fd51ce
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 24 deletions.
57 changes: 34 additions & 23 deletions backend/FwLite/LcmCrdt.Tests/JsonPatchRewriteTests.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
using LcmCrdt.Changes;
using System.Text.Json;
using LcmCrdt.Changes;
using MiniLcm.Models;
using SystemTextJsonPatch;
using SystemTextJsonPatch.Operations;
using SemanticDomain = LcmCrdt.Objects.SemanticDomain;
using Sense = LcmCrdt.Objects.Sense;

namespace LcmCrdt.Tests;

public class JsonPatchRewriteTests
{
private JsonPatchDocument<MiniLcm.Models.Sense> _patchDocument = new() { Options = new JsonSerializerOptions(JsonSerializerDefaults.Web) };

private Sense _sense = new Sense()
{
Id = Guid.NewGuid(),
Expand All @@ -21,11 +25,10 @@ public class JsonPatchRewriteTests
public void RewritePartOfSpeechChangesIntoSetPartOfSpeechChange()
{
var newPartOfSpeechId = Guid.NewGuid();
var patchDocument = new JsonPatchDocument<MiniLcm.Models.Sense>();
patchDocument.Replace(s => s.PartOfSpeechId, newPartOfSpeechId);
patchDocument.Replace(s => s.Gloss["en"], "new gloss");
_patchDocument.Replace(s => s.PartOfSpeechId, newPartOfSpeechId);
_patchDocument.Replace(s => s.Gloss["en"], "new gloss");

var changes = Sense.ChangesFromJsonPatch(_sense, patchDocument).ToArray();
var changes = Sense.ChangesFromJsonPatch(_sense, _patchDocument).ToArray();

var setPartOfSpeechChange = changes.OfType<SetPartOfSpeechChange>().Should().ContainSingle().Subject;
setPartOfSpeechChange.EntityId.Should().Be(_sense.Id);
Expand All @@ -40,10 +43,9 @@ public void RewritePartOfSpeechChangesIntoSetPartOfSpeechChange()
public void JsonPatchChangeRewriteDoesNotReturnEmptyPatchChanges()
{
var newPartOfSpeechId = Guid.NewGuid();
var patchDocument = new JsonPatchDocument<MiniLcm.Models.Sense>();
patchDocument.Replace(s => s.PartOfSpeechId, newPartOfSpeechId);
_patchDocument.Replace(s => s.PartOfSpeechId, newPartOfSpeechId);

var changes = Sense.ChangesFromJsonPatch(_sense, patchDocument).ToArray();
var changes = Sense.ChangesFromJsonPatch(_sense, _patchDocument).ToArray();

var setPartOfSpeechChange = changes.Should().ContainSingle()
.Subject.Should().BeOfType<SetPartOfSpeechChange>().Subject;
Expand All @@ -55,11 +57,24 @@ public void JsonPatchChangeRewriteDoesNotReturnEmptyPatchChanges()
public void RewritesAddSemanticDomainChangesIntoAddSemanticDomainChange()
{
var newSemanticDomainId = Guid.NewGuid();
var patchDocument = new JsonPatchDocument<MiniLcm.Models.Sense>();
patchDocument.Add(s => s.SemanticDomains,
_patchDocument.Add(s => s.SemanticDomains,
new SemanticDomain() { Id = newSemanticDomainId, Code = "new code", Name = new MultiString() });

var changes = Sense.ChangesFromJsonPatch(_sense, patchDocument).ToArray();
var changes = Sense.ChangesFromJsonPatch(_sense, _patchDocument).ToArray();

var addSemanticDomainChange = (AddSemanticDomainChange)changes.Should().AllBeOfType<AddSemanticDomainChange>().And.ContainSingle().Subject;
addSemanticDomainChange.EntityId.Should().Be(_sense.Id);
addSemanticDomainChange.SemanticDomain.Id.Should().Be(newSemanticDomainId);
}

[Fact]
public void RewritesAddSemanticDomainChangesIntoAddSemanticDomainChange_JsonElement()
{
var newSemanticDomainId = Guid.Parse("46e4fe08-ffa0-4c8b-bf88-2c56138904d1");
_patchDocument.Operations.Add(new Operation<MiniLcm.Models.Sense>("add", "/semanticDomains/-", null,
JsonSerializer.Deserialize<JsonElement>("""{"deletedAt":null,"predefined":true,"id":"46e4fe08-ffa0-4c8b-bf88-2c56138904d1","name":{"en":"Sky"},"code":"1.1"}""")));

var changes = Sense.ChangesFromJsonPatch(_sense, _patchDocument).ToArray();

var addSemanticDomainChange = (AddSemanticDomainChange)changes.Should().AllBeOfType<AddSemanticDomainChange>().And.ContainSingle().Subject;
addSemanticDomainChange.EntityId.Should().Be(_sense.Id);
Expand All @@ -71,10 +86,9 @@ public void RewritesReplaceSemanticDomainPatchChangesIntoReplaceSemanticDomainCh
{
var oldSemanticDomainId = _sense.SemanticDomains[0].Id;
var newSemanticDomainId = Guid.NewGuid();
var patchDocument = new JsonPatchDocument<MiniLcm.Models.Sense>();
patchDocument.Replace(s => s.SemanticDomains, new SemanticDomain() { Id = newSemanticDomainId, Code = "new code", Name = new MultiString() }, 0);
_patchDocument.Replace(s => s.SemanticDomains, new SemanticDomain() { Id = newSemanticDomainId, Code = "new code", Name = new MultiString() }, 0);

var changes = Sense.ChangesFromJsonPatch(_sense, patchDocument).ToArray();
var changes = Sense.ChangesFromJsonPatch(_sense, _patchDocument).ToArray();

var replaceSemanticDomainChange = (ReplaceSemanticDomainChange)changes.Should().AllBeOfType<ReplaceSemanticDomainChange>().And.ContainSingle().Subject;
replaceSemanticDomainChange.EntityId.Should().Be(_sense.Id);
Expand All @@ -86,10 +100,9 @@ public void RewritesReplaceNoIndexSemanticDomainPatchChangesIntoReplaceSemanticD
{
var oldSemanticDomainId = _sense.SemanticDomains[0].Id;
var newSemanticDomainId = Guid.NewGuid();
var patchDocument = new JsonPatchDocument<MiniLcm.Models.Sense>();
patchDocument.Replace(s => s.SemanticDomains, new SemanticDomain() { Id = newSemanticDomainId, Code = "new code", Name = new MultiString() });
_patchDocument.Replace(s => s.SemanticDomains, new SemanticDomain() { Id = newSemanticDomainId, Code = "new code", Name = new MultiString() });

var changes = Sense.ChangesFromJsonPatch(_sense, patchDocument).ToArray();
var changes = Sense.ChangesFromJsonPatch(_sense, _patchDocument).ToArray();

var replaceSemanticDomainChange = (ReplaceSemanticDomainChange)changes.Should().AllBeOfType<ReplaceSemanticDomainChange>().And.ContainSingle().Subject;
replaceSemanticDomainChange.EntityId.Should().Be(_sense.Id);
Expand All @@ -100,11 +113,10 @@ public void RewritesReplaceNoIndexSemanticDomainPatchChangesIntoReplaceSemanticD
[Fact]
public void RewritesRemoveSemanticDomainPatchChangesIntoReplaceSemanticDomainChange()
{
var patchDocument = new JsonPatchDocument<MiniLcm.Models.Sense>();
var semanticDomainIdToRemove = _sense.SemanticDomains[0].Id;
patchDocument.Remove(s => s.SemanticDomains, 0);
_patchDocument.Remove(s => s.SemanticDomains, 0);

var changes = Sense.ChangesFromJsonPatch(_sense, patchDocument).ToArray();
var changes = Sense.ChangesFromJsonPatch(_sense, _patchDocument).ToArray();

var removeSemanticDomainChange = (RemoveSemanticDomainChange)changes.Should().AllBeOfType<
RemoveSemanticDomainChange>().And.ContainSingle().Subject;
Expand All @@ -115,11 +127,10 @@ public void RewritesRemoveSemanticDomainPatchChangesIntoReplaceSemanticDomainCha
[Fact]
public void RewritesRemoveNoIndexSemanticDomainPatchChangesIntoReplaceSemanticDomainChange()
{
var patchDocument = new JsonPatchDocument<MiniLcm.Models.Sense>();
var semanticDomainIdToRemove = _sense.SemanticDomains[0].Id;
patchDocument.Remove(s => s.SemanticDomains);
_patchDocument.Remove(s => s.SemanticDomains);

var changes = Sense.ChangesFromJsonPatch(_sense, patchDocument).ToArray();
var changes = Sense.ChangesFromJsonPatch(_sense, _patchDocument).ToArray();

var removeSemanticDomainChange = (RemoveSemanticDomainChange)changes.Should().AllBeOfType<
RemoveSemanticDomainChange>().And.ContainSingle().Subject;
Expand Down
38 changes: 38 additions & 0 deletions backend/FwLite/LcmCrdt.Tests/LexboxApiTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,44 @@ public async Task UpdateSenseSemanticDomain()
semanticDomain.Id.Should().Be(newDomainId);
}

[Fact]
public async Task RemoveSenseSemanticDomain()
{
var newDomainId = Guid.NewGuid();
await DataModel.AddChange(Guid.NewGuid(), new CreateSemanticDomainChange(newDomainId, new MultiString() { { "en", "test" } }, "updated"));
var newSemanticDomain = await DataModel.GetLatest<Objects.SemanticDomain>(newDomainId);
ArgumentNullException.ThrowIfNull(newSemanticDomain);
var entry = await _api.CreateEntry(new Entry
{
LexemeForm = new MultiString
{
Values =
{
{ "en", "test" }
}
},
Senses = new List<Sense>
{
new Sense()
{
SemanticDomains = [newSemanticDomain],
Definition = new MultiString
{
Values =
{
{ "en", "test" }
}
}
}
}
});
var updatedSense = await _api.UpdateSense(entry.Id,
entry.Senses[0].Id,
new UpdateObjectInput<Sense>()
.Remove(e => e.SemanticDomains, 0));
updatedSense.SemanticDomains.Should().BeEmpty();
}

[Fact]
public async Task UpdateExampleSentence()
{
Expand Down
2 changes: 2 additions & 0 deletions backend/FwLite/LcmCrdt/LcmCrdtKernel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ public static void ConfigureCrdt(CrdtConfig config)
.Add<DeleteChange<SemanticDomain>>()
.Add<SetPartOfSpeechChange>()
.Add<AddSemanticDomainChange>()
.Add<RemoveSemanticDomainChange>()
.Add<ReplaceSemanticDomainChange>()
.Add<CreateEntryChange>()
.Add<CreateSenseChange>()
.Add<CreateExampleSentenceChange>()
Expand Down
17 changes: 16 additions & 1 deletion backend/FwLite/LcmCrdt/Utils/JsonPatchRewriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,22 @@ public static IEnumerable<IChange> RewriteChanges<T, TProp>(this JsonPatchDocume
}
}
patchDocument.Operations.Remove(operation);
yield return changeFactory((TProp?)operation.Value, index, operation.OperationType);
if (operation.Value is TProp value)
{
yield return changeFactory(value, index, operation.OperationType);
}
else if (operation.Value is JsonElement element)
{
yield return changeFactory(JsonSerializer.Deserialize<TProp>(element, patchDocument.Options), index, operation.OperationType);
}
else if (operation.Value is null)
{
yield return changeFactory(default, index, operation.OperationType);
}
else
{
throw new InvalidOperationException($"Unexpected value type {operation.Value?.GetType()}");
}
}
}

Expand Down

0 comments on commit 6fd51ce

Please sign in to comment.