From 172d3f18f656207d5b69a9d9cdedd03cfd13aabe Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 14 Mar 2026 00:31:53 +0200 Subject: [PATCH 1/8] Stop testing unspecced POST /send Signed-off-by: Tulir Asokan --- tests/csapi/invalid_test.go | 9 +++++---- tests/csapi/room_messages_test.go | 7 ++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/csapi/invalid_test.go b/tests/csapi/invalid_test.go index 7d59cb6d..76f1373b 100644 --- a/tests/csapi/invalid_test.go +++ b/tests/csapi/invalid_test.go @@ -1,6 +1,7 @@ package csapi_tests import ( + "fmt" "strings" "testing" @@ -34,8 +35,8 @@ func TestJson(t *testing.T) { []byte(`{"body": 1.1}`), } - for _, testCase := range testCases { - res := alice.Do(t, "POST", []string{"_matrix", "client", "v3", "rooms", roomID, "send", "complement.dummy"}, client.WithJSONBody(t, testCase)) + for i, testCase := range testCases { + res := alice.Do(t, "PUT", []string{"_matrix", "client", "v3", "rooms", roomID, "send", "complement.dummy", fmt.Sprintf("invalidnum-%d", i)}, client.WithJSONBody(t, testCase)) must.MatchResponse(t, res, match.HTTPResponse{ StatusCode: 400, @@ -56,8 +57,8 @@ func TestJson(t *testing.T) { []byte(`{"body": NaN}`), } - for _, testCase := range testCases { - res := alice.Do(t, "POST", []string{"_matrix", "client", "v3", "rooms", roomID, "send", "complement.dummy"}, client.WithJSONBody(t, testCase)) + for i, testCase := range testCases { + res := alice.Do(t, "POST", []string{"_matrix", "client", "v3", "rooms", roomID, "send", "complement.dummy", fmt.Sprintf("invalidval-%d", i)}, client.WithJSONBody(t, testCase)) must.MatchResponse(t, res, match.HTTPResponse{ StatusCode: 400, diff --git a/tests/csapi/room_messages_test.go b/tests/csapi/room_messages_test.go index 4900c063..45d0b44c 100644 --- a/tests/csapi/room_messages_test.go +++ b/tests/csapi/room_messages_test.go @@ -9,6 +9,7 @@ import ( "strconv" "strings" "testing" + "time" "github.com/tidwall/gjson" @@ -22,7 +23,7 @@ import ( "github.com/matrix-org/gomatrixserverlib/spec" ) -// sytest: POST /rooms/:room_id/send/:event_type sends a message +// sytest: PUT /rooms/:room_id/send/:event_type/:txn_id 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 +38,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", strconv.FormatInt(time.Now().UnixNano(), 10, 64)}, client.WithJSONBody(t, map[string]interface{}{ "msgtype": "m.text", "body": testMessage, })) From c67b4b289159a2711e96f2c6ec3e03526f2481b4 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 14 Mar 2026 20:14:55 +0200 Subject: [PATCH 2/8] Update tests/csapi/invalid_test.go --- tests/csapi/invalid_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/csapi/invalid_test.go b/tests/csapi/invalid_test.go index 76f1373b..b50a4c92 100644 --- a/tests/csapi/invalid_test.go +++ b/tests/csapi/invalid_test.go @@ -58,7 +58,7 @@ func TestJson(t *testing.T) { } for i, testCase := range testCases { - res := alice.Do(t, "POST", []string{"_matrix", "client", "v3", "rooms", roomID, "send", "complement.dummy", fmt.Sprintf("invalidval-%d", i)}, client.WithJSONBody(t, testCase)) + res := alice.Do(t, "PUT", []string{"_matrix", "client", "v3", "rooms", roomID, "send", "complement.dummy", fmt.Sprintf("invalidval-%d", i)}, client.WithJSONBody(t, testCase)) must.MatchResponse(t, res, match.HTTPResponse{ StatusCode: 400, From ce4bdcd7c6f3e3aa3476fd9ca7b0f78947d641f9 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 17 Mar 2026 20:41:19 +0200 Subject: [PATCH 3/8] Disable gotestfmt --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 4649da8d..d53df3b5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -118,7 +118,7 @@ jobs: - run: | set -o pipefail && - ${{ matrix.env }} go test -v -json -tags "${{ matrix.tags }}" -timeout "${{ matrix.timeout }}" ./tests ./tests/csapi ${{ matrix.packages }} | .ci/scripts/gotestfmt + ${{ matrix.env }} go test -v -json -tags "${{ matrix.tags }}" -timeout "${{ matrix.timeout }}" ./tests ./tests/csapi ${{ matrix.packages }} # | .ci/scripts/gotestfmt shell: bash # required for pipefail to be A Thing. pipefail is required to stop gotestfmt swallowing non-zero exit codes name: Run Complement Tests env: From d3f58e7d7914ed9db1ec83f60afdaf03618c82d6 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 17 Mar 2026 21:15:06 +0200 Subject: [PATCH 4/8] Fix format int call --- tests/csapi/room_messages_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/csapi/room_messages_test.go b/tests/csapi/room_messages_test.go index 45d0b44c..9b250b33 100644 --- a/tests/csapi/room_messages_test.go +++ b/tests/csapi/room_messages_test.go @@ -39,7 +39,7 @@ func TestSendAndFetchMessage(t *testing.T) { _, token := alice.MustSync(t, client.SyncReq{}) // first use the send endpoint - alice.MustDo(t, "PUT", []string{"_matrix", "client", "v3", "rooms", roomID, "send", "m.room.message", strconv.FormatInt(time.Now().UnixNano(), 10, 64)}, client.WithJSONBody(t, map[string]interface{}{ + alice.MustDo(t, "PUT", []string{"_matrix", "client", "v3", "rooms", roomID, "send", "m.room.message", strconv.FormatInt(time.Now().UnixNano(), 10)}, client.WithJSONBody(t, map[string]interface{}{ "msgtype": "m.text", "body": testMessage, })) From 000e60f9d62a0be70565eb57eb6d8b810cad3326 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 17 Mar 2026 21:15:12 +0200 Subject: [PATCH 5/8] Revert "Disable gotestfmt" This reverts commit ce4bdcd7c6f3e3aa3476fd9ca7b0f78947d641f9. --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d53df3b5..4649da8d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -118,7 +118,7 @@ jobs: - run: | set -o pipefail && - ${{ matrix.env }} go test -v -json -tags "${{ matrix.tags }}" -timeout "${{ matrix.timeout }}" ./tests ./tests/csapi ${{ matrix.packages }} # | .ci/scripts/gotestfmt + ${{ matrix.env }} go test -v -json -tags "${{ matrix.tags }}" -timeout "${{ matrix.timeout }}" ./tests ./tests/csapi ${{ matrix.packages }} | .ci/scripts/gotestfmt shell: bash # required for pipefail to be A Thing. pipefail is required to stop gotestfmt swallowing non-zero exit codes name: Run Complement Tests env: From 02f111cbd4b050bfd3478241614a698ddbeb2ca3 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 17 Mar 2026 23:00:31 +0200 Subject: [PATCH 6/8] Add helper for generating unique transaction IDs --- helpers/txnid.go | 14 ++++++++++++++ tests/csapi/invalid_test.go | 9 ++++----- tests/csapi/room_messages_test.go | 3 +-- tests/room_timestamp_to_event_test.go | 13 +------------ 4 files changed, 20 insertions(+), 19 deletions(-) create mode 100644 helpers/txnid.go diff --git a/helpers/txnid.go b/helpers/txnid.go new file mode 100644 index 00000000..b6ba1114 --- /dev/null +++ b/helpers/txnid.go @@ -0,0 +1,14 @@ +package helpers + +import ( + "fmt" + "sync/atomic" + "time" +) + +var txnCounter atomic.Int64 +var start = time.Now().Unix() + +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 b50a4c92..697ae832 100644 --- a/tests/csapi/invalid_test.go +++ b/tests/csapi/invalid_test.go @@ -1,7 +1,6 @@ package csapi_tests import ( - "fmt" "strings" "testing" @@ -35,8 +34,8 @@ func TestJson(t *testing.T) { []byte(`{"body": 1.1}`), } - for i, testCase := range testCases { - res := alice.Do(t, "PUT", []string{"_matrix", "client", "v3", "rooms", roomID, "send", "complement.dummy", fmt.Sprintf("invalidnum-%d", i)}, client.WithJSONBody(t, testCase)) + for _, testCase := range testCases { + 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,8 +56,8 @@ func TestJson(t *testing.T) { []byte(`{"body": NaN}`), } - for i, testCase := range testCases { - res := alice.Do(t, "PUT", []string{"_matrix", "client", "v3", "rooms", roomID, "send", "complement.dummy", fmt.Sprintf("invalidval-%d", i)}, client.WithJSONBody(t, testCase)) + for _, testCase := range testCases { + 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/room_messages_test.go b/tests/csapi/room_messages_test.go index 9b250b33..05eb1784 100644 --- a/tests/csapi/room_messages_test.go +++ b/tests/csapi/room_messages_test.go @@ -9,7 +9,6 @@ import ( "strconv" "strings" "testing" - "time" "github.com/tidwall/gjson" @@ -39,7 +38,7 @@ func TestSendAndFetchMessage(t *testing.T) { _, token := alice.MustSync(t, client.SyncReq{}) // first use the send endpoint - alice.MustDo(t, "PUT", []string{"_matrix", "client", "v3", "rooms", roomID, "send", "m.room.message", strconv.FormatInt(time.Now().UnixNano(), 10)}, client.WithJSONBody(t, map[string]interface{}{ + 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{ From 5de6121a8057828c4a4bb8ef71c178d3bc35e03c Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 18 Mar 2026 17:33:45 +0200 Subject: [PATCH 7/8] Add doc for GetTxnID --- helpers/txnid.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/helpers/txnid.go b/helpers/txnid.go index b6ba1114..b24cd2bc 100644 --- a/helpers/txnid.go +++ b/helpers/txnid.go @@ -9,6 +9,8 @@ import ( 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)) } From 8ee78ab51cf511f18393e0eee1f0308f2496470f Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 18 Mar 2026 17:33:49 +0200 Subject: [PATCH 8/8] Remove sytest comment The txn send endpoint is tested below, so TestSendAndFetchMessage is only relevant for testing /messages --- tests/csapi/room_messages_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/csapi/room_messages_test.go b/tests/csapi/room_messages_test.go index 05eb1784..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: PUT /rooms/:room_id/send/:event_type/:txn_id sends a message // sytest: GET /rooms/:room_id/messages returns a message func TestSendAndFetchMessage(t *testing.T) { runtime.SkipIf(t, runtime.Dendrite) // flakey