Some checks failed
Reachability Corpus Validation / validate-corpus (push) Waiting to run
Reachability Corpus Validation / validate-ground-truths (push) Waiting to run
Reachability Corpus Validation / determinism-check (push) Blocked by required conditions
Scanner Analyzers / Discover Analyzers (push) Waiting to run
Scanner Analyzers / Build Analyzers (push) Blocked by required conditions
Scanner Analyzers / Test Language Analyzers (push) Blocked by required conditions
Scanner Analyzers / Validate Test Fixtures (push) Waiting to run
Scanner Analyzers / Verify Deterministic Output (push) Blocked by required conditions
Signals CI & Image / signals-ci (push) Waiting to run
Signals Reachability Scoring & Events / reachability-smoke (push) Waiting to run
Signals Reachability Scoring & Events / sign-and-upload (push) Blocked by required conditions
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Findings Ledger CI / build-test (push) Has been cancelled
Findings Ledger CI / migration-validation (push) Has been cancelled
Findings Ledger CI / generate-manifest (push) Has been cancelled
Lighthouse CI / Lighthouse Audit (push) Has been cancelled
Lighthouse CI / Axe Accessibility Audit (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
- Introduced `all-edge-reasons.json` to test edge resolution reasons in .NET. - Added `all-visibility-levels.json` to validate method visibility levels in .NET. - Created `dotnet-aspnetcore-minimal.json` for a minimal ASP.NET Core application. - Included `go-gin-api.json` for a Go Gin API application structure. - Added `java-spring-boot.json` for the Spring PetClinic application in Java. - Introduced `legacy-no-schema.json` for legacy application structure without schema. - Created `node-express-api.json` for an Express.js API application structure.
733 lines
20 KiB
C#
733 lines
20 KiB
C#
using FluentAssertions;
|
|
using StellaOps.Signals.Models;
|
|
using StellaOps.Signals.Parsing;
|
|
using Xunit;
|
|
|
|
namespace StellaOps.Signals.Reachability.Tests;
|
|
|
|
/// <summary>
|
|
/// Unit tests for CallgraphSchemaMigrator.
|
|
/// Verifies schema migration from legacy format to stella.callgraph.v1.
|
|
/// </summary>
|
|
public class CallgraphSchemaMigratorTests
|
|
{
|
|
#region EnsureV1 - Schema Version Tests
|
|
|
|
[Fact]
|
|
public void EnsureV1_SetsSchemaToV1_WhenNotSet()
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
Schema = string.Empty
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Schema.Should().Be(CallgraphSchemaVersions.V1);
|
|
}
|
|
|
|
[Fact]
|
|
public void EnsureV1_PreservesV1Schema_WhenAlreadySet()
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
Schema = CallgraphSchemaVersions.V1
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Schema.Should().Be(CallgraphSchemaVersions.V1);
|
|
}
|
|
|
|
[Fact]
|
|
public void EnsureV1_UpdatesLegacySchema_ToV1()
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
Schema = "legacy-schema-1.0"
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Schema.Should().Be(CallgraphSchemaVersions.V1);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region EnsureV1 - Language Parsing Tests
|
|
|
|
[Theory]
|
|
[InlineData("dotnet", CallgraphLanguage.DotNet)]
|
|
[InlineData(".net", CallgraphLanguage.DotNet)]
|
|
[InlineData("csharp", CallgraphLanguage.DotNet)]
|
|
[InlineData("c#", CallgraphLanguage.DotNet)]
|
|
[InlineData("java", CallgraphLanguage.Java)]
|
|
[InlineData("node", CallgraphLanguage.Node)]
|
|
[InlineData("nodejs", CallgraphLanguage.Node)]
|
|
[InlineData("javascript", CallgraphLanguage.Node)]
|
|
[InlineData("typescript", CallgraphLanguage.Node)]
|
|
[InlineData("python", CallgraphLanguage.Python)]
|
|
[InlineData("go", CallgraphLanguage.Go)]
|
|
[InlineData("golang", CallgraphLanguage.Go)]
|
|
[InlineData("rust", CallgraphLanguage.Rust)]
|
|
[InlineData("ruby", CallgraphLanguage.Ruby)]
|
|
[InlineData("php", CallgraphLanguage.Php)]
|
|
[InlineData("binary", CallgraphLanguage.Binary)]
|
|
[InlineData("native", CallgraphLanguage.Binary)]
|
|
[InlineData("elf", CallgraphLanguage.Binary)]
|
|
[InlineData("swift", CallgraphLanguage.Swift)]
|
|
[InlineData("kotlin", CallgraphLanguage.Kotlin)]
|
|
public void EnsureV1_ParsesLanguageString_ToEnum(string languageString, CallgraphLanguage expected)
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
Language = languageString,
|
|
LanguageType = CallgraphLanguage.Unknown
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.LanguageType.Should().Be(expected);
|
|
}
|
|
|
|
[Fact]
|
|
public void EnsureV1_PreservesLanguageType_WhenAlreadySet()
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
Language = "java",
|
|
LanguageType = CallgraphLanguage.DotNet // Already set to something different
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.LanguageType.Should().Be(CallgraphLanguage.DotNet);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region EnsureV1 - Node Visibility Inference Tests
|
|
|
|
[Fact]
|
|
public void EnsureV1_InfersPublicVisibility_ForStandardNames()
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
Nodes = new List<CallgraphNode>
|
|
{
|
|
new() { Id = "node1", Name = "ProcessOrder", Visibility = SymbolVisibility.Unknown }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Nodes.Should().ContainSingle()
|
|
.Which.Visibility.Should().Be(SymbolVisibility.Public);
|
|
}
|
|
|
|
[Fact]
|
|
public void EnsureV1_InfersPrivateVisibility_ForUnderscorePrefixed()
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
Nodes = new List<CallgraphNode>
|
|
{
|
|
new() { Id = "node1", Name = "_privateMethod", Visibility = SymbolVisibility.Unknown }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Nodes.Should().ContainSingle()
|
|
.Which.Visibility.Should().Be(SymbolVisibility.Private);
|
|
}
|
|
|
|
[Fact]
|
|
public void EnsureV1_InfersPrivateVisibility_ForAngleBracketNames()
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
Nodes = new List<CallgraphNode>
|
|
{
|
|
new() { Id = "node1", Name = "<Main>$", Visibility = SymbolVisibility.Unknown }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Nodes.Should().ContainSingle()
|
|
.Which.Visibility.Should().Be(SymbolVisibility.Private);
|
|
}
|
|
|
|
[Fact]
|
|
public void EnsureV1_InfersInternalVisibility_ForInternalNamespace()
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
Nodes = new List<CallgraphNode>
|
|
{
|
|
new() { Id = "node1", Name = "Helper", Namespace = "MyApp.Internal.Utils", Visibility = SymbolVisibility.Unknown }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Nodes.Should().ContainSingle()
|
|
.Which.Visibility.Should().Be(SymbolVisibility.Internal);
|
|
}
|
|
|
|
[Fact]
|
|
public void EnsureV1_PreservesVisibility_WhenAlreadySet()
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
Nodes = new List<CallgraphNode>
|
|
{
|
|
new() { Id = "node1", Name = "_privateMethod", Visibility = SymbolVisibility.Protected }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Nodes.Should().ContainSingle()
|
|
.Which.Visibility.Should().Be(SymbolVisibility.Protected);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region EnsureV1 - Symbol Key Building Tests
|
|
|
|
[Fact]
|
|
public void EnsureV1_BuildsSymbolKey_FromNamespaceAndName()
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
Nodes = new List<CallgraphNode>
|
|
{
|
|
new() { Id = "node1", Name = "ProcessOrder", Namespace = "MyApp.Services" }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Nodes.Should().ContainSingle()
|
|
.Which.SymbolKey.Should().Be("MyApp.Services.ProcessOrder");
|
|
}
|
|
|
|
[Fact]
|
|
public void EnsureV1_BuildsSymbolKey_FromNameOnly_WhenNoNamespace()
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
Nodes = new List<CallgraphNode>
|
|
{
|
|
new() { Id = "node1", Name = "GlobalMethod" }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Nodes.Should().ContainSingle()
|
|
.Which.SymbolKey.Should().Be("GlobalMethod");
|
|
}
|
|
|
|
[Fact]
|
|
public void EnsureV1_PreservesSymbolKey_WhenAlreadySet()
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
Nodes = new List<CallgraphNode>
|
|
{
|
|
new() { Id = "node1", Name = "Method", Namespace = "Ns", SymbolKey = "Custom.Key" }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Nodes.Should().ContainSingle()
|
|
.Which.SymbolKey.Should().Be("Custom.Key");
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region EnsureV1 - Entrypoint Candidate Detection Tests
|
|
|
|
[Theory]
|
|
[InlineData("Main")]
|
|
[InlineData("main")]
|
|
[InlineData("MAIN")]
|
|
public void EnsureV1_DetectsEntrypointCandidate_ForMainMethod(string methodName)
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
Nodes = new List<CallgraphNode>
|
|
{
|
|
new() { Id = "node1", Name = methodName }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Nodes.Should().ContainSingle()
|
|
.Which.IsEntrypointCandidate.Should().BeTrue();
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("OrdersController")]
|
|
[InlineData("UserController")]
|
|
public void EnsureV1_DetectsEntrypointCandidate_ForControllerNames(string name)
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
Nodes = new List<CallgraphNode>
|
|
{
|
|
new() { Id = "node1", Name = name }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Nodes.Should().ContainSingle()
|
|
.Which.IsEntrypointCandidate.Should().BeTrue();
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("RequestHandler")]
|
|
[InlineData("EventHandler")]
|
|
public void EnsureV1_DetectsEntrypointCandidate_ForHandlerNames(string name)
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
Nodes = new List<CallgraphNode>
|
|
{
|
|
new() { Id = "node1", Name = name }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Nodes.Should().ContainSingle()
|
|
.Which.IsEntrypointCandidate.Should().BeTrue();
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(".cctor")]
|
|
[InlineData("ModuleInitializer")]
|
|
public void EnsureV1_DetectsEntrypointCandidate_ForModuleInitializers(string name)
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
Nodes = new List<CallgraphNode>
|
|
{
|
|
new() { Id = "node1", Name = name }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Nodes.Should().ContainSingle()
|
|
.Which.IsEntrypointCandidate.Should().BeTrue();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region EnsureV1 - Edge Reason Inference Tests
|
|
|
|
[Theory]
|
|
[InlineData("call", EdgeReason.DirectCall)]
|
|
[InlineData("direct", EdgeReason.DirectCall)]
|
|
[InlineData("virtual", EdgeReason.VirtualCall)]
|
|
[InlineData("callvirt", EdgeReason.VirtualCall)]
|
|
[InlineData("newobj", EdgeReason.NewObj)]
|
|
[InlineData("new", EdgeReason.NewObj)]
|
|
[InlineData("ldftn", EdgeReason.DelegateCreate)]
|
|
[InlineData("delegate", EdgeReason.DelegateCreate)]
|
|
[InlineData("reflection", EdgeReason.ReflectionString)]
|
|
[InlineData("di", EdgeReason.DiBinding)]
|
|
[InlineData("injection", EdgeReason.DiBinding)]
|
|
[InlineData("async", EdgeReason.AsyncContinuation)]
|
|
[InlineData("continuation", EdgeReason.AsyncContinuation)]
|
|
[InlineData("event", EdgeReason.EventHandler)]
|
|
[InlineData("generic", EdgeReason.GenericInstantiation)]
|
|
[InlineData("native", EdgeReason.NativeInterop)]
|
|
[InlineData("pinvoke", EdgeReason.NativeInterop)]
|
|
[InlineData("ffi", EdgeReason.NativeInterop)]
|
|
public void EnsureV1_InfersEdgeReason_FromLegacyType(string legacyType, EdgeReason expected)
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
Edges = new List<CallgraphEdge>
|
|
{
|
|
new() { SourceId = "n1", TargetId = "n2", Type = legacyType, Reason = EdgeReason.Unknown }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Edges.Should().ContainSingle()
|
|
.Which.Reason.Should().Be(expected);
|
|
}
|
|
|
|
[Fact]
|
|
public void EnsureV1_InfersRuntimeMinted_ForRuntimeKind()
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
Edges = new List<CallgraphEdge>
|
|
{
|
|
new() { SourceId = "n1", TargetId = "n2", Type = "unknown", Kind = EdgeKind.Runtime, Reason = EdgeReason.Unknown }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Edges.Should().ContainSingle()
|
|
.Which.Reason.Should().Be(EdgeReason.RuntimeMinted);
|
|
}
|
|
|
|
[Fact]
|
|
public void EnsureV1_InfersDynamicImport_ForHeuristicKind()
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
Edges = new List<CallgraphEdge>
|
|
{
|
|
new() { SourceId = "n1", TargetId = "n2", Type = "unknown", Kind = EdgeKind.Heuristic, Reason = EdgeReason.Unknown }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Edges.Should().ContainSingle()
|
|
.Which.Reason.Should().Be(EdgeReason.DynamicImport);
|
|
}
|
|
|
|
[Fact]
|
|
public void EnsureV1_PreservesEdgeReason_WhenAlreadySet()
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
Edges = new List<CallgraphEdge>
|
|
{
|
|
new() { SourceId = "n1", TargetId = "n2", Type = "call", Reason = EdgeReason.VirtualCall }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Edges.Should().ContainSingle()
|
|
.Which.Reason.Should().Be(EdgeReason.VirtualCall);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region EnsureV1 - Entrypoint Inference Tests
|
|
|
|
[Fact]
|
|
public void EnsureV1_InfersEntrypoints_FromEntrypointCandidateNodes()
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
LanguageType = CallgraphLanguage.DotNet,
|
|
Nodes = new List<CallgraphNode>
|
|
{
|
|
new() { Id = "main", Name = "Main", IsEntrypointCandidate = true },
|
|
new() { Id = "helper", Name = "Helper", IsEntrypointCandidate = false }
|
|
},
|
|
Entrypoints = new List<CallgraphEntrypoint>()
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Entrypoints.Should().ContainSingle()
|
|
.Which.NodeId.Should().Be("main");
|
|
}
|
|
|
|
[Fact]
|
|
public void EnsureV1_InfersEntrypoints_FromExplicitRoots()
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
LanguageType = CallgraphLanguage.DotNet,
|
|
Nodes = new List<CallgraphNode>
|
|
{
|
|
new() { Id = "init", Name = "Initialize" }
|
|
},
|
|
Roots = new List<CallgraphRoot>
|
|
{
|
|
new("init", "init", "module_init")
|
|
},
|
|
Entrypoints = new List<CallgraphEntrypoint>()
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Entrypoints.Should().ContainSingle()
|
|
.Which.NodeId.Should().Be("init");
|
|
}
|
|
|
|
[Fact]
|
|
public void EnsureV1_PreservesEntrypoints_WhenAlreadyPresent()
|
|
{
|
|
// Arrange
|
|
var existingEntrypoint = new CallgraphEntrypoint
|
|
{
|
|
NodeId = "existing",
|
|
Kind = EntrypointKind.Http,
|
|
Route = "/api/test"
|
|
};
|
|
|
|
var document = new CallgraphDocument
|
|
{
|
|
Nodes = new List<CallgraphNode>
|
|
{
|
|
new() { Id = "main", Name = "Main", IsEntrypointCandidate = true }
|
|
},
|
|
Entrypoints = new List<CallgraphEntrypoint> { existingEntrypoint }
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Entrypoints.Should().ContainSingle()
|
|
.Which.NodeId.Should().Be("existing");
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region EnsureV1 - Ordering Tests
|
|
|
|
[Fact]
|
|
public void EnsureV1_SortsNodes_ByIdAlphabetically()
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
Nodes = new List<CallgraphNode>
|
|
{
|
|
new() { Id = "z-node", Name = "Z" },
|
|
new() { Id = "a-node", Name = "A" },
|
|
new() { Id = "m-node", Name = "M" }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Nodes.Select(n => n.Id).Should().BeInAscendingOrder();
|
|
}
|
|
|
|
[Fact]
|
|
public void EnsureV1_SortsEdges_BySourceThenTargetThenTypeThenOffset()
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
Edges = new List<CallgraphEdge>
|
|
{
|
|
new() { SourceId = "b", TargetId = "x", Type = "call", Offset = 10 },
|
|
new() { SourceId = "a", TargetId = "y", Type = "call", Offset = 5 },
|
|
new() { SourceId = "a", TargetId = "x", Type = "virtual", Offset = 0 },
|
|
new() { SourceId = "a", TargetId = "x", Type = "call", Offset = 20 }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
var sortedEdges = result.Edges.ToList();
|
|
sortedEdges[0].SourceId.Should().Be("a");
|
|
sortedEdges[0].TargetId.Should().Be("x");
|
|
sortedEdges[0].Type.Should().Be("call");
|
|
}
|
|
|
|
[Fact]
|
|
public void EnsureV1_SortsEntrypoints_ByPhaseThenOrder()
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
LanguageType = CallgraphLanguage.DotNet,
|
|
Nodes = new List<CallgraphNode>
|
|
{
|
|
new() { Id = "main", Name = "Main", IsEntrypointCandidate = true },
|
|
new() { Id = "init", Name = ".cctor", IsEntrypointCandidate = true }
|
|
},
|
|
Entrypoints = new List<CallgraphEntrypoint>()
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Entrypoints.Should().HaveCount(2);
|
|
// ModuleInit phase should come before Runtime
|
|
result.Entrypoints.First().NodeId.Should().Be("init");
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region EnsureV1 - Null Handling Tests
|
|
|
|
[Fact]
|
|
public void EnsureV1_ThrowsArgumentNullException_ForNullDocument()
|
|
{
|
|
// Act & Assert
|
|
Assert.Throws<ArgumentNullException>(() => CallgraphSchemaMigrator.EnsureV1(null!));
|
|
}
|
|
|
|
[Fact]
|
|
public void EnsureV1_HandlesEmptyNodes_Gracefully()
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
Nodes = new List<CallgraphNode>()
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Nodes.Should().BeEmpty();
|
|
}
|
|
|
|
[Fact]
|
|
public void EnsureV1_HandlesEmptyEdges_Gracefully()
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
Edges = new List<CallgraphEdge>()
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Edges.Should().BeEmpty();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Framework Inference Tests
|
|
|
|
[Fact]
|
|
public void EnsureV1_InfersAspNetCoreFramework_ForDotNetController()
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
LanguageType = CallgraphLanguage.DotNet,
|
|
Nodes = new List<CallgraphNode>
|
|
{
|
|
new() { Id = "ctrl", Name = "OrdersController", IsEntrypointCandidate = true }
|
|
},
|
|
Entrypoints = new List<CallgraphEntrypoint>()
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Entrypoints.Should().ContainSingle()
|
|
.Which.Framework.Should().Be(EntrypointFramework.AspNetCore);
|
|
}
|
|
|
|
[Fact]
|
|
public void EnsureV1_InfersSpringFramework_ForJavaController()
|
|
{
|
|
// Arrange
|
|
var document = new CallgraphDocument
|
|
{
|
|
LanguageType = CallgraphLanguage.Java,
|
|
Nodes = new List<CallgraphNode>
|
|
{
|
|
new() { Id = "ctrl", Name = "OrderController", IsEntrypointCandidate = true }
|
|
},
|
|
Entrypoints = new List<CallgraphEntrypoint>()
|
|
};
|
|
|
|
// Act
|
|
var result = CallgraphSchemaMigrator.EnsureV1(document);
|
|
|
|
// Assert
|
|
result.Entrypoints.Should().ContainSingle()
|
|
.Which.Framework.Should().Be(EntrypointFramework.Spring);
|
|
}
|
|
|
|
#endregion
|
|
}
|