// ----------------------------------------------------------------------------- // GoCallGraphExtractorTests.cs // Sprint: SPRINT_3610_0002_0001_go_callgraph // Description: Unit tests for Go call graph extraction. // ----------------------------------------------------------------------------- using Microsoft.Extensions.Logging.Abstractions; using StellaOps.Scanner.CallGraph.Go; using StellaOps.Scanner.Reachability; using Xunit; namespace StellaOps.Scanner.CallGraph.Tests; public class GoCallGraphExtractorTests { [Fact] public void BuildFunctionId_CreatesCorrectFormat() { // Arrange & Act var id = GoSymbolIdBuilder.BuildFunctionId("github.com/example/pkg", "HandleRequest"); // Assert Assert.Equal("go:github.com/example/pkg.HandleRequest", id); } [Fact] public void BuildMethodId_CreatesCorrectFormat() { // Arrange & Act var id = GoSymbolIdBuilder.BuildMethodId("github.com/example/pkg", "Server", "Start"); // Assert Assert.Equal("go:github.com/example/pkg.Server.Start", id); } [Fact] public void BuildExternalId_CreatesCorrectFormat() { // Arrange & Act var id = GoSymbolIdBuilder.BuildExternalId("fmt", "Println"); // Assert Assert.Equal("go:external/fmt.Println", id); } [Fact] public void Parse_ParsesFunctionId() { // Arrange var id = "go:github.com/example/pkg.HandleRequest"; // Act var result = GoSymbolIdBuilder.Parse(id); // Assert Assert.NotNull(result); Assert.Equal("github.com/example/pkg", result.PackagePath); Assert.Equal("HandleRequest", result.Name); Assert.Null(result.ReceiverType); Assert.False(result.IsMethod); Assert.False(result.IsExternal); } [Fact] public void Parse_ParsesExternalId() { // Arrange var id = "go:external/os/exec.Command"; // Act var result = GoSymbolIdBuilder.Parse(id); // Assert Assert.NotNull(result); Assert.Equal("os/exec", result.PackagePath); Assert.Equal("Command", result.Name); Assert.True(result.IsExternal); } [Fact] public void IsStdLib_ReturnsTrueForStandardLibrary() { // Arrange & Act & Assert Assert.True(GoSymbolIdBuilder.IsStdLib("go:external/fmt.Println")); Assert.True(GoSymbolIdBuilder.IsStdLib("go:external/os/exec.Command")); Assert.False(GoSymbolIdBuilder.IsStdLib("go:external/github.com/gin-gonic/gin.New")); } [Fact] public void GoSsaResultParser_ParsesValidJson() { // Arrange var json = """ { "module": "github.com/example/app", "nodes": [ { "id": "go:github.com/example/app.main", "package": "github.com/example/app", "name": "main" } ], "edges": [], "entrypoints": [ { "id": "go:github.com/example/app.main", "type": "cli_command" } ] } """; // Act var result = GoSsaResultParser.Parse(json); // Assert Assert.Equal("github.com/example/app", result.Module); Assert.Single(result.Nodes); Assert.Empty(result.Edges); Assert.Single(result.Entrypoints); } [Fact] public void GoSsaResultParser_ThrowsOnEmptyInput() { // Arrange & Act & Assert Assert.Throws(() => GoSsaResultParser.Parse("")); Assert.Throws(() => GoSsaResultParser.Parse(" ")); } [Fact] public void GoEntrypointClassifier_ClassifiesHttpHandler() { // Arrange var classifier = new GoEntrypointClassifier(); var node = new CallGraphNode( NodeId: "go:example/api.HandleUsers", Symbol: "HandleUsers", File: "api.go", Line: 10, Package: "example/api", Visibility: Visibility.Public, IsEntrypoint: false, EntrypointType: null, IsSink: false, SinkCategory: null); var func = new GoFunctionInfo { NodeId = "go:example/api.GetUsers", Name = "GetUsers", Package = "example/api", Receiver = "", IsExported = true, HasGinContext = true, Annotations = ["gin.GET", "route:/users"] }; // Act var result = classifier.Classify(func); // Assert Assert.True(result.HasValue); Assert.Equal(EntrypointType.HttpHandler, result.Value); } [Fact] public void GoSinkMatcher_MatchesExecCommand() { // Arrange var matcher = new GoSinkMatcher(); // Act var result = matcher.Match("os/exec", "Command"); // Assert Assert.Equal(SinkCategory.CmdExec, result); } [Fact] public void GoSinkMatcher_MatchesSqlQuery() { // Arrange var matcher = new GoSinkMatcher(); // Act var result = matcher.Match("database/sql", "Query"); // Assert Assert.Equal(SinkCategory.SqlRaw, result); } }