package pulls import ( "context" "encoding/json" "io" "log/slog" "net/http" "net/http/httptest" "testing" "time" "github.com/bluesky-social/indigo/atproto/syntax" "tangled.org/core/api/tangled" "tangled.org/core/appview/config" "tangled.org/core/appview/models" "tangled.org/core/appview/pages" ) const ( resubmitTestOwnerDID = "did:plc:boltless" resubmitTestRepoDID = "did:plc:akshay" resubmitTestBranch = "feature" resubmitTestSourceRev = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ) func newKnotmirrorStub(t *testing.T, hash string) *httptest.Server { t.Helper() return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/xrpc/"+tangled.GitTempGetBranchNSID { t.Errorf("unexpected path %q", r.URL.Path) http.Error(w, "wrong path", http.StatusNotFound) return } repoQuery := r.URL.Query().Get("repo") if _, err := syntax.ParseDID(repoQuery); err != nil { t.Errorf("repo param %q is not a DID: %v", repoQuery, err) http.Error(w, "repo must be a DID", http.StatusBadRequest) return } if got := r.URL.Query().Get("name"); got != resubmitTestBranch { t.Errorf("name param = %q, want %q", got, resubmitTestBranch) } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(tangled.GitTempGetBranch_Output{ Name: resubmitTestBranch, Hash: hash, When: time.Now().UTC().Format(time.RFC3339), }) })) } func newPullsFromKnotURL(url string) *Pulls { return &Pulls{ logger: slog.New(slog.NewTextHandler(io.Discard, nil)), config: &config.Config{ KnotMirror: config.KnotMirrorConfig{Url: url}, }, } } func newForkPull(state models.PullState) (*models.Pull, *models.Repo) { sourceRepoDid := syntax.DID(resubmitTestRepoDID) sourceBranch := resubmitTestBranch pull := &models.Pull{ State: state, OwnerDid: resubmitTestOwnerDID, TargetBranch: "main", Versions: []models.PullVersion{ {Head: resubmitTestSourceRev}, }, SourceBranch: &sourceBranch, SourceRepo: sourceRepoDid, } repo := &models.Repo{RepoDid: resubmitTestRepoDID} return pull, repo } func TestResubmitCheck_BranchAdvanced(t *testing.T) { srv := newKnotmirrorStub(t, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb") defer srv.Close() s := newPullsFromKnotURL(srv.URL) ctx := t.Context() pull, _ := newForkPull(models.PullOpen) got := s.resubmitCheck(ctx, pull) if got != pages.ShouldResubmit { t.Errorf("resubmitCheck() = %v, want ShouldResubmit", got) } } func TestResubmitCheck_BranchUpToDate(t *testing.T) { srv := newKnotmirrorStub(t, resubmitTestSourceRev) defer srv.Close() s := newPullsFromKnotURL(srv.URL) ctx := t.Context() pull, _ := newForkPull(models.PullOpen) got := s.resubmitCheck(ctx, pull) if got != pages.ShouldNotResubmit { t.Errorf("resubmitCheck() = %v, want ShouldNotResubmit", got) } } func TestResubmitCheck_MergedReturnsUnknown(t *testing.T) { s := newPullsFromKnotURL("http://unused") ctx := t.Context() pull, _ := newForkPull(models.PullMerged) if got := s.resubmitCheck(ctx, pull); got != pages.Unknown { t.Errorf("resubmitCheck() = %v, want Unknown for merged pull", got) } } func TestResubmitCheck_PatchBasedReturnsUnknown(t *testing.T) { s := newPullsFromKnotURL("http://unused") ctx := t.Context() pull, _ := newForkPull(models.PullOpen) pull.SourceBranch = nil if got := s.resubmitCheck(ctx, pull); got != pages.Unknown { t.Errorf("resubmitCheck() = %v, want Unknown for patch-based pull", got) } } func TestResubmitCheck_KnotUnreachableReturnsUnknown(t *testing.T) { s := newPullsFromKnotURL("http://127.0.0.1:1") ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond) defer cancel() pull, _ := newForkPull(models.PullOpen) if got := s.resubmitCheck(ctx, pull); got != pages.Unknown { t.Errorf("resubmitCheck() = %v, want Unknown when knot unreachable", got) } }