|
6 | 6 | "database/sql" |
7 | 7 | "encoding/json" |
8 | 8 | "errors" |
| 9 | + "fmt" |
9 | 10 | "os" |
10 | 11 | "path/filepath" |
11 | 12 | "slices" |
@@ -911,6 +912,104 @@ func TestStatusJSONCorrelatesSubmissionToPublish(t *testing.T) { |
911 | 912 | } |
912 | 913 | } |
913 | 914 |
|
| 915 | +func TestStatusAndWaitSurfaceRetryCommandForRejectedPublishAfterRemoteAdvance(t *testing.T) { |
| 916 | + repoRoot, remoteDir := createTestRepoWithRemote(t) |
| 917 | + initRepoForWorker(t, repoRoot) |
| 918 | + runTestCommand(t, repoRoot, "git", "push", "origin", "main") |
| 919 | + updatePublishMode(t, repoRoot, "auto") |
| 920 | + |
| 921 | + featurePath := filepath.Join(t.TempDir(), "feature-status-publish-reject") |
| 922 | + runTestCommand(t, repoRoot, "git", "worktree", "add", "-b", "feature/status-publish-reject", featurePath) |
| 923 | + writeFileAndCommit(t, featurePath, "status.txt", "status\n", "status feature") |
| 924 | + submitBranch(t, featurePath) |
| 925 | + runOnce(t, repoRoot) |
| 926 | + |
| 927 | + layout, err := git.DiscoverRepositoryLayout(repoRoot) |
| 928 | + if err != nil { |
| 929 | + t.Fatalf("DiscoverRepositoryLayout: %v", err) |
| 930 | + } |
| 931 | + store := state.NewStore(state.DefaultPath(layout.GitDir)) |
| 932 | + repoRecord, err := store.GetRepositoryByPath(context.Background(), layout.RepositoryRoot) |
| 933 | + if err != nil { |
| 934 | + t.Fatalf("GetRepositoryByPath: %v", err) |
| 935 | + } |
| 936 | + submissions, err := store.ListIntegrationSubmissions(context.Background(), repoRecord.ID) |
| 937 | + if err != nil { |
| 938 | + t.Fatalf("ListIntegrationSubmissions: %v", err) |
| 939 | + } |
| 940 | + requests, err := store.ListPublishRequests(context.Background(), repoRecord.ID) |
| 941 | + if err != nil { |
| 942 | + t.Fatalf("ListPublishRequests: %v", err) |
| 943 | + } |
| 944 | + if len(submissions) != 1 || len(requests) != 1 { |
| 945 | + t.Fatalf("expected 1 submission and 1 publish request, got %+v %+v", submissions, requests) |
| 946 | + } |
| 947 | + if _, err := store.UpdatePublishRequestStatus(context.Background(), requests[0].ID, domain.PublishStatusFailed, sql.NullInt64{}); err != nil { |
| 948 | + t.Fatalf("UpdatePublishRequestStatus: %v", err) |
| 949 | + } |
| 950 | + if err := appendStateEvent(context.Background(), store, state.EventRecord{ |
| 951 | + RepoID: repoRecord.ID, |
| 952 | + ItemType: domain.ItemTypePublishRequest, |
| 953 | + ItemID: state.NullInt64(requests[0].ID), |
| 954 | + EventType: domain.EventTypePublishFailed, |
| 955 | + Payload: mustJSON(map[string]string{ |
| 956 | + "target_sha": requests[0].TargetSHA, |
| 957 | + "error": "git push was rejected: To github.com:recallnet/tradecore.git\n ! [rejected] main -> main (fetch first)\nerror: failed to push some refs to 'github.com:recallnet/tradecore.git'", |
| 958 | + "kind": publishFailureKindGitPushFailed, |
| 959 | + "stage": publishStagePush, |
| 960 | + }), |
| 961 | + }); err != nil { |
| 962 | + t.Fatalf("appendStateEvent: %v", err) |
| 963 | + } |
| 964 | + |
| 965 | + upstreamClone := filepath.Join(t.TempDir(), "upstream-clone") |
| 966 | + runTestCommand(t, t.TempDir(), "git", "clone", remoteDir, upstreamClone) |
| 967 | + runTestCommand(t, upstreamClone, "git", "config", "user.name", "Test User") |
| 968 | + runTestCommand(t, upstreamClone, "git", "config", "user.email", "test@example.com") |
| 969 | + runTestCommand(t, upstreamClone, "git", "config", "core.hooksPath", ".git/hooks") |
| 970 | + writeFileAndCommit(t, upstreamClone, "upstream.txt", "upstream\n", "upstream advance") |
| 971 | + runTestCommand(t, upstreamClone, "git", "push", "origin", "main") |
| 972 | + runTestCommand(t, repoRoot, "git", "fetch", "origin", "main") |
| 973 | + |
| 974 | + var waitOut bytes.Buffer |
| 975 | + var waitErr bytes.Buffer |
| 976 | + err = runCLI([]string{"wait", "--repo", repoRoot, "--submission", strconv.FormatInt(submissions[0].ID, 10), "--for", "landed", "--json", "--timeout", "1s"}, newStepPrinter(&waitOut), &waitErr) |
| 977 | + if err == nil { |
| 978 | + t.Fatalf("expected landed wait to fail") |
| 979 | + } |
| 980 | + |
| 981 | + var waitResult submissionWaitResult |
| 982 | + if err := json.Unmarshal(waitOut.Bytes(), &waitResult); err != nil { |
| 983 | + t.Fatalf("Unmarshal wait: %v", err) |
| 984 | + } |
| 985 | + if waitResult.RetryHint != "retry-publish-after-protected-reconcile" { |
| 986 | + t.Fatalf("expected protected reconcile retry hint, got %+v", waitResult) |
| 987 | + } |
| 988 | + if !strings.Contains(waitResult.PublishFailureSummary, "mq retry --repo") || !strings.Contains(waitResult.PublishFailureSummary, fmt.Sprintf("--publish %d", requests[0].ID)) { |
| 989 | + t.Fatalf("expected retry command in wait summary, got %+v", waitResult) |
| 990 | + } |
| 991 | + |
| 992 | + var statusOut bytes.Buffer |
| 993 | + var statusErr bytes.Buffer |
| 994 | + if err := runCLI([]string{"status", "--repo", repoRoot, "--json"}, newStepPrinter(&statusOut), &statusErr); err != nil { |
| 995 | + t.Fatalf("runCLI status returned error: %v", err) |
| 996 | + } |
| 997 | + |
| 998 | + var statusResult statusResult |
| 999 | + if err := json.Unmarshal(statusOut.Bytes(), &statusResult); err != nil { |
| 1000 | + t.Fatalf("Unmarshal status: %v", err) |
| 1001 | + } |
| 1002 | + if statusResult.LatestSubmission == nil { |
| 1003 | + t.Fatalf("expected latest submission, got %+v", statusResult) |
| 1004 | + } |
| 1005 | + if !strings.Contains(statusResult.LatestSubmission.PublishFailureSummary, "mq retry --repo") || !strings.Contains(statusResult.LatestSubmission.PublishFailureSummary, fmt.Sprintf("--publish %d", requests[0].ID)) { |
| 1006 | + t.Fatalf("expected retry command in status summary, got %+v", statusResult.LatestSubmission) |
| 1007 | + } |
| 1008 | + if len(statusResult.LatestSubmission.NextActions) == 0 || !strings.Contains(statusResult.LatestSubmission.NextActions[0].Command, "mq retry --repo") || !strings.Contains(statusResult.LatestSubmission.NextActions[0].Command, fmt.Sprintf("--publish %d", requests[0].ID)) { |
| 1009 | + t.Fatalf("expected retry next action, got %+v", statusResult.LatestSubmission.NextActions) |
| 1010 | + } |
| 1011 | +} |
| 1012 | + |
914 | 1013 | func TestStatusJSONReportsPublishExecutionAndProtectedWorktreeActivity(t *testing.T) { |
915 | 1014 | repoRoot, _ := createTestRepoWithRemote(t) |
916 | 1015 | initRepoForWorker(t, repoRoot) |
|
0 commit comments