Monorepo for Tangled
tangled.org
1package pulls
2
3import (
4 "context"
5 "encoding/json"
6 "io"
7 "log/slog"
8 "net/http"
9 "net/http/httptest"
10 "testing"
11 "time"
12
13 "github.com/bluesky-social/indigo/atproto/syntax"
14 "tangled.org/core/api/tangled"
15 "tangled.org/core/appview/config"
16 "tangled.org/core/appview/models"
17 "tangled.org/core/appview/pages"
18)
19
20const (
21 resubmitTestOwnerDID = "did:plc:boltless"
22 resubmitTestRepoDID = "did:plc:akshay"
23 resubmitTestBranch = "feature"
24 resubmitTestSourceRev = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
25)
26
27func newKnotmirrorStub(t *testing.T, hash string) *httptest.Server {
28 t.Helper()
29 return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
30 if r.URL.Path != "/xrpc/"+tangled.GitTempGetBranchNSID {
31 t.Errorf("unexpected path %q", r.URL.Path)
32 http.Error(w, "wrong path", http.StatusNotFound)
33 return
34 }
35 repoQuery := r.URL.Query().Get("repo")
36 if _, err := syntax.ParseDID(repoQuery); err != nil {
37 t.Errorf("repo param %q is not a DID: %v", repoQuery, err)
38 http.Error(w, "repo must be a DID", http.StatusBadRequest)
39 return
40 }
41 if got := r.URL.Query().Get("name"); got != resubmitTestBranch {
42 t.Errorf("name param = %q, want %q", got, resubmitTestBranch)
43 }
44 w.Header().Set("Content-Type", "application/json")
45 _ = json.NewEncoder(w).Encode(tangled.GitTempGetBranch_Output{
46 Name: resubmitTestBranch,
47 Hash: hash,
48 When: time.Now().UTC().Format(time.RFC3339),
49 })
50 }))
51}
52
53func newPullsFromKnotURL(url string) *Pulls {
54 return &Pulls{
55 logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
56 config: &config.Config{
57 KnotMirror: config.KnotMirrorConfig{Url: url},
58 },
59 }
60}
61
62func newForkPull(state models.PullState) (*models.Pull, *models.Repo, models.Stack) {
63 sourceRepoDid := syntax.DID(resubmitTestRepoDID)
64 pull := &models.Pull{
65 State: state,
66 OwnerDid: resubmitTestOwnerDID,
67 TargetBranch: "main",
68 Submissions: []*models.PullSubmission{
69 {SourceRev: resubmitTestSourceRev},
70 },
71 PullSource: &models.PullSource{
72 Branch: resubmitTestBranch,
73 RepoDid: &sourceRepoDid,
74 },
75 }
76 repo := &models.Repo{RepoDid: resubmitTestRepoDID}
77 stack := models.Stack{pull}
78 return pull, repo, stack
79}
80
81func TestResubmitCheck_BranchAdvanced(t *testing.T) {
82 srv := newKnotmirrorStub(t, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
83 defer srv.Close()
84
85 s := newPullsFromKnotURL(srv.URL)
86 req := httptest.NewRequest(http.MethodGet, "/", nil)
87 pull, repo, stack := newForkPull(models.PullOpen)
88
89 got := s.resubmitCheck(req, repo, pull, stack)
90 if got != pages.ShouldResubmit {
91 t.Errorf("resubmitCheck() = %v, want ShouldResubmit", got)
92 }
93}
94
95func TestResubmitCheck_BranchUpToDate(t *testing.T) {
96 srv := newKnotmirrorStub(t, resubmitTestSourceRev)
97 defer srv.Close()
98
99 s := newPullsFromKnotURL(srv.URL)
100 req := httptest.NewRequest(http.MethodGet, "/", nil)
101 pull, repo, stack := newForkPull(models.PullOpen)
102
103 got := s.resubmitCheck(req, repo, pull, stack)
104 if got != pages.ShouldNotResubmit {
105 t.Errorf("resubmitCheck() = %v, want ShouldNotResubmit", got)
106 }
107}
108
109func TestResubmitCheck_MergedReturnsUnknown(t *testing.T) {
110 s := newPullsFromKnotURL("http://unused")
111 req := httptest.NewRequest(http.MethodGet, "/", nil)
112 pull, repo, stack := newForkPull(models.PullMerged)
113
114 if got := s.resubmitCheck(req, repo, pull, stack); got != pages.Unknown {
115 t.Errorf("resubmitCheck() = %v, want Unknown for merged pull", got)
116 }
117}
118
119func TestResubmitCheck_PatchBasedReturnsUnknown(t *testing.T) {
120 s := newPullsFromKnotURL("http://unused")
121 req := httptest.NewRequest(http.MethodGet, "/", nil)
122 pull, repo, stack := newForkPull(models.PullOpen)
123 pull.PullSource = nil
124
125 if got := s.resubmitCheck(req, repo, pull, stack); got != pages.Unknown {
126 t.Errorf("resubmitCheck() = %v, want Unknown for patch-based pull", got)
127 }
128}
129
130func TestResubmitCheck_KnotUnreachableReturnsUnknown(t *testing.T) {
131 s := newPullsFromKnotURL("http://127.0.0.1:1")
132 ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
133 defer cancel()
134 req := httptest.NewRequest(http.MethodGet, "/", nil).WithContext(ctx)
135 pull, repo, stack := newForkPull(models.PullOpen)
136
137 if got := s.resubmitCheck(req, repo, pull, stack); got != pages.Unknown {
138 t.Errorf("resubmitCheck() = %v, want Unknown when knot unreachable", got)
139 }
140}
141
142func TestResubmitCheck_NonForkUsesRepoDid(t *testing.T) {
143 const targetRepoDID = "did:plc:scallop"
144 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
145 if got := r.URL.Query().Get("repo"); got != targetRepoDID {
146 t.Errorf("repo param = %q, want %q for non-fork pull", got, targetRepoDID)
147 }
148 w.Header().Set("Content-Type", "application/json")
149 _ = json.NewEncoder(w).Encode(tangled.GitTempGetBranch_Output{
150 Name: resubmitTestBranch,
151 Hash: resubmitTestSourceRev,
152 When: time.Now().UTC().Format(time.RFC3339),
153 })
154 }))
155 defer srv.Close()
156
157 s := newPullsFromKnotURL(srv.URL)
158 req := httptest.NewRequest(http.MethodGet, "/", nil)
159 pull, _, stack := newForkPull(models.PullOpen)
160 pull.PullSource.RepoDid = nil
161 repo := &models.Repo{RepoDid: targetRepoDID}
162
163 if got := s.resubmitCheck(req, repo, pull, stack); got != pages.ShouldNotResubmit {
164 t.Errorf("resubmitCheck() = %v, want ShouldNotResubmit", got)
165 }
166}