Files
zzet--gortex/internal/indexer/spring_datasource_test.go
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

171 lines
4.9 KiB
Go

package indexer
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"github.com/zzet/gortex/internal/config"
"github.com/zzet/gortex/internal/graph"
"github.com/zzet/gortex/internal/parser"
"github.com/zzet/gortex/internal/parser/languages"
)
func TestIndex_SpringDatasourceConditionalOnPropertyReadsConfig(t *testing.T) {
dir := t.TempDir()
javaDir := filepath.Join(dir, "src", "main", "java", "com", "example")
resDir := filepath.Join(dir, "src", "main", "resources")
require.NoError(t, os.MkdirAll(javaDir, 0o755))
require.NoError(t, os.MkdirAll(resDir, 0o755))
writeFile(t, filepath.Join(javaDir, "JdbcConfig.java"), `
package com.example;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnProperty(prefix = "spring.datasource", name = "url")
public class JdbcConfig {
}
`)
writeFile(t, filepath.Join(resDir, "application.yml"), `
spring:
datasource:
url: jdbc:h2:mem:test
username: sa
`)
g := graph.New()
reg := parser.NewRegistry()
languages.RegisterAll(reg)
idx := New(g, reg, config.Default().Index, zap.NewNop())
_, err := idx.Index(dir)
require.NoError(t, err)
javaGraphPath := filepath.Join("src", "main", "java", "com", "example", "JdbcConfig.java")
cfgID := javaGraphPath + "::JdbcConfig"
keyID := "cfg::spring::spring.datasource.url"
if g.GetNode(keyID) == nil {
t.Logf("file nodes: %v", nodeIDsOfKind(g, graph.KindFile))
t.Logf("config nodes: %v", nodeIDsOfKind(g, graph.KindConfigKey))
}
require.NotNil(t, g.GetNode(keyID), "spring.datasource.url config key should be indexed")
require.True(t, hasEdge(g, cfgID, keyID, graph.EdgeReadsConfig),
"JdbcConfig should read spring.datasource.url via @ConditionalOnProperty")
}
func TestIndex_SpringExplicitDataSourceBeanLinksToBeanConsumers(t *testing.T) {
dir := t.TempDir()
javaDir := filepath.Join(dir, "src", "main", "java", "com", "example")
require.NoError(t, os.MkdirAll(javaDir, 0o755))
writeFile(t, filepath.Join(javaDir, "JdbcConfig.java"), `
package com.example;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;
public class JdbcConfig {
@Bean
DataSource dataSource() {
return null;
}
@Bean
JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
`)
g := graph.New()
reg := parser.NewRegistry()
languages.RegisterAll(reg)
idx := New(g, reg, config.Default().Index, zap.NewNop())
_, err := idx.Index(dir)
require.NoError(t, err)
javaGraphPath := filepath.Join("src", "main", "java", "com", "example", "JdbcConfig.java")
classID := javaGraphPath + "::JdbcConfig"
dataSourceID := classID + ".dataSource"
if !hasEdge(g, classID, dataSourceID, graph.EdgeCalls) {
t.Logf("calls edges: %v", edgePairsOfKind(g, graph.EdgeCalls))
t.Logf("provides edges: %v", edgePairsOfKind(g, graph.EdgeProvides))
}
require.True(t, hasEdge(g, classID, dataSourceID, graph.EdgeCalls),
"JdbcConfig should have a spring.Bean edge to the DataSource factory because jdbcTemplate consumes DataSource")
}
func TestIndex_SpringDataSourcePlainMethodDoesNotLinkBeanFactory(t *testing.T) {
dir := t.TempDir()
javaDir := filepath.Join(dir, "src", "main", "java", "com", "example")
require.NoError(t, os.MkdirAll(javaDir, 0o755))
writeFile(t, filepath.Join(javaDir, "JdbcConfig.java"), `
package com.example;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
public class JdbcConfig {
@Bean
DataSource dataSource() {
return null;
}
void inspect(DataSource dataSource) {
}
}
`)
g := graph.New()
reg := parser.NewRegistry()
languages.RegisterAll(reg)
idx := New(g, reg, config.Default().Index, zap.NewNop())
_, err := idx.Index(dir)
require.NoError(t, err)
javaGraphPath := filepath.Join("src", "main", "java", "com", "example", "JdbcConfig.java")
classID := javaGraphPath + "::JdbcConfig"
dataSourceID := classID + ".dataSource"
if hasEdge(g, classID, dataSourceID, graph.EdgeCalls) {
t.Logf("calls edges: %v", edgePairsOfKind(g, graph.EdgeCalls))
}
require.False(t, hasEdge(g, classID, dataSourceID, graph.EdgeCalls),
"a plain method parameter should not make the class look like a Spring bean consumer")
}
func hasEdge(g *graph.Graph, from, to string, kind graph.EdgeKind) bool {
for _, e := range g.AllEdges() {
if e.From == from && e.To == to && e.Kind == kind {
return true
}
}
return false
}
func nodeIDsOfKind(g *graph.Graph, kind graph.NodeKind) []string {
var out []string
for _, n := range g.AllNodes() {
if n.Kind == kind {
out = append(out, n.ID)
}
}
return out
}
func edgePairsOfKind(g *graph.Graph, kind graph.EdgeKind) []string {
var out []string
for _, e := range g.AllEdges() {
if e.Kind == kind {
out = append(out, e.From+"->"+e.To)
}
}
return out
}