HTTP API: server, credential and the list of chats (closes #4)
check / check (push) Successful in 1m1s
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
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
@@ -43,6 +44,19 @@ const (
|
||||
|
||||
contactConnected = `{"type":"contactConnected","user":{"userId":1},
|
||||
"contact":{"contactId":3,"localDisplayName":"alice"}}`
|
||||
|
||||
// Two contacts with the same display name, which the chat client
|
||||
// tells apart by the local name it gives the second. The second has
|
||||
// deleted its chat with the bot, and is still listed.
|
||||
contactsList = `{"type":"contactsList","user":{"userId":1},"contacts":[
|
||||
{"contactId":3,"localDisplayName":"tester","profile":{"profileId":3,
|
||||
"displayName":"tester","fullName":"","localAlias":""},
|
||||
"activeConn":{"connId":2,"connStatus":{"type":"ready"}},
|
||||
"contactUsed":true,"contactStatus":"active","chatDeleted":false},
|
||||
{"contactId":4,"localDisplayName":"tester_1","profile":{"profileId":4,
|
||||
"displayName":"tester","fullName":"","localAlias":""},
|
||||
"activeConn":{"connId":3,"connStatus":{"type":"deleted"}},
|
||||
"contactUsed":true,"contactStatus":"deleted","chatDeleted":false}]}`
|
||||
)
|
||||
|
||||
// fakeChat stands in for the chat client's API. It answers each command
|
||||
@@ -220,6 +234,38 @@ func TestAddressSetup(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestContacts: the contacts of the given user come back with their
|
||||
// ids, display names and whether they are deleted.
|
||||
func TestContacts(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
f, url := newFakeChat(t, map[string]string{"/_contacts": contactsList})
|
||||
c, ctx := dial(t, url, nil)
|
||||
|
||||
contacts, err := c.Contacts(ctx, 1)
|
||||
if err != nil {
|
||||
t.Fatalf("Contacts: %v", err)
|
||||
}
|
||||
|
||||
if got := f.next(t); got != "/_contacts 1" {
|
||||
t.Errorf("command = %s, want /_contacts 1", got)
|
||||
}
|
||||
|
||||
tester := simplex.Profile{DisplayName: "tester"}
|
||||
|
||||
want := []simplex.Contact{
|
||||
{ContactID: 3, Profile: tester, Status: "active"},
|
||||
{ContactID: 4, Profile: tester, Status: "deleted"},
|
||||
}
|
||||
if !slices.Equal(contacts, want) {
|
||||
t.Fatalf("Contacts = %+v\nwant %+v", contacts, want)
|
||||
}
|
||||
|
||||
if contacts[0].Deleted() || !contacts[1].Deleted() {
|
||||
t.Error("Deleted must be false for contact 3 and true for contact 4")
|
||||
}
|
||||
}
|
||||
|
||||
// TestRefusedCommand: a command the chat client refuses is an error
|
||||
// that names the reason.
|
||||
func TestRefusedCommand(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user