feat: add reset action for integration watches (#1381)
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestStore_ReviewWatch_ListTaskIDsAndReset pins the contract used by the
|
||||
// review watch reset flow: every dedup row's task_id (including empty
|
||||
// reservations) is enumerable, and ResetReviewWatchState wipes those rows
|
||||
// atomically alongside last_polled_at.
|
||||
func TestStore_ReviewWatch_ListTaskIDsAndReset(t *testing.T) {
|
||||
store := newTestStore(t)
|
||||
ctx := context.Background()
|
||||
|
||||
rw := &ReviewWatch{
|
||||
WorkspaceID: "ws-1",
|
||||
WorkflowID: "wf-1",
|
||||
WorkflowStepID: "step-1",
|
||||
AgentProfileID: "agent-1",
|
||||
ExecutorProfileID: "exec-1",
|
||||
ReviewScope: "user_and_teams",
|
||||
Enabled: true,
|
||||
}
|
||||
if err := store.CreateReviewWatch(ctx, rw); err != nil {
|
||||
t.Fatalf("create review watch: %v", err)
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
rw.LastPolledAt = &now
|
||||
rw.UpdatedAt = now
|
||||
if err := store.UpdateReviewWatch(ctx, rw); err != nil {
|
||||
t.Fatalf("stamp last polled: %v", err)
|
||||
}
|
||||
|
||||
tasks := []*ReviewPRTask{
|
||||
{ReviewWatchID: rw.ID, RepoOwner: "acme", RepoName: "widget", PRNumber: 1, PRURL: "https://github.com/acme/widget/pull/1", TaskID: "task-a", CreatedAt: now},
|
||||
{ReviewWatchID: rw.ID, RepoOwner: "acme", RepoName: "widget", PRNumber: 2, PRURL: "https://github.com/acme/widget/pull/2", TaskID: "task-b", CreatedAt: now},
|
||||
{ReviewWatchID: rw.ID, RepoOwner: "acme", RepoName: "widget", PRNumber: 3, PRURL: "https://github.com/acme/widget/pull/3", TaskID: "", CreatedAt: now},
|
||||
}
|
||||
for _, tp := range tasks {
|
||||
if err := store.CreateReviewPRTask(ctx, tp); err != nil {
|
||||
t.Fatalf("create review pr task: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
ids, err := store.ListReviewPRTaskIDsByWatch(ctx, rw.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("list ids: %v", err)
|
||||
}
|
||||
if len(ids) != 3 {
|
||||
t.Fatalf("ListReviewPRTaskIDsByWatch returned %d rows, want 3 (including empty reservation)", len(ids))
|
||||
}
|
||||
nonEmpty := 0
|
||||
for _, id := range ids {
|
||||
if id != "" {
|
||||
nonEmpty++
|
||||
}
|
||||
}
|
||||
if nonEmpty != 2 {
|
||||
t.Errorf("expected 2 non-empty task IDs, got %d", nonEmpty)
|
||||
}
|
||||
|
||||
if err := store.ResetReviewWatchState(ctx, rw.ID); err != nil {
|
||||
t.Fatalf("reset: %v", err)
|
||||
}
|
||||
|
||||
idsAfter, err := store.ListReviewPRTaskIDsByWatch(ctx, rw.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("list ids after reset: %v", err)
|
||||
}
|
||||
if len(idsAfter) != 0 {
|
||||
t.Errorf("expected 0 dedup rows after reset, got %d", len(idsAfter))
|
||||
}
|
||||
got, err := store.GetReviewWatch(ctx, rw.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("get watch: %v", err)
|
||||
}
|
||||
if got.LastPolledAt != nil {
|
||||
t.Errorf("expected LastPolledAt to be nil after reset, got %v", got.LastPolledAt)
|
||||
}
|
||||
}
|
||||
|
||||
// TestStore_IssueWatch_ListTaskIDsAndReset mirrors the review watch test for
|
||||
// the issue watch reset path.
|
||||
func TestStore_IssueWatch_ListTaskIDsAndReset(t *testing.T) {
|
||||
store := newTestStore(t)
|
||||
ctx := context.Background()
|
||||
|
||||
iw := &IssueWatch{
|
||||
WorkspaceID: "ws-1",
|
||||
WorkflowID: "wf-1",
|
||||
WorkflowStepID: "step-1",
|
||||
AgentProfileID: "agent-1",
|
||||
ExecutorProfileID: "exec-1",
|
||||
Enabled: true,
|
||||
}
|
||||
if err := store.CreateIssueWatch(ctx, iw); err != nil {
|
||||
t.Fatalf("create issue watch: %v", err)
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
iw.LastPolledAt = &now
|
||||
iw.UpdatedAt = now
|
||||
if err := store.UpdateIssueWatch(ctx, iw); err != nil {
|
||||
t.Fatalf("stamp last polled: %v", err)
|
||||
}
|
||||
|
||||
// Three reservations, only two get task IDs assigned so the third
|
||||
// exercises the empty-ID inclusion the reset flow depends on.
|
||||
for _, n := range []int{1, 2, 3} {
|
||||
if _, err := store.ReserveIssueWatchTask(ctx, iw.ID, "acme", "widget", n, "https://github.com/acme/widget/issues/0"); err != nil {
|
||||
t.Fatalf("reserve %d: %v", n, err)
|
||||
}
|
||||
}
|
||||
if err := store.AssignIssueWatchTaskID(ctx, iw.ID, "acme", "widget", 1, "task-a"); err != nil {
|
||||
t.Fatalf("assign 1: %v", err)
|
||||
}
|
||||
if err := store.AssignIssueWatchTaskID(ctx, iw.ID, "acme", "widget", 2, "task-b"); err != nil {
|
||||
t.Fatalf("assign 2: %v", err)
|
||||
}
|
||||
|
||||
ids, err := store.ListIssueWatchTaskIDsByWatch(ctx, iw.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("list ids: %v", err)
|
||||
}
|
||||
if len(ids) != 3 {
|
||||
t.Fatalf("ListIssueWatchTaskIDsByWatch returned %d rows, want 3 (including empty reservation)", len(ids))
|
||||
}
|
||||
|
||||
if err := store.ResetIssueWatchState(ctx, iw.ID); err != nil {
|
||||
t.Fatalf("reset: %v", err)
|
||||
}
|
||||
|
||||
idsAfter, err := store.ListIssueWatchTaskIDsByWatch(ctx, iw.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("list ids after reset: %v", err)
|
||||
}
|
||||
if len(idsAfter) != 0 {
|
||||
t.Errorf("expected 0 dedup rows after reset, got %d", len(idsAfter))
|
||||
}
|
||||
got, err := store.GetIssueWatch(ctx, iw.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("get watch: %v", err)
|
||||
}
|
||||
if got.LastPolledAt != nil {
|
||||
t.Errorf("expected LastPolledAt to be nil after reset, got %v", got.LastPolledAt)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user