Files
zzet--gortex/internal/indexer/contracts_bulk_commit_test.go
wehub-resource-sync a06f331eb8
install-script / powershell-syntax (push) Waiting to run
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
install-script / install (macos-14) (push) Waiting to run
install-script / install (ubuntu-latest) (push) Waiting to run
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
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

209 lines
6.2 KiB
Go

package indexer
import (
"os"
"path/filepath"
"sync/atomic"
"testing"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"github.com/zzet/gortex/internal/config"
"github.com/zzet/gortex/internal/contracts"
"github.com/zzet/gortex/internal/graph"
"github.com/zzet/gortex/internal/parser"
)
// recordingBulkGraph embeds *graph.Graph (auto-satisfying graph.Store)
// and adds the BulkLoader methods so it also satisfies
// graph.BulkLoader. It records the order of BeginBulkLoad / AddBatch
// / FlushBulk calls so a test can assert that the contracts commit
// path routes through the bulk fast lane instead of per-row
// AddNode / AddEdge writes.
type recordingBulkGraph struct {
*graph.Graph
calls []string
addNode atomic.Int64
addEdge atomic.Int64
}
func newRecordingBulkGraph() *recordingBulkGraph {
return &recordingBulkGraph{Graph: graph.New()}
}
func (r *recordingBulkGraph) BeginBulkLoad() {
r.calls = append(r.calls, "BeginBulkLoad")
}
func (r *recordingBulkGraph) FlushBulk() error {
r.calls = append(r.calls, "FlushBulk")
return nil
}
func (r *recordingBulkGraph) AddNode(n *graph.Node) {
r.addNode.Add(1)
r.Graph.AddNode(n)
}
func (r *recordingBulkGraph) AddEdge(e *graph.Edge) {
r.addEdge.Add(1)
r.Graph.AddEdge(e)
}
func (r *recordingBulkGraph) AddBatch(nodes []*graph.Node, edges []*graph.Edge) {
r.calls = append(r.calls, "AddBatch")
r.Graph.AddBatch(nodes, edges)
}
// TestCommitContracts_BatchesViaAddBatch asserts that the final
// write phase of commitContracts emits all contract nodes and
// edges through a single AddBatch call and does NOT engage the
// BulkLoader COPY bracket. Contract IDs frequently coincide with
// existing source-symbol IDs (a handler appears as both a Go
// function and an HTTP-contract anchor), and the on-disk backend's
// bulk load is INSERT-only on the node table — wrapping the contracts
// pass in BeginBulkLoad/FlushBulk would crash on the first collision.
// AddBatch's per-call MERGE path absorbs duplicates safely.
func TestCommitContracts_BatchesViaAddBatch(t *testing.T) {
g := newRecordingBulkGraph()
require.Implements(t, (*graph.BulkLoader)(nil), graph.Store(g))
// Anchor symbol the contract's provides-edge will point from.
g.Graph.AddNode(&graph.Node{
ID: "pkg/foo.go::Handler.List",
Kind: graph.KindMethod,
Name: "List",
FilePath: "pkg/foo.go",
Language: "go",
})
idx := New(g, parser.NewRegistry(), config.Default().Index, zap.NewNop())
reg := contracts.NewRegistry()
reg.Add(contracts.Contract{
ID: "http::GET::/v1/items",
Type: contracts.ContractHTTP,
Role: contracts.RoleProvider,
SymbolID: "pkg/foo.go::Handler.List",
FilePath: "pkg/foo.go",
Line: 42,
})
reg.Add(contracts.Contract{
ID: "http::POST::/v1/items",
Type: contracts.ContractHTTP,
Role: contracts.RoleConsumer,
SymbolID: "pkg/foo.go::Handler.List",
FilePath: "pkg/foo.go",
Line: 58,
})
idx.commitContracts(reg)
require.Equal(t,
[]string{"AddBatch"},
g.calls,
"contracts commit must batch through a single AddBatch call",
)
require.Zero(t, g.addNode.Load(), "no per-row AddNode calls expected")
require.Zero(t, g.addEdge.Load(), "no per-row AddEdge calls expected")
require.NotNil(t, g.GetNode("http::GET::/v1/items"))
require.NotNil(t, g.GetNode("http::POST::/v1/items"))
// Provider contract emits both EdgeProvides and EdgeHandlesRoute;
// consumer contract emits only EdgeConsumes.
provides := g.GetOutEdges("pkg/foo.go::Handler.List")
var nProvides, nConsumes, nHandles int
for _, e := range provides {
switch e.Kind {
case graph.EdgeProvides:
nProvides++
case graph.EdgeConsumes:
nConsumes++
case graph.EdgeHandlesRoute:
nHandles++
}
}
require.Equal(t, 1, nProvides, "expected 1 EdgeProvides for the provider contract")
require.Equal(t, 1, nConsumes, "expected 1 EdgeConsumes for the consumer contract")
require.Equal(t, 1, nHandles, "expected 1 EdgeHandlesRoute for the HTTP provider")
}
// TestCommitContracts_NoBulkLoader_FallsBackToAddBatch asserts that
// when the backend does not implement graph.BulkLoader (the
// in-memory *graph.Graph case) commitContracts still issues a
// single AddBatch — not the per-row AddNode / AddEdge writes — and
// does not attempt to call BeginBulkLoad / FlushBulk.
func TestCommitContracts_NoBulkLoader_FallsBackToAddBatch(t *testing.T) {
g := graph.New()
require.NotImplements(t, (*graph.BulkLoader)(nil), graph.Store(g))
g.AddNode(&graph.Node{
ID: "pkg/foo.go::Handler.List",
Kind: graph.KindMethod,
Name: "List",
FilePath: "pkg/foo.go",
Language: "go",
})
idx := New(g, parser.NewRegistry(), config.Default().Index, zap.NewNop())
reg := contracts.NewRegistry()
reg.Add(contracts.Contract{
ID: "http::GET::/v1/items",
Type: contracts.ContractHTTP,
Role: contracts.RoleProvider,
SymbolID: "pkg/foo.go::Handler.List",
FilePath: "pkg/foo.go",
Line: 42,
})
idx.commitContracts(reg)
require.NotNil(t, g.GetNode("http::GET::/v1/items"))
out := g.GetOutEdges("pkg/foo.go::Handler.List")
var nProvides, nHandles int
for _, e := range out {
switch e.Kind {
case graph.EdgeProvides:
nProvides++
case graph.EdgeHandlesRoute:
nHandles++
}
}
require.Equal(t, 1, nProvides)
require.Equal(t, 1, nHandles)
}
// TestExtractGoModContracts_UsesAddBatch asserts that go.mod
// dependency-contract emission goes through a single AddBatch
// call (with the bulk path engaged when the backend supports it)
// instead of the per-row AddNode loop that previously did one
// round-trip per dependency on the on-disk backend.
func TestExtractGoModContracts_UsesAddBatch(t *testing.T) {
dir := t.TempDir()
goMod := []byte(`module example.com/test
go 1.22
require (
github.com/dep/one v1.0.0
github.com/dep/two v0.5.0
)
`)
require.NoError(t, os.WriteFile(filepath.Join(dir, "go.mod"), goMod, 0o644))
g := newRecordingBulkGraph()
idx := New(g, parser.NewRegistry(), config.Default().Index, zap.NewNop())
idx.rootPath = dir
reg := contracts.NewRegistry()
idx.extractGoModContracts(reg)
require.Contains(t, g.calls, "AddBatch",
"extractGoModContracts must emit dep nodes via a single AddBatch")
require.Zero(t, g.addNode.Load(), "no per-row AddNode calls expected")
}