112 lines
3.2 KiB
Go
112 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func newTestAS(t *testing.T, dispatched *[][]Event) (*AppService, *Store) {
|
|
t.Helper()
|
|
st, err := OpenStore(filepath.Join(t.TempDir(), "t.db"))
|
|
if err != nil {
|
|
t.Fatalf("open store: %v", err)
|
|
}
|
|
as := NewAppService(
|
|
&Config{HSToken: "secret", BotMXID: "@ai:vojo.chat"},
|
|
log.New(io.Discard, "", 0),
|
|
st,
|
|
func(_ context.Context, ev []Event) { *dispatched = append(*dispatched, ev) },
|
|
)
|
|
as.baseCtx = context.Background()
|
|
return as, st
|
|
}
|
|
|
|
func txnReq(txnID, auth, body string) *http.Request {
|
|
r := httptest.NewRequest(http.MethodPut, "/_matrix/app/v1/transactions/"+txnID, strings.NewReader(body))
|
|
r.SetPathValue("txnId", txnID)
|
|
if auth != "" {
|
|
r.Header.Set("Authorization", "Bearer "+auth)
|
|
}
|
|
return r
|
|
}
|
|
|
|
func TestTransactionAuthAndIdempotency(t *testing.T) {
|
|
var dispatched [][]Event
|
|
as, st := newTestAS(t, &dispatched)
|
|
defer st.Close()
|
|
body := `{"events":[{"type":"m.room.message","room_id":"!r:vojo.chat","event_id":"$1","sender":"@u:vojo.chat"}]}`
|
|
|
|
// Bad hs_token → 403, nothing dispatched.
|
|
w := httptest.NewRecorder()
|
|
as.handleTransaction(w, txnReq("txn1", "wrong", body))
|
|
if w.Code != http.StatusForbidden {
|
|
t.Fatalf("bad token: got %d, want 403", w.Code)
|
|
}
|
|
if len(dispatched) != 0 {
|
|
t.Fatalf("bad token must not dispatch, got %d", len(dispatched))
|
|
}
|
|
|
|
// Good hs_token → 200, one batch dispatched.
|
|
w = httptest.NewRecorder()
|
|
as.handleTransaction(w, txnReq("txn1", "secret", body))
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("good token: got %d, want 200", w.Code)
|
|
}
|
|
if len(dispatched) != 1 || len(dispatched[0]) != 1 {
|
|
t.Fatalf("expected one dispatched batch of one event, got %v", dispatched)
|
|
}
|
|
|
|
// Same txnId again → idempotent no-op (still 200, no re-dispatch).
|
|
w = httptest.NewRecorder()
|
|
as.handleTransaction(w, txnReq("txn1", "secret", body))
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("retry: got %d, want 200", w.Code)
|
|
}
|
|
if len(dispatched) != 1 {
|
|
t.Fatalf("retried transaction must not re-dispatch, got %d batches", len(dispatched))
|
|
}
|
|
}
|
|
|
|
func TestTransactionLegacyQueryTokenAccepted(t *testing.T) {
|
|
var dispatched [][]Event
|
|
as, st := newTestAS(t, &dispatched)
|
|
defer st.Close()
|
|
|
|
r := httptest.NewRequest(http.MethodPut, "/transactions/txnX?access_token=secret", strings.NewReader(`{"events":[]}`))
|
|
r.SetPathValue("txnId", "txnX")
|
|
w := httptest.NewRecorder()
|
|
as.handleTransaction(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("legacy access_token query: got %d, want 200", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestUserQuery(t *testing.T) {
|
|
var dispatched [][]Event
|
|
as, st := newTestAS(t, &dispatched)
|
|
defer st.Close()
|
|
|
|
mk := func(uid string) *http.Request {
|
|
r := httptest.NewRequest(http.MethodGet, "/_matrix/app/v1/users/"+uid, nil)
|
|
r.SetPathValue("userId", uid)
|
|
r.Header.Set("Authorization", "Bearer secret")
|
|
return r
|
|
}
|
|
|
|
w := httptest.NewRecorder()
|
|
as.handleUserQuery(w, mk("@ai:vojo.chat"))
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("own user: got %d, want 200", w.Code)
|
|
}
|
|
w = httptest.NewRecorder()
|
|
as.handleUserQuery(w, mk("@someone:vojo.chat"))
|
|
if w.Code != http.StatusNotFound {
|
|
t.Fatalf("foreign user: got %d, want 404", w.Code)
|
|
}
|
|
}
|