80 lines
4.2 KiB
C#
80 lines
4.2 KiB
C#
using NUnit.Framework;
|
|
using ShrinkDataSaver;
|
|
using ShrinkSDK.Inspection;
|
|
using ShrinkNetwork;
|
|
using Cysharp.Threading.Tasks;
|
|
|
|
[NonParallelizable]
|
|
public class SaveSnapshotTests
|
|
{
|
|
private string _directory = null!;
|
|
[SetUp] public void Setup()
|
|
{
|
|
_directory = Path.Combine(Path.GetTempPath(), "ShrinkSaveTests", Guid.NewGuid().ToString("N"));
|
|
ShrinkSave.Initialize(new LocalStorageProvider(_directory), "saves", ".json", 1);
|
|
}
|
|
[TearDown] public void Cleanup()
|
|
{
|
|
ShrinkSave.UnregisterModule("test-snapshot");
|
|
if (Directory.Exists(_directory)) Directory.Delete(_directory, true);
|
|
}
|
|
|
|
[TestCase(false)] [TestCase(true)]
|
|
public async Task CaptureRunsOnCallerAndWorkerEncodingPreservesFormat(bool encrypt)
|
|
{
|
|
var owner = Environment.CurrentManagedThreadId;
|
|
var captured = 0;
|
|
var value = new SaveDto { Value = "original" };
|
|
ShrinkSave.RegisterModule("test-snapshot", () => { captured = Environment.CurrentManagedThreadId; return value; }, _ => { });
|
|
var options = new SaveOptions { EncodeInBackground = true, CollectTimings = true, Encrypt = encrypt, EncryptionKey = "test-only" };
|
|
var saving = ShrinkSave.SaveSlotAsync(0, options).AsTask();
|
|
value.Value = "changed-after-capture";
|
|
await saving;
|
|
var file = Directory.GetFiles(Path.Combine(_directory, "saves"), "*.json").Single();
|
|
var packet = DataSerializer.Deserialize<SavePacket>(await File.ReadAllBytesAsync(file));
|
|
var text = encrypt ? System.Text.Encoding.UTF8.GetString(SaveEncryptor.Decrypt(Convert.FromBase64String(packet.EncryptedModules), "test-only")) : packet.Modules["test-snapshot"].ToString();
|
|
Assert.That(captured, Is.EqualTo(owner));
|
|
Assert.That(text, Does.Contain("original").And.Not.Contain("changed-after-capture"));
|
|
Assert.That(options.LastTimings.EncodedBytes, Is.GreaterThan(0));
|
|
Assert.That(options.LastTimings.CaptureMilliseconds, Is.GreaterThanOrEqualTo(0));
|
|
}
|
|
|
|
[Test]
|
|
public async Task CancellationLeavesExistingFileAndBackupsRecoverCorruption()
|
|
{
|
|
var storage = new LocalStorageProvider(_directory);
|
|
await storage.WriteAsync("slot.json", new byte[] { 1 });
|
|
await storage.WriteAsync("slot.json", new byte[] { 2 });
|
|
using var cancellation = new CancellationTokenSource(); cancellation.Cancel();
|
|
Assert.CatchAsync<OperationCanceledException>(async () => await storage.WriteAsync("slot.json", new byte[] { 3 }, cancellation.Token));
|
|
Assert.That(await storage.ReadAsync("slot.json"), Is.EqualTo(new byte[] { 2 }));
|
|
Assert.That(await storage.ReadAsync("slot.json.bak1"), Is.EqualTo(new byte[] { 1 }));
|
|
Assert.That(File.Exists(Path.Combine(_directory, "slot.json.tmp")), Is.False);
|
|
}
|
|
|
|
[Test]
|
|
public async Task CorruptPrimarySaveFallsBackToPreviousValidPacket()
|
|
{
|
|
await ShrinkSave.SaveSlotAsync(0, new SaveOptions { SlotName = "previous" });
|
|
await ShrinkSave.SaveSlotAsync(0, new SaveOptions { SlotName = "latest" });
|
|
var primary = Directory.GetFiles(Path.Combine(_directory, "saves"), "*.json").Single();
|
|
await File.WriteAllTextAsync(primary, "corrupt");
|
|
Assert.That((await ShrinkSave.GetMetaAsync(0)).SlotName, Is.EqualTo("previous"));
|
|
}
|
|
|
|
[Test]
|
|
public void RuntimeSnapshotExportsBindingsWithoutSessionSecrets()
|
|
{
|
|
var service = new ShrinkNetworkService(); service.RegisterMessage<CodecMessage>(42, "test/message");
|
|
service.RegisterHandler<CodecMessage>(Receive);
|
|
var snapshot = new ShrinkInspectionSnapshot("build-a");
|
|
snapshot.AddNetwork("server", service);
|
|
var json = snapshot.ToJson();
|
|
Assert.That(snapshot.Relations.Any(r => r.From == "opcode:42" && r.To == "type:CodecMessage"));
|
|
Assert.That(snapshot.Relations.Any(r => r.To == "M:SaveSnapshotTests.Receive(ShrinkNetwork.ShrinkNetworkContext,CodecMessage)"));
|
|
Assert.That(json, Does.Not.Contain("Payload").And.Not.Contain("SessionToken"));
|
|
}
|
|
private static UniTask Receive(ShrinkNetworkContext ctx, CodecMessage message) => UniTask.CompletedTask;
|
|
public sealed class SaveDto { public string Value { get; set; } = ""; }
|
|
}
|