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) } }) }