save checkpoint
This commit is contained in:
@@ -46,14 +46,9 @@ public sealed class LineageStreamControllerTests
|
||||
{
|
||||
// Arrange
|
||||
var digest = "sha256:abc123";
|
||||
_graphService.SetupGraph(digest, new LineageGraphResponse(
|
||||
new LineageGraphDto(
|
||||
Nodes: ImmutableArray.Create(
|
||||
new LineageNodeDto(digest, "app", "1.0.0", 10),
|
||||
new LineageNodeDto("sha256:child", "lib", "1.0.0", 5)),
|
||||
Edges: ImmutableArray.Create(
|
||||
new LineageEdgeDto(digest, "sha256:child"))),
|
||||
Enrichment: null));
|
||||
_graphService.SetupGraph(digest, CreateGraphResponse(
|
||||
new[] { CreateNode(digest), CreateNode("sha256:child") },
|
||||
new[] { CreateEdge(digest, "sha256:child") }));
|
||||
|
||||
// Act
|
||||
var result = await _controller.GetOptimizedLineage(digest, maxDepth: 3, pageSize: 50, pageNumber: 0);
|
||||
@@ -103,12 +98,9 @@ public sealed class LineageStreamControllerTests
|
||||
{
|
||||
// Arrange
|
||||
var digest = "sha256:meta123";
|
||||
_graphService.SetupGraph(digest, new LineageGraphResponse(
|
||||
new LineageGraphDto(
|
||||
Nodes: ImmutableArray.Create(
|
||||
new LineageNodeDto(digest, "app", "1.0.0", 10)),
|
||||
Edges: ImmutableArray<LineageEdgeDto>.Empty),
|
||||
Enrichment: null));
|
||||
_graphService.SetupGraph(digest, CreateGraphResponse(
|
||||
new[] { CreateNode(digest) },
|
||||
Array.Empty<LineageEdge>()));
|
||||
|
||||
// Act
|
||||
var result = await _controller.GetMetadata(digest);
|
||||
@@ -145,16 +137,18 @@ public sealed class LineageStreamControllerTests
|
||||
{
|
||||
// Arrange
|
||||
var digest = "sha256:center";
|
||||
_graphService.SetupGraph(digest, new LineageGraphResponse(
|
||||
new LineageGraphDto(
|
||||
Nodes: ImmutableArray.Create(
|
||||
new LineageNodeDto(digest, "center-app", "1.0.0", 10),
|
||||
new LineageNodeDto("sha256:logging", "logging-lib", "1.0.0", 5),
|
||||
new LineageNodeDto("sha256:database", "database-lib", "1.0.0", 8)),
|
||||
Edges: ImmutableArray.Create(
|
||||
new LineageEdgeDto(digest, "sha256:logging"),
|
||||
new LineageEdgeDto(digest, "sha256:database"))),
|
||||
Enrichment: null));
|
||||
_graphService.SetupGraph(digest, CreateGraphResponse(
|
||||
new[]
|
||||
{
|
||||
CreateNode(digest),
|
||||
CreateNode("sha256:logging"),
|
||||
CreateNode("sha256:database")
|
||||
},
|
||||
new[]
|
||||
{
|
||||
CreateEdge(digest, "sha256:logging"),
|
||||
CreateEdge(digest, "sha256:database")
|
||||
}));
|
||||
|
||||
// Act
|
||||
var result = await _controller.GetOptimizedLineage(digest, searchTerm: "log");
|
||||
@@ -172,24 +166,17 @@ public sealed class LineageStreamControllerTests
|
||||
{
|
||||
// Arrange
|
||||
var digest = "sha256:center";
|
||||
var nodes = new List<LineageNodeDto>
|
||||
{
|
||||
new(digest, "center", "1.0.0", 10)
|
||||
};
|
||||
var edges = new List<LineageEdgeDto>();
|
||||
var nodes = new List<LineageNode> { CreateNode(digest) };
|
||||
var edges = new List<LineageEdge>();
|
||||
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
var childDigest = $"sha256:child{i:D2}";
|
||||
nodes.Add(new LineageNodeDto(childDigest, $"child-{i}", "1.0.0", i + 1));
|
||||
edges.Add(new LineageEdgeDto(digest, childDigest));
|
||||
nodes.Add(CreateNode(childDigest));
|
||||
edges.Add(CreateEdge(digest, childDigest));
|
||||
}
|
||||
|
||||
_graphService.SetupGraph(digest, new LineageGraphResponse(
|
||||
new LineageGraphDto(
|
||||
Nodes: nodes.ToImmutableArray(),
|
||||
Edges: edges.ToImmutableArray()),
|
||||
Enrichment: null));
|
||||
_graphService.SetupGraph(digest, CreateGraphResponse(nodes, edges));
|
||||
|
||||
// Act
|
||||
var result = await _controller.GetOptimizedLineage(digest, pageSize: 5, pageNumber: 0);
|
||||
@@ -201,6 +188,19 @@ public sealed class LineageStreamControllerTests
|
||||
graph.PageNumber.Should().Be(0);
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
private static LineageNode CreateNode(string artifactDigest) =>
|
||||
new(artifactDigest, Guid.NewGuid(), 1, DateTimeOffset.UtcNow, null);
|
||||
|
||||
private static LineageEdge CreateEdge(string parent, string child) =>
|
||||
new(Guid.NewGuid(), parent, child, LineageRelationship.Parent, Guid.Empty, DateTimeOffset.UtcNow);
|
||||
|
||||
private static LineageGraphResponse CreateGraphResponse(
|
||||
IEnumerable<LineageNode> nodes,
|
||||
IEnumerable<LineageEdge> edges) =>
|
||||
new(new LineageGraph(nodes.ToList(), edges.ToList()),
|
||||
new Dictionary<string, NodeEnrichment>());
|
||||
|
||||
// Test helper implementations
|
||||
private sealed class InMemoryLineageStreamService : ILineageStreamService
|
||||
{
|
||||
@@ -216,76 +216,80 @@ public sealed class LineageStreamControllerTests
|
||||
public async IAsyncEnumerable<LineageUpdateEvent> SubscribeAsync(
|
||||
Guid tenantId,
|
||||
IReadOnlyList<string>? watchDigests = null,
|
||||
CancellationToken ct = default)
|
||||
[System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
yield break;
|
||||
}
|
||||
|
||||
public Task PublishAsync(Guid tenantId, LineageUpdateEvent evt, CancellationToken ct = default)
|
||||
=> Task.CompletedTask;
|
||||
public ValueTask PublishAsync(LineageUpdateEvent updateEvent, CancellationToken ct = default)
|
||||
=> ValueTask.CompletedTask;
|
||||
|
||||
public Task NotifySbomAddedAsync(Guid tenantId, string artifactDigest, string? parentDigest,
|
||||
public ValueTask NotifySbomAddedAsync(Guid tenantId, string artifactDigest, string? parentDigest,
|
||||
SbomVersionSummary summary, CancellationToken ct = default)
|
||||
=> Task.CompletedTask;
|
||||
=> ValueTask.CompletedTask;
|
||||
|
||||
public Task NotifyVexChangedAsync(Guid tenantId, string artifactDigest, VexChangeData change,
|
||||
public ValueTask NotifyVexChangedAsync(Guid tenantId, string artifactDigest, VexChangeData change,
|
||||
CancellationToken ct = default)
|
||||
=> Task.CompletedTask;
|
||||
=> ValueTask.CompletedTask;
|
||||
|
||||
public Task NotifyReachabilityUpdatedAsync(Guid tenantId, string artifactDigest, ReachabilityUpdateData update,
|
||||
public ValueTask NotifyReachabilityUpdatedAsync(Guid tenantId, string artifactDigest, ReachabilityUpdateData update,
|
||||
CancellationToken ct = default)
|
||||
=> Task.CompletedTask;
|
||||
=> ValueTask.CompletedTask;
|
||||
|
||||
public Task NotifyEdgeChangedAsync(Guid tenantId, string fromDigest, string toDigest,
|
||||
public ValueTask NotifyEdgeChangedAsync(Guid tenantId, string fromDigest, string toDigest,
|
||||
LineageEdgeChangeType changeType, CancellationToken ct = default)
|
||||
=> Task.CompletedTask;
|
||||
=> ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
private sealed class InMemoryLineageGraphOptimizer : ILineageGraphOptimizer
|
||||
{
|
||||
public LineageOptimizationRequest? LastRequest { get; private set; }
|
||||
|
||||
public OptimizedLineageGraph Optimize(LineageOptimizationRequest request)
|
||||
public OptimizedLineageGraph Optimize(
|
||||
LineageGraph fullGraph,
|
||||
LineageOptimizationRequest request)
|
||||
{
|
||||
LastRequest = request;
|
||||
return new OptimizedLineageGraph
|
||||
{
|
||||
Nodes = request.AllNodes,
|
||||
Edges = request.AllEdges,
|
||||
Nodes = fullGraph.Nodes,
|
||||
Edges = fullGraph.Edges,
|
||||
BoundaryNodes = ImmutableArray<BoundaryNodeInfo>.Empty,
|
||||
TotalNodes = request.AllNodes.Length,
|
||||
HasMorePages = false
|
||||
TotalNodeCount = fullGraph.Nodes.Count,
|
||||
HasMore = false,
|
||||
Offset = request.Offset,
|
||||
Limit = request.Limit,
|
||||
OptimizationTimeMs = 0
|
||||
};
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<LineageLevel> TraverseLevelsAsync(
|
||||
string centerDigest,
|
||||
ImmutableArray<LineageNode> nodes,
|
||||
ImmutableArray<LineageEdge> edges,
|
||||
TraversalDirection direction,
|
||||
int maxDepth = 10,
|
||||
CancellationToken ct = default)
|
||||
Func<string, CancellationToken, Task<IReadOnlyList<LineageNode>>> getChildrenAsync,
|
||||
Func<string, CancellationToken, Task<IReadOnlyList<LineageNode>>> getParentsAsync,
|
||||
int maxDepth = 5,
|
||||
[System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
yield return new LineageLevel(0, nodes, true);
|
||||
yield return new LineageLevel
|
||||
{
|
||||
Level = 0,
|
||||
Direction = TraversalDirection.Center,
|
||||
NodeDigests = ImmutableArray.Create(centerDigest)
|
||||
};
|
||||
}
|
||||
|
||||
public Task<LineageGraphMetadata> GetOrComputeMetadataAsync(
|
||||
string artifactDigest,
|
||||
Guid tenantId,
|
||||
string centerDigest,
|
||||
ImmutableArray<LineageNode> nodes,
|
||||
ImmutableArray<LineageEdge> edges,
|
||||
Func<CancellationToken, Task<LineageGraphMetadata>> computeAsync,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
return Task.FromResult(new LineageGraphMetadata(
|
||||
TotalNodes: nodes.Length,
|
||||
TotalEdges: edges.Length,
|
||||
MaxDepth: 1,
|
||||
ComputedAt: DateTimeOffset.UtcNow));
|
||||
return computeAsync(ct);
|
||||
}
|
||||
|
||||
public Task InvalidateCacheAsync(Guid tenantId, string centerDigest, CancellationToken ct = default)
|
||||
public Task InvalidateCacheAsync(string artifactDigest, Guid tenantId, CancellationToken ct = default)
|
||||
=> Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -308,8 +312,10 @@ public sealed class LineageStreamControllerTests
|
||||
return ValueTask.FromResult(response);
|
||||
|
||||
return ValueTask.FromResult(new LineageGraphResponse(
|
||||
new LineageGraphDto(ImmutableArray<LineageNodeDto>.Empty, ImmutableArray<LineageEdgeDto>.Empty),
|
||||
null));
|
||||
new LineageGraph(
|
||||
ImmutableArray<LineageNode>.Empty,
|
||||
ImmutableArray<LineageEdge>.Empty),
|
||||
new Dictionary<string, NodeEnrichment>()));
|
||||
}
|
||||
|
||||
public ValueTask<LineageDiffResponse> GetDiffAsync(
|
||||
@@ -319,9 +325,15 @@ public sealed class LineageStreamControllerTests
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
return ValueTask.FromResult(new LineageDiffResponse(
|
||||
ImmutableArray<LineageChangeSummary>.Empty,
|
||||
ImmutableArray<LineageChangeSummary>.Empty,
|
||||
ImmutableArray<LineageChangeSummary>.Empty));
|
||||
fromDigest,
|
||||
toDigest,
|
||||
new SbomDiff(
|
||||
ImmutableArray<ComponentChange>.Empty,
|
||||
ImmutableArray<ComponentChange>.Empty,
|
||||
ImmutableArray<ComponentChange>.Empty),
|
||||
new VexDiff(
|
||||
ImmutableArray<VexDelta>.Empty, 0, 0, 0, 0),
|
||||
null));
|
||||
}
|
||||
|
||||
public ValueTask<ExportResult> ExportEvidencePackAsync(
|
||||
@@ -329,21 +341,11 @@ public sealed class LineageStreamControllerTests
|
||||
Guid tenantId,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
return ValueTask.FromResult(new ExportResult("https://example.com/pack.zip", 1024));
|
||||
return ValueTask.FromResult(new ExportResult(
|
||||
"https://example.com/pack.zip",
|
||||
DateTimeOffset.UtcNow.AddHours(1),
|
||||
1024,
|
||||
null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Placeholder types to match interface expectations
|
||||
file record LineageNodeDto(string Digest, string Name, string Version, int ComponentCount);
|
||||
file record LineageEdgeDto(string FromDigest, string ToDigest);
|
||||
file record LineageGraphDto(ImmutableArray<LineageNodeDto> Nodes, ImmutableArray<LineageEdgeDto> Edges);
|
||||
file record LineageGraphResponse(LineageGraphDto Graph, object? Enrichment);
|
||||
file record LineageDiffResponse(
|
||||
ImmutableArray<LineageChangeSummary> Added,
|
||||
ImmutableArray<LineageChangeSummary> Removed,
|
||||
ImmutableArray<LineageChangeSummary> Modified);
|
||||
file record LineageChangeSummary(string Digest, string Name);
|
||||
file record ExportRequest(string ArtifactDigest, int MaxDepth);
|
||||
file record ExportResult(string DownloadUrl, long SizeBytes);
|
||||
file record LineageQueryOptions(int MaxDepth, bool IncludeVerdicts, bool IncludeBadges);
|
||||
|
||||
Reference in New Issue
Block a user