package provider import ( "context" "net/http" "net/http/httptest" "testing" "orchestra/internal/domain" "orchestra/internal/store" ) func TestGiteaCommentsFetchAfterCursor(t *testing.T) { var path string srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { path = r.URL.Path // Deliberately out of order, to prove the source sorts by id. w.Write([]byte(`[ {"id":920,"body":"and keep the flag","user":{"login":"kami"},"created_at":"2026-08-26T12:02:00Z"}, {"id":917,"body":"older","user":{"login":"kami"},"created_at":"2026-08-26T11:00:00Z"}, {"id":918,"body":"no, use b","user":{"login":"kami"},"created_at":"2026-08-26T12:00:00Z"} ]`)) })) defer srv.Close() g := GiteaComments{Gitea{BaseURL: srv.URL, Owner: "kami", Repo: "orchestra", Project: "p"}} task := domain.Task{ID: "t1", Source: g.SourceName(), ExternalID: "381"} got, next, err := g.FetchAfter(context.Background(), task, store.SourceCursor{Cursor: "917"}) if err != nil { t.Fatal(err) } if path != "/api/v1/repos/kami/orchestra/issues/381/comments" { t.Fatalf("path = %s", path) } if len(got) != 2 || got[0].ExternalID != "918" || got[1].ExternalID != "920" { t.Fatalf("inputs = %+v", got) } if got[0].Body != "no, use b" || got[0].Author != "kami" || got[0].Provider != "gitea:p" { t.Fatalf("first input = %+v", got[0]) } if next.Cursor != "920" || next.TaskID != "t1" { t.Fatalf("next = %+v", next) } // Nothing new: the cursor must stay put rather than regress. got, next, err = g.FetchAfter(context.Background(), task, store.SourceCursor{Cursor: "920"}) if err != nil || len(got) != 0 { t.Fatalf("inputs=%+v err=%v", got, err) } if next.Cursor != "920" { t.Fatalf("next = %+v", next) } } func TestGiteaCommentsIgnoresForeignAndUnkeyedTasks(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { t.Errorf("unexpected request to %s", r.URL.Path) })) defer srv.Close() g := GiteaComments{Gitea{BaseURL: srv.URL, Owner: "kami", Repo: "orchestra", Project: "p"}} for name, task := range map[string]domain.Task{ "other source": {ID: "t1", Source: "vikunja", ExternalID: "381"}, "no issue": {ID: "t1", Source: g.SourceName()}, } { got, _, err := g.FetchAfter(context.Background(), task, store.SourceCursor{}) if err != nil || len(got) != 0 { t.Fatalf("%s: inputs=%+v err=%v", name, got, err) } } } func TestGiteaCommentsRejectsUnparsableCursorAndHTTPError(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Error(w, "boom", 500) })) defer srv.Close() g := GiteaComments{Gitea{BaseURL: srv.URL, Owner: "kami", Repo: "orchestra", Project: "p"}} task := domain.Task{ID: "t1", Source: g.SourceName(), ExternalID: "381"} if _, _, err := g.FetchAfter(context.Background(), task, store.SourceCursor{Cursor: "abc"}); err == nil { t.Fatal("bad cursor must not be treated as zero") } if _, _, err := g.FetchAfter(context.Background(), task, store.SourceCursor{}); err == nil { t.Fatal("http failure must be reported") } }