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
198 lines
5.4 KiB
Go
198 lines
5.4 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/zzet/gortex/internal/graph"
|
|
)
|
|
|
|
// TestRunRepo_LocalFixture is the end-to-end smoke test: runs the
|
|
// full bench against the in-tree nestjs fixture and asserts the
|
|
// shape of the resulting row. Uses a private cache dir so it doesn't
|
|
// touch the developer's real ~/.cache/gortex/bench tree.
|
|
func TestRunRepo_LocalFixture(t *testing.T) {
|
|
fixturePath, err := filepath.Abs(filepath.Join("..", "fixtures", "di", "nestjs"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := os.Stat(fixturePath); err != nil {
|
|
t.Skipf("fixture not available: %v", err)
|
|
}
|
|
|
|
// Run with a tiny query set to keep the test fast.
|
|
queries := []string{"AppModule", "ConfigService", "auth handler"}
|
|
row := runRepo(repoSpec{
|
|
Slug: "nestjs-fixture",
|
|
Path: fixturePath,
|
|
Local: true,
|
|
}, queries, budgets{
|
|
// Generous budgets so a slow CI doesn't flake — the real
|
|
// validation lives in main.go's strict-mode path.
|
|
ImpactP95Ms: 50.0,
|
|
SearchP95Ms: 100.0,
|
|
})
|
|
|
|
if row.Error != "" {
|
|
t.Fatalf("runRepo error: %s", row.Error)
|
|
}
|
|
if row.Files == 0 {
|
|
t.Errorf("expected indexed files, got 0")
|
|
}
|
|
if row.Nodes == 0 {
|
|
t.Errorf("expected graph nodes, got 0")
|
|
}
|
|
if row.ColdIndexMs <= 0 {
|
|
t.Errorf("cold-index time should be > 0, got %.3f", row.ColdIndexMs)
|
|
}
|
|
if row.SearchP95Ms <= 0 {
|
|
t.Errorf("search p95 should be > 0, got %.3f", row.SearchP95Ms)
|
|
}
|
|
if row.IncrementalMs <= 0 {
|
|
t.Errorf("incremental should be > 0, got %.3f", row.IncrementalMs)
|
|
}
|
|
if row.DBBytes <= 0 {
|
|
t.Errorf("DB size should be > 0, got %d", row.DBBytes)
|
|
}
|
|
if row.BudgetViolations > 0 {
|
|
t.Errorf("generous budgets shouldn't violate, got %d", row.BudgetViolations)
|
|
}
|
|
}
|
|
|
|
func TestPickImpactSeeds_RespectsLimit(t *testing.T) {
|
|
g := graph.New()
|
|
for i := range 5 {
|
|
g.AddNode(&graph.Node{
|
|
ID: funcID(i),
|
|
Name: funcName(i),
|
|
Kind: graph.KindFunction,
|
|
})
|
|
}
|
|
seeds := pickImpactSeeds(g, 3)
|
|
if len(seeds) != 3 {
|
|
t.Errorf("len(seeds)=%d, want 3 (n < pool)", len(seeds))
|
|
}
|
|
// Pool smaller than n: return all.
|
|
seeds = pickImpactSeeds(g, 10)
|
|
if len(seeds) != 5 {
|
|
t.Errorf("len(seeds)=%d, want 5 (n > pool)", len(seeds))
|
|
}
|
|
}
|
|
|
|
func TestPickImpactSeeds_FunctionsAndMethodsOnly(t *testing.T) {
|
|
g := graph.New()
|
|
g.AddNode(&graph.Node{ID: "f.go::F", Name: "F", Kind: graph.KindFunction})
|
|
g.AddNode(&graph.Node{ID: "f.go::M", Name: "M", Kind: graph.KindMethod})
|
|
g.AddNode(&graph.Node{ID: "f.go::T", Name: "T", Kind: graph.KindType})
|
|
g.AddNode(&graph.Node{ID: "f.go::V", Name: "V", Kind: graph.KindVariable})
|
|
seeds := pickImpactSeeds(g, 10)
|
|
if len(seeds) != 2 {
|
|
t.Errorf("got %d seeds %v, want 2 (function + method only)", len(seeds), seeds)
|
|
}
|
|
}
|
|
|
|
func TestPickIncrementalFiles_RespectsLimit(t *testing.T) {
|
|
g := graph.New()
|
|
for i := range 8 {
|
|
g.AddNode(&graph.Node{
|
|
ID: fileID(i),
|
|
Kind: graph.KindFile,
|
|
FilePath: fileName(i),
|
|
})
|
|
}
|
|
files := pickIncrementalFiles(g, 3)
|
|
if len(files) != 3 {
|
|
t.Errorf("len(files)=%d, want 3", len(files))
|
|
}
|
|
for _, f := range files {
|
|
if f == "" {
|
|
t.Errorf("empty file path returned")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTouchFile_MissingFileSilent(t *testing.T) {
|
|
if err := touchFile(filepath.Join(t.TempDir(), "missing.go")); err != nil {
|
|
t.Errorf("missing file should not error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestTouchFile_BumpsMtime(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "x.go")
|
|
if err := os.WriteFile(path, []byte("// x"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
st1, _ := os.Stat(path)
|
|
// Sleep to ensure mtime granularity is exceeded.
|
|
old := st1.ModTime()
|
|
if err := touchFile(path); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
st2, _ := os.Stat(path)
|
|
if !st2.ModTime().After(old) && !st2.ModTime().Equal(old) {
|
|
t.Errorf("mtime regressed: %v → %v", old, st2.ModTime())
|
|
}
|
|
}
|
|
|
|
func TestEstimateDBSize_ScalesWithGraph(t *testing.T) {
|
|
small := graph.New()
|
|
small.AddNode(&graph.Node{ID: "s1", Name: "s1", Kind: graph.KindFunction})
|
|
smallSize := estimateDBSize(small)
|
|
|
|
large := graph.New()
|
|
for i := range 100 {
|
|
large.AddNode(&graph.Node{ID: funcID(i), Name: funcName(i), Kind: graph.KindFunction})
|
|
}
|
|
largeSize := estimateDBSize(large)
|
|
|
|
if smallSize <= 0 || largeSize <= 0 {
|
|
t.Errorf("DB size estimates should be positive, got small=%d large=%d", smallSize, largeSize)
|
|
}
|
|
if largeSize <= smallSize {
|
|
t.Errorf("larger graph should yield larger estimate, got small=%d large=%d", smallSize, largeSize)
|
|
}
|
|
}
|
|
|
|
func TestDefaultRepoSet_IncludeLinuxFlag(t *testing.T) {
|
|
without := defaultRepoSet(false)
|
|
with := defaultRepoSet(true)
|
|
if len(without) != 3 || len(with) != 4 {
|
|
t.Errorf("expected 3 without linux + 4 with, got %d / %d", len(without), len(with))
|
|
}
|
|
// linux must be in the includeLinux=true set, NOT in the false set.
|
|
hasLinux := func(rs []repoSpec) bool {
|
|
for _, r := range rs {
|
|
if r.Slug == "linux" {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
if hasLinux(without) {
|
|
t.Error("defaultRepoSet(false) should not include linux")
|
|
}
|
|
if !hasLinux(with) {
|
|
t.Error("defaultRepoSet(true) should include linux")
|
|
}
|
|
}
|
|
|
|
// --- helpers --------------------------------------------------------
|
|
|
|
func funcID(i int) string { return "f.go::F" + itoa(i) }
|
|
func funcName(i int) string { return "F" + itoa(i) }
|
|
func fileID(i int) string { return "file" + itoa(i) + ".go" }
|
|
func fileName(i int) string { return "file" + itoa(i) + ".go" }
|
|
func itoa(i int) string {
|
|
if i == 0 {
|
|
return "0"
|
|
}
|
|
var out []byte
|
|
for i > 0 {
|
|
out = append([]byte{byte('0' + i%10)}, out...)
|
|
i /= 10
|
|
}
|
|
return string(out)
|
|
}
|