You've already forked agentic-coding-workflow
bugfix
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
package codeindex
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/yan1h/agent-coding-workflow/internal/infra/git"
|
||||
"github.com/yan1h/agent-coding-workflow/internal/infra/llm"
|
||||
"github.com/yan1h/agent-coding-workflow/internal/jobs"
|
||||
)
|
||||
|
||||
// ===== fakes =====
|
||||
|
||||
type fakeRepo struct {
|
||||
runs map[uuid.UUID]*Run
|
||||
replaced map[uuid.UUID][]ChunkRow
|
||||
completed map[uuid.UUID]bool
|
||||
failed map[uuid.UUID]string
|
||||
running map[uuid.UUID]bool
|
||||
}
|
||||
|
||||
func newFakeRepo() *fakeRepo {
|
||||
return &fakeRepo{
|
||||
runs: map[uuid.UUID]*Run{}, replaced: map[uuid.UUID][]ChunkRow{},
|
||||
completed: map[uuid.UUID]bool{}, failed: map[uuid.UUID]string{}, running: map[uuid.UUID]bool{},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *fakeRepo) InsertRun(_ context.Context, run *Run) (*Run, error) {
|
||||
cp := *run
|
||||
r.runs[run.ID] = &cp
|
||||
return &cp, nil
|
||||
}
|
||||
func (r *fakeRepo) GetRun(_ context.Context, id uuid.UUID) (*Run, error) {
|
||||
run, ok := r.runs[id]
|
||||
if !ok {
|
||||
return nil, errors.New("not found")
|
||||
}
|
||||
cp := *run
|
||||
return &cp, nil
|
||||
}
|
||||
func (r *fakeRepo) GetRunByCommit(_ context.Context, wsID uuid.UUID, commit, model string) (*Run, error) {
|
||||
for _, run := range r.runs {
|
||||
if run.WorkspaceID == wsID && run.CommitSHA == commit && run.EmbeddingModel == model {
|
||||
cp := *run
|
||||
return &cp, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
func (r *fakeRepo) GetLatestCompletedRun(_ context.Context, wsID uuid.UUID) (*Run, error) {
|
||||
for _, run := range r.runs {
|
||||
if run.WorkspaceID == wsID && run.Status == StatusCompleted {
|
||||
cp := *run
|
||||
return &cp, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
func (r *fakeRepo) MarkRunning(_ context.Context, id uuid.UUID) error {
|
||||
r.running[id] = true
|
||||
return nil
|
||||
}
|
||||
func (r *fakeRepo) MarkCompleted(_ context.Context, id uuid.UUID, files, chunks int) error {
|
||||
r.completed[id] = true
|
||||
if run := r.runs[id]; run != nil {
|
||||
run.Status = StatusCompleted
|
||||
run.FilesIndexed = files
|
||||
run.ChunksIndexed = chunks
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (r *fakeRepo) MarkFailed(_ context.Context, id uuid.UUID, msg string) error {
|
||||
r.failed[id] = msg
|
||||
if run := r.runs[id]; run != nil {
|
||||
run.Status = StatusFailed
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (r *fakeRepo) MarkStale(_ context.Context, _ uuid.UUID) error { return nil }
|
||||
func (r *fakeRepo) MarkStuckStale(_ context.Context, _ time.Time) ([]uuid.UUID, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (r *fakeRepo) ReplaceChunks(_ context.Context, runID, _ uuid.UUID, _ string, rows []ChunkRow) error {
|
||||
r.replaced[runID] = rows
|
||||
return nil
|
||||
}
|
||||
func (r *fakeRepo) SearchKNN(context.Context, uuid.UUID, []float32, int, string, string) ([]Snippet, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type fakeLocator struct {
|
||||
dir string
|
||||
wsID uuid.UUID
|
||||
}
|
||||
|
||||
func (l fakeLocator) LocateWorkspace(context.Context, uuid.UUID) (WorkspaceInfo, error) {
|
||||
return WorkspaceInfo{ID: l.wsID, MainPath: l.dir, DefaultBranch: "main"}, nil
|
||||
}
|
||||
func (l fakeLocator) LocateWorktree(context.Context, uuid.UUID) (string, string, uuid.UUID, error) {
|
||||
return l.dir, "main", l.wsID, nil
|
||||
}
|
||||
|
||||
type fakeEmbedSel struct {
|
||||
emb llm.Embedder
|
||||
err error
|
||||
}
|
||||
|
||||
func (s fakeEmbedSel) Embedder(context.Context) (llm.Embedder, uuid.UUID, string, int, error) {
|
||||
if s.err != nil {
|
||||
return nil, uuid.Nil, "", 0, s.err
|
||||
}
|
||||
return s.emb, uuid.New(), "fake-model", llm.PlatformEmbeddingDims, nil
|
||||
}
|
||||
|
||||
func mustGit(t *testing.T, dir string, args ...string) {
|
||||
t.Helper()
|
||||
c := exec.Command("git", args...)
|
||||
c.Dir = dir
|
||||
out, err := c.CombinedOutput()
|
||||
require.NoError(t, err, "git %v: %s", args, string(out))
|
||||
}
|
||||
|
||||
func tempGitRepo(t *testing.T) (dir, sha string) {
|
||||
t.Helper()
|
||||
if _, err := exec.LookPath("git"); err != nil {
|
||||
t.Skip("git not in PATH")
|
||||
}
|
||||
dir = t.TempDir()
|
||||
mustGit(t, dir, "init", "-b", "main")
|
||||
mustGit(t, dir, "config", "user.email", "t@e.com")
|
||||
mustGit(t, dir, "config", "user.name", "T")
|
||||
require.NoError(t, os.WriteFile(filepath.Join(dir, "main.go"), []byte("package main\n\nfunc main() {}\n"), 0o644))
|
||||
require.NoError(t, os.WriteFile(filepath.Join(dir, "util.go"), []byte("package main\n\nfunc add(a, b int) int { return a + b }\n"), 0o644))
|
||||
mustGit(t, dir, "add", ".")
|
||||
mustGit(t, dir, "commit", "-m", "init")
|
||||
out, err := exec.Command("git", "-C", dir, "rev-parse", "HEAD").Output()
|
||||
require.NoError(t, err)
|
||||
sha = string(out[:len(out)-1])
|
||||
return dir, sha
|
||||
}
|
||||
|
||||
func TestBuildRunner_Success(t *testing.T) {
|
||||
dir, sha := tempGitRepo(t)
|
||||
wsID := uuid.New()
|
||||
repo := newFakeRepo()
|
||||
run := &Run{
|
||||
ID: uuid.New(), WorkspaceID: wsID, Branch: "main", CommitSHA: sha,
|
||||
Status: StatusPending, EmbeddingModel: "fake-model", Dims: llm.PlatformEmbeddingDims,
|
||||
}
|
||||
repo.runs[run.ID] = run
|
||||
|
||||
gitr := git.NewDefaultRunner(git.DefaultConfig())
|
||||
br := NewBuildRunner(repo, fakeLocator{dir: dir, wsID: wsID}, gitr,
|
||||
fakeEmbedSel{emb: llm.NewFakeEmbedder()}, NewChunker(), nil, nil)
|
||||
|
||||
payload, _ := json.Marshal(buildPayload{RunID: run.ID})
|
||||
err := br.Handle(context.Background(), jobs.Job{Payload: payload})
|
||||
require.NoError(t, err)
|
||||
require.True(t, repo.completed[run.ID])
|
||||
require.True(t, repo.running[run.ID])
|
||||
require.NotEmpty(t, repo.replaced[run.ID], "chunks must be inserted")
|
||||
// Each inserted row must carry an embedding of platform dims.
|
||||
for _, row := range repo.replaced[run.ID] {
|
||||
require.Len(t, row.Embedding, llm.PlatformEmbeddingDims)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildRunner_FailureMarksFailedAndNotifies(t *testing.T) {
|
||||
dir, sha := tempGitRepo(t)
|
||||
wsID := uuid.New()
|
||||
repo := newFakeRepo()
|
||||
run := &Run{
|
||||
ID: uuid.New(), WorkspaceID: wsID, Branch: "main", CommitSHA: sha,
|
||||
Status: StatusPending, EmbeddingModel: "fake-model", Dims: llm.PlatformEmbeddingDims,
|
||||
}
|
||||
repo.runs[run.ID] = run
|
||||
|
||||
embErr := errors.New("embed boom")
|
||||
notified := false
|
||||
gitr := git.NewDefaultRunner(git.DefaultConfig())
|
||||
br := NewBuildRunner(repo, fakeLocator{dir: dir, wsID: wsID}, gitr,
|
||||
fakeEmbedSel{emb: &llm.FakeEmbedder{Dims: llm.PlatformEmbeddingDims, Err: embErr}},
|
||||
NewChunker(),
|
||||
func(context.Context, *Run, error) { notified = true }, nil)
|
||||
|
||||
payload, _ := json.Marshal(buildPayload{RunID: run.ID})
|
||||
err := br.Handle(context.Background(), jobs.Job{Payload: payload})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, repo.failed[run.ID], "embed boom")
|
||||
require.True(t, notified)
|
||||
}
|
||||
|
||||
func TestBuildRunner_SkipsAlreadyCompleted(t *testing.T) {
|
||||
repo := newFakeRepo()
|
||||
run := &Run{ID: uuid.New(), Status: StatusCompleted, EmbeddingModel: "fake-model", Dims: llm.PlatformEmbeddingDims}
|
||||
repo.runs[run.ID] = run
|
||||
br := NewBuildRunner(repo, fakeLocator{}, nil, fakeEmbedSel{emb: llm.NewFakeEmbedder()}, NewChunker(), nil, nil)
|
||||
payload, _ := json.Marshal(buildPayload{RunID: run.ID})
|
||||
require.NoError(t, br.Handle(context.Background(), jobs.Job{Payload: payload}))
|
||||
require.Empty(t, repo.replaced[run.ID])
|
||||
}
|
||||
|
||||
var _ Repository = (*fakeRepo)(nil)
|
||||
Reference in New Issue
Block a user