114 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			114 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using System.Collections.Generic;
 | |
| using System.IO;
 | |
| using System.Linq;
 | |
| using System.Text;
 | |
| using System.Text.Json;
 | |
| using System.Security.Cryptography;
 | |
| using StellaOps.Feedser.Exporter.TrivyDb;
 | |
| 
 | |
| namespace StellaOps.Feedser.Exporter.TrivyDb.Tests;
 | |
| 
 | |
| public sealed class TrivyDbOciWriterTests : IDisposable
 | |
| {
 | |
|     private readonly string _root;
 | |
| 
 | |
|     public TrivyDbOciWriterTests()
 | |
|     {
 | |
|         _root = Directory.CreateTempSubdirectory("feedser-trivy-oci-tests").FullName;
 | |
|     }
 | |
| 
 | |
|     [Fact]
 | |
|     public async Task WritesOciLayoutWithManifestIndex()
 | |
|     {
 | |
|         var metadata = Encoding.UTF8.GetBytes("{\"generatedAt\":\"2024-08-01T00:00:00Z\",\"schema\":1}");
 | |
|         var archive = Enumerable.Range(0, 128).Select(static b => (byte)b).ToArray();
 | |
|         var generatedAt = DateTimeOffset.Parse("2024-08-01T00:00:00Z");
 | |
|         var archivePath = Path.Combine(_root, "db.bin");
 | |
|         File.WriteAllBytes(archivePath, archive);
 | |
|         var archiveDigest = ComputeDigest(archive);
 | |
|         var request = new TrivyDbPackageRequest(metadata, archivePath, archiveDigest, archive.LongLength, generatedAt, "2024.08.01");
 | |
| 
 | |
|         var builder = new TrivyDbPackageBuilder();
 | |
|         var package = builder.BuildPackage(request);
 | |
| 
 | |
|         var writer = new TrivyDbOciWriter();
 | |
|         var result = await writer.WriteAsync(package, Path.Combine(_root, "oci"), "feedser:v2024.08.01", CancellationToken.None);
 | |
| 
 | |
|         Assert.Equal(package.Manifest.Layers[0].Digest, package.Config.DatabaseDigest);
 | |
|         Assert.NotEmpty(result.BlobDigests);
 | |
|         Assert.Contains(result.ManifestDigest, result.BlobDigests);
 | |
| 
 | |
|         var layoutPath = Path.Combine(result.RootDirectory, "oci-layout");
 | |
|         Assert.True(File.Exists(layoutPath));
 | |
|         var layoutJson = await File.ReadAllTextAsync(layoutPath, CancellationToken.None);
 | |
|         Assert.Contains("\"imageLayoutVersion\":\"1.0.0\"", layoutJson, StringComparison.Ordinal);
 | |
| 
 | |
|         var metadataPath = Path.Combine(result.RootDirectory, "metadata.json");
 | |
|         Assert.True(File.Exists(metadataPath));
 | |
|         var roundTripMetadata = await File.ReadAllBytesAsync(metadataPath, CancellationToken.None);
 | |
|         Assert.Equal(metadata, roundTripMetadata);
 | |
| 
 | |
|         var indexPath = Path.Combine(result.RootDirectory, "index.json");
 | |
|         Assert.True(File.Exists(indexPath));
 | |
|         using var indexDocument = JsonDocument.Parse(await File.ReadAllBytesAsync(indexPath, CancellationToken.None));
 | |
|         var manifestElement = indexDocument.RootElement.GetProperty("manifests")[0];
 | |
|         Assert.Equal(result.ManifestDigest, manifestElement.GetProperty("digest").GetString());
 | |
|         Assert.Equal(TrivyDbMediaTypes.OciManifest, manifestElement.GetProperty("mediaType").GetString());
 | |
|         Assert.Equal("feedser:v2024.08.01", manifestElement.GetProperty("annotations").GetProperty("org.opencontainers.image.ref.name").GetString());
 | |
| 
 | |
|         var manifestPath = Path.Combine(result.RootDirectory, "blobs", "sha256", result.ManifestDigest.Split(':')[1]);
 | |
|         var manifestBytes = await File.ReadAllBytesAsync(manifestPath, CancellationToken.None);
 | |
|         using var manifestDocument = JsonDocument.Parse(manifestBytes);
 | |
|         var configDescriptor = manifestDocument.RootElement.GetProperty("config");
 | |
|         Assert.Equal(package.Manifest.Config.Digest, configDescriptor.GetProperty("digest").GetString());
 | |
|         Assert.Equal(package.Manifest.Config.MediaType, configDescriptor.GetProperty("mediaType").GetString());
 | |
|         var layer = manifestDocument.RootElement.GetProperty("layers")[0];
 | |
|         Assert.Equal(package.Manifest.Layers[0].Digest, layer.GetProperty("digest").GetString());
 | |
|         Assert.Equal(package.Manifest.Layers[0].MediaType, layer.GetProperty("mediaType").GetString());
 | |
| 
 | |
|         foreach (var digest in package.Blobs.Keys)
 | |
|         {
 | |
|             var blobPath = Path.Combine(result.RootDirectory, "blobs", "sha256", digest.Split(':')[1]);
 | |
|             Assert.True(File.Exists(blobPath));
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     [Fact]
 | |
|     public async Task ThrowsOnUnsupportedDigest()
 | |
|     {
 | |
|         var package = new TrivyDbPackage(
 | |
|             new OciManifest(2, TrivyDbMediaTypes.OciManifest, new OciDescriptor(TrivyDbMediaTypes.TrivyConfig, "sha256:abcd", 4), Array.Empty<OciDescriptor>()),
 | |
|             new TrivyConfigDocument(TrivyDbMediaTypes.TrivyConfig, DateTimeOffset.UtcNow, "1", "sha256:abcd", 4),
 | |
|             new Dictionary<string, TrivyDbBlob>
 | |
|             {
 | |
|                 ["md5:deadbeef"] = TrivyDbBlob.FromBytes(new byte[] { 1, 2, 3, 4 }),
 | |
|             },
 | |
|             new byte[] { 123 });
 | |
| 
 | |
|         var writer = new TrivyDbOciWriter();
 | |
|         await Assert.ThrowsAsync<InvalidOperationException>(() => writer.WriteAsync(package, Path.Combine(_root, "invalid"), "feedser:bad", CancellationToken.None));
 | |
|     }
 | |
| 
 | |
|     public void Dispose()
 | |
|     {
 | |
|         try
 | |
|         {
 | |
|             if (Directory.Exists(_root))
 | |
|             {
 | |
|                 Directory.Delete(_root, recursive: true);
 | |
|             }
 | |
|         }
 | |
|         catch
 | |
|         {
 | |
|             // ignore cleanup issues
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     private static string ComputeDigest(byte[] payload)
 | |
|     {
 | |
|         var hash = SHA256.HashData(payload);
 | |
|         return "sha256:" + Convert.ToHexString(hash).ToLowerInvariant();
 | |
|     }
 | |
| }
 |