GET /api/v1/history took a database id for before, but history responses carry only each message's id, so a client had nothing to pass for the next page. before now takes that id: the server finds the message among those sent to the target and returns the page older than it. An id with no such message gets 400 "unknown before id" instead of a silent full page. A handler test pages back through a channel using only ids from earlier responses, and README.md's history section describes the new parameter. Model: opus-5-5 Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
This commit was merged in pull request #136.
This commit is contained in:
@@ -14,6 +14,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"slices"
|
||||
"strconv"
|
||||
@@ -1482,6 +1483,164 @@ func TestHistoryNonMember(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// getHistory fetches one page of target's history and
|
||||
// returns the status and, on 200, the messages on it.
|
||||
func (tserver *testServer) getHistory(
|
||||
token, target, before string,
|
||||
limit int,
|
||||
) (int, []db.IRCMessage) {
|
||||
tserver.t.Helper()
|
||||
|
||||
query := url.Values{}
|
||||
query.Set("target", target)
|
||||
query.Set("before", before)
|
||||
query.Set("limit", strconv.Itoa(limit))
|
||||
|
||||
resp, err := doRequestAuth(
|
||||
tserver.t, http.MethodGet,
|
||||
tserver.url("/api/v1/history?"+query.Encode()),
|
||||
token, nil,
|
||||
)
|
||||
if err != nil {
|
||||
tserver.t.Fatalf("get history: %v", err)
|
||||
}
|
||||
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return resp.StatusCode, nil
|
||||
}
|
||||
|
||||
var msgs []db.IRCMessage
|
||||
|
||||
decErr := json.NewDecoder(resp.Body).Decode(&msgs)
|
||||
if decErr != nil {
|
||||
tserver.t.Fatalf("decode history: %v", decErr)
|
||||
}
|
||||
|
||||
return resp.StatusCode, msgs
|
||||
}
|
||||
|
||||
func TestHistoryPaging(t *testing.T) {
|
||||
const (
|
||||
channel = "#paging"
|
||||
msgCount = 5
|
||||
pageSize = 2
|
||||
wantPages = 3
|
||||
)
|
||||
|
||||
tserver := newTestServer(t)
|
||||
token := tserver.createSession("pager")
|
||||
|
||||
tserver.sendCommand(token, map[string]any{
|
||||
commandKey: joinCmd, toKey: channel,
|
||||
})
|
||||
|
||||
want := make([]string, 0, msgCount)
|
||||
|
||||
for i := range msgCount {
|
||||
text := fmt.Sprintf("message %d", i+1)
|
||||
want = append(want, text)
|
||||
|
||||
tserver.sendCommand(token, map[string]any{
|
||||
commandKey: privmsgCmd,
|
||||
toKey: channel,
|
||||
bodyKey: []string{text},
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
got []string
|
||||
pages int
|
||||
)
|
||||
|
||||
// The first page has no before; each later one passes the
|
||||
// id of the oldest message on the page fetched before it.
|
||||
before := ""
|
||||
|
||||
for range msgCount {
|
||||
status, page := tserver.getHistory(
|
||||
token, channel, before, pageSize,
|
||||
)
|
||||
if status != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d", status)
|
||||
}
|
||||
|
||||
if len(page) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
pages++
|
||||
|
||||
texts := make([]string, 0, len(page))
|
||||
|
||||
for _, msg := range page {
|
||||
var body []string
|
||||
|
||||
err := json.Unmarshal(msg.Body, &body)
|
||||
if err != nil {
|
||||
t.Fatalf("decode body: %v", err)
|
||||
}
|
||||
|
||||
texts = append(texts, body...)
|
||||
}
|
||||
|
||||
got = slices.Insert(got, 0, texts...)
|
||||
before = page[0].ID
|
||||
}
|
||||
|
||||
if pages != wantPages {
|
||||
t.Fatalf(
|
||||
"expected %d pages, got %d", wantPages, pages,
|
||||
)
|
||||
}
|
||||
|
||||
if !slices.Equal(got, want) {
|
||||
t.Fatalf("expected %q, got %q", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHistoryUnknownBefore(t *testing.T) {
|
||||
tserver := newTestServer(t)
|
||||
token := tserver.createSession("lost")
|
||||
|
||||
for _, channel := range []string{"#here", "#elsewhere"} {
|
||||
tserver.sendCommand(token, map[string]any{
|
||||
commandKey: joinCmd, toKey: channel,
|
||||
})
|
||||
tserver.sendCommand(token, map[string]any{
|
||||
commandKey: privmsgCmd,
|
||||
toKey: channel,
|
||||
bodyKey: []string{"hi there"},
|
||||
})
|
||||
}
|
||||
|
||||
_, elsewhere := tserver.getHistory(
|
||||
token, "#elsewhere", "", 1,
|
||||
)
|
||||
if len(elsewhere) != 1 {
|
||||
t.Fatalf(
|
||||
"expected 1 message, got %d", len(elsewhere),
|
||||
)
|
||||
}
|
||||
|
||||
cases := []struct{ name, before string }{
|
||||
{"made-up id", "no-such-message"},
|
||||
{"id from another channel", elsewhere[0].ID},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
status, _ := tserver.getHistory(
|
||||
token, "#here", tc.before, 1,
|
||||
)
|
||||
if status != http.StatusBadRequest {
|
||||
t.Fatalf(
|
||||
"%s: expected 400, got %d", tc.name, status,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelList(t *testing.T) {
|
||||
tserver := newTestServer(t)
|
||||
token := tserver.createSession("lister")
|
||||
|
||||
Reference in New Issue
Block a user