Files
zzet--gortex/internal/semantic/enrichment_marker_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

256 lines
9.3 KiB
Go

package semantic
import (
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"github.com/zzet/gortex/internal/graph"
"github.com/zzet/gortex/internal/graph/store_sqlite"
)
// newMarkerStore opens an on-disk SQLite store — the only backend that
// persists the enrichment completion marker (graph.EnrichmentStateStore). A
// memory graph does not implement it, which is what makes the gate a no-op
// there (see TestEnrichAll_MemoryBackendNeverGates).
func newMarkerStore(t *testing.T) *store_sqlite.Store {
t.Helper()
s, err := store_sqlite.Open(filepath.Join(t.TempDir(), "store.sqlite"))
require.NoError(t, err)
t.Cleanup(func() { _ = s.Close() })
return s
}
// markerManager builds a Manager with one available "test-go" provider whose
// Enrich flips *ran when it runs, so a test can assert whether the marker gate
// skipped the pass before the provider was ever invoked.
func markerManager(t *testing.T) (*Manager, *bool) {
t.Helper()
ran := new(bool)
cfg := Config{
Enabled: true,
Providers: []ProviderConfig{
{Name: "test-go", Languages: []string{"go"}, Priority: 1, Enabled: true},
},
}
mgr := NewManager(cfg, zap.NewNop())
mgr.RegisterProvider(&mockProvider{
name: "test-go",
languages: []string{"go"},
available: true,
enrichFunc: func(g graph.Store, root string) (*EnrichResult, error) {
*ran = true
return &EnrichResult{Provider: "test-go", Language: "go", CoveragePercent: 95.0}, nil
},
})
return mgr, ran
}
const markerRepo = "myrepo"
func markerRoots(t *testing.T) map[string]string {
t.Helper()
return map[string]string{markerRepo: t.TempDir()}
}
// TestEnrichAll_SkipsWhenMarkerCurrent: a persisted marker whose sha matches
// the threaded sha, on a clean tree, skips the provider's pass entirely.
func TestEnrichAll_SkipsWhenMarkerCurrent(t *testing.T) {
mgr, ran := markerManager(t)
g := newMarkerStore(t)
require.NoError(t, g.SetEnrichmentState(graph.EnrichmentState{
RepoPrefix: markerRepo, Provider: "test-go", IndexedSHA: "abc123",
}))
opts := EnrichOptions{RepoState: map[string]RepoEnrichState{
markerRepo: {SHA: "abc123", Dirty: false},
}}
results, partial, err := mgr.EnrichAll(g, markerRoots(t), opts)
require.NoError(t, err)
assert.False(t, *ran, "provider must be skipped when the marker sha matches on a clean tree")
assert.Empty(t, results)
assert.False(t, partial[markerRepo])
}
// TestEnrichAll_NoSkipCases: the gate never skips when the sha moved, the tree
// is dirty, no marker row exists, or the caller supplied no sha.
func TestEnrichAll_NoSkipCases(t *testing.T) {
cases := []struct {
name string
seedSHA string // "" → don't seed a marker row
optSHA string
dirty bool
}{
{"sha moved", "old", "new", false},
{"dirty tree", "abc", "abc", true},
{"missing marker", "", "abc", false},
{"no sha supplied", "abc", "", false},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
mgr, ran := markerManager(t)
g := newMarkerStore(t)
if c.seedSHA != "" {
require.NoError(t, g.SetEnrichmentState(graph.EnrichmentState{
RepoPrefix: markerRepo, Provider: "test-go", IndexedSHA: c.seedSHA,
}))
}
opts := EnrichOptions{RepoState: map[string]RepoEnrichState{
markerRepo: {SHA: c.optSHA, Dirty: c.dirty},
}}
results, _, err := mgr.EnrichAll(g, markerRoots(t), opts)
require.NoError(t, err)
assert.True(t, *ran, "provider must run (gate must not skip)")
assert.Len(t, results, 1)
})
}
}
// TestEnrichAll_WritesMarkerOnCleanCompletion: a non-partial pass persists the
// completion marker with the threaded sha and the pass's coverage.
func TestEnrichAll_WritesMarkerOnCleanCompletion(t *testing.T) {
mgr, _ := markerManager(t)
g := newMarkerStore(t)
opts := EnrichOptions{RepoState: map[string]RepoEnrichState{
markerRepo: {SHA: "sha-xyz"},
}}
results, partial, err := mgr.EnrichAll(g, markerRoots(t), opts)
require.NoError(t, err)
require.Len(t, results, 1)
assert.False(t, partial[markerRepo])
got, found, err := g.GetEnrichmentState(markerRepo, "test-go")
require.NoError(t, err)
require.True(t, found, "a clean non-partial completion must persist a marker")
assert.Equal(t, "sha-xyz", got.IndexedSHA)
assert.InDelta(t, 95.0, got.Coverage, 0.001)
assert.NotZero(t, got.CompletedAt)
}
// TestEnrichAll_PartialWritesNoMarker: a partial pass marks the repo
// incomplete and persists NO completion marker (it must not claim the repo is
// fully enriched at this sha).
func TestEnrichAll_PartialWritesNoMarker(t *testing.T) {
cfg := Config{
Enabled: true,
Providers: []ProviderConfig{
{Name: "test-go", Languages: []string{"go"}, Priority: 1, Enabled: true},
},
}
mgr := NewManager(cfg, zap.NewNop())
mgr.RegisterProvider(&mockProvider{
name: "test-go",
languages: []string{"go"},
available: true,
enrichFunc: func(g graph.Store, root string) (*EnrichResult, error) {
return &EnrichResult{Provider: "test-go", Language: "go", Partial: true, CoveragePercent: 40}, nil
},
})
g := newMarkerStore(t)
opts := EnrichOptions{RepoState: map[string]RepoEnrichState{
markerRepo: {SHA: "sha-xyz"},
}}
_, partial, err := mgr.EnrichAll(g, markerRoots(t), opts)
require.NoError(t, err)
assert.True(t, partial[markerRepo], "a partial pass marks the repo incomplete")
_, found, err := g.GetEnrichmentState(markerRepo, "test-go")
require.NoError(t, err)
assert.False(t, found, "a partial pass must NOT persist a completion marker")
}
// TestEnrichAll_DirtyWritesNoMarker: a non-partial pass over a DIRTY working
// tree must persist NO completion marker. The pass enriched uncommitted
// content, so its edges do not describe the committed state the HEAD sha names;
// recording a marker keyed by that sha would be honored as authoritative once
// the tree becomes clean at the same sha (enrichMarkerCurrent refuses to skip
// while dirty but skips once clean), suppressing the re-enrichment the reverted
// files need.
func TestEnrichAll_DirtyWritesNoMarker(t *testing.T) {
mgr, ran := markerManager(t)
g := newMarkerStore(t)
opts := EnrichOptions{RepoState: map[string]RepoEnrichState{
markerRepo: {SHA: "sha-xyz", Dirty: true},
}}
results, partial, err := mgr.EnrichAll(g, markerRoots(t), opts)
require.NoError(t, err)
require.True(t, *ran, "a dirty tree has no current marker, so the provider must run")
require.Len(t, results, 1)
assert.False(t, partial[markerRepo], "the pass completed non-partial")
_, found, err := g.GetEnrichmentState(markerRepo, "test-go")
require.NoError(t, err)
assert.False(t, found, "a dirty pass must NOT persist a completion marker")
}
// TestEnrichAll_ForceEnvBypassesMarkerGate: GORTEX_WARMUP_FORCE_ENRICH=1
// forces the pass even when the marker is current.
func TestEnrichAll_ForceEnvBypassesMarkerGate(t *testing.T) {
t.Setenv("GORTEX_WARMUP_FORCE_ENRICH", "1")
mgr, ran := markerManager(t)
g := newMarkerStore(t)
require.NoError(t, g.SetEnrichmentState(graph.EnrichmentState{
RepoPrefix: markerRepo, Provider: "test-go", IndexedSHA: "abc123",
}))
opts := EnrichOptions{RepoState: map[string]RepoEnrichState{
markerRepo: {SHA: "abc123"},
}}
results, _, err := mgr.EnrichAll(g, markerRoots(t), opts)
require.NoError(t, err)
assert.True(t, *ran, "force env must bypass the marker skip gate")
assert.Len(t, results, 1)
}
// TestEnrichAll_ForceRepoStateBypassesMarkerGate: a repo flagged Force in its
// RepoEnrichState re-runs the provider even when the completion marker still
// records its sha on a clean tree. A whole-repo re-track sets Force because it
// evicted the persisted enrichment edges the marker claims are present, so
// skipping re-enrichment would leave the graph durably missing them.
func TestEnrichAll_ForceRepoStateBypassesMarkerGate(t *testing.T) {
mgr, ran := markerManager(t)
g := newMarkerStore(t)
require.NoError(t, g.SetEnrichmentState(graph.EnrichmentState{
RepoPrefix: markerRepo, Provider: "test-go", IndexedSHA: "abc123",
}))
opts := EnrichOptions{RepoState: map[string]RepoEnrichState{
markerRepo: {SHA: "abc123", Force: true},
}}
results, partial, err := mgr.EnrichAll(g, markerRoots(t), opts)
require.NoError(t, err)
assert.True(t, *ran, "Force must bypass the marker skip gate even at a matching clean sha")
require.Len(t, results, 1)
assert.False(t, partial[markerRepo])
// The clean non-partial completion refreshed the marker at the same sha, so
// a later unchanged restart (Force=false) skips again — Force invalidates
// the stale marker, it does not stop the pass from recording a fresh one.
got, found, err := g.GetEnrichmentState(markerRepo, "test-go")
require.NoError(t, err)
require.True(t, found, "a forced clean completion must still persist a marker")
assert.Equal(t, "abc123", got.IndexedSHA)
}
// TestEnrichAll_MemoryBackendNeverGates: a backend that does not persist
// enrichment state (the in-memory graph) never gates — the type assertion
// fails, so the provider always runs even with a current-looking sha.
func TestEnrichAll_MemoryBackendNeverGates(t *testing.T) {
mgr, ran := markerManager(t)
g := graph.New()
opts := EnrichOptions{RepoState: map[string]RepoEnrichState{
markerRepo: {SHA: "abc123"},
}}
results, _, err := mgr.EnrichAll(g, markerRoots(t), opts)
require.NoError(t, err)
assert.True(t, *ran, "memory backend does not persist markers, so it never gates")
assert.Len(t, results, 1)
}