Skip to content

Commit 33b4f77

Browse files
committed
add pagination to GET /api/v1/find endpoint
`format=tree` pagination provides top-level comments with all replies and returns the last top-level comment as `last_comment` to be used as `offset` for the next page. If comments and replies overflow the limit, the one stepping out of the limit will not be returned. If the first comment and its replies after the given offset overflow the limit, it will be returned with all the replies. `format=plain` pagination works by providing all comments and returning the last comment as `last_comment` to be used as `offset` for the next page.
1 parent 7e5fdf0 commit 33b4f77

File tree

6 files changed

+282
-41
lines changed

6 files changed

+282
-41
lines changed

backend/app/rest/api/rest_public.go

+60-4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
cache "github.com/go-pkgz/lcw"
1717
log "github.com/go-pkgz/lgr"
1818
R "github.com/go-pkgz/rest"
19+
"github.com/google/uuid"
1920
"github.com/skip2/go-qrcode"
2021

2122
"github.com/umputun/remark42/backend/app/rest"
@@ -48,10 +49,18 @@ type pubStore interface {
4849
Counts(siteID string, postIDs []string) ([]store.PostInfo, error)
4950
}
5051

51-
// GET /find?site=siteID&url=post-url&format=[tree|plain]&sort=[+/-time|+/-score|+/-controversy]&view=[user|all]&since=unix_ts_msec
52-
// find comments for given post. Returns in tree or plain formats, sorted
52+
// GET /find?site=siteID&url=post-url&format=[tree|plain]&sort=[+/-time|+/-score|+/-controversy]&view=[user|all]&since=unix_ts_msec&limit=100&offset_id={id}
53+
// find comments for given post. Returns in tree or plain formats, sorted.
5354
//
5455
// When `url` parameter is not set (e.g. request is for site-wide comments), does not return deleted comments.
56+
//
57+
// When `limit` is set, first {limit} comments are returned. When `offset_id` is set, comments are returned starting
58+
// after the comment with the given id.
59+
// format="tree" limits comments by top-level comments and all their replies,
60+
// and never returns parent comment with only part of replies.
61+
//
62+
// `count` in the response refers to total number of non-deleted comments,
63+
// `count_left` to amount of comments left to be returned _including deleted_.
5564
func (s *public) findCommentsCtrl(w http.ResponseWriter, r *http.Request) {
5665
locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")}
5766
sort := r.URL.Query().Get("sort")
@@ -70,7 +79,24 @@ func (s *public) findCommentsCtrl(w http.ResponseWriter, r *http.Request) {
7079
since = time.Time{} // since doesn't make sense for tree
7180
}
7281

73-
log.Printf("[DEBUG] get comments for %+v, sort %s, format %s, since %v", locator, sort, format, since)
82+
limitParam := r.URL.Query().Get("limit")
83+
var limit int
84+
if limitParam != "" {
85+
if limit, err = strconv.Atoi(limitParam); err != nil {
86+
rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "bad limit value", rest.ErrCommentNotFound)
87+
return
88+
}
89+
}
90+
91+
offsetID := r.URL.Query().Get("offset_id")
92+
if offsetID != "" {
93+
if _, err = uuid.Parse(offsetID); err != nil {
94+
rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "bad offset_id value", rest.ErrCommentNotFound)
95+
return
96+
}
97+
}
98+
99+
log.Printf("[DEBUG] get comments for %+v, sort %s, format %s, since %v, limit %d, offset %s", locator, sort, format, since, limit, offsetID)
74100

75101
key := cache.NewKey(locator.SiteID).ID(URLKeyWithUser(r)).Scopes(locator.SiteID, locator.URL)
76102
data, err := s.cache.Get(key, func() ([]byte, error) {
@@ -102,12 +128,20 @@ func (s *public) findCommentsCtrl(w http.ResponseWriter, r *http.Request) {
102128
var b []byte
103129
switch format {
104130
case "tree":
105-
withInfo := treeWithInfo{Tree: service.MakeTree(comments, sort), Info: commentsInfo}
131+
withInfo := treeWithInfo{Tree: service.MakeTree(comments, sort, limit, offsetID), Info: commentsInfo}
132+
withInfo.Info.CountLeft = withInfo.Tree.CountLeft()
133+
withInfo.Info.LastComment = withInfo.Tree.LastComment()
106134
if withInfo.Nodes == nil { // eliminate json nil serialization
107135
withInfo.Nodes = []*service.Node{}
108136
}
109137
b, e = encodeJSONWithHTML(withInfo)
110138
default:
139+
if limit > 0 || offsetID != "" {
140+
comments, commentsInfo.CountLeft = limitComments(comments, limit, offsetID)
141+
}
142+
if limit > 0 && len(comments) > 0 {
143+
commentsInfo.LastComment = comments[len(comments)-1].ID
144+
}
111145
withInfo := commentsWithInfo{Comments: comments, Info: commentsInfo}
112146
b, e = encodeJSONWithHTML(withInfo)
113147
}
@@ -432,3 +466,25 @@ func (s *public) parseSince(r *http.Request) (time.Time, error) {
432466
}
433467
return sinceTS, nil
434468
}
469+
470+
// limitComments returns limited list of comments and count of comments left after limit.
471+
// If offsetID is provided, the list will be sliced starting from the comment with this ID.
472+
// If offsetID is not found, the full list will be returned.
473+
// It's used for only "
474+
func limitComments(c []store.Comment, limit int, offsetID string) (comments []store.Comment, countLeft int) {
475+
if offsetID != "" {
476+
for i, comment := range c {
477+
if comment.ID == offsetID {
478+
c = c[i+1:]
479+
break
480+
}
481+
}
482+
}
483+
484+
if limit > 0 && len(c) > limit {
485+
countLeft = len(c) - limit
486+
c = c[:limit]
487+
}
488+
489+
return c, countLeft
490+
}

backend/app/rest/api/rest_public_test.go

+102-17
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414

1515
cache "github.com/go-pkgz/lcw"
1616
R "github.com/go-pkgz/rest"
17+
"github.com/google/uuid"
1718
"github.com/stretchr/testify/assert"
1819
"github.com/stretchr/testify/require"
1920

@@ -580,7 +581,7 @@ func TestPublic_FindCommentsCtrl_ConsistentCount(t *testing.T) {
580581
setScore(commentLocator, ids[4], -3)
581582
time.Sleep(time.Millisecond * 5)
582583

583-
c6 := store.Comment{Text: "third-level comment 2", ParentID: ids[4], Locator: commentLocator}
584+
c6 := store.Comment{Text: "deleted third-level comment 2", ParentID: ids[4], Locator: commentLocator}
584585
ids[5], timestamps[5] = addCommentGetCreatedTime(t, c6, ts)
585586
// deleted later so not visible in site-wide requests
586587
setScore(commentLocator, ids[5], 10)
@@ -594,7 +595,7 @@ func TestPublic_FindCommentsCtrl_ConsistentCount(t *testing.T) {
594595
setScore(commentLocator, ids[6], 1)
595596
time.Sleep(time.Millisecond * 5)
596597

597-
c8 := store.Comment{Text: "second-level comment 3", ParentID: ids[6], Locator: commentLocator}
598+
c8 := store.Comment{Text: "deleted second-level comment 3", ParentID: ids[6], Locator: commentLocator}
598599
ids[7], timestamps[7] = addCommentGetCreatedTime(t, c8, ts)
599600
// deleted later so not visible in site-wide requests
600601
setScore(commentLocator, ids[7], -20)
@@ -618,24 +619,24 @@ func TestPublic_FindCommentsCtrl_ConsistentCount(t *testing.T) {
618619
sinceTS[i] = strconv.FormatInt(created.UnixNano()/1000000, 10)
619620
formattedTS[i] = created.Format(time.RFC3339Nano)
620621
}
621-
t.Logf("last timestamp: %v", timestamps[7])
622622

623623
testCases := []struct {
624624
params string
625625
expectedBody string
626626
}{
627-
{"", fmt.Sprintf(`"info":{"count":7,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
628-
{"url=test-url", fmt.Sprintf(`"info":{"url":"test-url","count":6,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
629-
{"format=plain", fmt.Sprintf(`"info":{"count":7,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
630-
{"format=plain&url=test-url", fmt.Sprintf(`"info":{"url":"test-url","count":6,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
631-
{"since=" + sinceTenSecondsAgo, fmt.Sprintf(`"info":{"count":7,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
632-
{"url=test-url&since=" + sinceTenSecondsAgo, fmt.Sprintf(`"info":{"url":"test-url","count":6,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
633-
{"since=" + sinceTS[0], fmt.Sprintf(`"info":{"count":7,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
634-
{"url=test-url&since=" + sinceTS[0], fmt.Sprintf(`"info":{"url":"test-url","count":6,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
635-
{"since=" + sinceTS[1], fmt.Sprintf(`"info":{"count":6,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
636-
{"url=test-url&since=" + sinceTS[1], fmt.Sprintf(`"info":{"url":"test-url","count":5,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
637-
{"since=" + sinceTS[4], fmt.Sprintf(`"info":{"count":3,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
638-
{"url=test-url&since=" + sinceTS[4], fmt.Sprintf(`"info":{"url":"test-url","count":2,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
627+
// test parameters url, format, since, sort
628+
{"", fmt.Sprintf(`"info":{"count":7,"count_left":0,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
629+
{"url=test-url", fmt.Sprintf(`"info":{"url":"test-url","count":6,"count_left":0,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
630+
{"format=plain", fmt.Sprintf(`"info":{"count":7,"count_left":0,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
631+
{"format=plain&url=test-url", fmt.Sprintf(`"info":{"url":"test-url","count":6,"count_left":0,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
632+
{"since=" + sinceTenSecondsAgo, fmt.Sprintf(`"info":{"count":7,"count_left":0,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
633+
{"url=test-url&since=" + sinceTenSecondsAgo, fmt.Sprintf(`"info":{"url":"test-url","count":6,"count_left":0,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
634+
{"since=" + sinceTS[0], fmt.Sprintf(`"info":{"count":7,"count_left":0,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
635+
{"url=test-url&since=" + sinceTS[0], fmt.Sprintf(`"info":{"url":"test-url","count":6,"count_left":0,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
636+
{"since=" + sinceTS[1], fmt.Sprintf(`"info":{"count":6,"count_left":0,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
637+
{"url=test-url&since=" + sinceTS[1], fmt.Sprintf(`"info":{"url":"test-url","count":5,"count_left":0,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
638+
{"since=" + sinceTS[4], fmt.Sprintf(`"info":{"count":3,"count_left":0,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
639+
{"url=test-url&since=" + sinceTS[4], fmt.Sprintf(`"info":{"url":"test-url","count":2,"count_left":0,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
639640
{"format=tree", `"info":{"count":7`},
640641
{"format=tree&url=test-url", `"info":{"url":"test-url","count":6`},
641642
{"format=tree&sort=+time", `"info":{"count":7`},
@@ -654,17 +655,101 @@ func TestPublic_FindCommentsCtrl_ConsistentCount(t *testing.T) {
654655
{"sort=+controversy&url=test-url&since=" + sinceTS[5], fmt.Sprintf(`"score":-2,"vote":0,"controversy":1.5874010519681994,"time":%q}],"info":{"url":"test-url","count":1`, formattedTS[6])},
655656
// three comments of which last one deleted and doesn't have controversy so returned last
656657
{"sort=-controversy&url=test-url&since=" + sinceTS[5], fmt.Sprintf(`"score":0,"vote":0,"time":%q,"delete":true}],"info":{"url":"test-url","count":1`, formattedTS[7])},
658+
659+
// test parameters limit, offset_id for format=plain
660+
{"limit=bad", `{"code":1,"details":"bad limit value","error":"strconv.Atoi: parsing \"bad\": invalid syntax"}`},
661+
{"offset_id=bad", `{"code":1,"details":"bad offset_id value","error":"invalid UUID length: 3"}`},
662+
{"limit=2", `"info":{"count":7,"count_left":5,"last_comment":"` + ids[1]},
663+
{"limit=6", `"info":{"count":7,"count_left":1,"last_comment":"` + ids[6]},
664+
{"limit=7", `"info":{"count":7,"count_left":0,"last_comment":"` + ids[8]},
665+
{"limit=2&url=test-url", `"info":{"url":"test-url","count":6,"count_left":6,"last_comment":"` + ids[1]},
666+
{"limit=6&url=test-url", `"info":{"url":"test-url","count":6,"count_left":2,"last_comment":"` + ids[5]},
667+
{"limit=7&url=test-url", `"info":{"url":"test-url","count":6,"count_left":1,"last_comment":"` + ids[6]},
668+
{fmt.Sprintf("limit=2&offset_id=%s", ids[2]), `"info":{"count":7,"count_left":2,"last_comment":"` + ids[4]},
669+
{fmt.Sprintf("limit=2&offset_id=%s", ids[3]), `"info":{"count":7,"count_left":1,"last_comment":"` + ids[6]},
670+
{fmt.Sprintf("limit=2&offset_id=%s", ids[4]), `"info":{"count":7,"count_left":0`},
671+
{fmt.Sprintf("limit=1&offset_id=%s", ids[6]), `"info":{"count":7,"count_left":0`},
672+
{fmt.Sprintf("limit=2&offset_id=%s", ids[8]), `"info":{"count":7,"count_left":0`},
673+
{fmt.Sprintf("limit=2&url=test-url&offset_id=%s", ids[2]), `"info":{"url":"test-url","count":6,"count_left":3,"last_comment":"` + ids[4]},
674+
{fmt.Sprintf("limit=2&url=test-url&offset_id=%s", ids[3]), `"info":{"url":"test-url","count":6,"count_left":2,"last_comment":"` + ids[5]},
675+
{fmt.Sprintf("limit=2&url=test-url&offset_id=%s", ids[4]), `"info":{"url":"test-url","count":6,"count_left":1,"last_comment":"` + ids[6]},
676+
{fmt.Sprintf("limit=1&url=test-url&offset_id=%s", ids[6]), `"info":{"url":"test-url","count":6,"count_left":0,"last_comment":"` + ids[7]},
677+
{fmt.Sprintf("limit=2&url=test-url&offset_id=%s", ids[8]), `"info":{"url":"test-url","count":6,"count_left":6,`},
678+
// deleted comment, offset is ignored in site-wide request but not for particular URL
679+
{fmt.Sprintf("limit=2&offset_id=%s", ids[5]), `"info":{"count":7,"count_left":5,"last_comment":"` + ids[1]},
680+
{fmt.Sprintf("limit=2&url=test-url&offset_id=%s", ids[5]), `"info":{"url":"test-url","count":6,"count_left":0,"last_comment":"` + ids[7]},
681+
// non-existing comment, offset is ignored, deleted comments included into request with "url"
682+
{fmt.Sprintf("limit=1&offset_id=%s", uuid.New().String()), `"info":{"count":7,"count_left":6,"last_comment":"` + ids[0]},
683+
{fmt.Sprintf("limit=1&url=test-url&offset_id=%s", uuid.New().String()), `"info":{"url":"test-url","count":6,"count_left":7,"last_comment":"` + ids[0]},
684+
// since is ignored for tree format, so we test it only for plain
685+
{"limit=6&since=" + sinceTenSecondsAgo, `"info":{"count":7,"count_left":1,"last_comment":"` + ids[6]},
686+
{"limit=1&since=" + sinceTS[4], `"info":{"count":3,"count_left":2,"last_comment":"` + ids[4]},
687+
{"limit=6&url=test-url&since=" + sinceTenSecondsAgo, `"info":{"url":"test-url","count":6,"count_left":2,"last_comment":"` + ids[5]},
688+
{"limit=1&url=test-url&since=" + sinceTS[4], `"info":{"url":"test-url","count":2,"count_left":3,"last_comment":"` + ids[4]},
689+
// start with deleted comment timestamp
690+
{"limit=1&since=" + sinceTS[5], `"info":{"count":2,"count_left":1,"last_comment":"` + ids[6]},
691+
{"limit=1&since=" + sinceTS[6], `"info":{"count":2,"count_left":1,"last_comment":"` + ids[6]},
692+
{"limit=1&url=test-url&since=" + sinceTS[5], `"info":{"url":"test-url","count":1,"count_left":2,"last_comment":"` + ids[5]},
693+
{"limit=1&url=test-url&since=" + sinceTS[6], `"info":{"url":"test-url","count":1,"count_left":1,"last_comment":"` + ids[6]},
694+
// test sort
695+
{"limit=1&sort=+time&url=test-url", `"info":{"url":"test-url","count":6,"count_left":7,"last_comment":"` + ids[0]},
696+
{"limit=1&sort=-time&url=test-url", `"info":{"url":"test-url","count":6,"count_left":7,"last_comment":"` + ids[7]},
697+
{"limit=1&sort=+score&url=test-url", `"info":{"url":"test-url","count":6,"count_left":7,"last_comment":"` + ids[6]},
698+
{"limit=1&sort=-score&url=test-url", `"info":{"url":"test-url","count":6,"count_left":7,"last_comment":"` + ids[2]},
699+
{"limit=1&sort=+controversy&url=test-url", `"info":{"url":"test-url","count":6,"count_left":7,"last_comment":"` + ids[0]},
700+
{"limit=1&sort=-controversy&url=test-url", `"info":{"url":"test-url","count":6,"count_left":7,"last_comment":"` + ids[3]},
701+
702+
// test parameters limit, offset_id for format=tree
703+
{"format=tree&limit=bad", `{"code":1,"details":"bad limit value","error":"strconv.Atoi: parsing \"bad\": invalid syntax"}`},
704+
{"format=tree&offset_id=bad", `{"code":1,"details":"bad offset_id value","error":"invalid UUID length: 3"}`},
705+
{"format=tree&limit=2", `"info":{"count":7,"count_left":4,"last_comment":"` + ids[0]},
706+
{"format=tree&limit=6", `"info":{"count":7,"count_left":2,"last_comment":"` + ids[1]},
707+
{"format=tree&limit=7", `"info":{"count":7,"count_left":1,"last_comment":"` + ids[6]},
708+
{"format=tree&url=test-url&limit=2", `"info":{"url":"test-url","count":6,"count_left":3,"last_comment":"` + ids[0]},
709+
{"format=tree&url=test-url&limit=6", `"info":{"url":"test-url","count":6,"count_left":1,"last_comment":"` + ids[1]},
710+
{"format=tree&url=test-url&limit=7", `"info":{"url":"test-url","count":6,"count_left":0,"last_comment":"` + ids[6]},
711+
// start after first top-level comment
712+
{fmt.Sprintf("format=tree&limit=2&offset_id=%s", ids[0]), `"info":{"count":7,"count_left":2,"last_comment":"` + ids[1]},
713+
{fmt.Sprintf("format=tree&url=test-url&limit=2&offset_id=%s", ids[0]), `"info":{"url":"test-url","count":6,"count_left":1,"last_comment":"` + ids[1]},
714+
// start after second top-level comment
715+
{fmt.Sprintf("format=tree&limit=2&offset_id=%s", ids[1]), `"info":{"count":7,"count_left":1,"last_comment":"` + ids[6]},
716+
{fmt.Sprintf("format=tree&url=test-url&limit=2&offset_id=%s", ids[1]), `"info":{"url":"test-url","count":6,"count_left":0,"last_comment":"` + ids[6]},
717+
// start after third top-level comment, so expect comment to post 2, or no comments on post 1 if "url" is set
718+
{fmt.Sprintf("format=tree&limit=1&offset_id=%s", ids[6]), `"info":{"count":7,"count_left":0,"last_comment":"` + ids[8]},
719+
{fmt.Sprintf("format=tree&url=test-url&limit=1&offset_id=%s", ids[6]), `"info":{"url":"test-url","count":6,"count_left":0`},
720+
// non-root comment IDs or non-existing IDs are ignored
721+
{fmt.Sprintf("format=tree&limit=2&offset_id=%s", ids[2]), `"info":{"count":7,"count_left":4,"last_comment":"` + ids[0]},
722+
{fmt.Sprintf("format=tree&limit=2&offset_id=%s", ids[3]), `"info":{"count":7,"count_left":4,"last_comment":"` + ids[0]},
723+
{fmt.Sprintf("format=tree&limit=2&offset_id=%s", ids[4]), `"info":{"count":7,"count_left":4,"last_comment":"` + ids[0]},
724+
{fmt.Sprintf("format=tree&limit=2&offset_id=%s", ids[7]), `"info":{"count":7,"count_left":4,"last_comment":"` + ids[0]},
725+
{fmt.Sprintf("format=tree&limit=1&offset_id=%s", uuid.New().String()), `"info":{"count":7,"count_left":4,"last_comment":"` + ids[0]},
726+
{fmt.Sprintf("format=tree&url=test-url&limit=2&offset_id=%s", ids[2]), `"info":{"url":"test-url","count":6,"count_left":3,"last_comment":"` + ids[0]},
727+
{fmt.Sprintf("format=tree&url=test-url&limit=2&offset_id=%s", ids[3]), `"info":{"url":"test-url","count":6,"count_left":3,"last_comment":"` + ids[0]},
728+
{fmt.Sprintf("format=tree&url=test-url&limit=2&offset_id=%s", ids[4]), `"info":{"url":"test-url","count":6,"count_left":3,"last_comment":"` + ids[0]},
729+
{fmt.Sprintf("format=tree&url=test-url&limit=2&offset_id=%s", ids[7]), `"info":{"url":"test-url","count":6,"count_left":3,"last_comment":"` + ids[0]},
730+
{fmt.Sprintf("format=tree&url=test-url&limit=1&offset_id=%s", uuid.New().String()), `"info":{"url":"test-url","count":6,"count_left":3,"last_comment":"` + ids[0]},
731+
// test sort
732+
{"format=tree&limit=1&sort=+time&url=test-url", `"info":{"url":"test-url","count":6,"count_left":3,"last_comment":"` + ids[0]},
733+
{"format=tree&limit=1&sort=-time&url=test-url", `"info":{"url":"test-url","count":6,"count_left":5,"last_comment":"` + ids[6]},
734+
{"format=tree&limit=1&sort=+score&url=test-url", `"info":{"url":"test-url","count":6,"count_left":5,"last_comment":"` + ids[6]},
735+
{"format=tree&limit=1&sort=-score&url=test-url", `"info":{"url":"test-url","count":6,"count_left":4,"last_comment":"` + ids[1]},
736+
{"format=tree&limit=1&sort=+controversy&url=test-url", `"info":{"url":"test-url","count":6,"count_left":3,"last_comment":"` + ids[0]},
737+
{"format=tree&limit=1&sort=-controversy&url=test-url", `"info":{"url":"test-url","count":6,"count_left":5,"last_comment":"` + ids[6]},
657738
}
658739

659740
for _, tc := range testCases {
660741
t.Run(tc.params, func(t *testing.T) {
661742
url := fmt.Sprintf(ts.URL+"/api/v1/find?site=remark42&%s", tc.params)
662743
body, code := get(t, url)
663-
assert.Equal(t, http.StatusOK, code)
744+
expectedStatus := http.StatusOK
745+
if strings.Contains(tc.params, "=bad") {
746+
expectedStatus = http.StatusBadRequest
747+
}
748+
assert.Equal(t, expectedStatus, code)
664749
assert.Contains(t, body, tc.expectedBody)
665750
t.Log(body)
666751
// prevent hit limiter from engaging
667-
time.Sleep(50 * time.Millisecond)
752+
time.Sleep(80 * time.Millisecond)
668753
})
669754
}
670755
}

0 commit comments

Comments
 (0)