diff --git a/client/client.go b/client/client.go index fc584a2b..487570c5 100644 --- a/client/client.go +++ b/client/client.go @@ -538,7 +538,7 @@ func (c *CSAPI) SendRedaction(t ct.TestLike, roomID string, content map[string]i return c.Do(t, "PUT", paths, WithJSONBody(t, content)) } -// MustGetStateEvent returns the event content for the given state event. Fails the test if the state event does not exist. +// MustGetStateEventContent returns the event content for the given state event. Fails the test if the state event does not exist. func (c *CSAPI) MustGetStateEventContent(t ct.TestLike, roomID, eventType, stateKey string) (content gjson.Result) { t.Helper() res := c.GetStateEventContent(t, roomID, eventType, stateKey) @@ -547,12 +547,27 @@ func (c *CSAPI) MustGetStateEventContent(t ct.TestLike, roomID, eventType, state return gjson.ParseBytes(body) } -// GetStateEvent returns the event content for the given state event. Use this form to detect absence via 404. +// GetStateEventContent returns the event content for the given state event. Use this form to detect absence via 404. func (c *CSAPI) GetStateEventContent(t ct.TestLike, roomID, eventType, stateKey string) *http.Response { t.Helper() return c.Do(t, "GET", []string{"_matrix", "client", "v3", "rooms", roomID, "state", eventType, stateKey}) } +// MustGetEvent returns the event content for the given state event. Fails the test if the state event does not exist. +func (c *CSAPI) MustGetEvent(t ct.TestLike, roomID, eventID string) (eventJson gjson.Result) { + t.Helper() + res := c.GetEvent(t, roomID, eventID) + mustRespond2xx(t, res) + body := ParseJSON(t, res) + return gjson.ParseBytes(body) +} + +// GetEvent returns the event JSON. Use this form to detect absence via 404. +func (c *CSAPI) GetEvent(t ct.TestLike, roomID, eventID string) *http.Response { + t.Helper() + return c.Do(t, "GET", []string{"_matrix", "client", "v3", "rooms", roomID, "event", eventID}) +} + // MustSendTyping marks this user as typing until the timeout is reached. If isTyping is false, timeout is ignored. func (c *CSAPI) MustSendTyping(t ct.TestLike, roomID string, isTyping bool, timeoutMillis int) { res := c.SendTyping(t, roomID, isTyping, timeoutMillis) diff --git a/helpers/txnid.go b/helpers/txnid.go new file mode 100644 index 00000000..b24cd2bc --- /dev/null +++ b/helpers/txnid.go @@ -0,0 +1,16 @@ +package helpers + +import ( + "fmt" + "sync/atomic" + "time" +) + +var txnCounter atomic.Int64 +var start = time.Now().Unix() + +// GetTxnID generates a unique transaction ID for use with endpoints like /send/{eventType/{txnId}. +// The prefix is only for debugging purposes. +func GetTxnID(prefix string) (txnID string) { + return fmt.Sprintf("%s-%d-%d", prefix, start, txnCounter.Add(1)) +} diff --git a/tests/csapi/invalid_test.go b/tests/csapi/invalid_test.go index 7d59cb6d..697ae832 100644 --- a/tests/csapi/invalid_test.go +++ b/tests/csapi/invalid_test.go @@ -35,7 +35,7 @@ func TestJson(t *testing.T) { } for _, testCase := range testCases { - res := alice.Do(t, "POST", []string{"_matrix", "client", "v3", "rooms", roomID, "send", "complement.dummy"}, client.WithJSONBody(t, testCase)) + res := alice.Do(t, "PUT", []string{"_matrix", "client", "v3", "rooms", roomID, "send", "complement.dummy", helpers.GetTxnID("TestJson-InvalidNum")}, client.WithJSONBody(t, testCase)) must.MatchResponse(t, res, match.HTTPResponse{ StatusCode: 400, @@ -57,7 +57,7 @@ func TestJson(t *testing.T) { } for _, testCase := range testCases { - res := alice.Do(t, "POST", []string{"_matrix", "client", "v3", "rooms", roomID, "send", "complement.dummy"}, client.WithJSONBody(t, testCase)) + res := alice.Do(t, "PUT", []string{"_matrix", "client", "v3", "rooms", roomID, "send", "complement.dummy", helpers.GetTxnID("TestJson-InvalidVal")}, client.WithJSONBody(t, testCase)) must.MatchResponse(t, res, match.HTTPResponse{ StatusCode: 400, diff --git a/tests/csapi/redact_test.go b/tests/csapi/redact_test.go new file mode 100644 index 00000000..184da0fb --- /dev/null +++ b/tests/csapi/redact_test.go @@ -0,0 +1,60 @@ +package csapi_tests + +import ( + "testing" + + "github.com/matrix-org/complement" + "github.com/matrix-org/complement/b" + "github.com/matrix-org/complement/helpers" + "github.com/matrix-org/complement/match" + "github.com/matrix-org/complement/must" +) + +// Test `PUT /_matrix/client/v3/rooms/{roomId}/redact/{eventId}/{txnId} ` (redactions) +func TestRedact(t *testing.T) { + deployment := complement.Deploy(t, 1) + defer deployment.Destroy(t) + + alice := deployment.Register(t, "hs1", helpers.RegistrationOpts{ + LocalpartSuffix: "alice", + }) + bob := deployment.Register(t, "hs1", helpers.RegistrationOpts{ + LocalpartSuffix: "bob", + }) + + // Alice creates the room + roomID := alice.MustCreateRoom(t, map[string]interface{}{ + // Just making it easier for everyone to join + "preset": "public_chat", + }) + // Bob joins the room + bob.MustJoinRoom(t, roomID, nil) + + t.Run("Event content is redacted", func(t *testing.T) { + // Alice creates an event + expectedEventContent := map[string]interface{}{ + "msgtype": "m.text", + "body": "expected message body", + } + eventIDToRedact := alice.SendEventSynced(t, roomID, b.Event{ + Type: "m.room.message", + Content:expectedEventContent, + }) + + // Bob can see the event content + eventJsonBefore := bob.MustGetEvent(t, roomID, eventIDToRedact) + must.MatchGJSON(t, eventJsonBefore, match.JSONKeyEqual("content", expectedEventContent)) + + // Alice redacts the event + alice.MustSendRedaction(t, roomID, map[string]interface{}{ + "reason": "reasons...", + }, eventIDToRedact) + + // Bob can no longer see the event content + eventJsonAfter := bob.MustGetEvent(t, roomID, eventIDToRedact) + must.MatchGJSON(t, eventJsonAfter, match.JSONKeyEqual("content", map[string]interface{}{ + // no content + })) + }) + +} diff --git a/tests/csapi/room_messages_test.go b/tests/csapi/room_messages_test.go index 4900c063..b21aa55a 100644 --- a/tests/csapi/room_messages_test.go +++ b/tests/csapi/room_messages_test.go @@ -22,7 +22,6 @@ import ( "github.com/matrix-org/gomatrixserverlib/spec" ) -// sytest: POST /rooms/:room_id/send/:event_type sends a message // sytest: GET /rooms/:room_id/messages returns a message func TestSendAndFetchMessage(t *testing.T) { runtime.SkipIf(t, runtime.Dendrite) // flakey @@ -37,8 +36,8 @@ func TestSendAndFetchMessage(t *testing.T) { _, token := alice.MustSync(t, client.SyncReq{}) - // first use the non-txn endpoint - alice.MustDo(t, "POST", []string{"_matrix", "client", "v3", "rooms", roomID, "send", "m.room.message"}, client.WithJSONBody(t, map[string]interface{}{ + // first use the send endpoint + alice.MustDo(t, "PUT", []string{"_matrix", "client", "v3", "rooms", roomID, "send", "m.room.message", helpers.GetTxnID("TestSendAndFetchMessage")}, client.WithJSONBody(t, map[string]interface{}{ "msgtype": "m.text", "body": testMessage, })) diff --git a/tests/room_timestamp_to_event_test.go b/tests/room_timestamp_to_event_test.go index f6de01cc..a63b0947 100644 --- a/tests/room_timestamp_to_event_test.go +++ b/tests/room_timestamp_to_event_test.go @@ -11,7 +11,6 @@ import ( "fmt" "net/url" "strconv" - "sync/atomic" "testing" "time" @@ -253,16 +252,6 @@ type eventTime struct { AfterTimestamp time.Time } -var txnCounter int64 = 0 - -func getTxnID(prefix string) (txnID string) { - txnId := fmt.Sprintf("%s-%d", prefix, atomic.LoadInt64(&txnCounter)) - - atomic.AddInt64(&txnCounter, 1) - - return txnId -} - func createTestRoom(t *testing.T, c *client.CSAPI) (roomID string, eventA, eventB *eventTime) { t.Helper() @@ -305,7 +294,7 @@ func sendMessageWithTimestamp(t *testing.T, as *client.CSAPI, c *client.CSAPI, r // // We can't use as.SendEventSynced(...) because application services can't use // the /sync API. - sendRes := as.Do(t, "PUT", []string{"_matrix", "client", "v3", "rooms", roomID, "send", "m.room.message", getTxnID("sendMessageWithTimestamp-txn")}, client.WithContentType("application/json"), client.WithJSONBody(t, map[string]interface{}{ + sendRes := as.Do(t, "PUT", []string{"_matrix", "client", "v3", "rooms", roomID, "send", "m.room.message", helpers.GetTxnID("sendMessageWithTimestamp-txn")}, client.WithContentType("application/json"), client.WithJSONBody(t, map[string]interface{}{ "body": message, "msgtype": "m.text", }), client.WithQueries(url.Values{