diff options
Diffstat (limited to 'internal/storage/db_test.go')
| -rw-r--r-- | internal/storage/db_test.go | 372 |
1 files changed, 371 insertions, 1 deletions
diff --git a/internal/storage/db_test.go b/internal/storage/db_test.go index 752c5b1..0e67e02 100644 --- a/internal/storage/db_test.go +++ b/internal/storage/db_test.go @@ -41,7 +41,6 @@ func TestCreateTask_AndGetTask(t *testing.T) { Type: "claude", Model: "sonnet", Instructions: "do it", - ProjectDir: "/tmp", MaxBudgetUSD: 2.5, }, Priority: task.PriorityHigh, @@ -990,6 +989,128 @@ func TestAppendTaskInteraction_NotFound(t *testing.T) { } } +func TestCreateTask_Project_RoundTrip(t *testing.T) { + db := testDB(t) + now := time.Now().UTC().Truncate(time.Second) + + tk := &task.Task{ + ID: "proj-1", + Name: "Project Task", + Project: "my-project", + Agent: task.AgentConfig{Type: "claude", Instructions: "do it"}, + Priority: task.PriorityNormal, + Tags: []string{}, + DependsOn: []string{}, + Retry: task.RetryConfig{MaxAttempts: 1, Backoff: "linear"}, + State: task.StatePending, + CreatedAt: now, + UpdatedAt: now, + } + if err := db.CreateTask(tk); err != nil { + t.Fatalf("creating task: %v", err) + } + + got, err := db.GetTask("proj-1") + if err != nil { + t.Fatalf("getting task: %v", err) + } + if got.Project != "my-project" { + t.Errorf("project: want %q, got %q", "my-project", got.Project) + } +} + +// ── Push subscription tests ─────────────────────────────────────────────────── + +func TestPushSubscription_SaveAndList(t *testing.T) { + db := testDB(t) + + sub := PushSubscription{ + ID: "sub-1", + Endpoint: "https://push.example.com/endpoint1", + P256DHKey: "p256dhkey1", + AuthKey: "authkey1", + } + if err := db.SavePushSubscription(sub); err != nil { + t.Fatalf("SavePushSubscription: %v", err) + } + + subs, err := db.ListPushSubscriptions() + if err != nil { + t.Fatalf("ListPushSubscriptions: %v", err) + } + if len(subs) != 1 { + t.Fatalf("want 1 subscription, got %d", len(subs)) + } + if subs[0].Endpoint != sub.Endpoint { + t.Errorf("endpoint: want %q, got %q", sub.Endpoint, subs[0].Endpoint) + } + if subs[0].P256DHKey != sub.P256DHKey { + t.Errorf("p256dh_key: want %q, got %q", sub.P256DHKey, subs[0].P256DHKey) + } + if subs[0].AuthKey != sub.AuthKey { + t.Errorf("auth_key: want %q, got %q", sub.AuthKey, subs[0].AuthKey) + } +} + +func TestPushSubscription_Delete(t *testing.T) { + db := testDB(t) + + sub := PushSubscription{ + ID: "sub-del", + Endpoint: "https://push.example.com/todelete", + P256DHKey: "key", + AuthKey: "auth", + } + if err := db.SavePushSubscription(sub); err != nil { + t.Fatalf("SavePushSubscription: %v", err) + } + + if err := db.DeletePushSubscription(sub.Endpoint); err != nil { + t.Fatalf("DeletePushSubscription: %v", err) + } + + subs, err := db.ListPushSubscriptions() + if err != nil { + t.Fatalf("ListPushSubscriptions: %v", err) + } + if len(subs) != 0 { + t.Errorf("want 0 subscriptions after delete, got %d", len(subs)) + } +} + +func TestPushSubscription_UniqueEndpoint(t *testing.T) { + db := testDB(t) + + sub := PushSubscription{ + ID: "sub-uq", + Endpoint: "https://push.example.com/unique", + P256DHKey: "key1", + AuthKey: "auth1", + } + if err := db.SavePushSubscription(sub); err != nil { + t.Fatalf("SavePushSubscription first: %v", err) + } + + // Save again with same endpoint — should update or replace, not error. + sub2 := PushSubscription{ + ID: "sub-uq2", + Endpoint: "https://push.example.com/unique", + P256DHKey: "key2", + AuthKey: "auth2", + } + if err := db.SavePushSubscription(sub2); err != nil { + t.Fatalf("SavePushSubscription second (upsert): %v", err) + } + + subs, err := db.ListPushSubscriptions() + if err != nil { + t.Fatalf("ListPushSubscriptions: %v", err) + } + if len(subs) != 1 { + t.Errorf("want 1 subscription after upsert, got %d", len(subs)) + } +} + func TestExecution_StoreAndRetrieveChangestats(t *testing.T) { db := testDB(t) now := time.Now().UTC().Truncate(time.Second) @@ -1032,3 +1153,252 @@ func TestExecution_StoreAndRetrieveChangestats(t *testing.T) { } } +func TestCreateProject(t *testing.T) { + db := testDB(t) + defer db.Close() + + p := &task.Project{ + ID: "proj-1", + Name: "claudomator", + RemoteURL: "/bare/claudomator.git", + LocalPath: "/workspace/claudomator", + Type: "web", + } + if err := db.CreateProject(p); err != nil { + t.Fatalf("CreateProject: %v", err) + } + got, err := db.GetProject("proj-1") + if err != nil { + t.Fatalf("GetProject: %v", err) + } + if got.Name != "claudomator" { + t.Errorf("Name: want claudomator, got %q", got.Name) + } + if got.LocalPath != "/workspace/claudomator" { + t.Errorf("LocalPath: want /workspace/claudomator, got %q", got.LocalPath) + } +} + +func TestListProjects(t *testing.T) { + db := testDB(t) + defer db.Close() + + for _, p := range []*task.Project{ + {ID: "p1", Name: "alpha", Type: "web"}, + {ID: "p2", Name: "beta", Type: "android"}, + } { + if err := db.CreateProject(p); err != nil { + t.Fatalf("CreateProject: %v", err) + } + } + list, err := db.ListProjects() + if err != nil { + t.Fatalf("ListProjects: %v", err) + } + if len(list) != 2 { + t.Errorf("want 2 projects, got %d", len(list)) + } +} + +func TestUpdateProject(t *testing.T) { + db := testDB(t) + defer db.Close() + + p := &task.Project{ID: "p1", Name: "original", Type: "web"} + if err := db.CreateProject(p); err != nil { + t.Fatalf("CreateProject: %v", err) + } + p.Name = "updated" + if err := db.UpdateProject(p); err != nil { + t.Fatalf("UpdateProject: %v", err) + } + got, _ := db.GetProject("p1") + if got.Name != "updated" { + t.Errorf("Name after update: want updated, got %q", got.Name) + } +} + +func TestCreateStory(t *testing.T) { + db := testDB(t) + st := &task.Story{ + ID: "story-1", + Name: "My Story", + Status: task.StoryPending, + } + if err := db.CreateStory(st); err != nil { + t.Fatalf("CreateStory: %v", err) + } +} + +func TestGetStory(t *testing.T) { + db := testDB(t) + st := &task.Story{ + ID: "story-2", + Name: "Get Story", + ProjectID: "proj-1", + Status: task.StoryPending, + } + if err := db.CreateStory(st); err != nil { + t.Fatalf("CreateStory: %v", err) + } + got, err := db.GetStory("story-2") + if err != nil { + t.Fatalf("GetStory: %v", err) + } + if got.Name != "Get Story" { + t.Errorf("Name: want 'Get Story', got %q", got.Name) + } + if got.ProjectID != "proj-1" { + t.Errorf("ProjectID: want 'proj-1', got %q", got.ProjectID) + } + if got.Status != task.StoryPending { + t.Errorf("Status: want PENDING, got %q", got.Status) + } +} + +func TestListStories(t *testing.T) { + db := testDB(t) + for _, name := range []string{"A", "B", "C"} { + if err := db.CreateStory(&task.Story{ID: name, Name: name, Status: task.StoryPending}); err != nil { + t.Fatalf("CreateStory %s: %v", name, err) + } + } + stories, err := db.ListStories() + if err != nil { + t.Fatalf("ListStories: %v", err) + } + if len(stories) != 3 { + t.Errorf("want 3 stories, got %d", len(stories)) + } +} + +func TestUpdateStoryStatus(t *testing.T) { + db := testDB(t) + st := &task.Story{ID: "story-upd", Name: "Upd", Status: task.StoryPending} + if err := db.CreateStory(st); err != nil { + t.Fatalf("CreateStory: %v", err) + } + if err := db.UpdateStoryStatus("story-upd", task.StoryInProgress); err != nil { + t.Fatalf("UpdateStoryStatus: %v", err) + } + got, _ := db.GetStory("story-upd") + if got.Status != task.StoryInProgress { + t.Errorf("Status: want IN_PROGRESS, got %q", got.Status) + } +} + +func TestListTasksByStory(t *testing.T) { + db := testDB(t) + now := time.Now().UTC() + + if err := db.CreateStory(&task.Story{ID: "story-tasks", Name: "S", Status: task.StoryPending}); err != nil { + t.Fatalf("CreateStory: %v", err) + } + + makeTask := func(id string) *task.Task { + return &task.Task{ + ID: id, + Name: id, + StoryID: "story-tasks", + Agent: task.AgentConfig{Type: "claude"}, + Priority: task.PriorityNormal, + Tags: []string{}, + DependsOn: []string{}, + Retry: task.RetryConfig{MaxAttempts: 1}, + State: task.StatePending, + CreatedAt: now, + UpdatedAt: now, + } + } + + if err := db.CreateTask(makeTask("t1")); err != nil { + t.Fatal(err) + } + if err := db.CreateTask(makeTask("t2")); err != nil { + t.Fatal(err) + } + + tasks, err := db.ListTasksByStory("story-tasks") + if err != nil { + t.Fatalf("ListTasksByStory: %v", err) + } + if len(tasks) != 2 { + t.Errorf("want 2 tasks, got %d", len(tasks)) + } + for _, tk := range tasks { + if tk.StoryID != "story-tasks" { + t.Errorf("task %s: StoryID want 'story-tasks', got %q", tk.ID, tk.StoryID) + } + } +} + +func TestUpdateTaskCheckerReport(t *testing.T) { + db := testDB(t) + tk := &task.Task{ + ID: "cr-1", Name: "orig", RepositoryURL: "https://github.com/x/y", + Agent: task.AgentConfig{Type: "claude", Instructions: "x"}, + Priority: task.PriorityNormal, + Retry: task.RetryConfig{MaxAttempts: 1, Backoff: "linear"}, + Tags: []string{}, DependsOn: []string{}, + State: task.StatePending, CreatedAt: time.Now().UTC(), UpdatedAt: time.Now().UTC(), + } + if err := db.CreateTask(tk); err != nil { + t.Fatalf("CreateTask: %v", err) + } + if err := db.UpdateTaskCheckerReport("cr-1", "Tests failed: missing endpoint"); err != nil { + t.Fatalf("UpdateTaskCheckerReport: %v", err) + } + got, err := db.GetTask("cr-1") + if err != nil { + t.Fatalf("GetTask: %v", err) + } + if got.CheckerReport != "Tests failed: missing endpoint" { + t.Errorf("expected checker report, got %q", got.CheckerReport) + } +} + +func TestGetCheckerTask(t *testing.T) { + db := testDB(t) + checked := &task.Task{ + ID: "chk-orig", Name: "orig", RepositoryURL: "https://github.com/x/y", + Agent: task.AgentConfig{Type: "claude", Instructions: "x"}, + Priority: task.PriorityNormal, + Retry: task.RetryConfig{MaxAttempts: 1, Backoff: "linear"}, + Tags: []string{}, DependsOn: []string{}, + State: task.StatePending, CreatedAt: time.Now().UTC(), UpdatedAt: time.Now().UTC(), + } + if err := db.CreateTask(checked); err != nil { + t.Fatalf("CreateTask checked: %v", err) + } + checker := &task.Task{ + ID: "chk-checker", Name: "Check: orig", CheckerForTaskID: "chk-orig", + RepositoryURL: "https://github.com/x/y", + Agent: task.AgentConfig{Type: "claude", Instructions: "validate"}, + Priority: task.PriorityNormal, + Retry: task.RetryConfig{MaxAttempts: 1, Backoff: "linear"}, + Tags: []string{}, DependsOn: []string{}, + State: task.StatePending, CreatedAt: time.Now().UTC(), UpdatedAt: time.Now().UTC(), + } + if err := db.CreateTask(checker); err != nil { + t.Fatalf("CreateTask checker: %v", err) + } + + // Should find the checker task. + got, err := db.GetCheckerTask("chk-orig") + if err != nil { + t.Fatalf("GetCheckerTask: %v", err) + } + if got == nil || got.ID != "chk-checker" { + t.Errorf("expected checker task ID chk-checker, got %v", got) + } + + // Should return nil when no checker exists. + none, err := db.GetCheckerTask("nonexistent") + if err != nil { + t.Fatalf("GetCheckerTask nonexistent: %v", err) + } + if none != nil { + t.Errorf("expected nil for task with no checker, got %v", none) + } +} + |
