Skip to content
121 changes: 76 additions & 45 deletions tests/msc4140/delayed_event_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,43 @@ func TestDelayedEvents(t *testing.T) {
})
})

t.Run("cannot schedule a delayed event without a positive delay", func(t *testing.T) {
for i, tc := range []struct {
name string
body map[string]interface{}
}{
{"missing delay_ms", map[string]interface{}{"content": map[string]interface{}{}}},
{"zero delay_ms", getDelayedEventBody(0, map[string]interface{}{})},
{"negative delay_ms", getDelayedEventBody(-1, map[string]interface{}{})},
{"non-integer delay_ms", map[string]interface{}{"delay_ms": 1.5, "content": map[string]interface{}{}}},
{"string delay_ms", map[string]interface{}{"delay_ms": "1000", "content": map[string]interface{}{}}},
{"missing content", map[string]interface{}{"delay_ms": 1000}},
} {
t.Run(tc.name, func(t *testing.T) {
res := user.Do(
t,
"PUT",
getPathForDelayedEvent(roomID, eventType, fmt.Sprintf("txn-delayed-invalid-delay-%d", i)),
client.WithJSONBody(t, tc.body),
)
must.MatchResponse(t, res, match.HTTPResponse{
StatusCode: 400,
})
})
}

matchDelayedEvents(t, user, delayedEventsNumberEqual(0))
})

t.Run("delayed event management is authenticated", func(t *testing.T) {
defer cleanupDelayedEvents(t, user)

// Schedule a delayed event that will not be sent during this test
res := user.MustDo(
t,
"PUT",
getPathForState(roomID, eventType, "to_manage_without_a_token"),
client.WithJSONBody(t, map[string]interface{}{}),
getDelayQueryParam("100000"),
getPathForDelayedEvent(roomID, eventType, "txn-delayed-mgmt-requires-auth"),
client.WithJSONBody(t, getDelayedStateEventBody(100000, "to_manage_without_a_token", map[string]interface{}{})),
)
delayID := client.GetJSONFieldStr(t, client.ParseJSON(t, res), "delay_id")

Expand Down Expand Up @@ -113,24 +140,24 @@ func TestDelayedEvents(t *testing.T) {

countKey := "count"
numEvents := 3
for i, delayStr := range []string{"700", "800", "900"} {
for i, delayMs := range []int64{700, 800, 900} {
body := getDelayedEventBody(delayMs, map[string]interface{}{
countKey: i + 1,
})
res = user.MustDo(
t,
"PUT",
getPathForSend(roomID, eventType, fmt.Sprintf(txnIdBase, i)),
client.WithJSONBody(t, map[string]interface{}{
countKey: i + 1,
}),
getDelayQueryParam(delayStr),
getPathForDelayedEvent(roomID, eventType, fmt.Sprintf(txnIdBase, i)),
client.WithJSONBody(t, body),
)
delayID := client.GetJSONFieldStr(t, client.ParseJSON(t, res), "delay_id")

t.Run("rerequesting delayed event path with the same txnID should have the same response", func(t *testing.T) {
res := user.MustDo(
t,
"PUT",
getPathForSend(roomID, eventType, fmt.Sprintf(txnIdBase, i)),
getDelayQueryParam(delayStr),
getPathForDelayedEvent(roomID, eventType, fmt.Sprintf(txnIdBase, i)),
client.WithJSONBody(t, body),
)
must.MatchResponse(t, res, match.HTTPResponse{
JSON: []match.JSON{
Expand Down Expand Up @@ -184,11 +211,10 @@ func TestDelayedEvents(t *testing.T) {
user.MustDo(
t,
"PUT",
getPathForState(roomID, eventType, stateKey),
client.WithJSONBody(t, map[string]interface{}{
getPathForDelayedEvent(roomID, eventType, "txn-delayed-state-timeout"),
client.WithJSONBody(t, getDelayedStateEventBody(900, stateKey, map[string]interface{}{
setterKey: setterExpected,
}),
getDelayQueryParam("900"),
})),
)

// Ensure that a delayed event is now scheduled
Expand Down Expand Up @@ -294,11 +320,10 @@ func TestDelayedEvents(t *testing.T) {
res = user.MustDo(
t,
"PUT",
getPathForState(roomID, eventType, stateKey),
client.WithJSONBody(t, map[string]interface{}{
getPathForDelayedEvent(roomID, eventType, "txn-delayed-mgmt-only-for-self"),
client.WithJSONBody(t, getDelayedStateEventBody(1500, stateKey, map[string]interface{}{
setterKey: setterExpected,
}),
getDelayQueryParam("1500"),
})),
)
delayID := client.GetJSONFieldStr(t, client.ParseJSON(t, res), "delay_id")

Expand Down Expand Up @@ -362,11 +387,10 @@ func TestDelayedEvents(t *testing.T) {
res = user.MustDo(
t,
"PUT",
getPathForState(roomID, eventType, stateKey),
client.WithJSONBody(t, map[string]interface{}{
getPathForDelayedEvent(roomID, eventType, "txn-delayed-state-cancel"),
client.WithJSONBody(t, getDelayedStateEventBody(1500, stateKey, map[string]interface{}{
setterKey: setterExpected,
}),
getDelayQueryParam("1500"),
})),
)
delayID := client.GetJSONFieldStr(t, client.ParseJSON(t, res), "delay_id")

Expand Down Expand Up @@ -416,11 +440,10 @@ func TestDelayedEvents(t *testing.T) {
res = user.MustDo(
t,
"PUT",
getPathForState(roomID, eventType, stateKey),
client.WithJSONBody(t, map[string]interface{}{
getPathForDelayedEvent(roomID, eventType, "txn-delayed-state-send"),
client.WithJSONBody(t, getDelayedStateEventBody(100000, stateKey, map[string]interface{}{
setterKey: setterExpected,
}),
getDelayQueryParam("100000"),
})),
)
delayID := client.GetJSONFieldStr(t, client.ParseJSON(t, res), "delay_id")

Expand Down Expand Up @@ -472,11 +495,10 @@ func TestDelayedEvents(t *testing.T) {
res = user.MustDo(
t,
"PUT",
getPathForState(roomID, eventType, stateKey),
client.WithJSONBody(t, map[string]interface{}{
getPathForDelayedEvent(roomID, eventType, "txn-delayed-state-restart"),
client.WithJSONBody(t, getDelayedStateEventBody(1500, stateKey, map[string]interface{}{
setterKey: setterExpected,
}),
getDelayQueryParam("1500"),
})),
)
delayID := client.GetJSONFieldStr(t, client.ParseJSON(t, res), "delay_id")

Expand Down Expand Up @@ -545,9 +567,8 @@ func TestDelayedEvents(t *testing.T) {
user.MustDo(
t,
"PUT",
getPathForState(roomID, eventType, stateKey1),
client.WithJSONBody(t, map[string]interface{}{}),
getDelayQueryParam("900"),
getPathForDelayedEvent(roomID, eventType, "txn-delayed-state-server-restart-1"),
client.WithJSONBody(t, getDelayedStateEventBody(900, stateKey1, map[string]interface{}{})),
)
numberOfDelayedEvents++

Expand Down Expand Up @@ -579,11 +600,10 @@ func TestDelayedEvents(t *testing.T) {
user.MustDo(
t,
"PUT",
getPathForDelayedEvent(roomID, eventType, fmt.Sprintf("txn-delayed-state-server-restart-%d", i+2)),
// Avoid clashing state keys as that would cancel previous delayed events on the
// same key (start at 2).
getPathForState(roomID, eventType, fmt.Sprintf("%d", i+2)),
client.WithJSONBody(t, map[string]interface{}{}),
getDelayQueryParam(fmt.Sprintf("%d", delay.Milliseconds())),
client.WithJSONBody(t, getDelayedStateEventBody(delay.Milliseconds(), fmt.Sprintf("%d", i+2), map[string]interface{}{})),
)
numberOfDelayedEvents++
}
Expand Down Expand Up @@ -640,18 +660,29 @@ func getPathForUpdateDelayedEvent(delayId string, action DelayedEventAction) []s
return append(getPathForDelayedEvents(), delayId, string(action))
}

func getPathForSend(roomID string, eventType string, txnId string) []string {
return []string{"_matrix", "client", "v3", "rooms", roomID, "send", eventType, txnId}
func getPathForDelayedEvent(roomID string, eventType string, txnID string) []string {
return []string{"_matrix", "client", "unstable", "org.matrix.msc4140", "rooms", roomID, "delayed_event", eventType, txnID}
}

func getPathForState(roomID string, eventType string, stateKey string) []string {
return []string{"_matrix", "client", "v3", "rooms", roomID, "state", eventType, stateKey}
// getDelayedEventBody returns the body of a request to schedule a delayed message event
// through `getPathForDelayedEvent`.
func getDelayedEventBody(delayMs int64, content map[string]interface{}) map[string]interface{} {
return map[string]interface{}{
"delay_ms": delayMs,
"content": content,
}
}

func getDelayQueryParam(delayStr string) client.RequestOpt {
return client.WithQueries(url.Values{
"org.matrix.msc4140.delay": []string{delayStr},
})
// getDelayedStateEventBody returns the body of a request to schedule a delayed state event
// through `getPathForDelayedEvent`.
func getDelayedStateEventBody(delayMs int64, stateKey string, content map[string]interface{}) map[string]interface{} {
body := getDelayedEventBody(delayMs, content)
body["state_key"] = stateKey
return body
}

func getPathForState(roomID string, eventType string, stateKey string) []string {
return []string{"_matrix", "client", "v3", "rooms", roomID, "state", eventType, stateKey}
}

func getDelayedEvents(t *testing.T, user *client.CSAPI) *http.Response {
Expand Down
11 changes: 11 additions & 0 deletions tests/msc4242/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package tests

import (
"testing"

"github.com/matrix-org/complement"
)

func TestMain(m *testing.M) {
complement.TestMain(m, "msc4242")
}
Loading