Public Access
Sync: take the server having no such item as a delete done, not a rejection nobody can clear
This commit is contained in:
1 parent
b2516ca7fb
commit
ff8a790f6c
2 files changed
+75
-1
No files matched your search
@@ -147,7 +147,8 @@ public sealed class UnpushedDeleteTests
|
||||
// The ambiguous case, and the reason the attempt count is part of the test rather than just the
|
||||
// expected version. A create that was dispatched may have been applied by a server whose answer never
|
||||
// arrived. Dropping it locally would strand a row nobody can see and nobody can delete; a tombstone
|
||||
// that turns out to be unnecessary is refused once and can be dealt with.
|
||||
// that turns out to be unnecessary is refused once, and the refusal is taken as the delete being done —
|
||||
// see the two tests after this one.
|
||||
using var harness = await CreateAsync();
|
||||
var laptop = harness.First;
|
||||
|
||||
@@ -166,6 +167,64 @@ public sealed class UnpushedDeleteTests
|
||||
tombstone.Operation.ShouldBe(SyncOperation.Delete);
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// The rest of that story, when the create never did land. The tombstone carries no expected version, and
|
||||
/// the server refuses it because it has no such item — which is what the delete wanted. It used to be
|
||||
/// parked as a rejection the user could neither resolve nor dismiss, with a pending count stuck above zero.
|
||||
/// </remarks>
|
||||
[Fact]
|
||||
public async Task ATombstoneForACreateThatNeverLanded_IsDoneWhenTheServerSaysItHasNoSuchItem()
|
||||
{
|
||||
using var harness = await CreateAsync();
|
||||
var laptop = harness.First;
|
||||
|
||||
var entityId = await laptop.CreateAsync(Host("in-flight"));
|
||||
var queued = (await laptop.Outbox.ListAllAsync(VaultId, Token)).ShouldHaveSingleItem();
|
||||
await laptop.Outbox.MarkDispatchedAsync(queued.Sequence, Token);
|
||||
|
||||
await laptop.DeleteAsync(entityId);
|
||||
|
||||
var report = await laptop.SyncAsync();
|
||||
|
||||
report.Parked.ShouldBe(0, "the server not having the item is the delete succeeding");
|
||||
(await laptop.Outbox.ListAllAsync(VaultId, Token)).ShouldBeEmpty();
|
||||
harness.Server.RowCount.ShouldBe(0);
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// And when it did land, the shortcut above must not be what answers. The pull brings the item back first,
|
||||
/// at a version the tombstone never knew about, and the engine's ordinary rule for that applies: the delete
|
||||
/// gives way, the item stays, and the user is told — nothing parked, nothing lost, nothing stuck in the
|
||||
/// outbox. Deleting it again is then an ordinary delete of an item the server holds.
|
||||
/// </remarks>
|
||||
[Fact]
|
||||
public async Task ATombstoneForACreateThatDidLand_GivesWayToIt_RatherThanBeingTakenAsDone()
|
||||
{
|
||||
using var harness = await CreateAsync();
|
||||
var laptop = harness.First;
|
||||
|
||||
var entityId = await laptop.CreateAsync(Host("in-flight"));
|
||||
|
||||
// The create reaches the server with its answer lost: the server holds the item while the laptop's own
|
||||
// row still says "sent, unanswered".
|
||||
var queued = (await laptop.Outbox.ListAllAsync(VaultId, Token)).ShouldHaveSingleItem();
|
||||
await laptop.Outbox.MarkDispatchedAsync(queued.Sequence, Token);
|
||||
harness.Server.ExternalUpsert(entityId, queued.Payload.ShouldNotBeNull(), queued.Fields);
|
||||
|
||||
await laptop.DeleteAsync(entityId);
|
||||
var report = await laptop.SyncAsync();
|
||||
|
||||
report.DeletesAbandoned.ShouldBe(1, "the user is told the item came back");
|
||||
report.Parked.ShouldBe(0);
|
||||
(await laptop.Outbox.ListAllAsync(VaultId, Token)).ShouldBeEmpty();
|
||||
harness.Server.RowCount.ShouldBe(1);
|
||||
|
||||
await laptop.DeleteAsync(entityId);
|
||||
await laptop.SyncAsync();
|
||||
|
||||
harness.Server.RowCount.ShouldBe(0, "deleting it again reaches the server");
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// The path most likely to meet this in practice, which is why it gets its own test rather than trusting
|
||||
/// the generic repository: trust is pinned by connecting and withdrawn from the host editor, so trusting a
|
||||
|
||||
Reference in new issue
Block a user