Skip to content

Commit 911d7d3

Browse files
authored
Add tests for MSC3757 (#733)
1 parent 26d5aa9 commit 911d7d3

File tree

2 files changed

+186
-0
lines changed

2 files changed

+186
-0
lines changed

tests/msc3757/main_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package tests
2+
3+
import (
4+
"testing"
5+
6+
"github.com/matrix-org/complement"
7+
)
8+
9+
func TestMain(m *testing.M) {
10+
complement.TestMain(m, "msc3757")
11+
}

tests/msc3757/owned_state_test.go

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
package tests
2+
3+
import (
4+
"net/http"
5+
"testing"
6+
7+
"github.com/matrix-org/complement"
8+
"github.com/matrix-org/complement/client"
9+
"github.com/matrix-org/complement/helpers"
10+
"github.com/matrix-org/complement/match"
11+
"github.com/matrix-org/complement/must"
12+
)
13+
14+
const hsName = "hs1"
15+
const stateEventTestType = "com.example.test"
16+
17+
// To stress-test parsing, include separator & sigil characters
18+
const stateKeySuffix = "_state_key_suffix:!@#$123"
19+
20+
func TestWithoutOwnedState(t *testing.T) {
21+
deployment := complement.Deploy(t, 1)
22+
defer deployment.Destroy(t)
23+
24+
creator := deployment.Register(t, hsName, helpers.RegistrationOpts{})
25+
user := deployment.Register(t, hsName, helpers.RegistrationOpts{})
26+
27+
roomID := creator.MustCreateRoom(t, map[string]interface{}{
28+
"room_version": "10",
29+
"preset": "public_chat",
30+
"power_level_content_override": map[string]interface{}{
31+
"events": map[string]int{
32+
stateEventTestType: 0,
33+
},
34+
},
35+
})
36+
user.MustJoinRoom(t, roomID, nil)
37+
38+
t.Run("parallel", func(t *testing.T) {
39+
t.Run("user can set state with their own user ID as state key", func(t *testing.T) {
40+
t.Parallel()
41+
res := sendState(t, roomID, user, user.UserID)
42+
must.MatchSuccess(t, res)
43+
})
44+
t.Run("room creator cannot set state with their own suffixed user ID as state key", func(t *testing.T) {
45+
t.Parallel()
46+
res := sendState(t, roomID, creator, creator.UserID+stateKeySuffix)
47+
mustMatchForbidden(t, res)
48+
})
49+
t.Run("room creator cannot set state with another user ID as state key", func(t *testing.T) {
50+
t.Parallel()
51+
res := sendState(t, roomID, creator, user.UserID)
52+
mustMatchForbidden(t, res)
53+
})
54+
t.Run("room creator cannot set state with another suffixed user ID as state key", func(t *testing.T) {
55+
t.Parallel()
56+
res := sendState(t, roomID, creator, user.UserID+stateKeySuffix)
57+
mustMatchForbidden(t, res)
58+
})
59+
t.Run("room creator cannot set state with a non-member user ID as state key", func(t *testing.T) {
60+
t.Parallel()
61+
res := sendState(t, roomID, creator, "@notinroom:hs2")
62+
mustMatchForbidden(t, res)
63+
})
64+
t.Run("room creator cannot set state with malformed user ID as state key", func(t *testing.T) {
65+
t.Parallel()
66+
res := sendState(t, roomID, creator, "@oops")
67+
mustMatchForbidden(t, res)
68+
})
69+
})
70+
}
71+
72+
func TestMSC3757OwnedState(t *testing.T) {
73+
deployment := complement.Deploy(t, 1)
74+
defer deployment.Destroy(t)
75+
76+
creator := deployment.Register(t, hsName, helpers.RegistrationOpts{})
77+
user1 := deployment.Register(t, hsName, helpers.RegistrationOpts{})
78+
user2 := deployment.Register(t, hsName, helpers.RegistrationOpts{})
79+
80+
roomID := creator.MustCreateRoom(t, map[string]interface{}{
81+
"room_version": "org.matrix.msc3757.10",
82+
"preset": "public_chat",
83+
"power_level_content_override": map[string]interface{}{
84+
"events": map[string]int{
85+
stateEventTestType: 0,
86+
},
87+
},
88+
})
89+
user1.MustJoinRoom(t, roomID, nil)
90+
user2.MustJoinRoom(t, roomID, nil)
91+
92+
t.Run("parallel", func(t *testing.T) {
93+
t.Run("user can set state with their own suffixed user ID as state key", func(t *testing.T) {
94+
t.Parallel()
95+
res := sendState(t, roomID, user1, user1.UserID+stateKeySuffix)
96+
must.MatchSuccess(t, res)
97+
})
98+
t.Run("room creator can set state with another user ID as state key", func(t *testing.T) {
99+
t.Parallel()
100+
res := sendState(t, roomID, creator, user1.UserID)
101+
must.MatchSuccess(t, res)
102+
})
103+
t.Run("room creator can set state with another suffixed user ID as state key", func(t *testing.T) {
104+
t.Parallel()
105+
res := sendState(t, roomID, creator, user1.UserID+stateKeySuffix)
106+
must.MatchSuccess(t, res)
107+
})
108+
t.Run("user cannot set state with another user ID as state key", func(t *testing.T) {
109+
t.Parallel()
110+
res := sendState(t, roomID, user1, user2.UserID)
111+
mustMatchForbidden(t, res)
112+
})
113+
t.Run("user cannot set state with another suffixed user ID as state key", func(t *testing.T) {
114+
t.Parallel()
115+
res := sendState(t, roomID, user1, user2.UserID+stateKeySuffix)
116+
mustMatchForbidden(t, res)
117+
})
118+
t.Run("user cannot set state with unseparated suffix in state key", func(t *testing.T) {
119+
t.Parallel()
120+
res := sendState(t, roomID, user1, user1.UserID+stateKeySuffix[1:])
121+
mustMatchForbidden(t, res)
122+
})
123+
t.Run("user cannot set state with misplaced user ID in state key", func(t *testing.T) {
124+
t.Parallel()
125+
// Still put @ at start of state key, because without it, there is no write protection at all
126+
res := sendState(t, roomID, user1, "@prefix_"+user1.UserID+stateKeySuffix)
127+
mustMatchForbidden(t, res)
128+
})
129+
t.Run("room creator can set state with a non-member user ID as state key", func(t *testing.T) {
130+
t.Parallel()
131+
res := sendState(t, roomID, creator, "@notinroom:hs2")
132+
must.MatchSuccess(t, res)
133+
})
134+
t.Run("room creator cannot set state with malformed user ID as state key", func(t *testing.T) {
135+
t.Parallel()
136+
res := sendState(t, roomID, creator, "@oops")
137+
mustMatchInvalid(t, res)
138+
})
139+
t.Run("room creator cannot set state with improperly suffixed state key", func(t *testing.T) {
140+
t.Parallel()
141+
res := sendState(t, roomID, creator, creator.UserID+"@"+stateKeySuffix[1:])
142+
mustMatchInvalid(t, res)
143+
})
144+
})
145+
}
146+
147+
func sendState(t *testing.T, roomID string, user *client.CSAPI, stateKey string) *http.Response {
148+
t.Helper()
149+
return user.Do(
150+
t,
151+
"PUT",
152+
[]string{"_matrix", "client", "v3", "rooms", roomID, "state", stateEventTestType, stateKey},
153+
client.WithJSONBody(t, map[string]interface{}{}),
154+
)
155+
}
156+
157+
func mustMatchForbidden(t *testing.T, res *http.Response) {
158+
t.Helper()
159+
must.MatchResponse(t, res, match.HTTPResponse{
160+
StatusCode: 403,
161+
JSON: []match.JSON{
162+
match.JSONKeyEqual("errcode", "M_FORBIDDEN"),
163+
},
164+
})
165+
}
166+
167+
func mustMatchInvalid(t *testing.T, res *http.Response) {
168+
t.Helper()
169+
must.MatchResponse(t, res, match.HTTPResponse{
170+
StatusCode: 400,
171+
JSON: []match.JSON{
172+
match.JSONKeyEqual("errcode", "M_BAD_JSON"),
173+
},
174+
})
175+
}

0 commit comments

Comments
 (0)