Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
24 changes: 14 additions & 10 deletions backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@
? FromLcmPartOfSpeech(partOfSpeech) : null);
}

public async Task<PartOfSpeech> CreatePartOfSpeech(PartOfSpeech partOfSpeech)

Check warning on line 297 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.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 297 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.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 297 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.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 297 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.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 297 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.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 297 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.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.
{
IPartOfSpeech? lcmPartOfSpeech = null;
if (partOfSpeech.Id == default) partOfSpeech.Id = Guid.NewGuid();
Expand Down Expand Up @@ -496,7 +496,7 @@
return new ComplexFormType() { Id = t.Guid, Name = FromLcmMultiString(t.Name) };
}

public async Task<ComplexFormType> CreateComplexFormType(ComplexFormType complexFormType)

Check warning on line 499 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.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 499 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.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 499 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.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 499 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.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 499 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.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 499 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.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.
{
if (complexFormType.Id == default) complexFormType.Id = Guid.NewGuid();
UndoableUnitOfWorkHelper.DoUsingNewOrCurrentUOW("Create complex form type",
Expand Down Expand Up @@ -956,8 +956,9 @@
return Task.FromResult<Entry?>(FromLexEntry(EntriesRepository.GetObject(id)));
}

public async Task<Entry> CreateEntry(Entry entry)
public async Task<Entry> CreateEntry(Entry entry, CreateEntryOptions? options = null)
{
options ??= CreateEntryOptions.Everything;
entry.Id = entry.Id == default ? Guid.NewGuid() : entry.Id;
try
{
Expand All @@ -983,15 +984,18 @@
AddComplexFormType(lexEntry, complexFormType.Id);
}

foreach (var component in entry.Components)
if (options.IncludeComplexFormsAndComponents)
{
AddComplexFormComponent(lexEntry, component);
}

foreach (var complexForm in entry.ComplexForms)
{
var complexLexEntry = EntriesRepository.GetObject(complexForm.ComplexFormEntryId);
AddComplexFormComponent(complexLexEntry, complexForm);
foreach (var component in entry.Components)
{
AddComplexFormComponent(lexEntry, component);
}

foreach (var complexForm in entry.ComplexForms)
{
var complexLexEntry = EntriesRepository.GetObject(complexForm.ComplexFormEntryId);
AddComplexFormComponent(complexLexEntry, complexForm);
}
}
// Subtract entry.Publications from Publications to get the publications that the entry should not be published in
var doNotPublishIn = Publications.PossibilitiesOS.Where(p => entry.PublishIn.All(ep => ep.Id != p.Guid));
Expand Down Expand Up @@ -1287,7 +1291,7 @@
"Revert entry",
async () =>
{
await EntrySync.Sync(before, after, api ?? this);
await EntrySync.SyncFull(before, after, api ?? this);
});
return await GetEntry(after.Id) ?? throw new NullReferenceException("unable to find entry with id " + after.Id);
}
Expand Down Expand Up @@ -1425,7 +1429,7 @@
return Task.FromResult(lcmSense is null ? null : FromLexSense(lcmSense));
}

public async Task<Sense> CreateSense(Guid entryId, Sense sense, BetweenPosition? between = null)

Check warning on line 1432 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.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 1432 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.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 1432 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.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 1432 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.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 1432 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.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 1432 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.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.
{
if (sense.Id == default) sense.Id = Guid.NewGuid();
if (!EntriesRepository.TryGetObject(entryId, out var lexEntry))
Expand Down Expand Up @@ -1570,7 +1574,7 @@
return CmTranslationFactory.Create(parent, freeTranslationType);
}

public async Task<ExampleSentence> CreateExampleSentence(Guid entryId, Guid senseId, ExampleSentence exampleSentence, BetweenPosition? between = null)

Check warning on line 1577 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.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 1577 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.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 1577 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.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 1577 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.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 1577 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.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 1577 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.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.
{
if (exampleSentence.Id == default) exampleSentence.Id = Guid.NewGuid();
if (!SenseRepository.TryGetObject(senseId, out var lexSense))
Expand Down
213 changes: 205 additions & 8 deletions backend/FwLite/FwLiteProjectSync.Tests/EntrySyncTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using MiniLcm.SyncHelpers;
using MiniLcm.Tests.AutoFakerHelpers;
using Soenneker.Utils.AutoBogus;
using Soenneker.Utils.AutoBogus.Config;

namespace FwLiteProjectSync.Tests;

Expand All @@ -29,7 +28,7 @@ public async Task CanSyncRandomEntries()
..after.Senses
])];

await EntrySync.Sync(createdEntry, after, Api);
await EntrySync.SyncFull(createdEntry, after, Api);
var actual = await Api.GetEntry(after.Id);
actual.Should().NotBeNull();
actual.Should().BeEquivalentTo(after, options => options
Expand Down Expand Up @@ -92,7 +91,7 @@ public async Task CanChangeComplexFormViaSync_Components()
complexFormAfter.Components[0].ComponentEntryId = component2.Id;
complexFormAfter.Components[0].ComponentHeadword = component2.Headword();

await EntrySync.Sync(complexForm, complexFormAfter, Api);
await EntrySync.SyncFull(complexForm, complexFormAfter, Api);

var actual = await Api.GetEntry(complexFormAfter.Id);
actual.Should().NotBeNull();
Expand Down Expand Up @@ -125,7 +124,7 @@ public async Task CanChangeComplexFormViaSync_ComplexForms()
componentAter.ComplexForms[0].ComplexFormEntryId = complexForm2.Id;
componentAter.ComplexForms[0].ComplexFormHeadword = complexForm2.Headword();

await EntrySync.Sync(component, componentAter, Api);
await EntrySync.SyncFull(component, componentAter, Api);

var actual = await Api.GetEntry(componentAter.Id);
actual.Should().NotBeNull();
Expand All @@ -140,7 +139,7 @@ public async Task CanChangeComplexFormTypeViaSync()
var entry = await Api.CreateEntry(new() { LexemeForm = { { "en", "complexForm1" } } });
var after = entry.Copy();
after.ComplexFormTypes = [complexFormType];
await EntrySync.Sync(entry, after, Api);
await EntrySync.SyncFull(entry, after, Api);

var actual = await Api.GetEntry(after.Id);
actual.Should().NotBeNull();
Expand Down Expand Up @@ -172,14 +171,14 @@ public async Task CanInsertComplexFormComponentViaSync(bool componentThenComplex
// this results in 2 crdt changes:
// (1) add complex-form (i.e. implicitly add component)
// (2) move component to the right place
await EntrySync.Sync([newComponentBefore, complexFormBefore], [newComponentAfter, complexFormAfter], Api);
await EntrySync.SyncFull([newComponentBefore, complexFormBefore], [newComponentAfter, complexFormAfter], Api);
}
else
{
// this results in 1 crdt change:
// the component is added in the right place
// (adding the complex-form becomes a no-op, because it already exists and a BetweenPosition is not specified)
await EntrySync.Sync([complexFormBefore, newComponentBefore], [complexFormAfter, newComponentAfter], Api);
await EntrySync.SyncFull([complexFormBefore, newComponentBefore], [complexFormAfter, newComponentAfter], Api);
}

// assert
Expand Down Expand Up @@ -210,7 +209,7 @@ public async Task CanSyncNewEntryReferencedByExistingEntry()
newEntry.Components.Add(newComplexFormComponent);

// act
await EntrySync.Sync([existingEntryBefore], [existingEntryAfter, newEntry], Api);
await EntrySync.SyncFull([existingEntryBefore], [existingEntryAfter, newEntry], Api);

// assert
var actualExistingEntry = await Api.GetEntry(existingEntryAfter.Id);
Expand All @@ -224,4 +223,202 @@ public async Task CanSyncNewEntryReferencedByExistingEntry()
.For(e => e.Components).Exclude(c => c.Id)
.For(e => e.Components).Exclude(c => c.Order));
}

[Fact]
public async Task CanSyncNewComplexFormComponentReferencingNewSense()
{
// arrange
// - before
var complexFormEntryBefore = await Api.CreateEntry(new() { LexemeForm = { { "en", "complex-form" } } });
var componentEntryBefore = await Api.CreateEntry(new() { LexemeForm = { { "en", "component" } } });

// - after
var complexFormEntryAfter = complexFormEntryBefore.Copy();
var componentEntryAfter = componentEntryBefore.Copy();
var senseId = Guid.NewGuid();
componentEntryAfter.Senses = [new Sense() { Id = senseId, EntryId = componentEntryAfter.Id }];

var component = ComplexFormComponent.FromEntries(complexFormEntryAfter, componentEntryAfter, senseId);
complexFormEntryAfter.Components.Add(component);
componentEntryAfter.ComplexForms.Add(component);

// act
await EntrySync.SyncFull(
// note: the entry with the added sense is at the end of the list
[complexFormEntryBefore, componentEntryBefore],
[complexFormEntryAfter, componentEntryAfter],
Api);

// assert
var actualComplexFormEntry = await Api.GetEntry(complexFormEntryAfter.Id);
actualComplexFormEntry.Should().BeEquivalentTo(complexFormEntryAfter,
options => SyncTests.SyncExclusions(options)
.Excluding(e => e.ComplexFormTypes) // LibLcm automatically creates a complex form type. Should we?
.WithStrictOrdering());

var actualComponentEntry = await Api.GetEntry(componentEntryAfter.Id);
actualComponentEntry.Should().BeEquivalentTo(componentEntryAfter,
options => SyncTests.SyncExclusions(options).WithStrictOrdering());
}

[Fact]
public async Task SyncWithoutComplexFormsAndComponents_CorrectlySyncsUpdatedEntries()
{
// Arrange
// - before
var componentBefore = await Api.CreateEntry(new() { LexemeForm = { { "en", "component" } } });

// - after
var componentAfter = componentBefore.Copy();
componentAfter.LexemeForm["en"] = "component updated";
var complexForm = new Entry()
{
Id = Guid.NewGuid(),
LexemeForm = { { "en", "complex form" } }
};
var complexFormComponent = ComplexFormComponent.FromEntries(complexForm, componentAfter);
componentAfter.ComplexForms.Add(complexFormComponent);
complexForm.Components.Add(complexFormComponent);

// act
var (changes, added) = await EntrySync.SyncWithoutComplexFormsAndComponents([componentBefore], [componentAfter, complexForm], Api);
added.Should().HaveCount(1);
var addedComplexForm = added.First();

// assert
var actualComponent = await Api.GetEntry(componentAfter.Id);
actualComponent.Should().BeEquivalentTo(componentAfter,
options => options.Excluding(e => e.ComplexForms));
actualComponent.ComplexForms.Should().BeEmpty();

var actualComplexForm = await Api.GetEntry(complexForm.Id);
addedComplexForm.Should().BeEquivalentTo(actualComplexForm);
actualComplexForm.Should().BeEquivalentTo(complexForm,
options => options.Excluding(e => e.Components));
actualComplexForm.Components.Should().BeEmpty();
}

[Fact]
public async Task SyncWithoutComplexFormsAndComponents_CorrectlySyncsAddedEntries()
{
// Arrange
// - after
var component = new Entry()
{
Id = Guid.NewGuid(),
LexemeForm = { { "en", "component" } }
};
var complexForm = new Entry()
{
Id = Guid.NewGuid(),
LexemeForm = { { "en", "complex form" } }
};
var complexFormComponent = ComplexFormComponent.FromEntries(complexForm, component);
component.ComplexForms.Add(complexFormComponent);
complexForm.Components.Add(complexFormComponent);

// act
var (_, added) = await EntrySync.SyncWithoutComplexFormsAndComponents([], [component, complexForm], Api);
added.Should().HaveCount(2);
var addedComponent = added.ElementAt(0);
var addedComplexForm = added.ElementAt(1);

// assert
var actualComponent = await Api.GetEntry(component.Id);
addedComponent.Should().BeEquivalentTo(actualComponent);
actualComponent.Should().BeEquivalentTo(component,
options => options.Excluding(e => e.ComplexForms));
actualComponent.ComplexForms.Should().BeEmpty();

var actualComplexForm = await Api.GetEntry(complexForm.Id);
addedComplexForm.Should().BeEquivalentTo(actualComplexForm);
actualComplexForm.Should().BeEquivalentTo(complexForm,
options => options.Excluding(e => e.Components));
actualComplexForm.Components.Should().BeEmpty();
}

[Fact]
public async Task SyncComplexFormsAndComponents_CorrectlySyncsUpdatedEntries()
{
// Arrange
// - before
var componentBefore = await Api.CreateEntry(new() { LexemeForm = { { "en", "component" } } });
var complexFormBefore = await Api.CreateEntry(new() { LexemeForm = { { "en", "complex form" } } });

// - after
var componentAfter = componentBefore.Copy();
componentAfter.LexemeForm["en"] = "component updated";
var complexFormAfter = complexFormBefore.Copy();
complexFormAfter.LexemeForm["en"] = "complex form updated";
var complexFormComponent = ComplexFormComponent.FromEntries(complexFormAfter, componentAfter);
componentAfter.ComplexForms.Add(complexFormComponent);
complexFormAfter.Components.Add(complexFormComponent);

// act
await EntrySync.SyncComplexFormsAndComponents([componentBefore, complexFormBefore], [componentAfter, complexFormAfter], Api);

// assert
var actualComponent = await Api.GetEntry(componentAfter.Id);
actualComponent.Should().NotBeNull();

// complex forms were synced
actualComponent.ComplexForms.Should().NotBeEmpty();
actualComponent.ComplexForms.Should().BeEquivalentTo(componentAfter.ComplexForms, options
=> options.Excluding(c => c.Id)
.Excluding(c => c.Order)
// The lexeme-form/headword wasn't synced so it doesn't match the "after" version
.Excluding(c => c.ComplexFormHeadword)
.Excluding(c => c.ComponentHeadword));

var actualComplexForm = await Api.GetEntry(complexFormAfter.Id);
actualComplexForm.Should().NotBeNull();
// components were synced
actualComplexForm.Components.Should().NotBeEmpty();
actualComplexForm.Components.Should().BeEquivalentTo(complexFormAfter.Components, options
=> options.Excluding(c => c.Id)
.Excluding(c => c.Order)
// The lexeme-form/headword wasn't synced so it doesn't match the "after" version
.Excluding(c => c.ComplexFormHeadword)
.Excluding(c => c.ComponentHeadword));

// Lexeme form was not synced
actualComponent.LexemeForm.Should().BeEquivalentTo(componentBefore.LexemeForm);
actualComponent.LexemeForm.Should().NotBeEquivalentTo(componentAfter.LexemeForm);
actualComplexForm.LexemeForm.Should().BeEquivalentTo(complexFormBefore.LexemeForm);
actualComplexForm.LexemeForm.Should().NotBeEquivalentTo(complexFormAfter.LexemeForm);
}

[Fact]
public async Task SyncComplexFormsAndComponents_ThrowsExceptionIfEntryNotInBefore()
{
// Arrange
var component = new Entry() { Id = Guid.NewGuid() };
var complexForm = new Entry() { Id = Guid.NewGuid() };
var complexFormComponent = ComplexFormComponent.FromEntries(complexForm, component);
component.ComplexForms.Add(complexFormComponent);
complexForm.Components.Add(complexFormComponent);

// Act
var act = () => EntrySync.SyncComplexFormsAndComponents([], [component, complexForm], Api);

// Assert
await act.Should().ThrowAsync<InvalidOperationException>();
}

[Fact]
public async Task SyncComplexFormsAndComponents_ThrowsExceptionIfEntryNotInAfter()
{
// Arrange
var component = new Entry() { Id = Guid.NewGuid() };
var complexForm = new Entry() { Id = Guid.NewGuid() };
var complexFormComponent = ComplexFormComponent.FromEntries(complexForm, component);
component.ComplexForms.Add(complexFormComponent);
complexForm.Components.Add(complexFormComponent);

// Act
var act = () => EntrySync.SyncComplexFormsAndComponents([component, complexForm], [], Api);

// Assert
await act.Should().ThrowAsync<InvalidOperationException>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,10 @@ Task<PartOfSpeech> IMiniLcmWriteApi.CreatePartOfSpeech(PartOfSpeech partOfSpeech
return _api.CreatePartOfSpeech(partOfSpeech);
}

Task<Entry> IMiniLcmWriteApi.CreateEntry(Entry entry)
Task<Entry> IMiniLcmWriteApi.CreateEntry(Entry entry, CreateEntryOptions? options)
{
ResumableTests.MaybeThrowRandom(random, 0.2);
return _api.CreateEntry(entry);
return _api.CreateEntry(entry, options);
}

async Task IMiniLcmWriteApi.BulkCreateEntries(IAsyncEnumerable<Entry> entries)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,10 @@ private async Task<SyncResult> Sync(IMiniLcmApi crdtApi, IMiniLcmApi fwdataApi,
fwdataChanges += await ComplexFormTypeSync.Sync(currentFwDataComplexFormTypes, await crdtApi.GetComplexFormTypes().ToArrayAsync(), fwdataApi);

var currentFwDataEntries = await fwdataApi.GetAllEntries().ToArrayAsync();
crdtChanges += await EntrySync.Sync(projectSnapshot.Entries, currentFwDataEntries, crdtApi);
crdtChanges += await EntrySync.SyncFull(projectSnapshot.Entries, currentFwDataEntries, crdtApi);
LogDryRun(crdtApi, "crdt");

fwdataChanges += await EntrySync.Sync(currentFwDataEntries, await crdtApi.GetAllEntries().ToArrayAsync(), fwdataApi);
fwdataChanges += await EntrySync.SyncFull(currentFwDataEntries, await crdtApi.GetAllEntries().ToArrayAsync(), fwdataApi);
LogDryRun(fwdataApi, "fwdata");

//todo push crdt changes to lexbox
Expand Down
13 changes: 9 additions & 4 deletions backend/FwLite/FwLiteProjectSync/DryRunMiniLcmApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,15 @@ public Task DeleteMorphTypeData(Guid id)
return Task.CompletedTask;
}

public Task<Entry> CreateEntry(Entry entry)
{
DryRunRecords.Add(new DryRunRecord(nameof(CreateEntry), $"Create entry {entry.Headword()}"));
return Task.FromResult(entry);
public Task<Entry> CreateEntry(Entry entry, CreateEntryOptions? options)
{
options ??= new CreateEntryOptions();
DryRunRecords.Add(new DryRunRecord(nameof(CreateEntry), $"Create entry {entry.Headword()} ({options})"));
// Only return what would have been persisted
if (options.IncludeComplexFormsAndComponents)
return Task.FromResult(entry);
else
return Task.FromResult(entry with { Components = [], ComplexForms = [] });
}

public Task<Entry> UpdateEntry(Guid id, UpdateObjectInput<Entry> update)
Expand Down
4 changes: 2 additions & 2 deletions backend/FwLite/FwLiteProjectSync/Import/ResumableImportApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@

// ********** Overrides go here **********

async Task<Entry> IMiniLcmWriteApi.CreateEntry(Entry entry)
async Task<Entry> IMiniLcmWriteApi.CreateEntry(Entry entry, CreateEntryOptions? options)
{
return await HasCreated(entry, _api.GetAllEntries(), () => _api.CreateEntry(entry));
return await HasCreated(entry, _api.GetAllEntries(), () => _api.CreateEntry(entry, options));
}

async Task<PartOfSpeech> IMiniLcmWriteApi.CreatePartOfSpeech(PartOfSpeech partOfSpeech)
Expand All @@ -66,7 +66,7 @@
{
return await HasCreated(publication, _api.GetPublications(), () => _api.CreatePublication(publication));
}
async Task<WritingSystem> IMiniLcmWriteApi.CreateWritingSystem(WritingSystem writingSystem, BetweenPosition<WritingSystemId?>? between = null)

Check warning on line 69 in backend/FwLite/FwLiteProjectSync/Import/ResumableImportApi.cs

View workflow job for this annotation

GitHub Actions / Build FwHeadless / publish-fw-headless

The default value specified for parameter 'between' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments

Check warning on line 69 in backend/FwLite/FwLiteProjectSync/Import/ResumableImportApi.cs

View workflow job for this annotation

GitHub Actions / Build FW Lite and run tests

The default value specified for parameter 'between' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments

Check warning on line 69 in backend/FwLite/FwLiteProjectSync/Import/ResumableImportApi.cs

View workflow job for this annotation

GitHub Actions / Publish FW Lite app for Mac

The default value specified for parameter 'between' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments

Check warning on line 69 in backend/FwLite/FwLiteProjectSync/Import/ResumableImportApi.cs

View workflow job for this annotation

GitHub Actions / Publish FW Lite app for Mac

The default value specified for parameter 'between' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments

Check warning on line 69 in backend/FwLite/FwLiteProjectSync/Import/ResumableImportApi.cs

View workflow job for this annotation

GitHub Actions / Publish FW Lite app for Linux

The default value specified for parameter 'between' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments

Check warning on line 69 in backend/FwLite/FwLiteProjectSync/Import/ResumableImportApi.cs

View workflow job for this annotation

GitHub Actions / Publish FW Lite app for Windows

The default value specified for parameter 'between' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments
{
return await HasCreated(writingSystem, AsyncWs(), () => _api.CreateWritingSystem(writingSystem, between), ws => ws.Type + ws.WsId.Code);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,10 @@ public void NotifyEntryDeleted(Guid entryId)

// ********** Overrides go here **********

async Task<Entry> IMiniLcmWriteApi.CreateEntry(Entry entry)
async Task<Entry> IMiniLcmWriteApi.CreateEntry(Entry entry, CreateEntryOptions? options)
{
await using var _ = BeginTrackingChanges();
var result = await _api.CreateEntry(entry);
var result = await _api.CreateEntry(entry, options);
NotifyEntryChanged(result);
return result;
}
Expand Down
2 changes: 1 addition & 1 deletion backend/FwLite/LcmCrdt.Tests/Changes/UseChangesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public async Task CanSyncAllChangesWithDuplicates()
await fixture.DataModel.AddChange(Guid.NewGuid(), duplicateChange);

var allEntries = await fixture.Api.GetEntries().ToArrayAsync();
var result = await EntrySync.Sync(allEntries, allEntries, fixture.Api);
var result = await EntrySync.SyncFull(allEntries, allEntries, fixture.Api);
result.Should().Be(0);

committedChanges.Add(change);
Expand Down
Loading
Loading