a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
173 lines
5.0 KiB
Go
173 lines
5.0 KiB
Go
package languages
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/zzet/gortex/internal/graph"
|
|
"github.com/zzet/gortex/internal/parser"
|
|
)
|
|
|
|
// TestXsjsAndExtensionMapping proves the extension cluster routes the
|
|
// previously-UNKNOWN server-side-JS / ESM-TS suffixes to the right extractor
|
|
// and that an IIFE/AMD-wrapped inner function is still mined — recall the
|
|
// bare-extension table missed. The mapping is asserted through the registry
|
|
// (the real routing path), not by calling the extractor directly.
|
|
func TestXsjsAndExtensionMapping(t *testing.T) {
|
|
reg := parser.NewRegistry()
|
|
RegisterAll(reg)
|
|
|
|
wantLang := map[string]string{
|
|
".xsjs": "javascript",
|
|
".xsjslib": "javascript",
|
|
".mts": "typescript",
|
|
".cts": "typescript",
|
|
}
|
|
for ext, lang := range wantLang {
|
|
ex, ok := reg.GetByExtension(ext)
|
|
if !ok {
|
|
t.Errorf("extension %q routes to no extractor", ext)
|
|
continue
|
|
}
|
|
if ex.Language() != lang {
|
|
t.Errorf("extension %q routes to %q, want %q", ext, ex.Language(), lang)
|
|
}
|
|
}
|
|
|
|
// IIFE / AMD module wrapper: the inner function must still be extracted —
|
|
// the tree-sitter query matches function_declaration at any depth.
|
|
const iife = `(function () {
|
|
function handleGet(req) { return req.body; }
|
|
})();
|
|
`
|
|
res, err := NewJavaScriptExtractor().Extract("service.xsjs", []byte(iife))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var found bool
|
|
for _, n := range res.Nodes {
|
|
if n.Kind == graph.KindFunction && n.Name == "handleGet" {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Error("IIFE inner function handleGet not extracted from .xsjs source")
|
|
}
|
|
}
|
|
|
|
// TestGeneratedDartSkip proves generated Dart boilerplate is excluded from the
|
|
// symbol surface (measured: same source yields symbols as a hand-written
|
|
// .dart, zero as a .g.dart/.freezed.dart/.pb.dart), while the file node is kept
|
|
// and flagged so incremental tracking still sees the file.
|
|
func TestGeneratedDartSkip(t *testing.T) {
|
|
const src = `class User {
|
|
String name;
|
|
void greet() {}
|
|
}
|
|
`
|
|
symbolCount := func(filePath string) (symbols int, generated bool) {
|
|
res, err := NewDartExtractor().Extract(filePath, []byte(src))
|
|
if err != nil {
|
|
t.Fatalf("Extract(%s): %v", filePath, err)
|
|
}
|
|
for _, n := range res.Nodes {
|
|
switch n.Kind {
|
|
case graph.KindType, graph.KindMethod, graph.KindFunction:
|
|
symbols++
|
|
case graph.KindFile:
|
|
if g, _ := n.Meta["generated"].(bool); g {
|
|
generated = true
|
|
}
|
|
}
|
|
}
|
|
return symbols, generated
|
|
}
|
|
|
|
// Hand-written source: real symbols, not flagged generated.
|
|
if n, gen := symbolCount("user.dart"); n == 0 || gen {
|
|
t.Errorf("user.dart: symbols=%d generated=%v, want symbols>0 generated=false", n, gen)
|
|
}
|
|
// Generated suffixes: no symbols, file flagged generated.
|
|
for _, fp := range []string{"user.g.dart", "user.freezed.dart", "user.pb.dart"} {
|
|
n, gen := symbolCount(fp)
|
|
if n != 0 {
|
|
t.Errorf("%s: extracted %d symbols, want 0 (generated file should be skipped)", fp, n)
|
|
}
|
|
if !gen {
|
|
t.Errorf("%s: file node not flagged generated", fp)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestCConstClassification proves a file-scope `const` is classified as a named
|
|
// constant (joining the value-ref impact surface) while a mutable global stays
|
|
// a variable — and a pointer-to-const never spuriously becomes a constant.
|
|
func TestCConstClassification(t *testing.T) {
|
|
const src = `const int MAX_RETRIES = 5;
|
|
int g_counter = 0;
|
|
const char *g_name = "x";
|
|
`
|
|
res, err := NewCExtractor().Extract("k.c", []byte(src))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
kindOf := map[string]graph.NodeKind{}
|
|
for _, n := range res.Nodes {
|
|
if n.Kind == graph.KindConstant || n.Kind == graph.KindVariable {
|
|
kindOf[n.Name] = n.Kind
|
|
}
|
|
}
|
|
if kindOf["MAX_RETRIES"] != graph.KindConstant {
|
|
t.Errorf("MAX_RETRIES kind=%q, want constant", kindOf["MAX_RETRIES"])
|
|
}
|
|
if kindOf["g_counter"] != graph.KindVariable {
|
|
t.Errorf("g_counter kind=%q, want variable", kindOf["g_counter"])
|
|
}
|
|
// `const char *p` is a pointer-to-const (mutable pointer) — it must not be
|
|
// misclassified as a named constant.
|
|
if kindOf["g_name"] == graph.KindConstant {
|
|
t.Error("pointer-to-const g_name wrongly classified as a constant")
|
|
}
|
|
}
|
|
|
|
// TestAnonClassNodes confirms Java and C# anonymous classes are first-class
|
|
// graph nodes (a navigable symbol carrying the anonymous marker), not dropped
|
|
// — the recall the audit flagged for verification.
|
|
func TestAnonClassNodes(t *testing.T) {
|
|
java := `class A {
|
|
void m() {
|
|
Runnable r = new Runnable() { public void run() {} };
|
|
}
|
|
}
|
|
`
|
|
jres, err := NewJavaExtractor().Extract("A.java", []byte(java))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !hasAnonymousNode(jres.Nodes) {
|
|
t.Error("Java anonymous class produced no anonymous node")
|
|
}
|
|
|
|
csharp := `class A {
|
|
void M() {
|
|
var x = new { Name = "a", Age = 1 };
|
|
}
|
|
}
|
|
`
|
|
cres, err := NewCSharpExtractor().Extract("A.cs", []byte(csharp))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !hasAnonymousNode(cres.Nodes) {
|
|
t.Error("C# anonymous type produced no anonymous node")
|
|
}
|
|
}
|
|
|
|
func hasAnonymousNode(nodes []*graph.Node) bool {
|
|
for _, n := range nodes {
|
|
if a, _ := n.Meta["anonymous"].(bool); a {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|