up
	
		
			
	
		
	
	
		
	
		
			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
				
			
		
		
	
	
				
					
				
			
		
			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
				
			This commit is contained in:
		
							
								
								
									
										132
									
								
								src/StellaOps.Vexer.Export.Tests/ExportEngineTests.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								src/StellaOps.Vexer.Export.Tests/ExportEngineTests.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,132 @@ | ||||
| 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)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user