Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

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
}