check / check (push) Successful in 1m1s
The bot now serves an HTTP API on PORT (default 8080) beside the chat client. Every request needs the credential read at startup from the file named by API_TOKEN_FILE, sent as a bearer token; without one, every request is refused. Responses carry the security headers, bodies are capped at 64 KiB and each request's work at 10 seconds. GET /api/v1/chats lists the bot's contacts from the chat client's /_contacts command, ordered by id, and marks the contacts who deleted their chat with the bot, which the chat client keeps listing. bot.Run starts the API after set-up and stops it within 5 seconds; a listener failure ends the bot as a chat client failure does. Model: opus-5-5
65 lines
2.1 KiB
Go
65 lines
2.1 KiB
Go
package api_test
|
|
|
|
import (
|
|
"net/http"
|
|
"testing"
|
|
|
|
"sneak.berlin/go/simplexcalc/internal/simplex"
|
|
)
|
|
|
|
// TestChats: the chats are the bot's contacts, ordered by id, deleted
|
|
// ones marked, and the chat client is asked for the bot's user with a
|
|
// deadline.
|
|
func TestChats(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
client := &fakeClient{contacts: []simplex.Contact{
|
|
{ContactID: 3, Profile: simplex.Profile{DisplayName: "bob"}, Status: "deleted"},
|
|
{ContactID: 2, Profile: simplex.Profile{DisplayName: "alice"}, Status: "active"},
|
|
}}
|
|
|
|
rec := request(t, newAPI(credential, client), http.MethodGet, chatsPath, bearer)
|
|
|
|
want := `{"chats":[{"id":2,"display_name":"alice","contact_deleted":false},` +
|
|
`{"id":3,"display_name":"bob","contact_deleted":true}]}` + "\n"
|
|
if rec.Code != http.StatusOK || rec.Body.String() != want {
|
|
t.Errorf("response = %d %q, want 200 %q", rec.Code, rec.Body.String(), want)
|
|
}
|
|
|
|
if got := rec.Header().Get("Content-Type"); got != "application/json" {
|
|
t.Errorf("Content-Type = %q, want application/json", got)
|
|
}
|
|
|
|
if client.userID != 1 || !client.hadDeadline {
|
|
t.Errorf("the chat client was asked for user %d, deadline %v; "+
|
|
"want user 1 with a deadline", client.userID, client.hadDeadline)
|
|
}
|
|
}
|
|
|
|
// TestNoChats: no contacts is an empty list, not null.
|
|
func TestNoChats(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
rec := request(t, newAPI(credential, &fakeClient{}),
|
|
http.MethodGet, chatsPath, bearer)
|
|
|
|
want := `{"chats":[]}` + "\n"
|
|
if rec.Code != http.StatusOK || rec.Body.String() != want {
|
|
t.Errorf("response = %d %q, want 200 %q", rec.Code, rec.Body.String(), want)
|
|
}
|
|
}
|
|
|
|
// TestChatsFailure: when the chat client fails, the response says so
|
|
// in a chosen sentence, never in the error's own text.
|
|
func TestChatsFailure(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
rec := request(t, newAPI(credential, &fakeClient{err: errChat}),
|
|
http.MethodGet, chatsPath, bearer)
|
|
|
|
want := `{"error":"the chats could not be read"}` + "\n"
|
|
if rec.Code != http.StatusInternalServerError || rec.Body.String() != want {
|
|
t.Errorf("response = %d %q, want 500 %q", rec.Code, rec.Body.String(), want)
|
|
}
|
|
}
|