feat: sync guard - Blockieren alter Clients beim sync

This commit is contained in:
2026-08-20 23:54:04 +02:00
parent 7b883555bf
commit 59bd8abb45
19 changed files with 369 additions and 26 deletions
@@ -34,6 +34,8 @@ public sealed class AttachmentSyncerTests
var request = Assert.Single(handler.Requests);
Assert.Equal(HttpMethod.Post, request.Method);
Assert.Equal($"/api/sync/attachments/{storageId}", request.RequestUri!.AbsolutePath);
Assert.Equal(SyncProtocol.CurrentVersion,
request.Headers.GetValues(SyncProtocol.VersionHeaderName).Single());
Assert.NotNull(uploadedEncrypted);
Assert.Equal([1, 2, 3, 4], SyncCrypto.Decrypt(uploadedEncrypted!, Key));
Assert.Empty(temp.Queue.GetPendingAttachmentUploads());
+27
View File
@@ -13,6 +13,33 @@ public sealed class SyncEngineTests
private static LiteDbContext NewInMemoryContext() => new(new MemoryStream());
private static readonly byte[] Key = SyncCrypto.GenerateKey();
[Fact]
public async Task SyncNowAsync_InkompatibleServerVersion_BrichtVorWeiterenRequestsAb()
{
using var temp = new TempEventQueue();
var pending = temp.Queue.Enqueue("this-device", DeviceType.Desktop,
"Lesson", Guid.NewGuid().ToString(), "Save", "lokale-daten");
var handler = new FakeHttpMessageHandler(req =>
{
var response = new HttpResponseMessage(HttpStatusCode.UpgradeRequired);
response.Headers.Add(SyncProtocol.VersionHeaderName, "2");
return response;
});
var engine = MakeEngine(temp, handler);
var result = await engine.SyncNowAsync();
Assert.False(result.Success);
Assert.Contains("Sync-Version 1", result.Reason);
Assert.Contains("Server Version 2", result.Reason);
Assert.Equal(SyncState.IncompatibleVersion, engine.Status.State);
Assert.Equal(pending.EventId, Assert.Single(temp.Queue.GetPending()).EventId);
var request = Assert.Single(handler.Requests);
Assert.Equal("/api/sync/push", request.RequestUri!.AbsolutePath);
Assert.Equal(SyncProtocol.CurrentVersion,
request.Headers.GetValues(SyncProtocol.VersionHeaderName).Single());
}
/// Regression: EventApplier.ApplyAsync fing früher nur LiteException ab. Jede andere Ausnahme
/// (z.B. eine ungültige/korrupte Payload eines einzelnen Ereignisses) fiel unbehandelt aus
/// SyncEngine.PullAsync heraus, BEVOR _queue.SetLastServerSeq() erreicht wurde — der nächste