Some checks failed
		
		
	
	Build Test Deploy / authority-container (push) Has been cancelled
				
			Build Test Deploy / docs (push) Has been cancelled
				
			Build Test Deploy / deploy (push) Has been cancelled
				
			Build Test Deploy / build-test (push) Has been cancelled
				
			Docs CI / lint-and-preview (push) Has been cancelled
				
			
		
			
				
	
	
		
			133 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			133 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using System.Collections.Immutable;
 | |
| using System.IO;
 | |
| using System.Text;
 | |
| using Microsoft.Extensions.Logging.Abstractions;
 | |
| using StellaOps.Vexer.Core;
 | |
| using StellaOps.Vexer.Export;
 | |
| using StellaOps.Vexer.Policy;
 | |
| using StellaOps.Vexer.Storage.Mongo;
 | |
| using Xunit;
 | |
| 
 | |
| namespace StellaOps.Vexer.Export.Tests;
 | |
| 
 | |
| public sealed class ExportEngineTests
 | |
| {
 | |
|     [Fact]
 | |
|     public async Task ExportAsync_GeneratesAndCachesManifest()
 | |
|     {
 | |
|         var store = new InMemoryExportStore();
 | |
|         var evaluator = new StaticPolicyEvaluator("baseline/v1");
 | |
|         var dataSource = new InMemoryExportDataSource();
 | |
|         var exporter = new DummyExporter(VexExportFormat.Json);
 | |
|         var engine = new VexExportEngine(store, evaluator, dataSource, new[] { exporter }, NullLogger<VexExportEngine>.Instance);
 | |
| 
 | |
|         var query = VexQuery.Create(new[] { new VexQueryFilter("vulnId", "CVE-2025-0001") });
 | |
|         var context = new VexExportRequestContext(query, VexExportFormat.Json, DateTimeOffset.UtcNow, ForceRefresh: false);
 | |
| 
 | |
|         var manifest = await engine.ExportAsync(context, CancellationToken.None);
 | |
| 
 | |
|         Assert.False(manifest.FromCache);
 | |
|         Assert.Equal(VexExportFormat.Json, manifest.Format);
 | |
|         Assert.Equal("baseline/v1", manifest.ConsensusRevision);
 | |
|         Assert.Equal(1, manifest.ClaimCount);
 | |
| 
 | |
|         // second call hits cache
 | |
|         var cached = await engine.ExportAsync(context, CancellationToken.None);
 | |
|         Assert.True(cached.FromCache);
 | |
|         Assert.Equal(manifest.ExportId, cached.ExportId);
 | |
|     }
 | |
| 
 | |
|     private sealed class InMemoryExportStore : IVexExportStore
 | |
|     {
 | |
|         private readonly Dictionary<string, VexExportManifest> _store = new(StringComparer.Ordinal);
 | |
| 
 | |
|         public ValueTask<VexExportManifest?> FindAsync(VexQuerySignature signature, VexExportFormat format, CancellationToken cancellationToken)
 | |
|         {
 | |
|             var key = CreateKey(signature.Value, format);
 | |
|             _store.TryGetValue(key, out var manifest);
 | |
|             return ValueTask.FromResult<VexExportManifest?>(manifest);
 | |
|         }
 | |
| 
 | |
|         public ValueTask SaveAsync(VexExportManifest manifest, CancellationToken cancellationToken)
 | |
|         {
 | |
|             var key = CreateKey(manifest.QuerySignature.Value, manifest.Format);
 | |
|             _store[key] = manifest;
 | |
|             return ValueTask.CompletedTask;
 | |
|         }
 | |
| 
 | |
|         private static string CreateKey(string signature, VexExportFormat format)
 | |
|             => FormattableString.Invariant($"{signature}|{format}");
 | |
|     }
 | |
| 
 | |
|     private sealed class StaticPolicyEvaluator : IVexPolicyEvaluator
 | |
|     {
 | |
|         public StaticPolicyEvaluator(string version)
 | |
|         {
 | |
|             Version = version;
 | |
|         }
 | |
| 
 | |
|         public string Version { get; }
 | |
| 
 | |
|         public VexPolicySnapshot Snapshot => VexPolicySnapshot.Default;
 | |
| 
 | |
|         public double GetProviderWeight(VexProvider provider) => 1.0;
 | |
| 
 | |
|         public bool IsClaimEligible(VexClaim claim, VexProvider provider, out string? rejectionReason)
 | |
|         {
 | |
|             rejectionReason = null;
 | |
|             return true;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     private sealed class InMemoryExportDataSource : IVexExportDataSource
 | |
|     {
 | |
|         public ValueTask<VexExportDataSet> FetchAsync(VexQuery query, CancellationToken cancellationToken)
 | |
|         {
 | |
|             var claim = new VexClaim(
 | |
|                 "CVE-2025-0001",
 | |
|                 "vendor",
 | |
|                 new VexProduct("pkg:demo/app", "Demo"),
 | |
|                 VexClaimStatus.Affected,
 | |
|                 new VexClaimDocument(VexDocumentFormat.Csaf, "sha256:demo", new Uri("https://example.org/demo")),
 | |
|                 DateTimeOffset.UtcNow,
 | |
|                 DateTimeOffset.UtcNow);
 | |
| 
 | |
|             var consensus = new VexConsensus(
 | |
|                 "CVE-2025-0001",
 | |
|                 claim.Product,
 | |
|                 VexConsensusStatus.Affected,
 | |
|                 DateTimeOffset.UtcNow,
 | |
|                 new[] { new VexConsensusSource("vendor", VexClaimStatus.Affected, "sha256:demo", 1.0) },
 | |
|                 conflicts: null,
 | |
|                 policyVersion: "baseline/v1",
 | |
|                 summary: "affected");
 | |
| 
 | |
|             return ValueTask.FromResult(new VexExportDataSet(
 | |
|                 ImmutableArray.Create(consensus),
 | |
|                 ImmutableArray.Create(claim),
 | |
|                 ImmutableArray.Create("vendor")));
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     private sealed class DummyExporter : IVexExporter
 | |
|     {
 | |
|         public DummyExporter(VexExportFormat format)
 | |
|         {
 | |
|             Format = format;
 | |
|         }
 | |
| 
 | |
|         public VexExportFormat Format { get; }
 | |
| 
 | |
|         public VexContentAddress Digest(VexExportRequest request)
 | |
|             => new("sha256", "deadbeef");
 | |
| 
 | |
|         public ValueTask<VexExportResult> SerializeAsync(VexExportRequest request, Stream output, CancellationToken cancellationToken)
 | |
|         {
 | |
|             var bytes = System.Text.Encoding.UTF8.GetBytes("{}");
 | |
|             output.Write(bytes);
 | |
|             return ValueTask.FromResult(new VexExportResult(new VexContentAddress("sha256", "deadbeef"), bytes.Length, ImmutableDictionary<string, string>.Empty));
 | |
|         }
 | |
|     }
 | |
| }
 |