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,186 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// NodeCallGraphExtractorTests.cs
|
||||
// Sprint: SPRINT_3610_0003_0001_nodejs_callgraph
|
||||
// Description: Unit tests for Node.js/JavaScript call graph extraction.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using StellaOps.Scanner.CallGraph.Node;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Scanner.CallGraph.Tests;
|
||||
|
||||
public class NodeCallGraphExtractorTests
|
||||
{
|
||||
[Fact]
|
||||
public void BabelResultParser_ParsesValidJson()
|
||||
{
|
||||
// Arrange
|
||||
var json = """
|
||||
{
|
||||
"module": "my-app",
|
||||
"version": "1.0.0",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "js:my-app/src/index.handleRequest",
|
||||
"package": "my-app",
|
||||
"name": "handleRequest",
|
||||
"visibility": "public"
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"from": "js:my-app/src/index.handleRequest",
|
||||
"to": "js:external/express.Router",
|
||||
"kind": "direct"
|
||||
}
|
||||
],
|
||||
"entrypoints": [
|
||||
{
|
||||
"id": "js:my-app/src/index.handleRequest",
|
||||
"type": "http_handler",
|
||||
"route": "/api/users",
|
||||
"method": "GET"
|
||||
}
|
||||
]
|
||||
}
|
||||
""";
|
||||
|
||||
// Act
|
||||
var result = BabelResultParser.Parse(json);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("my-app", result.Module);
|
||||
Assert.Equal("1.0.0", result.Version);
|
||||
Assert.Single(result.Nodes);
|
||||
Assert.Single(result.Edges);
|
||||
Assert.Single(result.Entrypoints);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BabelResultParser_ParsesNodeWithPosition()
|
||||
{
|
||||
// Arrange
|
||||
var json = """
|
||||
{
|
||||
"module": "test",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "js:test/app.main",
|
||||
"package": "test",
|
||||
"name": "main",
|
||||
"position": {
|
||||
"file": "app.js",
|
||||
"line": 10,
|
||||
"column": 5
|
||||
}
|
||||
}
|
||||
],
|
||||
"edges": [],
|
||||
"entrypoints": []
|
||||
}
|
||||
""";
|
||||
|
||||
// Act
|
||||
var result = BabelResultParser.Parse(json);
|
||||
|
||||
// Assert
|
||||
Assert.Single(result.Nodes);
|
||||
var node = result.Nodes[0];
|
||||
Assert.NotNull(node.Position);
|
||||
Assert.Equal("app.js", node.Position.File);
|
||||
Assert.Equal(10, node.Position.Line);
|
||||
Assert.Equal(5, node.Position.Column);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BabelResultParser_ParsesEdgeWithSite()
|
||||
{
|
||||
// Arrange
|
||||
var json = """
|
||||
{
|
||||
"module": "test",
|
||||
"nodes": [],
|
||||
"edges": [
|
||||
{
|
||||
"from": "js:test/a.foo",
|
||||
"to": "js:test/b.bar",
|
||||
"kind": "callback",
|
||||
"site": {
|
||||
"file": "a.js",
|
||||
"line": 25
|
||||
}
|
||||
}
|
||||
],
|
||||
"entrypoints": []
|
||||
}
|
||||
""";
|
||||
|
||||
// Act
|
||||
var result = BabelResultParser.Parse(json);
|
||||
|
||||
// Assert
|
||||
Assert.Single(result.Edges);
|
||||
var edge = result.Edges[0];
|
||||
Assert.Equal("callback", edge.Kind);
|
||||
Assert.NotNull(edge.Site);
|
||||
Assert.Equal("a.js", edge.Site.File);
|
||||
Assert.Equal(25, edge.Site.Line);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BabelResultParser_ThrowsOnEmptyInput()
|
||||
{
|
||||
// Arrange & Act & Assert
|
||||
Assert.Throws<ArgumentException>(() => BabelResultParser.Parse(""));
|
||||
Assert.Throws<ArgumentException>(() => BabelResultParser.Parse(null!));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BabelResultParser_ParsesNdjson()
|
||||
{
|
||||
// Arrange
|
||||
var ndjson = """
|
||||
{"type": "progress", "percent": 50}
|
||||
{"type": "progress", "percent": 100}
|
||||
{"module": "app", "nodes": [], "edges": [], "entrypoints": []}
|
||||
""";
|
||||
|
||||
// Act
|
||||
var result = BabelResultParser.ParseNdjson(ndjson);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("app", result.Module);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void JsEntrypointInfo_HasCorrectProperties()
|
||||
{
|
||||
// Arrange
|
||||
var json = """
|
||||
{
|
||||
"module": "api",
|
||||
"nodes": [],
|
||||
"edges": [],
|
||||
"entrypoints": [
|
||||
{
|
||||
"id": "js:api/routes.getUsers",
|
||||
"type": "http_handler",
|
||||
"route": "/users/:id",
|
||||
"method": "GET"
|
||||
}
|
||||
]
|
||||
}
|
||||
""";
|
||||
|
||||
// Act
|
||||
var result = BabelResultParser.Parse(json);
|
||||
|
||||
// Assert
|
||||
Assert.Single(result.Entrypoints);
|
||||
var ep = result.Entrypoints[0];
|
||||
Assert.Equal("js:api/routes.getUsers", ep.Id);
|
||||
Assert.Equal("http_handler", ep.Type);
|
||||
Assert.Equal("/users/:id", ep.Route);
|
||||
Assert.Equal("GET", ep.Method);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user