PrivaPub remembers a deleted remote post's id for 90 days so that a late Create cannot bring it back. Gancio numbers a new event after the last one it keeps, so deleting its last event gives the next the same id, and that event was dropped as deleted. A Create published after the deletion is now kept as the new post; the deleted one's own Create, however late, still is not. Checked live: Gancio's scenario passes again (17 checks). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
65 lines
2.5 KiB
C#
65 lines
2.5 KiB
C#
using MongoDB.Entities;
|
|
|
|
using PrivaPub.Models.Federation;
|
|
using PrivaPub.Models.Post;
|
|
using PrivaPub.Models.Social;
|
|
using PrivaPub.Tests.Support;
|
|
|
|
using System.Text.Json.Nodes;
|
|
|
|
using static PrivaPub.Tests.Support.FederatedSeeds;
|
|
|
|
namespace PrivaPub.Tests.Federation
|
|
{
|
|
// A deleted remote post is never brought back by its own Create, however late it comes; an id its server gives a new
|
|
// post afterwards (Gancio numbers an event after the last one it keeps) is that new post
|
|
[Trait("Category", "Integration")]
|
|
public sealed class ReusedIdTests : IAsyncLifetime
|
|
{
|
|
Harness _harness;
|
|
|
|
public async ValueTask InitializeAsync()
|
|
{
|
|
Assert.SkipUnless(MongoFixture.Enabled, MongoFixture.Skip);
|
|
_harness = await Harness.Start();
|
|
}
|
|
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
if (_harness != default)
|
|
await _harness.DisposeAsync();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task A_deleted_posts_create_stays_dropped_and_a_new_post_under_its_id_is_kept()
|
|
{
|
|
var token = TestContext.Current.CancellationToken;
|
|
var (_, alice) = await _harness.Persona("alice");
|
|
var carol = new RemoteActor(_harness.Peer, "carol");
|
|
await Follows(alice.Id, carol);
|
|
var first = PublicNote(carol, "<p>the first event</p>");
|
|
first["published"] = DateTime.UtcNow.AddMinutes(-10).ToString("O");
|
|
await _harness.Deliver(carol, "/human-centipede", Create(carol, first));
|
|
await _harness.Deliver(carol, "/human-centipede", new JsonObject
|
|
{
|
|
["id"] = NewId(carol, "deletes"), ["type"] = "Delete", ["actor"] = carol.Id,
|
|
["object"] = new JsonObject { ["id"] = IdOf(first), ["type"] = "Tombstone" }
|
|
});
|
|
Assert.True(await DB.Default.Find<DeletedObject>().Match(d => d.ObjectURI == IdOf(first)).ExecuteAnyAsync(token));
|
|
|
|
// the first Create again, late
|
|
await _harness.Deliver(carol, "/human-centipede", Create(carol, (JsonObject)first.DeepClone()));
|
|
Assert.False(await DB.Default.Find<Post>().Match(p => p.ObjectURI == IdOf(first) && !p.DeletedAt.HasValue).ExecuteAnyAsync(token));
|
|
|
|
var second = PublicNote(carol, "<p>a new event, numbered as the first</p>");
|
|
second["id"] = IdOf(first);
|
|
second["published"] = DateTime.UtcNow.ToString("O");
|
|
await _harness.Deliver(carol, "/human-centipede", Create(carol, second));
|
|
|
|
var kept = await DB.Default.Find<Post>().Match(p => p.ObjectURI == IdOf(first) && !p.DeletedAt.HasValue).ExecuteFirstAsync(token);
|
|
Assert.Contains("a new event", kept?.ContentHtml);
|
|
Assert.False(await DB.Default.Find<DeletedObject>().Match(d => d.ObjectURI == IdOf(first)).ExecuteAnyAsync(token));
|
|
}
|
|
}
|
|
}
|