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

287 lines
7.0 KiB
Go

package languages
import (
"testing"
"github.com/zzet/gortex/internal/graph"
)
func TestGoSQL_DetectsTablesFromQuery(t *testing.T) {
src := `package foo
type DB struct{}
func (d *DB) Query(q string, args ...any) (any, error) { return nil, nil }
func (d *DB) Exec(q string, args ...any) (any, error) { return nil, nil }
func Run(db *DB) {
_, _ = db.Query("SELECT id, name FROM users WHERE active = true")
_, _ = db.Exec("INSERT INTO sessions (user_id) VALUES (1)")
}
`
fix := runGoExtract(t, src)
tables := fix.nodesByKind[graph.KindTable]
if len(tables) != 2 {
t.Fatalf("expected 2 KindTable, got %d: %+v", len(tables), tables)
}
gotIDs := map[string]bool{}
for _, n := range tables {
gotIDs[n.ID] = true
}
if !gotIDs["db::generic::users"] {
t.Errorf("missing users table, got %v", gotIDs)
}
if !gotIDs["db::generic::sessions"] {
t.Errorf("missing sessions table, got %v", gotIDs)
}
queries := fix.edgesByKind[graph.EdgeQueries]
if len(queries) != 2 {
t.Errorf("expected 2 EdgeQueries, got %d", len(queries))
}
}
func TestGoSQL_EmitsColumnReadsAndWrites(t *testing.T) {
src := `package foo
type DB struct{}
func (d *DB) Query(q string, args ...any) (any, error) { return nil, nil }
func (d *DB) Exec(q string, args ...any) (any, error) { return nil, nil }
func Run(db *DB) {
_, _ = db.Query("SELECT id, email FROM users WHERE id = $1")
_, _ = db.Exec("UPDATE users SET email = $1, updated_at = NOW() WHERE id = $2")
_, _ = db.Exec("INSERT INTO sessions (user_id, token) VALUES ($1, $2)")
}
`
fix := runGoExtract(t, src)
cols := fix.nodesByKind[graph.KindColumn]
if len(cols) == 0 {
t.Fatalf("expected KindColumn nodes; got none")
}
wantCols := map[string]bool{
"col::generic::users.id": false,
"col::generic::users.email": false,
"col::generic::users.updated_at": false,
"col::generic::sessions.user_id": false,
"col::generic::sessions.token": false,
}
for _, n := range cols {
if _, ok := wantCols[n.ID]; ok {
wantCols[n.ID] = true
}
}
for id, found := range wantCols {
if !found {
t.Errorf("expected KindColumn %s; got %v", id, nodeIDs(cols))
}
}
reads := fix.edgesByKind[graph.EdgeReadsCol]
hasIDRead := false
for _, e := range reads {
if e.To == "col::generic::users.id" {
hasIDRead = true
}
}
if !hasIDRead {
t.Errorf("expected EdgeReadsCol → users.id; got %v", edgeTargets(reads))
}
writes := fix.edgesByKind[graph.EdgeWritesCol]
hasEmailWrite := false
hasTokenWrite := false
for _, e := range writes {
if e.To == "col::generic::users.email" {
hasEmailWrite = true
}
if e.To == "col::generic::sessions.token" {
hasTokenWrite = true
}
}
if !hasEmailWrite {
t.Errorf("expected EdgeWritesCol → users.email; got %v", edgeTargets(writes))
}
if !hasTokenWrite {
t.Errorf("expected EdgeWritesCol → sessions.token; got %v", edgeTargets(writes))
}
}
func nodeIDs(nodes []*graph.Node) []string {
out := make([]string, 0, len(nodes))
for _, n := range nodes {
out = append(out, n.ID)
}
return out
}
func TestGoSQL_DynamicQuerySkipped(t *testing.T) {
src := `package foo
type DB struct{}
func (d *DB) Query(q string) (any, error) { return nil, nil }
func Run(db *DB, q string) {
_, _ = db.Query(q)
}
`
fix := runGoExtract(t, src)
if got := len(fix.nodesByKind[graph.KindTable]); got != 0 {
t.Errorf("dynamic query should not produce tables, got %d", got)
}
}
func TestGoSQL_NonSQLMethodIgnored(t *testing.T) {
src := `package foo
type Cache struct{}
func (c *Cache) Set(key string, val any) {}
func Run(c *Cache) {
c.Set("FROM users", nil)
}
`
fix := runGoExtract(t, src)
if got := len(fix.nodesByKind[graph.KindTable]); got != 0 {
t.Errorf("non-SQL method must not produce tables, got %d", got)
}
}
func TestGoSQL_SchemaQualifiedTables(t *testing.T) {
src := `package foo
type DB struct{}
func (d *DB) Query(q string) (any, error) { return nil, nil }
func Run(db *DB) {
_, _ = db.Query("SELECT * FROM public.users JOIN auth.sessions ON id = session_id")
}
`
fix := runGoExtract(t, src)
tables := fix.nodesByKind[graph.KindTable]
if len(tables) != 2 {
t.Fatalf("expected 2 schema-qualified tables, got %d", len(tables))
}
gotSchemas := map[string]string{}
for _, n := range tables {
schema, _ := n.Meta["schema"].(string)
table, _ := n.Meta["table"].(string)
gotSchemas[table] = schema
}
if gotSchemas["users"] != "public" {
t.Errorf("users schema = %q", gotSchemas["users"])
}
if gotSchemas["sessions"] != "auth" {
t.Errorf("sessions schema = %q", gotSchemas["sessions"])
}
}
func TestGoSQL_DialectInferredFromPostgresImport(t *testing.T) {
src := `package foo
import (
_ "github.com/lib/pq"
"database/sql"
)
func Run(db *sql.DB) {
_, _ = db.Query("SELECT * FROM users")
}
`
fix := runGoExtract(t, src)
tables := fix.nodesByKind[graph.KindTable]
if len(tables) != 1 {
t.Fatalf("expected 1 table, got %d", len(tables))
}
if tables[0].ID != "db::postgres::users" {
t.Errorf("expected postgres dialect on ID, got %q", tables[0].ID)
}
if d, _ := tables[0].Meta["dialect"].(string); d != "postgres" {
t.Errorf("dialect meta = %q", d)
}
}
func TestGoSQL_DialectInferredFromMysqlImport(t *testing.T) {
src := `package foo
import (
_ "github.com/go-sql-driver/mysql"
"database/sql"
)
func Run(db *sql.DB) {
_, _ = db.Query("SELECT * FROM orders")
}
`
fix := runGoExtract(t, src)
tables := fix.nodesByKind[graph.KindTable]
if len(tables) != 1 || tables[0].ID != "db::mysql::orders" {
t.Errorf("expected mysql dialect, got %+v", tables)
}
}
func TestGoSQL_DialectInferredFromPgxV5(t *testing.T) {
// Major-version-suffix imports (pgx/v5) resolve via the
// prefix-stripped lookup.
src := `package foo
import (
"github.com/jackc/pgx/v5"
)
type Pool struct{}
func (p *Pool) Query(q string) (any, error) { return nil, nil }
func Run(p *Pool) {
_, _ = p.Query("SELECT * FROM users")
}
`
fix := runGoExtract(t, src)
tables := fix.nodesByKind[graph.KindTable]
if len(tables) != 1 || tables[0].ID != "db::postgres::users" {
t.Errorf("expected postgres (pgx/v5 prefix-stripped to pgx), got %+v", tables)
}
}
func TestGoSQL_NoDriverFallsBackToGeneric(t *testing.T) {
src := `package foo
type DB struct{}
func (d *DB) Query(q string) (any, error) { return nil, nil }
func Run(db *DB) {
_, _ = db.Query("SELECT * FROM users")
}
`
fix := runGoExtract(t, src)
tables := fix.nodesByKind[graph.KindTable]
if len(tables) != 1 || tables[0].ID != "db::generic::users" {
t.Errorf("expected generic dialect fallback, got %+v", tables)
}
}
func TestGoSQL_DedupedAcrossCallSites(t *testing.T) {
src := `package foo
type DB struct{}
func (d *DB) Query(q string) (any, error) { return nil, nil }
func A(db *DB) { _, _ = db.Query("SELECT * FROM users") }
func B(db *DB) { _, _ = db.Query("SELECT id FROM users WHERE id = 1") }
`
fix := runGoExtract(t, src)
tables := fix.nodesByKind[graph.KindTable]
if len(tables) != 1 {
t.Errorf("expected 1 deduped users node, got %d", len(tables))
}
if got := len(fix.edgesByKind[graph.EdgeQueries]); got != 2 {
t.Errorf("expected 2 EdgeQueries (one per caller), got %d", got)
}
}