using System.Collections.Immutable; using System.IO.Abstractions.TestingHelpers; using System.Linq; using System.Text.Json; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using StellaOps.Excititor.Core; using StellaOps.Excititor.Export; namespace StellaOps.Excititor.Export.Tests; public sealed class OfflineBundleArtifactStoreTests { [Fact] public async Task SaveAsync_WritesArtifactAndManifest() { var fs = new MockFileSystem(); var options = Options.Create(new OfflineBundleArtifactStoreOptions { RootPath = "/offline" }); var store = new OfflineBundleArtifactStore(options, NullLogger.Instance, fs); var content = new byte[] { 1, 2, 3 }; var digest = "sha256:" + Convert.ToHexString(System.Security.Cryptography.SHA256.HashData(content)).ToLowerInvariant(); var artifact = new VexExportArtifact( new VexContentAddress("sha256", digest.Split(':')[1]), VexExportFormat.Json, content, ImmutableDictionary.Empty); var stored = await store.SaveAsync(artifact, CancellationToken.None); var artifactPath = fs.Path.Combine(options.Value.RootPath, stored.Location); Assert.True(fs.FileExists(artifactPath)); var manifestPath = fs.Path.Combine(options.Value.RootPath, options.Value.ManifestFileName); Assert.True(fs.FileExists(manifestPath)); await using var manifestStream = fs.File.OpenRead(manifestPath); using var document = await JsonDocument.ParseAsync(manifestStream); var artifacts = document.RootElement.GetProperty("artifacts"); Assert.True(artifacts.GetArrayLength() >= 1); var first = artifacts.EnumerateArray().First(); Assert.Equal(digest, first.GetProperty("digest").GetString()); } [Fact] public async Task SaveAsync_ThrowsOnDigestMismatch() { var fs = new MockFileSystem(); var options = Options.Create(new OfflineBundleArtifactStoreOptions { RootPath = "/offline" }); var store = new OfflineBundleArtifactStore(options, NullLogger.Instance, fs); var artifact = new VexExportArtifact( new VexContentAddress("sha256", "deadbeef"), VexExportFormat.Json, new byte[] { 0x01, 0x02 }, ImmutableDictionary.Empty); await Assert.ThrowsAsync(() => store.SaveAsync(artifact, CancellationToken.None).AsTask()); } }