Files
fn_registry/functions/infra/audit_uses_functions_test.go
T
egutierrez f65178025d feat(audit+pipelines): mejor deteccion + auto-recovery TBD
- audit_uses_functions: parsea Go func name del signature (no solo PascalCase de name); skip _test.go y dirs e2e/tests/testdata/build/dist/vendor/node_modules; add scanner TS para frontend/ con import "@fn_library/<area>/<name>" → <name>_ts_<area>; unused solo flagea langs efectivamente escaneados
- full_git_push: si pre-commit hook bloquea, retry con --no-verify y reporta bypass; si push rechazado por non-fast-forward, fetch + merge --no-ff auto y reintenta; exit code 1 + bloque [!!] ERRORES si quedan errores reales
- full_git_pull: si pull --ff-only diverge, intenta merge --no-ff auto contra @{u}; conserva [merged-auto] o aborta con [diverged] si conflicto; exit code 1 si quedan repos pendientes
- slash commands /full-git-push y /full-git-pull: documentadas obligaciones del agente para garantizar TBD (master siempre alineado con remote)
- kanban app.md: quita percentile_int64 (transitivo via duration_stats)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 03:57:51 +02:00

179 lines
4.6 KiB
Go

package infra
import (
"database/sql"
"os"
"path/filepath"
"testing"
_ "github.com/mattn/go-sqlite3"
)
// createTestRegistryDB creates a minimal registry.db with the given apps and
// a single function (random_hex_id_go_core in domain core, lang go).
func createTestRegistryDB(t *testing.T, root string, apps []struct {
id string
lang string
dirPath string
usesFunctions string
}) {
t.Helper()
dbPath := filepath.Join(root, "registry.db")
db, err := sql.Open("sqlite3", dbPath)
if err != nil {
t.Fatal(err)
}
defer db.Close()
_, err = db.Exec(`
CREATE TABLE functions (
id TEXT PRIMARY KEY,
name TEXT,
domain TEXT,
lang TEXT,
signature TEXT,
file_path TEXT
);
CREATE TABLE apps (
id TEXT PRIMARY KEY,
lang TEXT,
dir_path TEXT,
uses_functions TEXT DEFAULT '[]'
);
INSERT INTO functions (id, name, domain, lang, file_path)
VALUES ('random_hex_id_go_core','random_hex_id','core','go','functions/core/random_hex_id.go');
`)
if err != nil {
t.Fatal(err)
}
for _, a := range apps {
_, err = db.Exec(
`INSERT INTO apps (id, lang, dir_path, uses_functions) VALUES (?,?,?,?)`,
a.id, a.lang, a.dirPath, a.usesFunctions,
)
if err != nil {
t.Fatalf("insert app %s: %v", a.id, err)
}
}
}
// TestAuditUsesFunctions_DetectsMissing verifies that a Go app that calls
// RandomHexID in its source but declares empty uses_functions gets
// random_hex_id_go_core reported as missing.
func TestAuditUsesFunctions_DetectsMissing(t *testing.T) {
t.Run("missing function detected for Go app", func(t *testing.T) {
root := t.TempDir()
createTestRegistryDB(t, root, []struct {
id, lang, dirPath, usesFunctions string
}{
{"testapp_go_tools", "go", "apps/testapp", `[]`},
})
appDir := filepath.Join(root, "apps", "testapp")
if err := os.MkdirAll(appDir, 0755); err != nil {
t.Fatal(err)
}
goSrc := `package main
import (
"fmt"
"fn-registry/functions/core"
)
func main() {
id := core.RandomHexID(8)
fmt.Println(id)
}
`
if err := os.WriteFile(filepath.Join(appDir, "main.go"), []byte(goSrc), 0644); err != nil {
t.Fatal(err)
}
results, err := AuditUsesFunctions(root)
if err != nil {
t.Fatalf("AuditUsesFunctions: %v", err)
}
if len(results) != 1 {
t.Fatalf("expected 1 result, got %d", len(results))
}
got := results[0]
if len(got.Missing) != 1 || got.Missing[0] != "random_hex_id_go_core" {
t.Errorf("Missing = %v, want [random_hex_id_go_core]", got.Missing)
}
if len(got.Unused) != 0 {
t.Errorf("Unused = %v, want []", got.Unused)
}
})
}
// TestAuditUsesFunctions_DetectsUnused verifies that a function declared in
// uses_functions but not called in source is reported as unused.
func TestAuditUsesFunctions_DetectsUnused(t *testing.T) {
t.Run("unused function detected for Go app", func(t *testing.T) {
root := t.TempDir()
createTestRegistryDB(t, root, []struct {
id, lang, dirPath, usesFunctions string
}{
{"testapp2_go_tools", "go", "apps/testapp2", `["random_hex_id_go_core"]`},
})
appDir := filepath.Join(root, "apps", "testapp2")
if err := os.MkdirAll(appDir, 0755); err != nil {
t.Fatal(err)
}
goSrc := `package main
import "fmt"
func main() { fmt.Println("hello") }
`
if err := os.WriteFile(filepath.Join(appDir, "main.go"), []byte(goSrc), 0644); err != nil {
t.Fatal(err)
}
results, err := AuditUsesFunctions(root)
if err != nil {
t.Fatalf("AuditUsesFunctions: %v", err)
}
if len(results) != 1 {
t.Fatalf("expected 1 result, got %d", len(results))
}
got := results[0]
if len(got.Unused) != 1 || got.Unused[0] != "random_hex_id_go_core" {
t.Errorf("Unused = %v, want [random_hex_id_go_core]", got.Unused)
}
if len(got.Missing) != 0 {
t.Errorf("Missing = %v, want []", got.Missing)
}
})
}
// TestAuditUsesFunctions_MissingDir verifies that apps whose dir_path does not
// exist on disk get an entry with nil Missing/Unused slices (cannot inspect).
func TestAuditUsesFunctions_MissingDir(t *testing.T) {
t.Run("missing dir returns entry with nil slices", func(t *testing.T) {
root := t.TempDir()
createTestRegistryDB(t, root, []struct {
id, lang, dirPath, usesFunctions string
}{
{"testapp3_go_tools", "go", "apps/testapp3", `[]`},
})
// intentionally do NOT create apps/testapp3 on disk
results, err := AuditUsesFunctions(root)
if err != nil {
t.Fatalf("AuditUsesFunctions: %v", err)
}
if len(results) != 1 {
t.Fatalf("expected 1 result, got %d", len(results))
}
got := results[0]
if got.Missing != nil {
t.Errorf("Missing should be nil for missing dir, got %v", got.Missing)
}
if got.Unused != nil {
t.Errorf("Unused should be nil for missing dir, got %v", got.Unused)
}
})
}