mirror of
https://github.com/matrix-org/dendrite
synced 2024-09-19 17:38:56 +02:00
db83789654
Fixes a variety of issues where clients were receiving pseudoIDs in places that should be userIDs. This change makes pseudoIDs work with sliding sync & element x. --------- Co-authored-by: Till <2353100+S7evinK@users.noreply.github.com>
561 lines
16 KiB
Go
561 lines
16 KiB
Go
/* Copyright 2017 Vector Creations Ltd
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package synctypes
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/matrix-org/gomatrixserverlib"
|
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
|
)
|
|
|
|
func queryUserIDForSender(senderID spec.SenderID) (*spec.UserID, error) {
|
|
if senderID == "" {
|
|
return nil, nil
|
|
}
|
|
|
|
return spec.NewUserID(string(senderID), true)
|
|
}
|
|
|
|
const testSenderID = "testSenderID"
|
|
const testUserID = "@test:localhost"
|
|
|
|
type EventFieldsToVerify struct {
|
|
EventID string
|
|
Type string
|
|
OriginServerTS spec.Timestamp
|
|
StateKey *string
|
|
Content spec.RawJSON
|
|
Unsigned spec.RawJSON
|
|
Sender string
|
|
Depth int64
|
|
PrevEvents []string
|
|
AuthEvents []string
|
|
}
|
|
|
|
func verifyEventFields(t *testing.T, got EventFieldsToVerify, want EventFieldsToVerify) {
|
|
if got.EventID != want.EventID {
|
|
t.Errorf("ClientEvent.EventID: wanted %s, got %s", want.EventID, got.EventID)
|
|
}
|
|
if got.OriginServerTS != want.OriginServerTS {
|
|
t.Errorf("ClientEvent.OriginServerTS: wanted %d, got %d", want.OriginServerTS, got.OriginServerTS)
|
|
}
|
|
if got.StateKey == nil && want.StateKey != nil {
|
|
t.Errorf("ClientEvent.StateKey: no state key present when one was wanted: %s", *want.StateKey)
|
|
}
|
|
if got.StateKey != nil && want.StateKey == nil {
|
|
t.Errorf("ClientEvent.StateKey: state key present when one was not wanted: %s", *got.StateKey)
|
|
}
|
|
if got.StateKey != nil && want.StateKey != nil && *got.StateKey != *want.StateKey {
|
|
t.Errorf("ClientEvent.StateKey: wanted %s, got %s", *want.StateKey, *got.StateKey)
|
|
}
|
|
if got.Type != want.Type {
|
|
t.Errorf("ClientEvent.Type: wanted %s, got %s", want.Type, got.Type)
|
|
}
|
|
if !bytes.Equal(got.Content, want.Content) {
|
|
t.Errorf("ClientEvent.Content: wanted %s, got %s", string(want.Content), string(got.Content))
|
|
}
|
|
if !bytes.Equal(got.Unsigned, want.Unsigned) {
|
|
t.Errorf("ClientEvent.Unsigned: wanted %s, got %s", string(want.Unsigned), string(got.Unsigned))
|
|
}
|
|
if got.Sender != want.Sender {
|
|
t.Errorf("ClientEvent.Sender: wanted %s, got %s", want.Sender, got.Sender)
|
|
}
|
|
if got.Depth != want.Depth {
|
|
t.Errorf("ClientEvent.Depth: wanted %d, got %d", want.Depth, got.Depth)
|
|
}
|
|
if !reflect.DeepEqual(got.PrevEvents, want.PrevEvents) {
|
|
t.Errorf("ClientEvent.PrevEvents: wanted %v, got %v", want.PrevEvents, got.PrevEvents)
|
|
}
|
|
if !reflect.DeepEqual(got.AuthEvents, want.AuthEvents) {
|
|
t.Errorf("ClientEvent.AuthEvents: wanted %v, got %v", want.AuthEvents, got.AuthEvents)
|
|
}
|
|
}
|
|
|
|
func TestToClientEvent(t *testing.T) { // nolint: gocyclo
|
|
ev, err := gomatrixserverlib.MustGetRoomVersion(gomatrixserverlib.RoomVersionV1).NewEventFromTrustedJSON([]byte(`{
|
|
"type": "m.room.name",
|
|
"state_key": "",
|
|
"event_id": "$test:localhost",
|
|
"room_id": "!test:localhost",
|
|
"sender": "@test:localhost",
|
|
"content": {
|
|
"name": "Hello World"
|
|
},
|
|
"origin_server_ts": 123456,
|
|
"unsigned": {
|
|
"prev_content": {
|
|
"name": "Goodbye World"
|
|
}
|
|
}
|
|
}`), false)
|
|
if err != nil {
|
|
t.Fatalf("failed to create Event: %s", err)
|
|
}
|
|
userID, err := spec.NewUserID("@test:localhost", true)
|
|
if err != nil {
|
|
t.Fatalf("failed to create userID: %s", err)
|
|
}
|
|
sk := ""
|
|
ce, err := ToClientEvent(ev, FormatAll, func(roomID spec.RoomID, senderID spec.SenderID) (*spec.UserID, error) {
|
|
return queryUserIDForSender(senderID)
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("failed to create ClientEvent: %s", err)
|
|
}
|
|
|
|
verifyEventFields(t,
|
|
EventFieldsToVerify{
|
|
EventID: ce.EventID,
|
|
Type: ce.Type,
|
|
OriginServerTS: ce.OriginServerTS,
|
|
StateKey: ce.StateKey,
|
|
Content: ce.Content,
|
|
Unsigned: ce.Unsigned,
|
|
Sender: ce.Sender,
|
|
},
|
|
EventFieldsToVerify{
|
|
EventID: ev.EventID(),
|
|
Type: ev.Type(),
|
|
OriginServerTS: ev.OriginServerTS(),
|
|
StateKey: &sk,
|
|
Content: ev.Content(),
|
|
Unsigned: ev.Unsigned(),
|
|
Sender: userID.String(),
|
|
})
|
|
|
|
j, err := json.Marshal(ce)
|
|
if err != nil {
|
|
t.Fatalf("failed to Marshal ClientEvent: %s", err)
|
|
}
|
|
// Marshal sorts keys in structs by the order they are defined in the struct, which is alphabetical
|
|
out := `{"content":{"name":"Hello World"},"event_id":"$test:localhost","origin_server_ts":123456,` +
|
|
`"room_id":"!test:localhost","sender":"@test:localhost","state_key":"","type":"m.room.name",` +
|
|
`"unsigned":{"prev_content":{"name":"Goodbye World"}}}`
|
|
if !bytes.Equal([]byte(out), j) {
|
|
t.Errorf("ClientEvent marshalled to wrong bytes: wanted %s, got %s", out, string(j))
|
|
}
|
|
}
|
|
|
|
func TestToClientFormatSync(t *testing.T) {
|
|
ev, err := gomatrixserverlib.MustGetRoomVersion(gomatrixserverlib.RoomVersionV1).NewEventFromTrustedJSON([]byte(`{
|
|
"type": "m.room.name",
|
|
"state_key": "",
|
|
"event_id": "$test:localhost",
|
|
"room_id": "!test:localhost",
|
|
"sender": "@test:localhost",
|
|
"content": {
|
|
"name": "Hello World"
|
|
},
|
|
"origin_server_ts": 123456,
|
|
"unsigned": {
|
|
"prev_content": {
|
|
"name": "Goodbye World"
|
|
}
|
|
}
|
|
}`), false)
|
|
if err != nil {
|
|
t.Fatalf("failed to create Event: %s", err)
|
|
}
|
|
ce, err := ToClientEvent(ev, FormatSync, func(roomID spec.RoomID, senderID spec.SenderID) (*spec.UserID, error) {
|
|
return queryUserIDForSender(senderID)
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("failed to create ClientEvent: %s", err)
|
|
}
|
|
if ce.RoomID != "" {
|
|
t.Errorf("ClientEvent.RoomID: wanted '', got %s", ce.RoomID)
|
|
}
|
|
}
|
|
|
|
func TestToClientEventFormatSyncFederation(t *testing.T) { // nolint: gocyclo
|
|
ev, err := gomatrixserverlib.MustGetRoomVersion(gomatrixserverlib.RoomVersionV10).NewEventFromTrustedJSON([]byte(`{
|
|
"type": "m.room.name",
|
|
"state_key": "",
|
|
"event_id": "$test:localhost",
|
|
"room_id": "!test:localhost",
|
|
"sender": "@test:localhost",
|
|
"content": {
|
|
"name": "Hello World"
|
|
},
|
|
"origin_server_ts": 123456,
|
|
"unsigned": {
|
|
"prev_content": {
|
|
"name": "Goodbye World"
|
|
}
|
|
},
|
|
"depth": 8,
|
|
"prev_events": [
|
|
"$f597Tp0Mm1PPxEgiprzJc2cZAjVhxCxACOGuwJb33Oo"
|
|
],
|
|
"auth_events": [
|
|
"$Bj0ZGgX6VTqAQdqKH4ZG3l6rlbxY3rZlC5D3MeuK1OQ",
|
|
"$QsMs6A1PUVUhgSvmHBfpqEYJPgv4DXt96r8P2AK7iXQ",
|
|
"$tBteKtlnFiwlmPJsv0wkKTMEuUVWpQH89H7Xskxve1Q"
|
|
]
|
|
}`), false)
|
|
if err != nil {
|
|
t.Fatalf("failed to create Event: %s", err)
|
|
}
|
|
userID, err := spec.NewUserID("@test:localhost", true)
|
|
if err != nil {
|
|
t.Fatalf("failed to create userID: %s", err)
|
|
}
|
|
sk := ""
|
|
ce, err := ToClientEvent(ev, FormatSyncFederation, func(roomID spec.RoomID, senderID spec.SenderID) (*spec.UserID, error) {
|
|
return queryUserIDForSender(senderID)
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("failed to create ClientEvent: %s", err)
|
|
}
|
|
|
|
verifyEventFields(t,
|
|
EventFieldsToVerify{
|
|
EventID: ce.EventID,
|
|
Type: ce.Type,
|
|
OriginServerTS: ce.OriginServerTS,
|
|
StateKey: ce.StateKey,
|
|
Content: ce.Content,
|
|
Unsigned: ce.Unsigned,
|
|
Sender: ce.Sender,
|
|
Depth: ce.Depth,
|
|
PrevEvents: ce.PrevEvents,
|
|
AuthEvents: ce.AuthEvents,
|
|
},
|
|
EventFieldsToVerify{
|
|
EventID: ev.EventID(),
|
|
Type: ev.Type(),
|
|
OriginServerTS: ev.OriginServerTS(),
|
|
StateKey: &sk,
|
|
Content: ev.Content(),
|
|
Unsigned: ev.Unsigned(),
|
|
Sender: userID.String(),
|
|
Depth: ev.Depth(),
|
|
PrevEvents: ev.PrevEventIDs(),
|
|
AuthEvents: ev.AuthEventIDs(),
|
|
})
|
|
}
|
|
|
|
func userIDForSender(roomID spec.RoomID, senderID spec.SenderID) (*spec.UserID, error) {
|
|
if senderID == "unknownSenderID" {
|
|
return nil, fmt.Errorf("Cannot find userID")
|
|
}
|
|
return spec.NewUserID(testUserID, true)
|
|
}
|
|
|
|
func TestToClientEventsFormatSyncFederation(t *testing.T) { // nolint: gocyclo
|
|
ev, err := gomatrixserverlib.MustGetRoomVersion(gomatrixserverlib.RoomVersionPseudoIDs).NewEventFromTrustedJSON([]byte(`{
|
|
"type": "m.room.name",
|
|
"state_key": "testSenderID",
|
|
"event_id": "$test:localhost",
|
|
"room_id": "!test:localhost",
|
|
"sender": "testSenderID",
|
|
"content": {
|
|
"name": "Hello World"
|
|
},
|
|
"origin_server_ts": 123456,
|
|
"unsigned": {
|
|
"prev_content": {
|
|
"name": "Goodbye World"
|
|
}
|
|
},
|
|
"depth": 8,
|
|
"prev_events": [
|
|
"$f597Tp0Mm1PPxEgiprzJc2cZAjVhxCxACOGuwJb33Oo"
|
|
],
|
|
"auth_events": [
|
|
"$Bj0ZGgX6VTqAQdqKH4ZG3l6rlbxY3rZlC5D3MeuK1OQ",
|
|
"$QsMs6A1PUVUhgSvmHBfpqEYJPgv4DXt96r8P2AK7iXQ",
|
|
"$tBteKtlnFiwlmPJsv0wkKTMEuUVWpQH89H7Xskxve1Q"
|
|
]
|
|
}`), false)
|
|
if err != nil {
|
|
t.Fatalf("failed to create Event: %s", err)
|
|
}
|
|
ev2, err := gomatrixserverlib.MustGetRoomVersion(gomatrixserverlib.RoomVersionPseudoIDs).NewEventFromTrustedJSON([]byte(`{
|
|
"type": "m.room.name",
|
|
"state_key": "testSenderID",
|
|
"event_id": "$test2:localhost",
|
|
"room_id": "!test:localhost",
|
|
"sender": "testSenderID",
|
|
"content": {
|
|
"name": "Hello World 2"
|
|
},
|
|
"origin_server_ts": 1234567,
|
|
"unsigned": {
|
|
"prev_content": {
|
|
"name": "Goodbye World 2"
|
|
},
|
|
"prev_sender": "testSenderID"
|
|
},
|
|
"depth": 9,
|
|
"prev_events": [
|
|
"$f597Tp0Mm1PPxEgiprzJc2cZAjVhxCxACOGuwJb33Oo"
|
|
],
|
|
"auth_events": [
|
|
"$Bj0ZGgX6VTqAQdqKH4ZG3l6rlbxY3rZlC5D3MeuK1OQ",
|
|
"$QsMs6A1PUVUhgSvmHBfpqEYJPgv4DXt96r8P2AK7iXQ",
|
|
"$tBteKtlnFiwlmPJsv0wkKTMEuUVWpQH89H7Xskxve1Q"
|
|
]
|
|
}`), false)
|
|
if err != nil {
|
|
t.Fatalf("failed to create Event: %s", err)
|
|
}
|
|
|
|
clientEvents := ToClientEvents([]gomatrixserverlib.PDU{ev, ev2}, FormatSyncFederation, userIDForSender)
|
|
ce := clientEvents[0]
|
|
sk := testSenderID
|
|
verifyEventFields(t,
|
|
EventFieldsToVerify{
|
|
EventID: ce.EventID,
|
|
Type: ce.Type,
|
|
OriginServerTS: ce.OriginServerTS,
|
|
StateKey: ce.StateKey,
|
|
Content: ce.Content,
|
|
Unsigned: ce.Unsigned,
|
|
Sender: ce.Sender,
|
|
Depth: ce.Depth,
|
|
PrevEvents: ce.PrevEvents,
|
|
AuthEvents: ce.AuthEvents,
|
|
},
|
|
EventFieldsToVerify{
|
|
EventID: ev.EventID(),
|
|
Type: ev.Type(),
|
|
OriginServerTS: ev.OriginServerTS(),
|
|
StateKey: &sk,
|
|
Content: ev.Content(),
|
|
Unsigned: ev.Unsigned(),
|
|
Sender: testSenderID,
|
|
Depth: ev.Depth(),
|
|
PrevEvents: ev.PrevEventIDs(),
|
|
AuthEvents: ev.AuthEventIDs(),
|
|
})
|
|
|
|
ce2 := clientEvents[1]
|
|
verifyEventFields(t,
|
|
EventFieldsToVerify{
|
|
EventID: ce2.EventID,
|
|
Type: ce2.Type,
|
|
OriginServerTS: ce2.OriginServerTS,
|
|
StateKey: ce2.StateKey,
|
|
Content: ce2.Content,
|
|
Unsigned: ce2.Unsigned,
|
|
Sender: ce2.Sender,
|
|
Depth: ce2.Depth,
|
|
PrevEvents: ce2.PrevEvents,
|
|
AuthEvents: ce2.AuthEvents,
|
|
},
|
|
EventFieldsToVerify{
|
|
EventID: ev2.EventID(),
|
|
Type: ev2.Type(),
|
|
OriginServerTS: ev2.OriginServerTS(),
|
|
StateKey: &sk,
|
|
Content: ev2.Content(),
|
|
Unsigned: ev2.Unsigned(),
|
|
Sender: testSenderID,
|
|
Depth: ev2.Depth(),
|
|
PrevEvents: ev2.PrevEventIDs(),
|
|
AuthEvents: ev2.AuthEventIDs(),
|
|
})
|
|
}
|
|
|
|
func TestToClientEventsFormatSync(t *testing.T) { // nolint: gocyclo
|
|
ev, err := gomatrixserverlib.MustGetRoomVersion(gomatrixserverlib.RoomVersionPseudoIDs).NewEventFromTrustedJSON([]byte(`{
|
|
"type": "m.room.name",
|
|
"state_key": "testSenderID",
|
|
"event_id": "$test:localhost",
|
|
"room_id": "!test:localhost",
|
|
"sender": "testSenderID",
|
|
"content": {
|
|
"name": "Hello World"
|
|
},
|
|
"origin_server_ts": 123456,
|
|
"unsigned": {
|
|
"prev_content": {
|
|
"name": "Goodbye World"
|
|
}
|
|
}
|
|
}`), false)
|
|
if err != nil {
|
|
t.Fatalf("failed to create Event: %s", err)
|
|
}
|
|
ev2, err := gomatrixserverlib.MustGetRoomVersion(gomatrixserverlib.RoomVersionPseudoIDs).NewEventFromTrustedJSON([]byte(`{
|
|
"type": "m.room.name",
|
|
"state_key": "testSenderID",
|
|
"event_id": "$test2:localhost",
|
|
"room_id": "!test:localhost",
|
|
"sender": "testSenderID",
|
|
"content": {
|
|
"name": "Hello World 2"
|
|
},
|
|
"origin_server_ts": 1234567,
|
|
"unsigned": {
|
|
"prev_content": {
|
|
"name": "Goodbye World 2"
|
|
},
|
|
"prev_sender": "testSenderID"
|
|
},
|
|
"depth": 9
|
|
}`), false)
|
|
if err != nil {
|
|
t.Fatalf("failed to create Event: %s", err)
|
|
}
|
|
|
|
clientEvents := ToClientEvents([]gomatrixserverlib.PDU{ev, ev2}, FormatSync, userIDForSender)
|
|
ce := clientEvents[0]
|
|
sk := testUserID
|
|
verifyEventFields(t,
|
|
EventFieldsToVerify{
|
|
EventID: ce.EventID,
|
|
Type: ce.Type,
|
|
OriginServerTS: ce.OriginServerTS,
|
|
StateKey: ce.StateKey,
|
|
Content: ce.Content,
|
|
Unsigned: ce.Unsigned,
|
|
Sender: ce.Sender,
|
|
},
|
|
EventFieldsToVerify{
|
|
EventID: ev.EventID(),
|
|
Type: ev.Type(),
|
|
OriginServerTS: ev.OriginServerTS(),
|
|
StateKey: &sk,
|
|
Content: ev.Content(),
|
|
Unsigned: ev.Unsigned(),
|
|
Sender: testUserID,
|
|
})
|
|
|
|
var prev PrevEventRef
|
|
prev.PrevContent = []byte(`{"name": "Goodbye World 2"}`)
|
|
prev.PrevSenderID = testUserID
|
|
expectedUnsigned, _ := json.Marshal(prev)
|
|
|
|
ce2 := clientEvents[1]
|
|
verifyEventFields(t,
|
|
EventFieldsToVerify{
|
|
EventID: ce2.EventID,
|
|
Type: ce2.Type,
|
|
OriginServerTS: ce2.OriginServerTS,
|
|
StateKey: ce2.StateKey,
|
|
Content: ce2.Content,
|
|
Unsigned: ce2.Unsigned,
|
|
Sender: ce2.Sender,
|
|
},
|
|
EventFieldsToVerify{
|
|
EventID: ev2.EventID(),
|
|
Type: ev2.Type(),
|
|
OriginServerTS: ev2.OriginServerTS(),
|
|
StateKey: &sk,
|
|
Content: ev2.Content(),
|
|
Unsigned: expectedUnsigned,
|
|
Sender: testUserID,
|
|
})
|
|
}
|
|
|
|
func TestToClientEventsFormatSyncUnknownPrevSender(t *testing.T) { // nolint: gocyclo
|
|
ev, err := gomatrixserverlib.MustGetRoomVersion(gomatrixserverlib.RoomVersionPseudoIDs).NewEventFromTrustedJSON([]byte(`{
|
|
"type": "m.room.name",
|
|
"state_key": "testSenderID",
|
|
"event_id": "$test:localhost",
|
|
"room_id": "!test:localhost",
|
|
"sender": "testSenderID",
|
|
"content": {
|
|
"name": "Hello World"
|
|
},
|
|
"origin_server_ts": 123456,
|
|
"unsigned": {
|
|
"prev_content": {
|
|
"name": "Goodbye World"
|
|
}
|
|
}
|
|
}`), false)
|
|
if err != nil {
|
|
t.Fatalf("failed to create Event: %s", err)
|
|
}
|
|
ev2, err := gomatrixserverlib.MustGetRoomVersion(gomatrixserverlib.RoomVersionPseudoIDs).NewEventFromTrustedJSON([]byte(`{
|
|
"type": "m.room.name",
|
|
"state_key": "testSenderID",
|
|
"event_id": "$test2:localhost",
|
|
"room_id": "!test:localhost",
|
|
"sender": "testSenderID",
|
|
"content": {
|
|
"name": "Hello World 2"
|
|
},
|
|
"origin_server_ts": 1234567,
|
|
"unsigned": {
|
|
"prev_content": {
|
|
"name": "Goodbye World 2"
|
|
},
|
|
"prev_sender": "unknownSenderID"
|
|
},
|
|
"depth": 9
|
|
}`), false)
|
|
if err != nil {
|
|
t.Fatalf("failed to create Event: %s", err)
|
|
}
|
|
|
|
clientEvents := ToClientEvents([]gomatrixserverlib.PDU{ev, ev2}, FormatSync, userIDForSender)
|
|
ce := clientEvents[0]
|
|
sk := testUserID
|
|
verifyEventFields(t,
|
|
EventFieldsToVerify{
|
|
EventID: ce.EventID,
|
|
Type: ce.Type,
|
|
OriginServerTS: ce.OriginServerTS,
|
|
StateKey: ce.StateKey,
|
|
Content: ce.Content,
|
|
Unsigned: ce.Unsigned,
|
|
Sender: ce.Sender,
|
|
},
|
|
EventFieldsToVerify{
|
|
EventID: ev.EventID(),
|
|
Type: ev.Type(),
|
|
OriginServerTS: ev.OriginServerTS(),
|
|
StateKey: &sk,
|
|
Content: ev.Content(),
|
|
Unsigned: ev.Unsigned(),
|
|
Sender: testUserID,
|
|
})
|
|
|
|
var prev PrevEventRef
|
|
prev.PrevContent = []byte(`{"name": "Goodbye World 2"}`)
|
|
prev.PrevSenderID = "unknownSenderID"
|
|
expectedUnsigned, _ := json.Marshal(prev)
|
|
|
|
ce2 := clientEvents[1]
|
|
verifyEventFields(t,
|
|
EventFieldsToVerify{
|
|
EventID: ce2.EventID,
|
|
Type: ce2.Type,
|
|
OriginServerTS: ce2.OriginServerTS,
|
|
StateKey: ce2.StateKey,
|
|
Content: ce2.Content,
|
|
Unsigned: ce2.Unsigned,
|
|
Sender: ce2.Sender,
|
|
},
|
|
EventFieldsToVerify{
|
|
EventID: ev2.EventID(),
|
|
Type: ev2.Type(),
|
|
OriginServerTS: ev2.OriginServerTS(),
|
|
StateKey: &sk,
|
|
Content: ev2.Content(),
|
|
Unsigned: expectedUnsigned,
|
|
Sender: testUserID,
|
|
})
|
|
}
|