using LehrerApp.Sync.Models; using Xunit; namespace LehrerApp.Api.Tests; public sealed class SnapshotStoreTests { [Fact] public void Store_ZweiterAufrufOhneCode_ErzeugtNeuenCode() { using var temp = new TempSnapshotStore(); var first = temp.Store.Store("user1", new SnapshotUploadRequest { EncryptedPayload = "p1" }); var second = temp.Store.Store("user1", new SnapshotUploadRequest { EncryptedPayload = "p2" }); Assert.NotEqual(first.Code, second.Code); } /// Regression: SnapshotService.CreateAndUploadAsync lädt in zwei Schritten hoch — Schritt 1 /// ohne EncryptedSyncKey (holt einen Code vom Server), Schritt 2 mit dem gerade erst mit /// diesem Code verschlüsselten Schlüssel. Ohne den zurückgereichten Code erzeugte Store() bei /// jedem Aufruf einen NEUEN Zufallscode — der an den Nutzer angezeigte Code (aus Schritt 2) /// passte dann nie zu dem Code, mit dem der Schlüssel tatsächlich verschlüsselt wurde, und das /// Einlösen auf dem zweiten Gerät schlug beim Schlüssel-Entschlüsseln immer fehl. [Fact] public void Store_ZweiterAufrufMitCodeDesErstenSchritts_AktualisiertDenselbenEintrag() { using var temp = new TempSnapshotStore(); var step1 = temp.Store.Store("user1", new SnapshotUploadRequest { EncryptedPayload = "payload" }); var step2 = temp.Store.Store("user1", new SnapshotUploadRequest { EncryptedPayload = "payload", EncryptedSyncKey = "encrypted-key", Code = step1.Code, }); Assert.Equal(step1.Code, step2.Code); var retrieved = temp.Store.Retrieve("user1", step2.Code); Assert.NotNull(retrieved); Assert.Equal("encrypted-key", retrieved!.EncryptedSyncKey); } [Fact] public void Store_CodeGehoertZuAnderemNutzer_ErzeugtStattdessenNeuenEintrag() { using var temp = new TempSnapshotStore(); var step1 = temp.Store.Store("user1", new SnapshotUploadRequest { EncryptedPayload = "payload" }); var result = temp.Store.Store("user2", new SnapshotUploadRequest { EncryptedPayload = "payload", EncryptedSyncKey = "encrypted-key", Code = step1.Code, }); Assert.NotEqual(step1.Code, result.Code); Assert.NotNull(temp.Store.Retrieve("user2", result.Code)); } [Fact] public void Retrieve_LoeschtDenEintragNachDemErstenAbruf() { using var temp = new TempSnapshotStore(); var uploaded = temp.Store.Store("user1", new SnapshotUploadRequest { EncryptedPayload = "payload" }); var first = temp.Store.Retrieve("user1", uploaded.Code); var second = temp.Store.Retrieve("user1", uploaded.Code); Assert.NotNull(first); Assert.Null(second); } private sealed class TempSnapshotStore : IDisposable { private readonly string _directory = Path.Combine( Path.GetTempPath(), $"lehrerapp-api-tests-{Guid.NewGuid():N}"); public LehrerApp.Api.SnapshotStore Store { get; } public TempSnapshotStore() { Directory.CreateDirectory(_directory); Store = new LehrerApp.Api.SnapshotStore(_directory); } public void Dispose() { Store.Dispose(); if (Directory.Exists(_directory)) Directory.Delete(_directory, recursive: true); } } }