mirror of
https://github.com/matrix-org/dendrite.git
synced 2025-12-07 06:53:09 -06:00
Tests and routing
This commit is contained in:
parent
efd92ac4f7
commit
b9676b8539
|
|
@ -2,10 +2,12 @@ package clientapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -1092,3 +1094,245 @@ func TestAdminMarkAsStale(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAdminQueryEventReports(t *testing.T) {
|
||||||
|
alice := test.NewUser(t, test.WithAccountType(uapi.AccountTypeAdmin))
|
||||||
|
bob := test.NewUser(t)
|
||||||
|
room := test.NewRoom(t, alice)
|
||||||
|
room2 := test.NewRoom(t, alice)
|
||||||
|
|
||||||
|
// room2 has a name and canonical alias
|
||||||
|
room2.CreateAndInsert(t, alice, spec.MRoomName, map[string]string{"name": "Testing"}, test.WithStateKey(""))
|
||||||
|
room2.CreateAndInsert(t, alice, spec.MRoomCanonicalAlias, map[string]string{"alias": "#testing"}, test.WithStateKey(""))
|
||||||
|
|
||||||
|
// Join the rooms with Bob
|
||||||
|
room.CreateAndInsert(t, bob, spec.MRoomMember, map[string]interface{}{
|
||||||
|
"membership": "join",
|
||||||
|
}, test.WithStateKey(bob.ID))
|
||||||
|
room2.CreateAndInsert(t, bob, spec.MRoomMember, map[string]interface{}{
|
||||||
|
"membership": "join",
|
||||||
|
}, test.WithStateKey(bob.ID))
|
||||||
|
|
||||||
|
// Create a few events to report
|
||||||
|
eventsToReportPerRoom := make(map[string][]string)
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
ev1 := room.CreateAndInsert(t, alice, "m.room.message", map[string]interface{}{"body": "hello world"})
|
||||||
|
ev2 := room2.CreateAndInsert(t, alice, "m.room.message", map[string]interface{}{"body": "hello world"})
|
||||||
|
eventsToReportPerRoom[room.ID] = append(eventsToReportPerRoom[room.ID], ev1.EventID())
|
||||||
|
eventsToReportPerRoom[room2.ID] = append(eventsToReportPerRoom[room2.ID], ev2.EventID())
|
||||||
|
}
|
||||||
|
|
||||||
|
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||||
|
/*if dbType == test.DBTypeSQLite {
|
||||||
|
t.Skip()
|
||||||
|
}*/
|
||||||
|
cfg, processCtx, close := testrig.CreateConfig(t, dbType)
|
||||||
|
routers := httputil.NewRouters()
|
||||||
|
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||||
|
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||||
|
defer close()
|
||||||
|
natsInstance := jetstream.NATSInstance{}
|
||||||
|
jsctx, _ := natsInstance.Prepare(processCtx, &cfg.Global.JetStream)
|
||||||
|
defer jetstream.DeleteAllStreams(jsctx, &cfg.Global.JetStream)
|
||||||
|
|
||||||
|
// Use an actual roomserver for this
|
||||||
|
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||||
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
|
|
||||||
|
if err := api.SendEvents(context.Background(), rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil {
|
||||||
|
t.Fatalf("failed to send events: %v", err)
|
||||||
|
}
|
||||||
|
if err := api.SendEvents(context.Background(), rsAPI, api.KindNew, room2.Events(), "test", "test", "test", nil, false); err != nil {
|
||||||
|
t.Fatalf("failed to send events: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||||
|
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||||
|
|
||||||
|
accessTokens := map[*test.User]userDevice{
|
||||||
|
alice: {},
|
||||||
|
bob: {},
|
||||||
|
}
|
||||||
|
createAccessTokens(t, accessTokens, userAPI, processCtx.Context(), routers)
|
||||||
|
|
||||||
|
reqBody := map[string]any{
|
||||||
|
"reason": "baaad",
|
||||||
|
"score": -100,
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(reqBody)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
var req *http.Request
|
||||||
|
// Report all events
|
||||||
|
for roomID, eventIDs := range eventsToReportPerRoom {
|
||||||
|
for _, eventID := range eventIDs {
|
||||||
|
req = httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/report/%s", roomID, eventID), strings.NewReader(string(body)))
|
||||||
|
req.Header.Set("Authorization", "Bearer "+accessTokens[bob].accessToken)
|
||||||
|
|
||||||
|
routers.Client.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected report to succeed, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type response struct {
|
||||||
|
EventReports []api.QueryAdminEventReportsResponse `json:"event_reports"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
NextToken *int64 `json:"next_token,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("Can query all reports", func(t *testing.T) {
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
req = httptest.NewRequest(http.MethodGet, "/_synapse/admin/v1/event_reports", strings.NewReader(string(body)))
|
||||||
|
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
|
||||||
|
|
||||||
|
routers.SynapseAdmin.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected getting reports to succeed, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||||
|
}
|
||||||
|
var resp response
|
||||||
|
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
wantCount := 20
|
||||||
|
// Only validating the count
|
||||||
|
if len(resp.EventReports) != wantCount {
|
||||||
|
t.Fatalf("expected %d events, got %d", wantCount, len(resp.EventReports))
|
||||||
|
}
|
||||||
|
if resp.Total != int64(wantCount) {
|
||||||
|
t.Fatalf("expected total to be %d, got %d", wantCount, resp.Total)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Can filter on room", func(t *testing.T) {
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/_synapse/admin/v1/event_reports?room_id=%s", room.ID), strings.NewReader(string(body)))
|
||||||
|
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
|
||||||
|
|
||||||
|
routers.SynapseAdmin.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected getting reports to succeed, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||||
|
}
|
||||||
|
var resp response
|
||||||
|
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
wantCount := 10
|
||||||
|
// Only validating the count
|
||||||
|
if len(resp.EventReports) != wantCount {
|
||||||
|
t.Fatalf("expected %d events, got %d", wantCount, len(resp.EventReports))
|
||||||
|
}
|
||||||
|
if resp.Total != int64(wantCount) {
|
||||||
|
t.Fatalf("expected total to be %d, got %d", wantCount, resp.Total)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Can filter on user_id", func(t *testing.T) {
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/_synapse/admin/v1/event_reports?user_id=%s", "@doesnotexist:test"), strings.NewReader(string(body)))
|
||||||
|
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
|
||||||
|
|
||||||
|
routers.SynapseAdmin.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected getting reports to succeed, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||||
|
}
|
||||||
|
var resp response
|
||||||
|
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The user does not exist, so we expect no results
|
||||||
|
wantCount := 0
|
||||||
|
// Only validating the count
|
||||||
|
if len(resp.EventReports) != wantCount {
|
||||||
|
t.Fatalf("expected %d events, got %d", wantCount, len(resp.EventReports))
|
||||||
|
}
|
||||||
|
if resp.Total != int64(wantCount) {
|
||||||
|
t.Fatalf("expected total to be %d, got %d", wantCount, resp.Total)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Can set direction=f", func(t *testing.T) {
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/_synapse/admin/v1/event_reports?room_id=%s&dir=f", room.ID), strings.NewReader(string(body)))
|
||||||
|
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
|
||||||
|
|
||||||
|
routers.SynapseAdmin.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected getting reports to succeed, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||||
|
}
|
||||||
|
var resp response
|
||||||
|
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
wantCount := 10
|
||||||
|
// Only validating the count
|
||||||
|
if len(resp.EventReports) != wantCount {
|
||||||
|
t.Fatalf("expected %d events, got %d", wantCount, len(resp.EventReports))
|
||||||
|
}
|
||||||
|
if resp.Total != int64(wantCount) {
|
||||||
|
t.Fatalf("expected total to be %d, got %d", wantCount, resp.Total)
|
||||||
|
}
|
||||||
|
// we now should have the first reported event
|
||||||
|
wantEventID := eventsToReportPerRoom[room.ID][0]
|
||||||
|
gotEventID := resp.EventReports[0].EventID
|
||||||
|
if gotEventID != wantEventID {
|
||||||
|
t.Fatalf("expected eventID to be %v, got %v", wantEventID, gotEventID)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Can limit and paginate", func(t *testing.T) {
|
||||||
|
var from int64 = 0
|
||||||
|
var limit int64 = 5
|
||||||
|
var wantTotal int64 = 10 // We expect there to be 10 events in total
|
||||||
|
var resp response
|
||||||
|
for from+limit <= wantTotal {
|
||||||
|
resp = response{}
|
||||||
|
t.Logf("Getting reports starting from %d", from)
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/_synapse/admin/v1/event_reports?room_id=%s&limit=%d&from=%d", room2.ID, limit, from), strings.NewReader(string(body)))
|
||||||
|
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
|
||||||
|
|
||||||
|
routers.SynapseAdmin.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
t.Fatalf("expected getting reports to succeed, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wantCount := 5 // we are limited to 5
|
||||||
|
if len(resp.EventReports) != wantCount {
|
||||||
|
t.Fatalf("expected %d events, got %d", wantCount, len(resp.EventReports))
|
||||||
|
}
|
||||||
|
if resp.Total != int64(wantTotal) {
|
||||||
|
t.Fatalf("expected total to be %d, got %d", wantCount, resp.Total)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've reached the end
|
||||||
|
if (from + int64(len(resp.EventReports))) == wantTotal {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// The next_token should be set
|
||||||
|
if resp.NextToken == nil {
|
||||||
|
t.Fatal("expected nextToken to be set")
|
||||||
|
}
|
||||||
|
from = *resp.NextToken
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -495,3 +495,45 @@ func AdminDownloadState(req *http.Request, device *api.Device, rsAPI roomserverA
|
||||||
JSON: struct{}{},
|
JSON: struct{}{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetEventReports returns reported events for a given user/room.
|
||||||
|
func GetEventReports(
|
||||||
|
req *http.Request,
|
||||||
|
rsAPI roomserverAPI.ClientRoomserverAPI,
|
||||||
|
from, limit uint64,
|
||||||
|
backwards bool,
|
||||||
|
userID, roomID string,
|
||||||
|
) util.JSONResponse {
|
||||||
|
|
||||||
|
eventReports, count, err := rsAPI.QueryAdminEventReports(req.Context(), from, limit, backwards, userID, roomID)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Error("failed to query event reports")
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
JSON: spec.InternalServerError{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := map[string]any{
|
||||||
|
"event_reports": eventReports,
|
||||||
|
"total": count,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a next_token if there are still reports
|
||||||
|
if int64(from+limit) < count {
|
||||||
|
resp["next_token"] = int(from) + len(eventReports)
|
||||||
|
}
|
||||||
|
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
JSON: resp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseUint64OrDefault(input string, defaultValue uint64) uint64 {
|
||||||
|
v, err := strconv.ParseUint(input, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1533,4 +1533,18 @@ func Setup(
|
||||||
return ReportEvent(req, device, vars["roomID"], vars["eventID"], rsAPI)
|
return ReportEvent(req, device, vars["roomID"], vars["eventID"], rsAPI)
|
||||||
}),
|
}),
|
||||||
).Methods(http.MethodPost, http.MethodOptions)
|
).Methods(http.MethodPost, http.MethodOptions)
|
||||||
|
|
||||||
|
synapseAdminRouter.Handle("/admin/v1/event_reports",
|
||||||
|
httputil.MakeAdminAPI("admin_report_event", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
|
from := parseUint64OrDefault(req.URL.Query().Get("from"), 0)
|
||||||
|
limit := parseUint64OrDefault(req.URL.Query().Get("limit"), 100)
|
||||||
|
dir := req.URL.Query().Get("dir")
|
||||||
|
userID := req.URL.Query().Get("user_id")
|
||||||
|
roomID := req.URL.Query().Get("room_id")
|
||||||
|
|
||||||
|
// Go backwards if direction is empty or "b"
|
||||||
|
backwards := dir == "" || dir == "b"
|
||||||
|
return GetEventReports(req, rsAPI, from, limit, backwards, userID, roomID)
|
||||||
|
}),
|
||||||
|
).Methods(http.MethodGet, http.MethodOptions)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue