Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chore/fix serilization error add entry component change #1310

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions backend/FwLite/LcmCrdt.Tests/Changes/ChangeSerializationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using System.Text.Json;
using FluentAssertions.Execution;
using LcmCrdt.Changes;
using LcmCrdt.Changes.Entries;
using SIL.Harmony.Changes;
using SystemTextJsonPatch;

namespace LcmCrdt.Tests.Changes;

public class ChangeSerializationTests
{
public static IEnumerable<object[]> Changes()
{
yield return
[
new AddEntryComponentChange(ComplexFormComponent.FromEntries(
new Entry() { Id = Guid.NewGuid() },
new Entry() { Id = Guid.NewGuid() }))
];
yield return
[
new CreateEntryChange(new Entry() { Id = Guid.NewGuid() })
];
yield return [
new CreateSenseChange(new Sense() { Id = Guid.NewGuid() }, Guid.NewGuid())
];
yield return [
new CreateExampleSentenceChange(new ExampleSentence() { Id = Guid.NewGuid() }, Guid.NewGuid())
];
yield return
[
new CreatePartOfSpeechChange(Guid.NewGuid(), new MultiString())
];
yield return
[
new CreateSemanticDomainChange(Guid.NewGuid(), new MultiString(), "1")
];
yield return
[
new CreateWritingSystemChange(new WritingSystem()
{
Id = Guid.NewGuid(),
WsId = "en",
Name = "en",
Abbreviation = "en",
Font = "Arial",
Exemplars = ["en"],
Type = WritingSystemType.Analysis,
Order = 1,
}, WritingSystemType.Analysis, Guid.NewGuid(), 1)
];
yield return [
new SetPartOfSpeechChange(Guid.NewGuid(), Guid.NewGuid())
];
yield return [new AddSemanticDomainChange(new SemanticDomain() { Id = Guid.NewGuid() }, Guid.NewGuid())];
yield return [new RemoveSemanticDomainChange(Guid.NewGuid(), Guid.NewGuid())];
yield return [new ReplaceSemanticDomainChange(Guid.NewGuid(), new SemanticDomain() { Id = Guid.NewGuid() }, Guid.NewGuid())];
yield return [new RemoveComplexFormTypeChange(Guid.NewGuid(), Guid.NewGuid())];
yield return [new CreateComplexFormType(Guid.NewGuid(), new MultiString())];
yield return [new AddComplexFormTypeChange(Guid.NewGuid(), new() { Id = Guid.NewGuid(), Name = new()})];
yield return [SetComplexFormComponentChange.NewComplexForm(Guid.NewGuid(), Guid.NewGuid())];
yield return [SetComplexFormComponentChange.NewComponent(Guid.NewGuid(), Guid.NewGuid())];
yield return [SetComplexFormComponentChange.NewComponentSense(Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid())];

yield return [new JsonPatchChange<Entry>(Guid.NewGuid(), new JsonPatchDocument<Entry>())];
yield return [new JsonPatchChange<Sense>(Guid.NewGuid(), new JsonPatchDocument<Sense>())];
yield return [new JsonPatchChange<ExampleSentence>(Guid.NewGuid(), new JsonPatchDocument<ExampleSentence>())];
yield return [new JsonPatchChange<WritingSystem>(Guid.NewGuid(), new JsonPatchDocument<WritingSystem>())];
yield return [new JsonPatchChange<PartOfSpeech>(Guid.NewGuid(), new JsonPatchDocument<PartOfSpeech>())];
yield return [new JsonPatchChange<SemanticDomain>(Guid.NewGuid(), new JsonPatchDocument<SemanticDomain>())];
yield return [new JsonPatchChange<ComplexFormType>(Guid.NewGuid(), new JsonPatchDocument<ComplexFormType>())];
yield return [new DeleteChange<Entry>(Guid.NewGuid())];
yield return [new DeleteChange<Sense>(Guid.NewGuid())];
yield return [new DeleteChange<ExampleSentence>(Guid.NewGuid())];
yield return [new DeleteChange<WritingSystem>(Guid.NewGuid())];
yield return [new DeleteChange<PartOfSpeech>(Guid.NewGuid())];
yield return [new DeleteChange<SemanticDomain>(Guid.NewGuid())];
yield return [new DeleteChange<ComplexFormType>(Guid.NewGuid())];
yield return [new DeleteChange<ComplexFormComponent>(Guid.NewGuid())];
}

[Theory]
[MemberData(nameof(Changes))]
public void CanRoundTripChanges(IChange change)
{
var config = new CrdtConfig();
LcmCrdtKernel.ConfigureCrdt(config);
var type = change.GetType();
var json = JsonSerializer.Serialize(change, config.JsonSerializerOptions);
var newChange = JsonSerializer.Deserialize(json, type, config.JsonSerializerOptions);
newChange.Should().BeEquivalentTo(change);
}

[Fact]
public void ChangesIncludesAllValidChangeTypes()
{
var allChangeTypes = LcmCrdtKernel.AllChangeTypes();
allChangeTypes.Should().NotBeEmpty();
var testedTypes = Changes().Select(c => c[0].GetType()).ToArray();
using (new AssertionScope())
{
foreach (var allChangeType in allChangeTypes)
{
testedTypes.Should().Contain(allChangeType);
hahn-kev marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}
2 changes: 2 additions & 0 deletions backend/FwLite/LcmCrdt.Tests/MiniLcmApiFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using MiniLcm;
using MiniLcm.Models;
using Xunit.Abstractions;
Expand All @@ -17,6 +18,7 @@ public class MiniLcmApiFixture : IAsyncLifetime
private LcmCrdtDbContext? _crdtDbContext;
public CrdtMiniLcmApi Api => (CrdtMiniLcmApi)_services.ServiceProvider.GetRequiredService<IMiniLcmApi>();
public DataModel DataModel => _services.ServiceProvider.GetRequiredService<DataModel>();
public CrdtConfig CrdtConfig => _services.ServiceProvider.GetRequiredService<IOptions<CrdtConfig>>().Value;

public MiniLcmApiFixture()
{
Expand Down
4 changes: 2 additions & 2 deletions backend/FwLite/LcmCrdt/Changes/AddSemanticDomainChange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

namespace LcmCrdt.Changes;

public class AddSemanticDomainChange(SemanticDomain semanticDomain, Guid senseId)
: EditChange<Sense>(senseId), ISelfNamedType<AddSemanticDomainChange>
public class AddSemanticDomainChange(SemanticDomain semanticDomain, Guid entityId)
: EditChange<Sense>(entityId), ISelfNamedType<AddSemanticDomainChange>
{
public SemanticDomain SemanticDomain { get; } = semanticDomain;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ public class AddEntryComponentChange : CreateChange<ComplexFormComponent>, ISelf
[JsonConstructor]
public AddEntryComponentChange(Guid entityId,
Guid complexFormEntryId,
string? complexFormHeadword,
Guid componentEntryId,
string? componentHeadword,
Guid? componentSenseId = null) : base(entityId)
{
ComplexFormEntryId = complexFormEntryId;
Expand All @@ -28,9 +26,7 @@ public AddEntryComponentChange(Guid entityId,

public AddEntryComponentChange(ComplexFormComponent component) : this(component.Id == default ? Guid.NewGuid() : component.Id,
component.ComplexFormEntryId,
component.ComplexFormHeadword,
component.ComponentEntryId,
component.ComponentHeadword,
component.ComponentSenseId)
{
}
Expand Down
4 changes: 2 additions & 2 deletions backend/FwLite/LcmCrdt/Changes/RemoveSemanticDomainChange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@

namespace LcmCrdt.Changes;

public class RemoveSemanticDomainChange(Guid semanticDomainId, Guid senseId)
: EditChange<Sense>(senseId), ISelfNamedType<RemoveSemanticDomainChange>
public class RemoveSemanticDomainChange(Guid semanticDomainId, Guid entityId)
: EditChange<Sense>(entityId), ISelfNamedType<RemoveSemanticDomainChange>
{
public Guid SemanticDomainId { get; } = semanticDomainId;

public override async ValueTask ApplyChange(Sense entity, ChangeContext context)

Check warning on line 11 in backend/FwLite/LcmCrdt/Changes/RemoveSemanticDomainChange.cs

View workflow job for this annotation

GitHub Actions / Build FwHeadless / publish-fw-headless

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 11 in backend/FwLite/LcmCrdt/Changes/RemoveSemanticDomainChange.cs

View workflow job for this annotation

GitHub Actions / Build FW Lite and run tests

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 11 in backend/FwLite/LcmCrdt/Changes/RemoveSemanticDomainChange.cs

View workflow job for this annotation

GitHub Actions / Publish FW Lite app for Linux

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 11 in backend/FwLite/LcmCrdt/Changes/RemoveSemanticDomainChange.cs

View workflow job for this annotation

GitHub Actions / Publish FW Lite app for Linux

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 11 in backend/FwLite/LcmCrdt/Changes/RemoveSemanticDomainChange.cs

View workflow job for this annotation

GitHub Actions / Publish FW Lite app for Mac

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 11 in backend/FwLite/LcmCrdt/Changes/RemoveSemanticDomainChange.cs

View workflow job for this annotation

GitHub Actions / Publish FW Lite app for Mac

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 11 in backend/FwLite/LcmCrdt/Changes/RemoveSemanticDomainChange.cs

View workflow job for this annotation

GitHub Actions / Publish FW Lite app for Windows

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 11 in backend/FwLite/LcmCrdt/Changes/RemoveSemanticDomainChange.cs

View workflow job for this annotation

GitHub Actions / Publish FW Lite app for Windows

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
{
entity.SemanticDomains = [..entity.SemanticDomains.Where(s => s.Id != SemanticDomainId)];
}
Expand Down
4 changes: 2 additions & 2 deletions backend/FwLite/LcmCrdt/Changes/ReplaceSemanticDomainChange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

namespace LcmCrdt.Changes;

public class ReplaceSemanticDomainChange(Guid oldSemanticDomainId, SemanticDomain semanticDomain, Guid senseId)
: EditChange<Sense>(senseId), ISelfNamedType<ReplaceSemanticDomainChange>
public class ReplaceSemanticDomainChange(Guid oldSemanticDomainId, SemanticDomain semanticDomain, Guid entityId)
: EditChange<Sense>(entityId), ISelfNamedType<ReplaceSemanticDomainChange>
{
public Guid OldSemanticDomainId { get; } = oldSemanticDomainId;
public SemanticDomain SemanticDomain { get; } = semanticDomain;
Expand Down
14 changes: 14 additions & 0 deletions backend/FwLite/LcmCrdt/LcmCrdtKernel.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Linq.Expressions;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
using SIL.Harmony;
using SIL.Harmony.Core;
using SIL.Harmony.Changes;
Expand Down Expand Up @@ -191,6 +193,18 @@ public static void ConfigureCrdt(CrdtConfig config)
.Add<CreateComplexFormType>();
}

public static Type[] AllChangeTypes()
{
var crdtConfig = new CrdtConfig();
ConfigureCrdt(crdtConfig);


var list = typeof(ChangeTypeListBuilder).GetProperty("Types", BindingFlags.Instance | BindingFlags.NonPublic)
?.GetValue(crdtConfig.ChangeTypeListBuilder) as List<JsonDerivedType>;
return list?.Select(t => t.DerivedType).ToArray() ?? [];
}


public static Task<IMiniLcmApi> OpenCrdtProject(this IServiceProvider services, CrdtProject project)
{
//this method must not be async, otherwise Setting the project scope will not work as expected.
Expand Down
Loading