Skip to content

Commit f7f18c8

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 4cd4ebe commit f7f18c8

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/v2"
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/v2"
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

@@ -598,7 +599,7 @@ func TestPublic_FindCommentsCtrl_ConsistentCount(t *testing.T) {
598599
setScore(commentLocator, ids[4], -3)
599600
time.Sleep(time.Millisecond * 5)
600601

601-
c6 := store.Comment{Text: "third-level comment 2", ParentID: ids[4], Locator: commentLocator}
602+
c6 := store.Comment{Text: "deleted third-level comment 2", ParentID: ids[4], Locator: commentLocator}
602603
ids[5], timestamps[5] = addCommentGetCreatedTime(t, c6, ts)
603604
// deleted later so not visible in site-wide requests
604605
setScore(commentLocator, ids[5], 10)
@@ -612,7 +613,7 @@ func TestPublic_FindCommentsCtrl_ConsistentCount(t *testing.T) {
612613
setScore(commentLocator, ids[6], 1)
613614
time.Sleep(time.Millisecond * 5)
614615

615-
c8 := store.Comment{Text: "second-level comment 3", ParentID: ids[6], Locator: commentLocator}
616+
c8 := store.Comment{Text: "deleted second-level comment 3", ParentID: ids[6], Locator: commentLocator}
616617
ids[7], timestamps[7] = addCommentGetCreatedTime(t, c8, ts)
617618
// deleted later so not visible in site-wide requests
618619
setScore(commentLocator, ids[7], -20)
@@ -636,24 +637,24 @@ func TestPublic_FindCommentsCtrl_ConsistentCount(t *testing.T) {
636637
sinceTS[i] = strconv.FormatInt(created.UnixNano()/1000000, 10)
637638
formattedTS[i] = created.Format(time.RFC3339Nano)
638639
}
639-
t.Logf("last timestamp: %v", timestamps[7])
640640

641641
testCases := []struct {
642642
params string
643643
expectedBody string
644644
}{
645-
{"", fmt.Sprintf(`"info":{"count":7,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
646-
{"url=test-url", fmt.Sprintf(`"info":{"url":"test-url","count":6,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
647-
{"format=plain", fmt.Sprintf(`"info":{"count":7,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
648-
{"format=plain&url=test-url", fmt.Sprintf(`"info":{"url":"test-url","count":6,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
649-
{"since=" + sinceTenSecondsAgo, fmt.Sprintf(`"info":{"count":7,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
650-
{"url=test-url&since=" + sinceTenSecondsAgo, fmt.Sprintf(`"info":{"url":"test-url","count":6,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
651-
{"since=" + sinceTS[0], fmt.Sprintf(`"info":{"count":7,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
652-
{"url=test-url&since=" + sinceTS[0], fmt.Sprintf(`"info":{"url":"test-url","count":6,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
653-
{"since=" + sinceTS[1], fmt.Sprintf(`"info":{"count":6,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
654-
{"url=test-url&since=" + sinceTS[1], fmt.Sprintf(`"info":{"url":"test-url","count":5,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
655-
{"since=" + sinceTS[4], fmt.Sprintf(`"info":{"count":3,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
656-
{"url=test-url&since=" + sinceTS[4], fmt.Sprintf(`"info":{"url":"test-url","count":2,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
645+
// test parameters url, format, since, sort
646+
{"", fmt.Sprintf(`"info":{"count":7,"count_left":0,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
647+
{"url=test-url", fmt.Sprintf(`"info":{"url":"test-url","count":6,"count_left":0,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
648+
{"format=plain", fmt.Sprintf(`"info":{"count":7,"count_left":0,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
649+
{"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])},
650+
{"since=" + sinceTenSecondsAgo, fmt.Sprintf(`"info":{"count":7,"count_left":0,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
651+
{"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])},
652+
{"since=" + sinceTS[0], fmt.Sprintf(`"info":{"count":7,"count_left":0,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
653+
{"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])},
654+
{"since=" + sinceTS[1], fmt.Sprintf(`"info":{"count":6,"count_left":0,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
655+
{"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])},
656+
{"since=" + sinceTS[4], fmt.Sprintf(`"info":{"count":3,"count_left":0,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
657+
{"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])},
657658
{"format=tree", `"info":{"count":7`},
658659
{"format=tree&url=test-url", `"info":{"url":"test-url","count":6`},
659660
{"format=tree&sort=+time", `"info":{"count":7`},
@@ -672,17 +673,101 @@ func TestPublic_FindCommentsCtrl_ConsistentCount(t *testing.T) {
672673
{"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])},
673674
// three comments of which last one deleted and doesn't have controversy so returned last
674675
{"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])},
676+
677+
// test parameters limit, offset_id for format=plain
678+
{"limit=bad", `{"code":1,"details":"bad limit value","error":"strconv.Atoi: parsing \"bad\": invalid syntax"}`},
679+
{"offset_id=bad", `{"code":1,"details":"bad offset_id value","error":"invalid UUID length: 3"}`},
680+
{"limit=2", `"info":{"count":7,"count_left":5,"last_comment":"` + ids[1]},
681+
{"limit=6", `"info":{"count":7,"count_left":1,"last_comment":"` + ids[6]},
682+
{"limit=7", `"info":{"count":7,"count_left":0,"last_comment":"` + ids[8]},
683+
{"limit=2&url=test-url", `"info":{"url":"test-url","count":6,"count_left":6,"last_comment":"` + ids[1]},
684+
{"limit=6&url=test-url", `"info":{"url":"test-url","count":6,"count_left":2,"last_comment":"` + ids[5]},
685+
{"limit=7&url=test-url", `"info":{"url":"test-url","count":6,"count_left":1,"last_comment":"` + ids[6]},
686+
{fmt.Sprintf("limit=2&offset_id=%s", ids[2]), `"info":{"count":7,"count_left":2,"last_comment":"` + ids[4]},
687+
{fmt.Sprintf("limit=2&offset_id=%s", ids[3]), `"info":{"count":7,"count_left":1,"last_comment":"` + ids[6]},
688+
{fmt.Sprintf("limit=2&offset_id=%s", ids[4]), `"info":{"count":7,"count_left":0`},
689+
{fmt.Sprintf("limit=1&offset_id=%s", ids[6]), `"info":{"count":7,"count_left":0`},
690+
{fmt.Sprintf("limit=2&offset_id=%s", ids[8]), `"info":{"count":7,"count_left":0`},
691+
{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]},
692+
{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]},
693+
{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]},
694+
{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]},
695+
{fmt.Sprintf("limit=2&url=test-url&offset_id=%s", ids[8]), `"info":{"url":"test-url","count":6,"count_left":6,`},
696+
// deleted comment, offset is ignored in site-wide request but not for particular URL
697+
{fmt.Sprintf("limit=2&offset_id=%s", ids[5]), `"info":{"count":7,"count_left":5,"last_comment":"` + ids[1]},
698+
{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]},
699+
// non-existing comment, offset is ignored, deleted comments included into request with "url"
700+
{fmt.Sprintf("limit=1&offset_id=%s", uuid.New().String()), `"info":{"count":7,"count_left":6,"last_comment":"` + ids[0]},
701+
{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]},
702+
// since is ignored for tree format, so we test it only for plain
703+
{"limit=6&since=" + sinceTenSecondsAgo, `"info":{"count":7,"count_left":1,"last_comment":"` + ids[6]},
704+
{"limit=1&since=" + sinceTS[4], `"info":{"count":3,"count_left":2,"last_comment":"` + ids[4]},
705+
{"limit=6&url=test-url&since=" + sinceTenSecondsAgo, `"info":{"url":"test-url","count":6,"count_left":2,"last_comment":"` + ids[5]},
706+
{"limit=1&url=test-url&since=" + sinceTS[4], `"info":{"url":"test-url","count":2,"count_left":3,"last_comment":"` + ids[4]},
707+
// start with deleted comment timestamp
708+
{"limit=1&since=" + sinceTS[5], `"info":{"count":2,"count_left":1,"last_comment":"` + ids[6]},
709+
{"limit=1&since=" + sinceTS[6], `"info":{"count":2,"count_left":1,"last_comment":"` + ids[6]},
710+
{"limit=1&url=test-url&since=" + sinceTS[5], `"info":{"url":"test-url","count":1,"count_left":2,"last_comment":"` + ids[5]},
711+
{"limit=1&url=test-url&since=" + sinceTS[6], `"info":{"url":"test-url","count":1,"count_left":1,"last_comment":"` + ids[6]},
712+
// test sort
713+
{"limit=1&sort=+time&url=test-url", `"info":{"url":"test-url","count":6,"count_left":7,"last_comment":"` + ids[0]},
714+
{"limit=1&sort=-time&url=test-url", `"info":{"url":"test-url","count":6,"count_left":7,"last_comment":"` + ids[7]},
715+
{"limit=1&sort=+score&url=test-url", `"info":{"url":"test-url","count":6,"count_left":7,"last_comment":"` + ids[6]},
716+
{"limit=1&sort=-score&url=test-url", `"info":{"url":"test-url","count":6,"count_left":7,"last_comment":"` + ids[2]},
717+
{"limit=1&sort=+controversy&url=test-url", `"info":{"url":"test-url","count":6,"count_left":7,"last_comment":"` + ids[0]},
718+
{"limit=1&sort=-controversy&url=test-url", `"info":{"url":"test-url","count":6,"count_left":7,"last_comment":"` + ids[3]},
719+
720+
// test parameters limit, offset_id for format=tree
721+
{"format=tree&limit=bad", `{"code":1,"details":"bad limit value","error":"strconv.Atoi: parsing \"bad\": invalid syntax"}`},
722+
{"format=tree&offset_id=bad", `{"code":1,"details":"bad offset_id value","error":"invalid UUID length: 3"}`},
723+
{"format=tree&limit=2", `"info":{"count":7,"count_left":4,"last_comment":"` + ids[0]},
724+
{"format=tree&limit=6", `"info":{"count":7,"count_left":2,"last_comment":"` + ids[1]},
725+
{"format=tree&limit=7", `"info":{"count":7,"count_left":1,"last_comment":"` + ids[6]},
726+
{"format=tree&url=test-url&limit=2", `"info":{"url":"test-url","count":6,"count_left":3,"last_comment":"` + ids[0]},
727+
{"format=tree&url=test-url&limit=6", `"info":{"url":"test-url","count":6,"count_left":1,"last_comment":"` + ids[1]},
728+
{"format=tree&url=test-url&limit=7", `"info":{"url":"test-url","count":6,"count_left":0,"last_comment":"` + ids[6]},
729+
// start after first top-level comment
730+
{fmt.Sprintf("format=tree&limit=2&offset_id=%s", ids[0]), `"info":{"count":7,"count_left":2,"last_comment":"` + ids[1]},
731+
{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]},
732+
// start after second top-level comment
733+
{fmt.Sprintf("format=tree&limit=2&offset_id=%s", ids[1]), `"info":{"count":7,"count_left":1,"last_comment":"` + ids[6]},
734+
{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]},
735+
// start after third top-level comment, so expect comment to post 2, or no comments on post 1 if "url" is set
736+
{fmt.Sprintf("format=tree&limit=1&offset_id=%s", ids[6]), `"info":{"count":7,"count_left":0,"last_comment":"` + ids[8]},
737+
{fmt.Sprintf("format=tree&url=test-url&limit=1&offset_id=%s", ids[6]), `"info":{"url":"test-url","count":6,"count_left":0`},
738+
// non-root comment IDs or non-existing IDs are ignored
739+
{fmt.Sprintf("format=tree&limit=2&offset_id=%s", ids[2]), `"info":{"count":7,"count_left":4,"last_comment":"` + ids[0]},
740+
{fmt.Sprintf("format=tree&limit=2&offset_id=%s", ids[3]), `"info":{"count":7,"count_left":4,"last_comment":"` + ids[0]},
741+
{fmt.Sprintf("format=tree&limit=2&offset_id=%s", ids[4]), `"info":{"count":7,"count_left":4,"last_comment":"` + ids[0]},
742+
{fmt.Sprintf("format=tree&limit=2&offset_id=%s", ids[7]), `"info":{"count":7,"count_left":4,"last_comment":"` + ids[0]},
743+
{fmt.Sprintf("format=tree&limit=1&offset_id=%s", uuid.New().String()), `"info":{"count":7,"count_left":4,"last_comment":"` + ids[0]},
744+
{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]},
745+
{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]},
746+
{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]},
747+
{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]},
748+
{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]},
749+
// test sort
750+
{"format=tree&limit=1&sort=+time&url=test-url", `"info":{"url":"test-url","count":6,"count_left":3,"last_comment":"` + ids[0]},
751+
{"format=tree&limit=1&sort=-time&url=test-url", `"info":{"url":"test-url","count":6,"count_left":5,"last_comment":"` + ids[6]},
752+
{"format=tree&limit=1&sort=+score&url=test-url", `"info":{"url":"test-url","count":6,"count_left":5,"last_comment":"` + ids[6]},
753+
{"format=tree&limit=1&sort=-score&url=test-url", `"info":{"url":"test-url","count":6,"count_left":4,"last_comment":"` + ids[1]},
754+
{"format=tree&limit=1&sort=+controversy&url=test-url", `"info":{"url":"test-url","count":6,"count_left":3,"last_comment":"` + ids[0]},
755+
{"format=tree&limit=1&sort=-controversy&url=test-url", `"info":{"url":"test-url","count":6,"count_left":5,"last_comment":"` + ids[6]},
675756
}
676757

677758
for _, tc := range testCases {
678759
t.Run(tc.params, func(t *testing.T) {
679760
url := fmt.Sprintf(ts.URL+"/api/v1/find?site=remark42&%s", tc.params)
680761
body, code := get(t, url)
681-
assert.Equal(t, http.StatusOK, code)
762+
expectedStatus := http.StatusOK
763+
if strings.Contains(tc.params, "=bad") {
764+
expectedStatus = http.StatusBadRequest
765+
}
766+
assert.Equal(t, expectedStatus, code)
682767
assert.Contains(t, body, tc.expectedBody)
683768
t.Log(body)
684769
// prevent hit limiter from engaging
685-
time.Sleep(50 * time.Millisecond)
770+
time.Sleep(80 * time.Millisecond)
686771
})
687772
}
688773
}

0 commit comments

Comments
 (0)