agent-orchestrator/backend/internal/service/project/service_test.go

157 lines
4.4 KiB
Go

package project_test
import (
"context"
"errors"
"os/exec"
"testing"
"github.com/aoagents/agent-orchestrator/backend/internal/domain"
"github.com/aoagents/agent-orchestrator/backend/internal/service/project"
"github.com/aoagents/agent-orchestrator/backend/internal/storage/sqlite"
)
// newManager builds a Manager over a real, throwaway sqlite store (pure-Go
// driver, migrations run on Open) — no in-memory store.
func newManager(t *testing.T) project.Manager {
t.Helper()
store, err := sqlite.Open(t.TempDir())
if err != nil {
t.Fatalf("open store: %v", err)
}
t.Cleanup(func() { _ = store.Close() })
return project.New(store)
}
// gitRepo creates a real git repository in a fresh temp dir and returns its path.
func gitRepo(t *testing.T) string {
t.Helper()
dir := t.TempDir()
if out, err := exec.Command("git", "init", dir).CombinedOutput(); err != nil {
t.Fatalf("git unavailable: %v (%s)", err, out)
}
return dir
}
func ptr(s string) *string { return &s }
// wantCode asserts err is a *project.Error carrying the given machine code.
func wantCode(t *testing.T, err error, code string) {
t.Helper()
var e *project.Error
if !errors.As(err, &e) {
t.Fatalf("error = %v, want *project.Error", err)
}
if e.Code != code {
t.Fatalf("code = %q, want %q", e.Code, code)
}
}
func TestManager_AddListGetRemove(t *testing.T) {
ctx := context.Background()
m := newManager(t)
repo := gitRepo(t)
if got, err := m.List(ctx); err != nil || len(got) != 0 {
t.Fatalf("List() = %v, %v; want empty", got, err)
}
proj, err := m.Add(ctx, project.AddInput{Path: repo, ProjectID: ptr("ao"), Name: ptr("Agent Orchestrator")})
if err != nil {
t.Fatalf("Add: %v", err)
}
if proj.ID != "ao" || proj.Name != "Agent Orchestrator" || proj.Path != repo || proj.DefaultBranch != "main" {
t.Fatalf("Add returned %#v", proj)
}
list, err := m.List(ctx)
if err != nil || len(list) != 1 || list[0].ID != "ao" {
t.Fatalf("List() = %v, %v; want [ao]", list, err)
}
res, err := m.Get(ctx, "ao")
if err != nil {
t.Fatalf("Get: %v", err)
}
if res.Status != "ok" || res.Project == nil || res.Project.ID != "ao" {
t.Fatalf("Get = %#v", res)
}
rm, err := m.Remove(ctx, "ao")
if err != nil {
t.Fatalf("Remove: %v", err)
}
if rm.ProjectID != "ao" || rm.RemovedStorageDir {
t.Fatalf("Remove = %#v", rm)
}
if list, _ := m.List(ctx); len(list) != 0 {
t.Fatalf("active list after remove = %d, want 0", len(list))
}
_, err = m.Get(ctx, "ao")
wantCode(t, err, "PROJECT_NOT_FOUND")
}
func TestManager_ReaddAfterRemove(t *testing.T) {
ctx := context.Background()
m := newManager(t)
repo := gitRepo(t)
if _, err := m.Add(ctx, project.AddInput{Path: repo, ProjectID: ptr("ao")}); err != nil {
t.Fatalf("first Add: %v", err)
}
if _, err := m.Remove(ctx, "ao"); err != nil {
t.Fatalf("Remove: %v", err)
}
if _, err := m.Add(ctx, project.AddInput{Path: repo, ProjectID: ptr("ao2")}); err != nil {
t.Fatalf("re-add same path after remove: %v", err)
}
otherRepo := gitRepo(t)
if _, err := m.Remove(ctx, "ao2"); err != nil {
t.Fatalf("Remove ao2: %v", err)
}
if _, err := m.Add(ctx, project.AddInput{Path: otherRepo, ProjectID: ptr("ao2")}); err != nil {
t.Fatalf("re-add same id at different path after remove: %v", err)
}
}
func TestManager_AddValidationAndConflicts(t *testing.T) {
ctx := context.Background()
m := newManager(t)
_, err := m.Add(ctx, project.AddInput{Path: ""})
wantCode(t, err, "PATH_REQUIRED")
_, err = m.Add(ctx, project.AddInput{Path: t.TempDir()}) // exists but not a git repo
wantCode(t, err, "NOT_A_GIT_REPO")
repoA, repoB := gitRepo(t), gitRepo(t)
if _, err := m.Add(ctx, project.AddInput{Path: repoA, ProjectID: ptr("shared")}); err != nil {
t.Fatalf("seed add: %v", err)
}
_, err = m.Add(ctx, project.AddInput{Path: repoA, ProjectID: ptr("other")})
wantCode(t, err, "PATH_ALREADY_REGISTERED")
_, err = m.Add(ctx, project.AddInput{Path: repoB, ProjectID: ptr("shared")})
wantCode(t, err, "ID_ALREADY_REGISTERED")
}
func TestManager_GetUpdateRemoveErrors(t *testing.T) {
ctx := context.Background()
m := newManager(t)
_, err := m.Get(ctx, "nope")
wantCode(t, err, "PROJECT_NOT_FOUND")
_, err = m.Get(ctx, domain.ProjectID("bad/id"))
wantCode(t, err, "INVALID_PROJECT_ID")
_, err = m.Remove(ctx, "nope")
wantCode(t, err, "PROJECT_NOT_FOUND")
repo := gitRepo(t)
if _, err := m.Add(ctx, project.AddInput{Path: repo, ProjectID: ptr("p")}); err != nil {
t.Fatalf("seed: %v", err)
}
}