feat: add stella-callgraph-node for JavaScript/TypeScript call graph extraction
- Implemented a new tool `stella-callgraph-node` that extracts call graphs from JavaScript/TypeScript projects using Babel AST. - Added command-line interface with options for JSON output and help. - Included functionality to analyze project structure, detect functions, and build call graphs. - Created a package.json file for dependency management. feat: introduce stella-callgraph-python for Python call graph extraction - Developed `stella-callgraph-python` to extract call graphs from Python projects using AST analysis. - Implemented command-line interface with options for JSON output and verbose logging. - Added framework detection to identify popular web frameworks and their entry points. - Created an AST analyzer to traverse Python code and extract function definitions and calls. - Included requirements.txt for project dependencies. chore: add framework detection for Python projects - Implemented framework detection logic to identify frameworks like Flask, FastAPI, Django, and others based on project files and import patterns. - Enhanced the AST analyzer to recognize entry points based on decorators and function definitions.
This commit is contained in:
@@ -0,0 +1,410 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// WitnessCommandGroupTests.cs
|
||||
// Sprint: SPRINT_3700_0005_0001_witness_ui_cli
|
||||
// Tasks: TEST-002
|
||||
// Description: Unit tests for witness CLI commands
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Parsing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
using StellaOps.Cli.Commands;
|
||||
|
||||
namespace StellaOps.Cli.Tests.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for witness CLI commands.
|
||||
/// </summary>
|
||||
public class WitnessCommandGroupTests
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly Option<bool> _verboseOption;
|
||||
private readonly CancellationToken _cancellationToken;
|
||||
|
||||
public WitnessCommandGroupTests()
|
||||
{
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddLogging();
|
||||
_services = serviceCollection.BuildServiceProvider();
|
||||
_verboseOption = new Option<bool>("--verbose", "-v");
|
||||
_cancellationToken = CancellationToken.None;
|
||||
}
|
||||
|
||||
#region Command Structure Tests
|
||||
|
||||
[Fact]
|
||||
public void BuildWitnessCommand_CreatesWitnessCommandTree()
|
||||
{
|
||||
// Act
|
||||
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("witness", command.Name);
|
||||
Assert.Equal("Reachability witness operations.", command.Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildWitnessCommand_HasShowSubcommand()
|
||||
{
|
||||
// Act
|
||||
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
||||
var showCommand = command.Subcommands.FirstOrDefault(c => c.Name == "show");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(showCommand);
|
||||
Assert.Equal("Display a witness with call path visualization.", showCommand.Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildWitnessCommand_HasVerifySubcommand()
|
||||
{
|
||||
// Act
|
||||
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
||||
var verifyCommand = command.Subcommands.FirstOrDefault(c => c.Name == "verify");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(verifyCommand);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildWitnessCommand_HasListSubcommand()
|
||||
{
|
||||
// Act
|
||||
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
||||
var listCommand = command.Subcommands.FirstOrDefault(c => c.Name == "list");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(listCommand);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildWitnessCommand_HasExportSubcommand()
|
||||
{
|
||||
// Act
|
||||
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
||||
var exportCommand = command.Subcommands.FirstOrDefault(c => c.Name == "export");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(exportCommand);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Show Command Tests
|
||||
|
||||
[Fact]
|
||||
public void ShowCommand_HasWitnessIdArgument()
|
||||
{
|
||||
// Arrange
|
||||
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
||||
var showCommand = command.Subcommands.First(c => c.Name == "show");
|
||||
|
||||
// Act
|
||||
var witnessIdArg = showCommand.Arguments.FirstOrDefault(a => a.Name == "witness-id");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(witnessIdArg);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShowCommand_HasFormatOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
||||
var showCommand = command.Subcommands.First(c => c.Name == "show");
|
||||
|
||||
// Act
|
||||
var formatOption = showCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("-f") || o.Aliases.Contains("--format"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(formatOption);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShowCommand_HasNoColorOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
||||
var showCommand = command.Subcommands.First(c => c.Name == "show");
|
||||
|
||||
// Act
|
||||
var noColorOption = showCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("--no-color"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(noColorOption);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShowCommand_HasPathOnlyOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
||||
var showCommand = command.Subcommands.First(c => c.Name == "show");
|
||||
|
||||
// Act
|
||||
var pathOnlyOption = showCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("--path-only"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(pathOnlyOption);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("text")]
|
||||
[InlineData("json")]
|
||||
[InlineData("yaml")]
|
||||
public void ShowCommand_FormatOption_AcceptsValidFormats(string format)
|
||||
{
|
||||
// Arrange
|
||||
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
||||
var showCommand = command.Subcommands.First(c => c.Name == "show");
|
||||
|
||||
// Act
|
||||
var parseResult = showCommand.Parse($"wit:abc123 --format {format}");
|
||||
|
||||
// Assert
|
||||
Assert.Empty(parseResult.Errors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShowCommand_FormatOption_RejectsInvalidFormat()
|
||||
{
|
||||
// Arrange
|
||||
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
||||
var showCommand = command.Subcommands.First(c => c.Name == "show");
|
||||
|
||||
// Act
|
||||
var parseResult = showCommand.Parse("wit:abc123 --format invalid");
|
||||
|
||||
// Assert
|
||||
Assert.NotEmpty(parseResult.Errors);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Verify Command Tests
|
||||
|
||||
[Fact]
|
||||
public void VerifyCommand_HasWitnessIdArgument()
|
||||
{
|
||||
// Arrange
|
||||
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
||||
var verifyCommand = command.Subcommands.First(c => c.Name == "verify");
|
||||
|
||||
// Act
|
||||
var witnessIdArg = verifyCommand.Arguments.FirstOrDefault(a => a.Name == "witness-id");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(witnessIdArg);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifyCommand_HasPublicKeyOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
||||
var verifyCommand = command.Subcommands.First(c => c.Name == "verify");
|
||||
|
||||
// Act
|
||||
var publicKeyOption = verifyCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("-k") || o.Aliases.Contains("--public-key"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(publicKeyOption);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifyCommand_HasOfflineOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
||||
var verifyCommand = command.Subcommands.First(c => c.Name == "verify");
|
||||
|
||||
// Act
|
||||
var offlineOption = verifyCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("--offline"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(offlineOption);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region List Command Tests
|
||||
|
||||
[Fact]
|
||||
public void ListCommand_HasScanOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
||||
var listCommand = command.Subcommands.First(c => c.Name == "list");
|
||||
|
||||
// Act
|
||||
var scanOption = listCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("--scan") || o.Aliases.Contains("-s"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(scanOption);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListCommand_HasVulnOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
||||
var listCommand = command.Subcommands.First(c => c.Name == "list");
|
||||
|
||||
// Act
|
||||
var vulnOption = listCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("--vuln") || o.Aliases.Contains("-v"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(vulnOption);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListCommand_HasReachableOnlyOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
||||
var listCommand = command.Subcommands.First(c => c.Name == "list");
|
||||
|
||||
// Act
|
||||
var reachableOption = listCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("--reachable-only"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(reachableOption);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Export Command Tests
|
||||
|
||||
[Fact]
|
||||
public void ExportCommand_HasWitnessIdArgument()
|
||||
{
|
||||
// Arrange
|
||||
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
||||
var exportCommand = command.Subcommands.First(c => c.Name == "export");
|
||||
|
||||
// Act
|
||||
var witnessIdArg = exportCommand.Arguments.FirstOrDefault(a => a.Name == "witness-id");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(witnessIdArg);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExportCommand_HasFormatOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
||||
var exportCommand = command.Subcommands.First(c => c.Name == "export");
|
||||
|
||||
// Act
|
||||
var formatOption = exportCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("-f") || o.Aliases.Contains("--format"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(formatOption);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExportCommand_HasOutputOption()
|
||||
{
|
||||
// Arrange
|
||||
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
||||
var exportCommand = command.Subcommands.First(c => c.Name == "export");
|
||||
|
||||
// Act
|
||||
var outputOption = exportCommand.Options.FirstOrDefault(o =>
|
||||
o.Aliases.Contains("-o") || o.Aliases.Contains("--output"));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(outputOption);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("json")]
|
||||
[InlineData("sarif")]
|
||||
public void ExportCommand_FormatOption_AcceptsValidFormats(string format)
|
||||
{
|
||||
// Arrange
|
||||
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
||||
var exportCommand = command.Subcommands.First(c => c.Name == "export");
|
||||
|
||||
// Act
|
||||
var parseResult = exportCommand.Parse($"wit:abc123 --format {format}");
|
||||
|
||||
// Assert
|
||||
Assert.Empty(parseResult.Errors);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Integration Tests
|
||||
|
||||
[Fact]
|
||||
public void WitnessCommand_CanParseShowCommand()
|
||||
{
|
||||
// Arrange
|
||||
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
||||
|
||||
// Act
|
||||
var parseResult = command.Parse("show wit:sha256:abc123 --format json");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("show", parseResult.CommandResult.Command.Name);
|
||||
Assert.Empty(parseResult.Errors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WitnessCommand_CanParseVerifyCommand()
|
||||
{
|
||||
// Arrange
|
||||
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
||||
|
||||
// Act
|
||||
var parseResult = command.Parse("verify wit:sha256:abc123 --offline");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("verify", parseResult.CommandResult.Command.Name);
|
||||
Assert.Empty(parseResult.Errors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WitnessCommand_CanParseListCommand()
|
||||
{
|
||||
// Arrange
|
||||
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
||||
|
||||
// Act
|
||||
var parseResult = command.Parse("list --scan scan-12345 --reachable-only");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("list", parseResult.CommandResult.Command.Name);
|
||||
Assert.Empty(parseResult.Errors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WitnessCommand_CanParseExportCommand()
|
||||
{
|
||||
// Arrange
|
||||
var command = WitnessCommandGroup.BuildWitnessCommand(_services, _verboseOption, _cancellationToken);
|
||||
|
||||
// Act
|
||||
var parseResult = command.Parse("export wit:sha256:abc123 --format sarif --output report.sarif");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("export", parseResult.CommandResult.Command.Name);
|
||||
Assert.Empty(parseResult.Errors);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user