Skip to content

Commit 4bbb980

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 a6f3b7e commit 4bbb980

File tree

6 files changed

+284
-42
lines changed

6 files changed

+284
-42
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

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

684766
for _, tc := range testCases {
685767
t.Run(tc.params, func(t *testing.T) {
686768
url := fmt.Sprintf(ts.URL+"/api/v1/find?site=remark42&%s", tc.params)
687769
body, code := get(t, url)
688-
assert.Equal(t, http.StatusOK, code)
770+
expectedStatus := http.StatusOK
771+
if strings.Contains(tc.params, "=bad") {
772+
expectedStatus = http.StatusBadRequest
773+
}
774+
assert.Equal(t, expectedStatus, code)
689775
assert.Contains(t, body, tc.expectedBody)
690776
t.Log(body)
691777
// prevent hit limiter from engaging
692-
time.Sleep(50 * time.Millisecond)
778+
time.Sleep(80 * time.Millisecond)
693779
})
694780
}
695781
}

0 commit comments

Comments
 (0)