Files
Andrey Kumanyaev 58809785dd feat(analysis): bring Java dead-code, entry-point and process analysis to parity
Dead-code analysis treated every Java symbol as exported: the export
check only understood Go capitalization and otherwise fell back to a
leading-underscore heuristic, so private and package-private Java methods
were never reported. isExportedNode now reads the extracted visibility
modifier, and a non-public @Override is rescued from a false positive
since it is reached through its supertype contract.

Add detectJava to the entrypoints package: Spring stereotypes and request
handlers, JAX-RS resources, annotated servlets, JUnit tests, lifecycle
callbacks and main are stamped as framework entry points. Unlike the
path-based detectors it stamps the individual annotated members, not the
file node, so a controller's dead private helper stays reportable. Detect
now also receives edges, since Java annotations are carried on annotation
edges.

Process discovery is visibility-aware and boosts stamped framework entry
points as process roots, excluding test fixtures which would only add
noise.
2026-06-05 23:20:39 +02:00

50 lines
2.1 KiB
Go

package analysis
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/zzet/gortex/internal/graph"
)
// TestScoreEntryPoint_JavaVisibility checks a private Java helper is not
// handed the public-API entry-point boost (it must score below an
// otherwise-identical public method).
func TestScoreEntryPoint_JavaVisibility(t *testing.T) {
pub := &graph.Node{
ID: "S.java::doWork", Kind: graph.KindMethod, Name: "doWork", Language: "java",
Meta: map[string]any{"visibility": "public"},
}
priv := &graph.Node{
ID: "S.java::doWorkImpl", Kind: graph.KindMethod, Name: "doWorkImpl", Language: "java",
Meta: map[string]any{"visibility": "private"},
}
pubScore := scoreEntryPoint(pub, 3, 0)
privScore := scoreEntryPoint(priv, 3, 0)
require.Greater(t, pubScore, privScore, "public method must outscore an identical private one")
}
// TestScoreEntryPoint_JavaEntryBoost checks a stamped Spring handler is
// boosted as a process root, while a stamped JUnit test is not (tests
// stay live for dead-code but are noise as top-level processes).
func TestScoreEntryPoint_JavaEntryBoost(t *testing.T) {
// All three share a name with no entry/util pattern boost, so the
// only score difference comes from the entry-point stamp.
plain := &graph.Node{
ID: "S.java::execute", Kind: graph.KindMethod, Name: "execute", Language: "java",
Meta: map[string]any{"visibility": "public"},
}
handler := &graph.Node{
ID: "C.java::execute", Kind: graph.KindMethod, Name: "execute", Language: "java",
Meta: map[string]any{"visibility": "public", "entry_point": true, "entry_point_kind": "spring:handler"},
}
test := &graph.Node{
ID: "T.java::execute", Kind: graph.KindMethod, Name: "execute", Language: "java",
Meta: map[string]any{"visibility": "public", "entry_point": true, "entry_point_kind": "junit:test"},
}
require.Greater(t, scoreEntryPoint(handler, 3, 0), scoreEntryPoint(plain, 3, 0),
"a stamped Spring handler must outscore a plain method")
require.Equal(t, scoreEntryPoint(test, 3, 0), scoreEntryPoint(plain, 3, 0),
"a JUnit test must not get the process-root boost")
}