aboutsummaryrefslogtreecommitdiff
path: root/clientapi
diff options
context:
space:
mode:
Diffstat (limited to 'clientapi')
-rw-r--r--clientapi/routing/sendevent.go25
-rw-r--r--clientapi/routing/sendevent_test.go275
-rw-r--r--clientapi/routing/state.go31
-rw-r--r--clientapi/routing/state_test.go253
4 files changed, 584 insertions, 0 deletions
diff --git a/clientapi/routing/sendevent.go b/clientapi/routing/sendevent.go
index a167a5a7..f81e9c1e 100644
--- a/clientapi/routing/sendevent.go
+++ b/clientapi/routing/sendevent.go
@@ -29,6 +29,7 @@ import (
"github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/dendrite/roomserver/types"
"github.com/matrix-org/dendrite/setup/config"
+ "github.com/matrix-org/dendrite/syncapi/synctypes"
userapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/gomatrixserverlib/spec"
@@ -92,6 +93,30 @@ func SendEvent(
}
}
+ // Translate user ID state keys to room keys in pseudo ID rooms
+ if roomVersion == gomatrixserverlib.RoomVersionPseudoIDs && stateKey != nil {
+ parsedRoomID, innerErr := spec.NewRoomID(roomID)
+ if innerErr != nil {
+ return util.JSONResponse{
+ Code: http.StatusBadRequest,
+ JSON: spec.InvalidParam("invalid room ID"),
+ }
+ }
+
+ newStateKey, innerErr := synctypes.FromClientStateKey(*parsedRoomID, *stateKey, func(roomID spec.RoomID, userID spec.UserID) (*spec.SenderID, error) {
+ return rsAPI.QuerySenderIDForUser(req.Context(), roomID, userID)
+ })
+ if innerErr != nil {
+ // TODO: work out better logic for failure cases (e.g. sender ID not found)
+ util.GetLogger(req.Context()).WithError(innerErr).Error("synctypes.FromClientStateKey failed")
+ return util.JSONResponse{
+ Code: http.StatusInternalServerError,
+ JSON: spec.Unknown("internal server error"),
+ }
+ }
+ stateKey = newStateKey
+ }
+
// create a mutex for the specific user in the specific room
// this avoids a situation where events that are received in quick succession are sent to the roomserver in a jumbled order
userID := device.UserID
diff --git a/clientapi/routing/sendevent_test.go b/clientapi/routing/sendevent_test.go
new file mode 100644
index 00000000..9cdd7535
--- /dev/null
+++ b/clientapi/routing/sendevent_test.go
@@ -0,0 +1,275 @@
+package routing
+
+import (
+ "context"
+ "crypto/ed25519"
+ "fmt"
+ "io"
+ "net/http"
+ "strings"
+ "testing"
+
+ rsapi "github.com/matrix-org/dendrite/roomserver/api"
+ "github.com/matrix-org/dendrite/roomserver/types"
+ "github.com/matrix-org/dendrite/setup/config"
+ uapi "github.com/matrix-org/dendrite/userapi/api"
+ "github.com/matrix-org/gomatrixserverlib"
+ "github.com/matrix-org/gomatrixserverlib/fclient"
+ "github.com/matrix-org/gomatrixserverlib/spec"
+ "gotest.tools/v3/assert"
+)
+
+// Mock roomserver API for testing
+//
+// Currently pretty specialised for the pseudo ID test, so will need
+// editing if future (other) sendevent tests are using this.
+type sendEventTestRoomserverAPI struct {
+ rsapi.ClientRoomserverAPI
+ t *testing.T
+ roomIDStr string
+ roomVersion gomatrixserverlib.RoomVersion
+ roomState []*types.HeaderedEvent
+
+ // userID -> room key
+ senderMapping map[string]ed25519.PrivateKey
+
+ savedInputRoomEvents []rsapi.InputRoomEvent
+}
+
+func (s *sendEventTestRoomserverAPI) QueryRoomVersionForRoom(ctx context.Context, roomID string) (gomatrixserverlib.RoomVersion, error) {
+ if roomID == s.roomIDStr {
+ return s.roomVersion, nil
+ } else {
+ s.t.Logf("room version queried for %s", roomID)
+ return "", fmt.Errorf("unknown room")
+ }
+}
+
+func (s *sendEventTestRoomserverAPI) QueryCurrentState(ctx context.Context, req *rsapi.QueryCurrentStateRequest, res *rsapi.QueryCurrentStateResponse) error {
+ res.StateEvents = map[gomatrixserverlib.StateKeyTuple]*types.HeaderedEvent{}
+ for _, stateKeyTuple := range req.StateTuples {
+ for _, stateEv := range s.roomState {
+ if stateEv.Type() == stateKeyTuple.EventType && stateEv.StateKey() != nil && *stateEv.StateKey() == stateKeyTuple.StateKey {
+ res.StateEvents[stateKeyTuple] = stateEv
+ }
+ }
+ }
+ return nil
+}
+
+func (s *sendEventTestRoomserverAPI) QueryLatestEventsAndState(ctx context.Context, req *rsapi.QueryLatestEventsAndStateRequest, res *rsapi.QueryLatestEventsAndStateResponse) error {
+ if req.RoomID == s.roomIDStr {
+ res.RoomExists = true
+ res.RoomVersion = s.roomVersion
+
+ res.StateEvents = make([]*types.HeaderedEvent, len(s.roomState))
+ copy(res.StateEvents, s.roomState)
+
+ res.LatestEvents = []string{}
+ res.Depth = 1
+ return nil
+ } else {
+ s.t.Logf("room event/state queried for %s", req.RoomID)
+ return fmt.Errorf("unknown room")
+ }
+
+}
+
+func (s *sendEventTestRoomserverAPI) QuerySenderIDForUser(
+ ctx context.Context,
+ roomID spec.RoomID,
+ userID spec.UserID,
+) (*spec.SenderID, error) {
+ if roomID.String() == s.roomIDStr {
+ if s.roomVersion == gomatrixserverlib.RoomVersionPseudoIDs {
+ roomKey, ok := s.senderMapping[userID.String()]
+ if ok {
+ sender := spec.SenderIDFromPseudoIDKey(roomKey)
+ return &sender, nil
+ } else {
+ return nil, nil
+ }
+ } else {
+ senderID := spec.SenderIDFromUserID(userID)
+ return &senderID, nil
+ }
+ }
+
+ return nil, fmt.Errorf("room not found")
+}
+
+func (s *sendEventTestRoomserverAPI) QueryUserIDForSender(
+ ctx context.Context,
+ roomID spec.RoomID,
+ senderID spec.SenderID,
+) (*spec.UserID, error) {
+ if roomID.String() == s.roomIDStr {
+ if s.roomVersion == gomatrixserverlib.RoomVersionPseudoIDs {
+ for uID, roomKey := range s.senderMapping {
+ if string(spec.SenderIDFromPseudoIDKey(roomKey)) == string(senderID) {
+ parsedUserID, err := spec.NewUserID(uID, true)
+ if err != nil {
+ s.t.Fatalf("Mock QueryUserIDForSender failed: %s", err)
+ }
+ return parsedUserID, nil
+ }
+ }
+ } else {
+ userID := senderID.ToUserID()
+ if userID == nil {
+ return nil, fmt.Errorf("bad sender ID")
+ }
+ return userID, nil
+ }
+ }
+
+ return nil, fmt.Errorf("room not found")
+}
+
+func (s *sendEventTestRoomserverAPI) SigningIdentityFor(ctx context.Context, roomID spec.RoomID, sender spec.UserID) (fclient.SigningIdentity, error) {
+ if s.roomIDStr == roomID.String() {
+ if s.roomVersion == gomatrixserverlib.RoomVersionPseudoIDs {
+ roomKey, ok := s.senderMapping[sender.String()]
+ if !ok {
+ s.t.Logf("SigningIdentityFor used with unknown user ID: %v", sender.String())
+ return fclient.SigningIdentity{}, fmt.Errorf("could not get signing identity for %v", sender.String())
+ }
+ return fclient.SigningIdentity{PrivateKey: roomKey}, nil
+ } else {
+ return fclient.SigningIdentity{PrivateKey: ed25519.NewKeyFromSeed(make([]byte, 32))}, nil
+ }
+ }
+
+ return fclient.SigningIdentity{}, fmt.Errorf("room not found")
+}
+
+func (s *sendEventTestRoomserverAPI) InputRoomEvents(ctx context.Context, req *rsapi.InputRoomEventsRequest, res *rsapi.InputRoomEventsResponse) {
+ s.savedInputRoomEvents = req.InputRoomEvents
+}
+
+// Test that user ID state keys are translated correctly
+func Test_SendEvent_PseudoIDStateKeys(t *testing.T) {
+ nonpseudoIDRoomVersion := gomatrixserverlib.RoomVersionV10
+ pseudoIDRoomVersion := gomatrixserverlib.RoomVersionPseudoIDs
+
+ senderKeySeed := make([]byte, 32)
+ senderUserID := "@testuser:domain"
+ senderPrivKey := ed25519.NewKeyFromSeed(senderKeySeed)
+ senderPseudoID := string(spec.SenderIDFromPseudoIDKey(senderPrivKey))
+
+ eventType := "com.example.test"
+ roomIDStr := "!id:domain"
+
+ device := &uapi.Device{
+ UserID: senderUserID,
+ }
+
+ t.Run("user ID state key are not translated to room key in non-pseudo ID room", func(t *testing.T) {
+ eventsJSON := []string{
+ fmt.Sprintf(`{"type":"m.room.create","state_key":"","room_id":"%v","sender":"%v","content":{"creator":"%v","room_version":"%v"}}`, roomIDStr, senderUserID, senderUserID, nonpseudoIDRoomVersion),
+ fmt.Sprintf(`{"type":"m.room.member","state_key":"%v","room_id":"%v","sender":"%v","content":{"membership":"join"}}`, senderUserID, roomIDStr, senderUserID),
+ }
+
+ roomState, err := createEvents(eventsJSON, nonpseudoIDRoomVersion)
+ if err != nil {
+ t.Fatalf("failed to prepare state events: %s", err.Error())
+ }
+
+ rsAPI := &sendEventTestRoomserverAPI{
+ t: t,
+ roomIDStr: roomIDStr,
+ roomVersion: nonpseudoIDRoomVersion,
+ roomState: roomState,
+ }
+
+ req, err := http.NewRequest("POST", "https://domain", io.NopCloser(strings.NewReader("{}")))
+ if err != nil {
+ t.Fatalf("failed to make new request: %s", err.Error())
+ }
+
+ cfg := &config.ClientAPI{}
+
+ resp := SendEvent(req, device, roomIDStr, eventType, nil, &senderUserID, cfg, rsAPI, nil)
+
+ if resp.Code != http.StatusOK {
+ t.Fatalf("non-200 HTTP code returned: %v\nfull response: %v", resp.Code, resp)
+ }
+
+ assert.Equal(t, len(rsAPI.savedInputRoomEvents), 1)
+
+ ev := rsAPI.savedInputRoomEvents[0]
+ stateKey := ev.Event.StateKey()
+ if stateKey == nil {
+ t.Fatalf("submitted InputRoomEvent has nil state key, when it should be %v", senderUserID)
+ }
+ if *stateKey != senderUserID {
+ t.Fatalf("expected submitted InputRoomEvent to have user ID state key\nfound: %v\nexpected: %v", *stateKey, senderUserID)
+ }
+ })
+
+ t.Run("user ID state key are translated to room key in pseudo ID room", func(t *testing.T) {
+ eventsJSON := []string{
+ fmt.Sprintf(`{"type":"m.room.create","state_key":"","room_id":"%v","sender":"%v","content":{"creator":"%v","room_version":"%v"}}`, roomIDStr, senderPseudoID, senderPseudoID, pseudoIDRoomVersion),
+ fmt.Sprintf(`{"type":"m.room.member","state_key":"%v","room_id":"%v","sender":"%v","content":{"membership":"join"}}`, senderPseudoID, roomIDStr, senderPseudoID),
+ }
+
+ roomState, err := createEvents(eventsJSON, pseudoIDRoomVersion)
+ if err != nil {
+ t.Fatalf("failed to prepare state events: %s", err.Error())
+ }
+
+ rsAPI := &sendEventTestRoomserverAPI{
+ t: t,
+ roomIDStr: roomIDStr,
+ roomVersion: pseudoIDRoomVersion,
+ senderMapping: map[string]ed25519.PrivateKey{
+ senderUserID: senderPrivKey,
+ },
+ roomState: roomState,
+ }
+
+ req, err := http.NewRequest("POST", "https://domain", io.NopCloser(strings.NewReader("{}")))
+ if err != nil {
+ t.Fatalf("failed to make new request: %s", err.Error())
+ }
+
+ cfg := &config.ClientAPI{}
+
+ resp := SendEvent(req, device, roomIDStr, eventType, nil, &senderUserID, cfg, rsAPI, nil)
+
+ if resp.Code != http.StatusOK {
+ t.Fatalf("non-200 HTTP code returned: %v\nfull response: %v", resp.Code, resp)
+ }
+
+ assert.Equal(t, len(rsAPI.savedInputRoomEvents), 1)
+
+ ev := rsAPI.savedInputRoomEvents[0]
+ stateKey := ev.Event.StateKey()
+ if stateKey == nil {
+ t.Fatalf("submitted InputRoomEvent has nil state key, when it should be %v", senderPseudoID)
+ }
+ if *stateKey != senderPseudoID {
+ t.Fatalf("expected submitted InputRoomEvent to have pseudo ID state key\nfound: %v\nexpected: %v", *stateKey, senderPseudoID)
+ }
+ })
+}
+
+func createEvents(eventsJSON []string, roomVer gomatrixserverlib.RoomVersion) ([]*types.HeaderedEvent, error) {
+ events := make([]*types.HeaderedEvent, len(eventsJSON))
+
+ roomVerImpl, err := gomatrixserverlib.GetRoomVersion(roomVer)
+ if err != nil {
+ return nil, fmt.Errorf("no roomver impl: %s", err.Error())
+ }
+
+ for i, eventJSON := range eventsJSON {
+ pdu, evErr := roomVerImpl.NewEventFromTrustedJSON([]byte(eventJSON), false)
+ if evErr != nil {
+ return nil, fmt.Errorf("failed to make event: %s", err.Error())
+ }
+ ev := types.HeaderedEvent{PDU: pdu}
+ events[i] = &ev
+ }
+
+ return events, nil
+}
diff --git a/clientapi/routing/state.go b/clientapi/routing/state.go
index f53cb301..7648dc47 100644
--- a/clientapi/routing/state.go
+++ b/clientapi/routing/state.go
@@ -217,6 +217,37 @@ func OnIncomingStateTypeRequest(
var worldReadable bool
var wantLatestState bool
+ roomVer, err := rsAPI.QueryRoomVersionForRoom(ctx, roomID)
+ if err != nil {
+ return util.JSONResponse{
+ Code: http.StatusForbidden,
+ JSON: spec.Forbidden(fmt.Sprintf("Unknown room %q or user %q has never joined this room", roomID, device.UserID)),
+ }
+ }
+
+ // Translate user ID state keys to room keys in pseudo ID rooms
+ if roomVer == gomatrixserverlib.RoomVersionPseudoIDs {
+ parsedRoomID, err := spec.NewRoomID(roomID)
+ if err != nil {
+ return util.JSONResponse{
+ Code: http.StatusNotFound,
+ JSON: spec.InvalidParam("invalid room ID"),
+ }
+ }
+ newStateKey, err := synctypes.FromClientStateKey(*parsedRoomID, stateKey, func(roomID spec.RoomID, userID spec.UserID) (*spec.SenderID, error) {
+ return rsAPI.QuerySenderIDForUser(ctx, roomID, userID)
+ })
+ if err != nil {
+ // TODO: work out better logic for failure cases (e.g. sender ID not found)
+ util.GetLogger(ctx).WithError(err).Error("synctypes.FromClientStateKey failed")
+ return util.JSONResponse{
+ Code: http.StatusInternalServerError,
+ JSON: spec.Unknown("internal server error"),
+ }
+ }
+ stateKey = *newStateKey
+ }
+
// Always fetch visibility so that we can work out whether to show
// the latest events or the last event from when the user was joined.
// Then include the requested event type and state key, assuming it
diff --git a/clientapi/routing/state_test.go b/clientapi/routing/state_test.go
new file mode 100644
index 00000000..93b04372
--- /dev/null
+++ b/clientapi/routing/state_test.go
@@ -0,0 +1,253 @@
+package routing
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "testing"
+
+ rsapi "github.com/matrix-org/dendrite/roomserver/api"
+ "github.com/matrix-org/dendrite/roomserver/types"
+ "github.com/matrix-org/dendrite/setup/config"
+ uapi "github.com/matrix-org/dendrite/userapi/api"
+ "github.com/matrix-org/gomatrixserverlib"
+ "github.com/matrix-org/gomatrixserverlib/spec"
+ "github.com/matrix-org/util"
+ "gotest.tools/v3/assert"
+)
+
+var ()
+
+type stateTestRoomserverAPI struct {
+ rsapi.RoomserverInternalAPI
+ t *testing.T
+ roomState map[gomatrixserverlib.StateKeyTuple]*types.HeaderedEvent
+ roomIDStr string
+ roomVersion gomatrixserverlib.RoomVersion
+ userIDStr string
+ // userID -> senderID
+ senderMapping map[string]string
+}
+
+func (s stateTestRoomserverAPI) QueryRoomVersionForRoom(ctx context.Context, roomID string) (gomatrixserverlib.RoomVersion, error) {
+ if roomID == s.roomIDStr {
+ return s.roomVersion, nil
+ } else {
+ s.t.Logf("room version queried for %s", roomID)
+ return "", fmt.Errorf("unknown room")
+ }
+}
+
+func (s stateTestRoomserverAPI) QueryLatestEventsAndState(
+ ctx context.Context,
+ req *rsapi.QueryLatestEventsAndStateRequest,
+ res *rsapi.QueryLatestEventsAndStateResponse,
+) error {
+ res.RoomExists = req.RoomID == s.roomIDStr
+ if !res.RoomExists {
+ return nil
+ }
+
+ res.StateEvents = []*types.HeaderedEvent{}
+ for _, stateKeyTuple := range req.StateToFetch {
+ val, ok := s.roomState[stateKeyTuple]
+ if ok && val != nil {
+ res.StateEvents = append(res.StateEvents, val)
+ }
+ }
+
+ return nil
+}
+
+func (s stateTestRoomserverAPI) QueryMembershipForUser(
+ ctx context.Context,
+ req *rsapi.QueryMembershipForUserRequest,
+ res *rsapi.QueryMembershipForUserResponse,
+) error {
+ if req.UserID.String() == s.userIDStr {
+ res.HasBeenInRoom = true
+ res.IsInRoom = true
+ res.RoomExists = true
+ res.Membership = spec.Join
+ }
+
+ return nil
+}
+
+func (s stateTestRoomserverAPI) QuerySenderIDForUser(
+ ctx context.Context,
+ roomID spec.RoomID,
+ userID spec.UserID,
+) (*spec.SenderID, error) {
+ sID, ok := s.senderMapping[userID.String()]
+ if ok {
+ sender := spec.SenderID(sID)
+ return &sender, nil
+ } else {
+ return nil, nil
+ }
+}
+
+func (s stateTestRoomserverAPI) QueryUserIDForSender(
+ ctx context.Context,
+ roomID spec.RoomID,
+ senderID spec.SenderID,
+) (*spec.UserID, error) {
+ for uID, sID := range s.senderMapping {
+ if sID == string(senderID) {
+ parsedUserID, err := spec.NewUserID(uID, true)
+ if err != nil {
+ s.t.Fatalf("Mock QueryUserIDForSender failed: %s", err)
+ }
+ return parsedUserID, nil
+ }
+ }
+ return nil, nil
+}
+
+func (s stateTestRoomserverAPI) QueryStateAfterEvents(
+ ctx context.Context,
+ req *rsapi.QueryStateAfterEventsRequest,
+ res *rsapi.QueryStateAfterEventsResponse,
+) error {
+ return nil
+}
+
+func Test_OnIncomingStateTypeRequest(t *testing.T) {
+ var tempRoomServerCfg config.RoomServer
+ tempRoomServerCfg.Defaults(config.DefaultOpts{})
+ defaultRoomVersion := tempRoomServerCfg.DefaultRoomVersion
+ pseudoIDRoomVersion := gomatrixserverlib.RoomVersionPseudoIDs
+ nonPseudoIDRoomVersion := gomatrixserverlib.RoomVersionV10
+
+ userIDStr := "@testuser:domain"
+ eventType := "com.example.test"
+ stateKey := "testStateKey"
+ roomIDStr := "!id:domain"
+
+ device := &uapi.Device{
+ UserID: userIDStr,
+ }
+
+ t.Run("request simple state key", func(t *testing.T) {
+ ctx := context.Background()
+
+ rsAPI := stateTestRoomserverAPI{
+ roomVersion: defaultRoomVersion,
+ roomIDStr: roomIDStr,
+ roomState: map[gomatrixserverlib.StateKeyTuple]*types.HeaderedEvent{
+ {
+ EventType: eventType,
+ StateKey: stateKey,
+ }: mustCreateStatePDU(t, defaultRoomVersion, roomIDStr, eventType, stateKey, map[string]interface{}{
+ "foo": "bar",
+ }),
+ },
+ userIDStr: userIDStr,
+ }
+
+ jsonResp := OnIncomingStateTypeRequest(ctx, device, rsAPI, roomIDStr, eventType, stateKey, false)
+
+ assert.DeepEqual(t, jsonResp, util.JSONResponse{
+ Code: http.StatusOK,
+ JSON: spec.RawJSON(`{"foo":"bar"}`),
+ })
+ })
+
+ t.Run("user ID key translated to room key in pseudo ID rooms", func(t *testing.T) {
+ ctx := context.Background()
+
+ stateSenderUserID := "@sender:domain"
+ stateSenderRoomKey := "testsenderkey"
+
+ rsAPI := stateTestRoomserverAPI{
+ roomVersion: pseudoIDRoomVersion,
+ roomIDStr: roomIDStr,
+ roomState: map[gomatrixserverlib.StateKeyTuple]*types.HeaderedEvent{
+ {
+ EventType: eventType,
+ StateKey: stateSenderRoomKey,
+ }: mustCreateStatePDU(t, pseudoIDRoomVersion, roomIDStr, eventType, stateSenderRoomKey, map[string]interface{}{
+ "foo": "bar",
+ }),
+ {
+ EventType: eventType,
+ StateKey: stateSenderUserID,
+ }: mustCreateStatePDU(t, pseudoIDRoomVersion, roomIDStr, eventType, stateSenderUserID, map[string]interface{}{
+ "not": "thisone",
+ }),
+ },
+ userIDStr: userIDStr,
+ senderMapping: map[string]string{
+ stateSenderUserID: stateSenderRoomKey,
+ },
+ }
+
+ jsonResp := OnIncomingStateTypeRequest(ctx, device, rsAPI, roomIDStr, eventType, stateSenderUserID, false)
+
+ assert.DeepEqual(t, jsonResp, util.JSONResponse{
+ Code: http.StatusOK,
+ JSON: spec.RawJSON(`{"foo":"bar"}`),
+ })
+ })
+
+ t.Run("user ID key not translated to room key in non-pseudo ID rooms", func(t *testing.T) {
+ ctx := context.Background()
+
+ stateSenderUserID := "@sender:domain"
+ stateSenderRoomKey := "testsenderkey"
+
+ rsAPI := stateTestRoomserverAPI{
+ roomVersion: nonPseudoIDRoomVersion,
+ roomIDStr: roomIDStr,
+ roomState: map[gomatrixserverlib.StateKeyTuple]*types.HeaderedEvent{
+ {
+ EventType: eventType,
+ StateKey: stateSenderRoomKey,
+ }: mustCreateStatePDU(t, nonPseudoIDRoomVersion, roomIDStr, eventType, stateSenderRoomKey, map[string]interface{}{
+ "not": "thisone",
+ }),
+ {
+ EventType: eventType,
+ StateKey: stateSenderUserID,
+ }: mustCreateStatePDU(t, nonPseudoIDRoomVersion, roomIDStr, eventType, stateSenderUserID, map[string]interface{}{
+ "foo": "bar",
+ }),
+ },
+ userIDStr: userIDStr,
+ senderMapping: map[string]string{
+ stateSenderUserID: stateSenderUserID,
+ },
+ }
+
+ jsonResp := OnIncomingStateTypeRequest(ctx, device, rsAPI, roomIDStr, eventType, stateSenderUserID, false)
+
+ assert.DeepEqual(t, jsonResp, util.JSONResponse{
+ Code: http.StatusOK,
+ JSON: spec.RawJSON(`{"foo":"bar"}`),
+ })
+ })
+}
+
+func mustCreateStatePDU(t *testing.T, roomVer gomatrixserverlib.RoomVersion, roomID string, stateType string, stateKey string, stateContent map[string]interface{}) *types.HeaderedEvent {
+ t.Helper()
+ roomVerImpl := gomatrixserverlib.MustGetRoomVersion(roomVer)
+
+ evBytes, err := json.Marshal(map[string]interface{}{
+ "room_id": roomID,
+ "type": stateType,
+ "state_key": stateKey,
+ "content": stateContent,
+ })
+ if err != nil {
+ t.Fatalf("failed to create event: %v", err)
+ }
+
+ ev, err := roomVerImpl.NewEventFromTrustedJSON(evBytes, false)
+ if err != nil {
+ t.Fatalf("failed to create event: %v", err)
+ }
+
+ return &types.HeaderedEvent{PDU: ev}
+}