using System.Text; using Microsoft.Extensions.Logging.Abstractions; using StellaOps.EvidenceLocker.Core.Configuration; using StellaOps.EvidenceLocker.Core.Domain; using StellaOps.EvidenceLocker.Core.Storage; using StellaOps.EvidenceLocker.Infrastructure.Storage; using StellaOps.TestKit; namespace StellaOps.EvidenceLocker.Tests; public sealed class FileSystemEvidenceObjectStoreTests : IDisposable { private readonly string _rootPath; public FileSystemEvidenceObjectStoreTests() { _rootPath = Path.Combine(Path.GetTempPath(), $"evidence-locker-tests-{Guid.NewGuid():N}"); } [Trait("Category", TestCategories.Unit)] [Fact] public async Task StoreAsync_EnforcesWriteOnceWhenConfigured() { var cancellationToken = TestContext.Current.CancellationToken; var store = CreateStore(enforceWriteOnce: true); var options = CreateWriteOptions(); using var first = CreateStream("payload-1"); await store.StoreAsync(first, options, cancellationToken); using var second = CreateStream("payload-1"); await Assert.ThrowsAsync(() => store.StoreAsync(second, options, cancellationToken)); } [Trait("Category", TestCategories.Unit)] [Fact] public async Task StoreAsync_AllowsOverwriteWhenWriteOnceDisabled() { var cancellationToken = TestContext.Current.CancellationToken; var store = CreateStore(enforceWriteOnce: false); var options = CreateWriteOptions() with { EnforceWriteOnce = false }; using var first = CreateStream("payload-1"); var firstMetadata = await store.StoreAsync(first, options, cancellationToken); using var second = CreateStream("payload-1"); using StellaOps.TestKit; var secondMetadata = await store.StoreAsync(second, options, cancellationToken); Assert.Equal(firstMetadata.Sha256, secondMetadata.Sha256); Assert.True(File.Exists(Path.Combine(_rootPath, secondMetadata.StorageKey.Replace('/', Path.DirectorySeparatorChar)))); } private FileSystemEvidenceObjectStore CreateStore(bool enforceWriteOnce) { var fileSystemOptions = new FileSystemStoreOptions { RootPath = _rootPath }; return new FileSystemEvidenceObjectStore( fileSystemOptions, enforceWriteOnce, NullLogger.Instance); } private static EvidenceObjectWriteOptions CreateWriteOptions() { var tenant = TenantId.FromGuid(Guid.NewGuid()); var bundle = EvidenceBundleId.FromGuid(Guid.NewGuid()); return new EvidenceObjectWriteOptions( tenant, bundle, "artifact.txt", "text/plain"); } private static MemoryStream CreateStream(string content) => new(Encoding.UTF8.GetBytes(content)); public void Dispose() { if (Directory.Exists(_rootPath)) { Directory.Delete(_rootPath, recursive: true); } } }