Skip to content

Commit

Permalink
Add tests for MSC3757 (#733)
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewFerr authored Sep 25, 2024
1 parent 26d5aa9 commit 911d7d3
Show file tree
Hide file tree
Showing 2 changed files with 186 additions and 0 deletions.
11 changes: 11 additions & 0 deletions tests/msc3757/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, "msc3757")
}
175 changes: 175 additions & 0 deletions tests/msc3757/owned_state_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package tests

import (
"net/http"
"testing"

"github.com/matrix-org/complement"
"github.com/matrix-org/complement/client"
"github.com/matrix-org/complement/helpers"
"github.com/matrix-org/complement/match"
"github.com/matrix-org/complement/must"
)

const hsName = "hs1"
const stateEventTestType = "com.example.test"

// To stress-test parsing, include separator & sigil characters
const stateKeySuffix = "_state_key_suffix:!@#$123"

func TestWithoutOwnedState(t *testing.T) {
deployment := complement.Deploy(t, 1)
defer deployment.Destroy(t)

creator := deployment.Register(t, hsName, helpers.RegistrationOpts{})
user := deployment.Register(t, hsName, helpers.RegistrationOpts{})

roomID := creator.MustCreateRoom(t, map[string]interface{}{
"room_version": "10",
"preset": "public_chat",
"power_level_content_override": map[string]interface{}{
"events": map[string]int{
stateEventTestType: 0,
},
},
})
user.MustJoinRoom(t, roomID, nil)

t.Run("parallel", func(t *testing.T) {
t.Run("user can set state with their own user ID as state key", func(t *testing.T) {
t.Parallel()
res := sendState(t, roomID, user, user.UserID)
must.MatchSuccess(t, res)
})
t.Run("room creator cannot set state with their own suffixed user ID as state key", func(t *testing.T) {
t.Parallel()
res := sendState(t, roomID, creator, creator.UserID+stateKeySuffix)
mustMatchForbidden(t, res)
})
t.Run("room creator cannot set state with another user ID as state key", func(t *testing.T) {
t.Parallel()
res := sendState(t, roomID, creator, user.UserID)
mustMatchForbidden(t, res)
})
t.Run("room creator cannot set state with another suffixed user ID as state key", func(t *testing.T) {
t.Parallel()
res := sendState(t, roomID, creator, user.UserID+stateKeySuffix)
mustMatchForbidden(t, res)
})
t.Run("room creator cannot set state with a non-member user ID as state key", func(t *testing.T) {
t.Parallel()
res := sendState(t, roomID, creator, "@notinroom:hs2")
mustMatchForbidden(t, res)
})
t.Run("room creator cannot set state with malformed user ID as state key", func(t *testing.T) {
t.Parallel()
res := sendState(t, roomID, creator, "@oops")
mustMatchForbidden(t, res)
})
})
}

func TestMSC3757OwnedState(t *testing.T) {
deployment := complement.Deploy(t, 1)
defer deployment.Destroy(t)

creator := deployment.Register(t, hsName, helpers.RegistrationOpts{})
user1 := deployment.Register(t, hsName, helpers.RegistrationOpts{})
user2 := deployment.Register(t, hsName, helpers.RegistrationOpts{})

roomID := creator.MustCreateRoom(t, map[string]interface{}{
"room_version": "org.matrix.msc3757.10",
"preset": "public_chat",
"power_level_content_override": map[string]interface{}{
"events": map[string]int{
stateEventTestType: 0,
},
},
})
user1.MustJoinRoom(t, roomID, nil)
user2.MustJoinRoom(t, roomID, nil)

t.Run("parallel", func(t *testing.T) {
t.Run("user can set state with their own suffixed user ID as state key", func(t *testing.T) {
t.Parallel()
res := sendState(t, roomID, user1, user1.UserID+stateKeySuffix)
must.MatchSuccess(t, res)
})
t.Run("room creator can set state with another user ID as state key", func(t *testing.T) {
t.Parallel()
res := sendState(t, roomID, creator, user1.UserID)
must.MatchSuccess(t, res)
})
t.Run("room creator can set state with another suffixed user ID as state key", func(t *testing.T) {
t.Parallel()
res := sendState(t, roomID, creator, user1.UserID+stateKeySuffix)
must.MatchSuccess(t, res)
})
t.Run("user cannot set state with another user ID as state key", func(t *testing.T) {
t.Parallel()
res := sendState(t, roomID, user1, user2.UserID)
mustMatchForbidden(t, res)
})
t.Run("user cannot set state with another suffixed user ID as state key", func(t *testing.T) {
t.Parallel()
res := sendState(t, roomID, user1, user2.UserID+stateKeySuffix)
mustMatchForbidden(t, res)
})
t.Run("user cannot set state with unseparated suffix in state key", func(t *testing.T) {
t.Parallel()
res := sendState(t, roomID, user1, user1.UserID+stateKeySuffix[1:])
mustMatchForbidden(t, res)
})
t.Run("user cannot set state with misplaced user ID in state key", func(t *testing.T) {
t.Parallel()
// Still put @ at start of state key, because without it, there is no write protection at all
res := sendState(t, roomID, user1, "@prefix_"+user1.UserID+stateKeySuffix)
mustMatchForbidden(t, res)
})
t.Run("room creator can set state with a non-member user ID as state key", func(t *testing.T) {
t.Parallel()
res := sendState(t, roomID, creator, "@notinroom:hs2")
must.MatchSuccess(t, res)
})
t.Run("room creator cannot set state with malformed user ID as state key", func(t *testing.T) {
t.Parallel()
res := sendState(t, roomID, creator, "@oops")
mustMatchInvalid(t, res)
})
t.Run("room creator cannot set state with improperly suffixed state key", func(t *testing.T) {
t.Parallel()
res := sendState(t, roomID, creator, creator.UserID+"@"+stateKeySuffix[1:])
mustMatchInvalid(t, res)
})
})
}

func sendState(t *testing.T, roomID string, user *client.CSAPI, stateKey string) *http.Response {
t.Helper()
return user.Do(
t,
"PUT",
[]string{"_matrix", "client", "v3", "rooms", roomID, "state", stateEventTestType, stateKey},
client.WithJSONBody(t, map[string]interface{}{}),
)
}

func mustMatchForbidden(t *testing.T, res *http.Response) {
t.Helper()
must.MatchResponse(t, res, match.HTTPResponse{
StatusCode: 403,
JSON: []match.JSON{
match.JSONKeyEqual("errcode", "M_FORBIDDEN"),
},
})
}

func mustMatchInvalid(t *testing.T, res *http.Response) {
t.Helper()
must.MatchResponse(t, res, match.HTTPResponse{
StatusCode: 400,
JSON: []match.JSON{
match.JSONKeyEqual("errcode", "M_BAD_JSON"),
},
})
}

0 comments on commit 911d7d3

Please sign in to comment.