Files
kandev/apps/backend/internal/gitlab/service_watch_reset_test.go
T
2026-07-21 23:24:22 +01:00

124 lines
4.0 KiB
Go

package gitlab
import (
"context"
"errors"
"testing"
taskservice "github.com/kandev/kandev/internal/task/service"
)
type recordingCascadeDeleter struct {
ids []string
failOn string
}
func (d *recordingCascadeDeleter) DeleteTaskTree(_ context.Context, id string, cascade bool) (*taskservice.CascadeOutcome, error) {
if !cascade {
panic("watch reset must cascade")
}
d.ids = append(d.ids, id)
if id == d.failOn {
return nil, errors.New("delete failed")
}
return &taskservice.CascadeOutcome{}, nil
}
func TestServiceResetIssueWatchAndDeleteContinueAfterTaskFailure(t *testing.T) {
store := newTestStore(t)
ctx := context.Background()
watch := &IssueWatch{
WorkspaceID: "ws-1", WorkflowID: "wf-1", WorkflowStepID: "step-1",
AgentProfileID: "agent-1", ExecutorProfileID: "exec-1", Enabled: true,
}
if err := store.CreateIssueWatch(ctx, watch); err != nil {
t.Fatalf("create watch: %v", err)
}
for iid, taskID := range []string{"task-fails", "task-ok"} {
if ok, err := store.ReserveIssueWatchTask(ctx, watch.ID, "group/project", iid+1, "https://gitlab/issue"); err != nil || !ok {
t.Fatalf("reserve %d: ok=%v err=%v", iid, ok, err)
}
if err := store.AssignIssueWatchTaskID(ctx, watch.ID, "group/project", iid+1, taskID); err != nil {
t.Fatalf("assign %d: %v", iid, err)
}
}
svc := NewService("", nil, "none", nil, newTestLogger(t))
svc.SetStore(store)
deleter := &recordingCascadeDeleter{failOn: "task-fails"}
svc.SetCascadeTaskDeleter(deleter)
deleted, err := svc.ResetIssueWatch(ctx, watch.ID)
if err != nil {
t.Fatalf("reset: %v", err)
}
if deleted != 1 || len(deleter.ids) != 2 {
t.Fatalf("deleted=%d calls=%v", deleted, deleter.ids)
}
rows, err := store.ListIssueWatchTasksByWatch(ctx, watch.ID)
if err != nil || len(rows) != 0 {
t.Fatalf("dedup after reset=%v err=%v", rows, err)
}
gotWatch, err := store.GetIssueWatch(ctx, watch.ID)
if err != nil || gotWatch == nil || !gotWatch.Enabled || gotWatch.LastPolledAt != nil {
t.Fatalf("watch after reset=%#v err=%v", gotWatch, err)
}
if ok, err := store.ReserveIssueWatchTask(ctx, watch.ID, "group/project", 3, "https://gitlab/issue"); err != nil || !ok {
t.Fatalf("reserve after reset: ok=%v err=%v", ok, err)
}
if err := svc.DeleteIssueWatch(ctx, watch.ID); err != nil {
t.Fatalf("delete watch: %v", err)
}
gotWatch, err = store.GetIssueWatch(ctx, watch.ID)
if err != nil || gotWatch != nil {
t.Fatalf("watch after delete=%#v err=%v", gotWatch, err)
}
rows, err = store.ListIssueWatchTasksByWatch(ctx, watch.ID)
if err != nil || len(rows) != 0 {
t.Fatalf("dedup after delete=%v err=%v", rows, err)
}
}
func TestServiceResetReviewWatchDeletesTasksAndClearsState(t *testing.T) {
store := newTestStore(t)
ctx := context.Background()
watch := &ReviewWatch{
WorkspaceID: "ws-1", WorkflowID: "wf-1", WorkflowStepID: "step-1",
AgentProfileID: "agent-1", ExecutorProfileID: "exec-1", Enabled: false,
}
if err := store.CreateReviewWatch(ctx, watch); err != nil {
t.Fatalf("create watch: %v", err)
}
for iid, taskID := range []string{"task-1", "", "task-2"} {
if ok, err := store.ReserveReviewMRTask(ctx, watch.ID, "group/project", iid+1, "https://gitlab/mr"); err != nil || !ok {
t.Fatalf("reserve %d: ok=%v err=%v", iid, ok, err)
}
if taskID != "" {
if err := store.AssignReviewMRTaskID(ctx, watch.ID, "group/project", iid+1, taskID); err != nil {
t.Fatalf("assign %d: %v", iid, err)
}
}
}
svc := NewService("", nil, "none", nil, newTestLogger(t))
svc.SetStore(store)
deleter := &recordingCascadeDeleter{}
svc.SetCascadeTaskDeleter(deleter)
preview, err := svc.PreviewResetReviewWatch(ctx, watch.ID)
if err != nil || preview != 2 {
t.Fatalf("preview=%d err=%v, want 2", preview, err)
}
deleted, err := svc.ResetReviewWatch(ctx, watch.ID)
if err != nil {
t.Fatalf("reset: %v", err)
}
if deleted != 2 || len(deleter.ids) != 2 {
t.Fatalf("deleted=%d calls=%v", deleted, deleter.ids)
}
rows, err := store.ListReviewMRTasksByWatch(ctx, watch.ID)
if err != nil || len(rows) != 0 {
t.Fatalf("dedup after reset=%v err=%v", rows, err)
}
}