Refactor code structure and optimize performance across multiple modules

This commit is contained in:
StellaOps Bot
2025-12-26 20:03:22 +02:00
parent c786faae84
commit b4fc66feb6
3353 changed files with 88254 additions and 1590657 deletions

View File

@@ -13,6 +13,7 @@ using MicrosoftOptions = Microsoft.Extensions.Options;
using StellaOps.Graph.Indexer.Storage.Postgres.Repositories;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Graph.Indexer.Storage.Postgres.Tests;
/// <summary>
@@ -44,7 +45,8 @@ public sealed class GraphQueryDeterminismTests : IAsyncLifetime
#region Result Ordering Determinism
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task MultipleIdempotencyQueries_ReturnSameOrder()
{
// Arrange - Insert multiple tokens
@@ -81,7 +83,8 @@ public sealed class GraphQueryDeterminismTests : IAsyncLifetime
results1.Should().AllBeEquivalentTo(true, "All tokens should be marked as seen");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ConcurrentQueries_ProduceDeterministicResults()
{
// Arrange
@@ -103,7 +106,8 @@ public sealed class GraphQueryDeterminismTests : IAsyncLifetime
#region Input Stability
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task SameInput_ProducesSameHash()
{
// Arrange
@@ -119,7 +123,8 @@ public sealed class GraphQueryDeterminismTests : IAsyncLifetime
hash2.Should().Be(hash3, "Hash should be stable across multiple computations");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ShuffledInputs_ProduceSameCanonicalOrdering()
{
// Arrange
@@ -135,7 +140,8 @@ public sealed class GraphQueryDeterminismTests : IAsyncLifetime
"Shuffled inputs should produce identical canonical ordering");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Timestamps_DoNotAffectOrdering()
{
// Arrange - Insert tokens at "different" times (same logical batch)
@@ -164,7 +170,8 @@ public sealed class GraphQueryDeterminismTests : IAsyncLifetime
#region Cross-Tenant Isolation with Determinism
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task CrossTenant_QueriesRemainIsolated()
{
// Arrange - Create tokens that could collide without tenant isolation

View File

@@ -10,6 +10,7 @@ using Microsoft.Extensions.Logging.Abstractions;
using MicrosoftOptions = Microsoft.Extensions.Options;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Graph.Indexer.Storage.Postgres.Tests;
/// <summary>
@@ -35,7 +36,8 @@ public sealed class GraphStorageMigrationTests : IAsyncLifetime
#region Schema Structure Verification
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Schema_ContainsRequiredTables()
{
// Arrange
@@ -59,7 +61,8 @@ public sealed class GraphStorageMigrationTests : IAsyncLifetime
}
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Schema_GraphNodes_HasRequiredColumns()
{
// Arrange
@@ -76,7 +79,8 @@ public sealed class GraphStorageMigrationTests : IAsyncLifetime
}
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Schema_GraphEdges_HasRequiredColumns()
{
// Arrange
@@ -97,7 +101,8 @@ public sealed class GraphStorageMigrationTests : IAsyncLifetime
#region Index Verification
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Schema_HasTenantIndexOnNodes()
{
// Act
@@ -108,7 +113,8 @@ public sealed class GraphStorageMigrationTests : IAsyncLifetime
"graph_nodes should have tenant index for multi-tenant queries");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Schema_HasTenantIndexOnEdges()
{
// Act
@@ -123,7 +129,8 @@ public sealed class GraphStorageMigrationTests : IAsyncLifetime
#region Migration Safety
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Migration_Assembly_IsReachable()
{
// Arrange & Act
@@ -134,7 +141,8 @@ public sealed class GraphStorageMigrationTests : IAsyncLifetime
assembly.GetTypes().Should().Contain(t => t.Name.Contains("Migration") || t.Name.Contains("DataSource"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Migration_SupportsIdempotentExecution()
{
// Act - Running migrations again should be idempotent

View File

@@ -4,6 +4,7 @@ using MicrosoftOptions = Microsoft.Extensions.Options;
using StellaOps.Graph.Indexer.Storage.Postgres.Repositories;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Graph.Indexer.Storage.Postgres.Tests;
[Collection(GraphIndexerPostgresCollection.Name)]
@@ -29,7 +30,8 @@ public sealed class PostgresIdempotencyStoreTests : IAsyncLifetime
public Task DisposeAsync() => Task.CompletedTask;
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task HasSeenAsync_ReturnsFalseForNewToken()
{
// Arrange
@@ -42,7 +44,8 @@ public sealed class PostgresIdempotencyStoreTests : IAsyncLifetime
result.Should().BeFalse();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task MarkSeenAsync_ThenHasSeenAsync_ReturnsTrue()
{
// Arrange
@@ -56,7 +59,8 @@ public sealed class PostgresIdempotencyStoreTests : IAsyncLifetime
result.Should().BeTrue();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task MarkSeenAsync_AllowsDifferentTokens()
{
// Arrange
@@ -74,7 +78,8 @@ public sealed class PostgresIdempotencyStoreTests : IAsyncLifetime
seen2.Should().BeTrue();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task MarkSeenAsync_IsIdempotent()
{
// Arrange

View File

@@ -29,6 +29,7 @@
<ItemGroup>
<ProjectReference Include="..\StellaOps.Graph.Indexer.Storage.Postgres\StellaOps.Graph.Indexer.Storage.Postgres.csproj" />
<ProjectReference Include="..\..\__Tests\__Libraries\StellaOps.Infrastructure.Postgres.Testing\StellaOps.Infrastructure.Postgres.Testing.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
</ItemGroup>
</Project>

View File

@@ -6,7 +6,8 @@ namespace StellaOps.Graph.Api.Tests;
public class AuditLoggerTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void LogsAndCapsSize()
{
var logger = new InMemoryAuditLogger();
@@ -27,6 +28,7 @@ public class AuditLoggerTests
Assert.True(recent.Count <= 100);
// First entry is the most recent (minute 509). Verify using total minutes from epoch.
var minutesFromEpoch = (int)(recent.First().Timestamp - DateTimeOffset.UnixEpoch).TotalMinutes;
using StellaOps.TestKit;
Assert.Equal(509, minutesFromEpoch);
}
}

View File

@@ -3,11 +3,13 @@ using StellaOps.Graph.Api.Contracts;
using StellaOps.Graph.Api.Services;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Graph.Api.Tests;
public class DiffServiceTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task DiffAsync_EmitsAddedRemovedChangedAndStats()
{
var repo = new InMemoryGraphRepository();
@@ -34,7 +36,8 @@ public class DiffServiceTests
Assert.Contains(lines, l => l.Contains("\"type\":\"stats\""));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task DiffAsync_WhenSnapshotMissing_ReturnsError()
{
var repo = new InMemoryGraphRepository();

View File

@@ -4,11 +4,13 @@ using StellaOps.Graph.Api.Contracts;
using StellaOps.Graph.Api.Services;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Graph.Api.Tests;
public class ExportServiceTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Export_ReturnsManifestAndDownloadablePayload()
{
var repo = new InMemoryGraphRepository();
@@ -28,7 +30,8 @@ public class ExportServiceTests
Assert.Equal(job.Sha256, fetched!.Sha256);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Export_IncludesEdgesWhenRequested()
{
var repo = new InMemoryGraphRepository();
@@ -41,7 +44,8 @@ public class ExportServiceTests
Assert.Contains("\"type\":\"edge\"", text);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Export_RespectsSnapshotSelection()
{
var repo = new InMemoryGraphRepository();

View File

@@ -16,6 +16,8 @@ using StellaOps.Graph.Api.Contracts;
using StellaOps.Graph.Api.Services;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Graph.Api.Tests;
/// <summary>
@@ -61,7 +63,8 @@ public sealed class GraphApiContractTests : IDisposable
#region GRAPH-5100-006: Contract Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Query_ReturnsNdjsonFormat()
{
// Arrange
@@ -88,7 +91,8 @@ public sealed class GraphApiContractTests : IDisposable
}
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Query_ReturnsNodeTypeInResponse()
{
// Arrange
@@ -109,7 +113,8 @@ public sealed class GraphApiContractTests : IDisposable
lines.Should().Contain(l => l.Contains("\"type\":\"node\""));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Query_WithEdges_ReturnsEdgeTypeInResponse()
{
// Arrange
@@ -131,7 +136,8 @@ public sealed class GraphApiContractTests : IDisposable
lines.Should().Contain(l => l.Contains("\"type\":\"edge\""));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Query_WithStats_ReturnsStatsTypeInResponse()
{
// Arrange
@@ -153,7 +159,8 @@ public sealed class GraphApiContractTests : IDisposable
lines.Should().Contain(l => l.Contains("\"type\":\"stats\""));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Query_ReturnsCursorInResponse()
{
// Arrange
@@ -174,7 +181,8 @@ public sealed class GraphApiContractTests : IDisposable
lines.Should().Contain(l => l.Contains("\"type\":\"cursor\""));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Query_EmptyResult_ReturnsEmptyCursor()
{
// Arrange
@@ -195,7 +203,8 @@ public sealed class GraphApiContractTests : IDisposable
lines.Should().Contain(l => l.Contains("\"type\":\"cursor\""));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Query_BudgetExceeded_ReturnsErrorResponse()
{
// Arrange
@@ -222,7 +231,8 @@ public sealed class GraphApiContractTests : IDisposable
#region GRAPH-5100-007: Auth Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void AuthScope_GraphRead_IsRequired()
{
// This is a validation test - actual scope enforcement is in middleware
@@ -233,7 +243,8 @@ public sealed class GraphApiContractTests : IDisposable
expectedScope.Should().NotBeNullOrEmpty();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void AuthScope_GraphWrite_IsRequired()
{
// This is a validation test - actual scope enforcement is in middleware
@@ -243,7 +254,8 @@ public sealed class GraphApiContractTests : IDisposable
expectedScope.Should().NotBeNullOrEmpty();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Query_ReturnsOnlyRequestedTenantData()
{
// Arrange - Request tenant1 data
@@ -264,7 +276,8 @@ public sealed class GraphApiContractTests : IDisposable
lines.Should().NotContain(l => l.Contains("tenant2"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Query_CrossTenant_ReturnsOnlyOwnData()
{
// Arrange - Request tenant2 data (which has only 1 artifact)
@@ -286,7 +299,8 @@ public sealed class GraphApiContractTests : IDisposable
nodesFound.Should().Be(1, "tenant2 has only 1 artifact");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Query_InvalidTenant_ReturnsEmptyResults()
{
// Arrange
@@ -312,7 +326,8 @@ public sealed class GraphApiContractTests : IDisposable
#region GRAPH-5100-008: OTel Trace Assertions
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Query_EmitsActivityWithTenantId()
{
// Arrange
@@ -344,7 +359,8 @@ public sealed class GraphApiContractTests : IDisposable
}
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Query_MetricsIncludeTenantDimension()
{
// Arrange
@@ -391,7 +407,8 @@ public sealed class GraphApiContractTests : IDisposable
tags.Should().NotBeEmpty("Metrics should be recorded during query");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GraphMetrics_HasExpectedInstruments()
{
// Arrange

View File

@@ -4,11 +4,13 @@ using StellaOps.Graph.Api.Contracts;
using StellaOps.Graph.Api.Services;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Graph.Api.Tests;
public sealed class LineageServiceTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task GetLineageAsync_ReturnsSbomAndArtifactChain()
{
var repository = new InMemoryGraphRepository();

View File

@@ -6,11 +6,13 @@ using StellaOps.Graph.Api.Contracts;
using StellaOps.Graph.Api.Services;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Graph.Api.Tests;
public class LoadTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task DeterministicOrdering_WithSyntheticGraph_RemainsStable()
{
var builder = new SyntheticGraphBuilder(seed: 42, nodeCount: 1000, edgeCount: 2000);
@@ -35,7 +37,8 @@ public class LoadTests
Assert.Equal(linesRun1, linesRun2); // strict deterministic ordering
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void QueryValidator_FuzzesInvalidInputs()
{
var rand = new Random(123);

View File

@@ -7,11 +7,14 @@ using StellaOps.Graph.Api.Contracts;
using StellaOps.Graph.Api.Services;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Graph.Api.Tests;
public class MetricsTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task BudgetDeniedCounter_IncrementsOnEdgeBudgetExceeded()
{
using var metrics = new GraphMetrics();
@@ -51,7 +54,8 @@ public class MetricsTests
Assert.Equal(1, count);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task OverlayCacheCounters_RecordHitsAndMisses()
{
// Start the listener before creating metrics so it can subscribe to instrument creation

View File

@@ -4,11 +4,13 @@ using StellaOps.Graph.Api.Contracts;
using StellaOps.Graph.Api.Services;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Graph.Api.Tests;
public class PathServiceTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FindPathsAsync_ReturnsShortestPathWithinDepth()
{
var repo = new InMemoryGraphRepository();
@@ -35,7 +37,8 @@ public class PathServiceTests
Assert.Contains(lines, l => l.Contains("\"type\":\"stats\""));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FindPathsAsync_WhenNoPath_ReturnsErrorTile()
{
var repo = new InMemoryGraphRepository();

View File

@@ -5,11 +5,14 @@ using StellaOps.Graph.Api.Contracts;
using StellaOps.Graph.Api.Services;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Graph.Api.Tests;
public class QueryServiceTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task QueryAsync_EmitsNodesEdgesStatsAndCursor()
{
var repo = new InMemoryGraphRepository();
@@ -36,7 +39,8 @@ public class QueryServiceTests
Assert.Contains(lines, l => l.Contains("\"type\":\"cursor\""));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task QueryAsync_ReturnsBudgetExceededError()
{
var repo = new InMemoryGraphRepository();
@@ -60,7 +64,8 @@ public class QueryServiceTests
Assert.Contains("GRAPH_BUDGET_EXCEEDED", lines[0]);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task QueryAsync_IncludesOverlaysAndSamplesExplainOnce()
{
var repo = new InMemoryGraphRepository(new[]

View File

@@ -2,6 +2,7 @@ using System;
using StellaOps.Graph.Api.Services;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Graph.Api.Tests;
internal sealed class FakeClock : IClock
@@ -11,7 +12,8 @@ internal sealed class FakeClock : IClock
public class RateLimiterServiceTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void AllowsWithinWindowUpToLimit()
{
var clock = new FakeClock { UtcNow = DateTimeOffset.UnixEpoch };
@@ -22,7 +24,8 @@ public class RateLimiterServiceTests
Assert.False(limiter.Allow("t1", "/r"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ResetsAfterWindow()
{
var clock = new FakeClock { UtcNow = DateTimeOffset.UnixEpoch };

View File

@@ -6,6 +6,8 @@ using StellaOps.Graph.Api.Services;
using Xunit;
using Xunit.Abstractions;
using StellaOps.TestKit;
namespace StellaOps.Graph.Api.Tests;
public class SearchServiceTests
@@ -18,7 +20,8 @@ public class SearchServiceTests
_output = output;
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task SearchAsync_ReturnsNodeAndCursorTiles()
{
var repo = new InMemoryGraphRepository(new[]
@@ -49,7 +52,8 @@ public class SearchServiceTests
Assert.False(string.IsNullOrEmpty(ExtractNodeId(firstNodeLine)));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task SearchAsync_RespectsCursorAndLimit()
{
var repo = new InMemoryGraphRepository(new[]
@@ -89,7 +93,8 @@ public class SearchServiceTests
}
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task SearchAsync_PrefersExactThenPrefixThenContains()
{
var repo = new InMemoryGraphRepository(new[]
@@ -110,7 +115,8 @@ public class SearchServiceTests
Assert.Contains("gn:t:component:example", lines.First());
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task QueryAsync_RespectsTileBudgetAndEmitsCursor()
{
// Test that budget limits output when combined with pagination.
@@ -145,7 +151,8 @@ public class SearchServiceTests
Assert.Equal(1, nodeCount); // Only 1 node due to Limit=1
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task QueryAsync_HonorsNodeAndEdgeBudgets()
{
// Test that node and edge budgets deny queries when exceeded.

View File

@@ -1,11 +1,13 @@
using System.Linq;
using StellaOps.Graph.Indexer.Analytics;
using StellaOps.TestKit;
namespace StellaOps.Graph.Indexer.Tests;
public sealed class GraphAnalyticsEngineTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compute_IsDeterministic_ForLinearGraph()
{
var snapshot = GraphAnalyticsTestData.CreateLinearSnapshot();

View File

@@ -2,11 +2,14 @@ using System.Collections.Immutable;
using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.Graph.Indexer.Analytics;
using StellaOps.TestKit;
namespace StellaOps.Graph.Indexer.Tests;
public sealed class GraphAnalyticsPipelineTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task RunAsync_WritesClustersAndCentrality()
{
var snapshot = GraphAnalyticsTestData.CreateLinearSnapshot();

View File

@@ -7,11 +7,14 @@ using Microsoft.Extensions.Options;
using StellaOps.Graph.Indexer.Incremental;
using StellaOps.Graph.Indexer.Ingestion.Sbom;
using StellaOps.TestKit;
namespace StellaOps.Graph.Indexer.Tests;
public sealed class GraphChangeStreamProcessorTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ApplyStreamAsync_SkipsDuplicates_AndRetries()
{
var tenant = "tenant-a";

View File

@@ -10,6 +10,7 @@ using FluentAssertions;
using StellaOps.Graph.Indexer.Documents;
using StellaOps.Graph.Indexer.Ingestion.Sbom;
using StellaOps.TestKit;
namespace StellaOps.Graph.Indexer.Tests;
/// <summary>
@@ -22,7 +23,8 @@ public sealed class GraphCoreLogicTests
{
#region GRAPH-5100-001: Graph Construction Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GraphConstruction_FromEvents_CreatesCorrectNodeCount()
{
// Arrange
@@ -51,7 +53,8 @@ public sealed class GraphCoreLogicTests
result.Adjacency.Nodes.Should().HaveCount(4);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GraphConstruction_FromEvents_CreatesCorrectEdgeCount()
{
// Arrange
@@ -84,7 +87,8 @@ public sealed class GraphCoreLogicTests
libANode.IncomingEdges.Should().HaveCount(1);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GraphConstruction_PreservesNodeAttributes()
{
// Arrange
@@ -111,7 +115,8 @@ public sealed class GraphCoreLogicTests
axiosNode.Should().NotBeNull();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GraphConstruction_HandlesDuplicateNodeIds_Deterministically()
{
// Arrange
@@ -138,7 +143,8 @@ public sealed class GraphCoreLogicTests
compNodes.Should().HaveCountGreaterOrEqualTo(1);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GraphConstruction_EmptyGraph_ReturnsEmptyAdjacency()
{
// Arrange
@@ -159,7 +165,8 @@ public sealed class GraphCoreLogicTests
#region GRAPH-5100-002: Graph Traversal Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GraphTraversal_DirectPath_ReturnsCorrectPath()
{
// Arrange
@@ -177,7 +184,8 @@ public sealed class GraphCoreLogicTests
path.Should().BeEquivalentTo(new[] { "node-0", "node-1", "node-2" });
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GraphTraversal_NoPath_ReturnsEmpty()
{
// Arrange - Disconnected graph
@@ -199,7 +207,8 @@ public sealed class GraphCoreLogicTests
path.Should().BeEmpty();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GraphTraversal_SelfLoop_ReturnsEmptyPath()
{
// Arrange
@@ -223,7 +232,8 @@ public sealed class GraphCoreLogicTests
path.Should().Contain("self");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GraphTraversal_MultiplePaths_ReturnsAPath()
{
// Arrange - Diamond graph: A → B, A → C, B → D, C → D
@@ -259,7 +269,8 @@ public sealed class GraphCoreLogicTests
#region GRAPH-5100-003: Graph Filtering Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GraphFilter_ByNodeType_ReturnsCorrectSubgraph()
{
// Arrange
@@ -290,7 +301,8 @@ public sealed class GraphCoreLogicTests
componentNodes.Should().Contain(n => n.NodeId == "comp-2");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GraphFilter_ByEdgeType_ReturnsCorrectSubgraph()
{
// Arrange
@@ -317,7 +329,8 @@ public sealed class GraphCoreLogicTests
dependencyNodes.Should().Contain(n => n.NodeId == "root");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GraphFilter_ByAttribute_ReturnsMatchingNodes()
{
// Arrange
@@ -345,7 +358,8 @@ public sealed class GraphCoreLogicTests
criticalNodes.Single().NodeId.Should().Be("critical");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GraphFilter_EmptyFilter_ReturnsAllNodes()
{
// Arrange
@@ -372,7 +386,8 @@ public sealed class GraphCoreLogicTests
allNodes.Should().HaveCount(3);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GraphFilter_NoMatches_ReturnsEmptySubgraph()
{
// Arrange

View File

@@ -1,11 +1,13 @@
using System.Collections.Immutable;
using StellaOps.Graph.Indexer.Schema;
using StellaOps.TestKit;
namespace StellaOps.Graph.Indexer.Tests;
public sealed class GraphIdentityTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeNodeId_IsDeterministic_WhenTupleOrderChanges()
{
var tupleA = ImmutableDictionary<string, string>.Empty
@@ -25,7 +27,8 @@ public sealed class GraphIdentityTests
Assert.StartsWith("gn:tenant-a:component:", idA);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeEdgeId_IsCaseInsensitiveExceptFingerprintFields()
{
var tupleLower = ImmutableDictionary<string, string>.Empty

View File

@@ -11,6 +11,7 @@ using StellaOps.Graph.Indexer.Documents;
using StellaOps.Graph.Indexer.Ingestion.Sbom;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Graph.Indexer.Tests;
/// <summary>
@@ -21,7 +22,8 @@ public sealed class GraphIndexerEndToEndTests
{
#region End-to-End SBOM Ingestion Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void IngestSbom_ProducesArtifactNode()
{
// Arrange
@@ -37,7 +39,8 @@ public sealed class GraphIndexerEndToEndTests
result.Adjacency.Nodes.Should().Contain(n => n.NodeId.Contains("artifact"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void IngestSbom_ProducesComponentNodes()
{
// Arrange
@@ -53,7 +56,8 @@ public sealed class GraphIndexerEndToEndTests
result.Adjacency.Nodes.Should().Contain(n => n.NodeId.Contains("component"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void IngestSbom_ProducesDependencyEdges()
{
// Arrange
@@ -71,7 +75,8 @@ public sealed class GraphIndexerEndToEndTests
rootNode!.OutgoingEdges.Should().NotBeEmpty();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void IngestSbom_PreservesDigestInformation()
{
// Arrange
@@ -90,7 +95,8 @@ public sealed class GraphIndexerEndToEndTests
result.SbomDigest.Should().Be(sbomDigest);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void IngestSbom_PreservesTenantIsolation()
{
// Arrange
@@ -118,7 +124,8 @@ public sealed class GraphIndexerEndToEndTests
#region Graph Tile Generation Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void IngestSbom_GeneratesManifestHash()
{
// Arrange
@@ -135,7 +142,8 @@ public sealed class GraphIndexerEndToEndTests
result.ManifestHash.Should().StartWith("sha256:");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void IngestSbom_ManifestHashIsDeterministic()
{
// Arrange
@@ -152,7 +160,8 @@ public sealed class GraphIndexerEndToEndTests
result1.ManifestHash.Should().Be(result2.ManifestHash);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void IngestSbom_ShuffledInputs_ProduceSameManifestHash()
{
// Arrange
@@ -192,7 +201,8 @@ public sealed class GraphIndexerEndToEndTests
#region Complex SBOM Scenarios
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void IngestSbom_DeepDependencyChain_ProducesCorrectGraph()
{
// Arrange - Create a deep dependency chain: root → a → b → c → d → e
@@ -233,7 +243,8 @@ public sealed class GraphIndexerEndToEndTests
depE.OutgoingEdges.Should().BeEmpty();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void IngestSbom_DiamondDependency_HandlesCorrectly()
{
// Arrange - Diamond: root → a, root → b, a → c, b → c
@@ -267,7 +278,8 @@ public sealed class GraphIndexerEndToEndTests
depC.IncomingEdges.Should().HaveCount(2);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void IngestSbom_CircularDependency_HandlesGracefully()
{
// Arrange - Circular: a → b → c → a

View File

@@ -3,11 +3,13 @@ using StellaOps.Graph.Indexer.Ingestion.Inspector;
using StellaOps.Graph.Indexer.Schema;
using Xunit.Sdk;
using StellaOps.TestKit;
namespace StellaOps.Graph.Indexer.Tests;
public sealed class GraphInspectorTransformerTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Transform_BuildsNodesAndEdges_FromInspectorSnapshot()
{
var snapshot = new GraphInspectorSnapshot
@@ -143,7 +145,8 @@ public sealed class GraphInspectorTransformerTests
Assert.Equal(6000, dependsOn["provenance"]!["event_offset"]!.GetValue<long>());
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Transform_AcceptsPublishedSample()
{
var samplePath = LocateRepoFile("docs/modules/graph/contracts/examples/graph.inspect.v1.sample.json");

View File

@@ -3,11 +3,13 @@ using System.Linq;
using StellaOps.Graph.Indexer.Analytics;
using StellaOps.Graph.Indexer.Ingestion.Sbom;
using StellaOps.TestKit;
namespace StellaOps.Graph.Indexer.Tests;
public sealed class GraphOverlayExporterTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ExportAsync_WritesDeterministicNdjson()
{
var snapshot = GraphAnalyticsTestData.CreateLinearSnapshot();

View File

@@ -3,11 +3,13 @@ using System.Text.Json.Nodes;
using StellaOps.Graph.Indexer.Documents;
using StellaOps.Graph.Indexer.Ingestion.Sbom;
using StellaOps.TestKit;
namespace StellaOps.Graph.Indexer.Tests;
public sealed class GraphSnapshotBuilderTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Build_ProducesDeterministicAdjacencyOrdering()
{
var snapshot = new SbomSnapshot
@@ -51,7 +53,8 @@ public sealed class GraphSnapshotBuilderTests
Assert.Equal(new[] { "edge-b" }, result.Adjacency.Nodes.Single(n => n.NodeId == "node-b").IncomingEdges.ToArray());
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Build_ComputesStableManifestHash_ForShuffledInputs()
{
var snapshot = new SbomSnapshot

View File

@@ -4,11 +4,13 @@ using System.Text.Json.Nodes;
using StellaOps.Graph.Indexer.Ingestion.Sbom;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Graph.Indexer.Tests;
public sealed class SbomLineageTransformerTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Transform_adds_lineage_edges_when_present()
{
var snapshot = new SbomSnapshot

View File

@@ -4,11 +4,13 @@ using StellaOps.Graph.Indexer.Documents;
using StellaOps.Graph.Indexer.Ingestion.Sbom;
using StellaOps.Graph.Indexer.Schema;
using StellaOps.TestKit;
namespace StellaOps.Graph.Indexer.Tests;
public sealed class SbomSnapshotExporterTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ExportAsync_WritesCanonicalFilesWithStableHash()
{
var snapshot = new SbomSnapshot