Skip to content

Commit 1e635b4

Browse files
authored
Merge branch 'main' into shay/no_redact
2 parents 4da40dc + 50b86f6 commit 1e635b4

File tree

1 file changed

+356
-0
lines changed

1 file changed

+356
-0
lines changed

tests/csapi/apidoc_search_test.go

Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
package csapi_tests
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"net/url"
7+
"testing"
8+
9+
"github.com/matrix-org/util"
10+
"github.com/tidwall/gjson"
11+
12+
"github.com/matrix-org/complement/internal/b"
13+
"github.com/matrix-org/complement/internal/client"
14+
"github.com/matrix-org/complement/internal/match"
15+
"github.com/matrix-org/complement/internal/must"
16+
"github.com/matrix-org/complement/runtime"
17+
)
18+
19+
// Note: In contrast to Sytest, we define a filter.rooms on each search request, this is to mimic
20+
// creating a new user and new room per test. This also allows us to run in parallel.
21+
func TestSearch(t *testing.T) {
22+
runtime.SkipIf(t, runtime.Dendrite) // https://github.com/matrix-org/dendrite/pull/2675
23+
deployment := Deploy(t, b.BlueprintAlice)
24+
defer deployment.Destroy(t)
25+
26+
alice := deployment.Client(t, "hs1", "@alice:hs1")
27+
28+
t.Run("parallel", func(t *testing.T) {
29+
// sytest: Can search for an event by body
30+
t.Run("Can search for an event by body", func(t *testing.T) {
31+
t.Parallel()
32+
roomID := alice.CreateRoom(t, map[string]interface{}{
33+
"preset": "private_chat",
34+
})
35+
eventID := alice.SendEventSynced(t, roomID, b.Event{
36+
Type: "m.room.message",
37+
Content: map[string]interface{}{
38+
"msgtype": "m.text",
39+
"body": "hello, world",
40+
},
41+
})
42+
43+
searchRequest := client.WithJSONBody(t, map[string]interface{}{
44+
"search_categories": map[string]interface{}{
45+
"room_events": map[string]interface{}{
46+
"keys": []string{"content.body"},
47+
"search_term": "hello",
48+
"filter": map[string]interface{}{
49+
"rooms": []string{roomID},
50+
},
51+
},
52+
},
53+
})
54+
55+
resp := alice.MustDoFunc(t, "POST", []string{"_matrix", "client", "v3", "search"}, searchRequest)
56+
sce := "search_categories.room_events"
57+
result0 := sce + ".results.0.result"
58+
must.MatchResponse(t, resp, match.HTTPResponse{
59+
StatusCode: http.StatusOK,
60+
JSON: []match.JSON{
61+
match.JSONKeyPresent(sce + ".count"),
62+
match.JSONKeyPresent(sce + ".results"),
63+
match.JSONKeyEqual(sce+".count", float64(1)),
64+
match.JSONKeyEqual(result0+".event_id", eventID),
65+
match.JSONKeyEqual(result0+".room_id", roomID),
66+
match.JSONKeyPresent(result0 + ".content"),
67+
match.JSONKeyPresent(result0 + ".type"),
68+
match.JSONKeyEqual(result0+".content.body", "hello, world"),
69+
},
70+
})
71+
})
72+
73+
// sytest: Can get context around search results
74+
t.Run("Can get context around search results", func(t *testing.T) {
75+
t.Parallel()
76+
roomID := alice.CreateRoom(t, map[string]interface{}{
77+
"preset": "private_chat",
78+
})
79+
80+
for i := 1; i <= 7; i++ {
81+
alice.SendEventSynced(t, roomID, b.Event{
82+
Type: "m.room.message",
83+
Content: map[string]interface{}{
84+
"body": fmt.Sprintf("Message number %d", i),
85+
"msgtype": "m.text",
86+
},
87+
})
88+
}
89+
90+
searchRequest := client.WithJSONBody(t, map[string]interface{}{
91+
"search_categories": map[string]interface{}{
92+
"room_events": map[string]interface{}{
93+
"keys": []string{"content.body"},
94+
"search_term": "Message 4",
95+
"order_by": "recent",
96+
"filter": map[string]interface{}{
97+
"limit": 1,
98+
"rooms": []string{roomID},
99+
},
100+
"event_context": map[string]interface{}{
101+
"before_limit": 2,
102+
"after_limit": 2,
103+
},
104+
},
105+
},
106+
})
107+
108+
resp := alice.MustDoFunc(t, "POST", []string{"_matrix", "client", "v3", "search"}, searchRequest)
109+
sce := "search_categories.room_events"
110+
result0 := sce + ".results.0.result"
111+
resBefore := sce + ".results.0.context.events_before"
112+
resAfter := sce + ".results.0.context.events_after"
113+
must.MatchResponse(t, resp, match.HTTPResponse{
114+
StatusCode: http.StatusOK,
115+
JSON: []match.JSON{
116+
match.JSONKeyPresent(sce + ".count"),
117+
match.JSONKeyPresent(sce + ".results"),
118+
match.JSONKeyPresent(sce + ".next_batch"),
119+
match.JSONKeyEqual(sce+".count", float64(1)),
120+
match.JSONKeyEqual(result0+".room_id", roomID),
121+
match.JSONKeyPresent(result0 + ".content"),
122+
match.JSONKeyPresent(result0 + ".type"),
123+
match.JSONKeyEqual(result0+".content.body", "Message number 4"),
124+
match.JSONKeyEqual(resBefore+".0.content.body", "Message number 3"),
125+
match.JSONKeyEqual(resBefore+".1.content.body", "Message number 2"),
126+
match.JSONKeyEqual(resAfter+".0.content.body", "Message number 5"),
127+
match.JSONKeyEqual(resAfter+".1.content.body", "Message number 6"),
128+
},
129+
})
130+
})
131+
132+
// sytest: Can back-paginate search results
133+
t.Run("Can back-paginate search results", func(t *testing.T) {
134+
t.Parallel()
135+
roomID := alice.CreateRoom(t, map[string]interface{}{
136+
"preset": "private_chat",
137+
})
138+
139+
eventIDs := make([]string, 20)
140+
for i := 0; i <= 19; i++ {
141+
eventID := alice.SendEventSynced(t, roomID, b.Event{
142+
Type: "m.room.message",
143+
Content: map[string]interface{}{
144+
"body": fmt.Sprintf("Message number %d", i),
145+
"msgtype": "m.text",
146+
},
147+
})
148+
eventIDs[i] = eventID
149+
}
150+
151+
searchRequest := client.WithJSONBody(t, map[string]interface{}{
152+
"search_categories": map[string]interface{}{
153+
"room_events": map[string]interface{}{
154+
"keys": []string{"content.body"},
155+
"search_term": "Message",
156+
"order_by": "recent",
157+
"filter": map[string]interface{}{
158+
"limit": 10,
159+
"rooms": []string{roomID},
160+
},
161+
},
162+
},
163+
})
164+
165+
resp := alice.MustDoFunc(t, "POST", []string{"_matrix", "client", "v3", "search"}, searchRequest)
166+
167+
// First search result
168+
nextBatch := checkBackpaginateResult(t, resp, 20, eventIDs[19], eventIDs[10])
169+
170+
if nextBatch == "" {
171+
t.Fatalf("no next batch set!")
172+
}
173+
174+
values := url.Values{}
175+
values.Set("next_batch", nextBatch)
176+
params := client.WithQueries(values)
177+
resp = alice.MustDoFunc(t, "POST", []string{"_matrix", "client", "v3", "search"}, searchRequest, params)
178+
// Second search result
179+
nextBatch = checkBackpaginateResult(t, resp, 20, eventIDs[9], eventIDs[0])
180+
181+
// At this point we expect next_batch to be empty
182+
values.Set("next_batch", nextBatch)
183+
params = client.WithQueries(values)
184+
resp = alice.MustDoFunc(t, "POST", []string{"_matrix", "client", "v3", "search"}, searchRequest, params)
185+
// third search result
186+
sce := "search_categories.room_events"
187+
result0 := sce + ".results.0.result"
188+
must.MatchResponse(t, resp, match.HTTPResponse{
189+
StatusCode: http.StatusOK,
190+
JSON: []match.JSON{
191+
match.JSONKeyPresent(sce + ".count"),
192+
match.JSONKeyPresent(sce + ".results"),
193+
match.JSONKeyEqual(sce+".count", float64(20)),
194+
match.JSONKeyMissing(result0),
195+
match.JSONKeyMissing(sce + ".next_batch"),
196+
},
197+
})
198+
})
199+
200+
// sytest: Search works across an upgraded room and its predecessor
201+
t.Run("Search works across an upgraded room and its predecessor", func(t *testing.T) {
202+
t.Parallel()
203+
roomID := alice.CreateRoom(t, map[string]interface{}{
204+
"preset": "private_chat",
205+
"version": "8",
206+
})
207+
208+
eventBeforeUpgrade := alice.SendEventSynced(t, roomID, b.Event{
209+
Type: "m.room.message",
210+
Content: map[string]interface{}{
211+
"body": "Message before upgrade",
212+
"msgtype": "m.text",
213+
},
214+
})
215+
216+
upgradeBody := client.WithJSONBody(t, map[string]string{
217+
"new_version": "9",
218+
})
219+
upgradeResp := alice.MustDoFunc(t, "POST", []string{"_matrix", "client", "v3", "rooms", roomID, "upgrade"}, upgradeBody)
220+
body := must.ParseJSON(t, upgradeResp.Body)
221+
newRoomID := must.GetJSONFieldStr(t, body, "replacement_room")
222+
223+
eventAfterUpgrade := alice.SendEventSynced(t, newRoomID, b.Event{
224+
Type: "m.room.message",
225+
Content: map[string]interface{}{
226+
"body": "Message after upgrade",
227+
"msgtype": "m.text",
228+
},
229+
})
230+
231+
searchRequest := client.WithJSONBody(t, map[string]interface{}{
232+
"search_categories": map[string]interface{}{
233+
"room_events": map[string]interface{}{
234+
"keys": []string{"content.body"},
235+
"search_term": "upgrade",
236+
"filter": map[string]interface{}{
237+
"rooms": []string{roomID, newRoomID},
238+
},
239+
},
240+
},
241+
})
242+
243+
resp := alice.MustDoFunc(t, "POST", []string{"_matrix", "client", "v3", "search"}, searchRequest)
244+
sce := "search_categories.room_events"
245+
result0 := sce + ".results.0.result"
246+
result1 := sce + ".results.1.result"
247+
must.MatchResponse(t, resp, match.HTTPResponse{
248+
StatusCode: http.StatusOK,
249+
JSON: []match.JSON{
250+
match.JSONKeyPresent(sce + ".count"),
251+
match.JSONKeyPresent(sce + ".results"),
252+
match.JSONKeyEqual(sce+".count", float64(2)),
253+
match.JSONKeyPresent(result0 + ".content"),
254+
match.JSONKeyPresent(result0 + ".type"),
255+
match.JSONKeyEqual(result0+".event_id", eventBeforeUpgrade),
256+
match.JSONKeyEqual(result1+".event_id", eventAfterUpgrade),
257+
match.JSONKeyEqual(result0+".content.body", "Message before upgrade"),
258+
match.JSONKeyEqual(result1+".content.body", "Message after upgrade"),
259+
},
260+
})
261+
})
262+
263+
for _, ordering := range []string{"rank", "recent"} {
264+
// sytest: Search results with $ordering_type ordering do not include redacted events
265+
t.Run(fmt.Sprintf("Search results with %s ordering do not include redacted events", ordering), func(t *testing.T) {
266+
t.Parallel()
267+
roomID := alice.CreateRoom(t, map[string]interface{}{
268+
"preset": "private_chat",
269+
})
270+
271+
redactedEventID := alice.SendEventSynced(t, roomID, b.Event{
272+
Type: "m.room.message",
273+
Content: map[string]interface{}{
274+
"body": "This message is going to be redacted",
275+
"msgtype": "m.text",
276+
},
277+
})
278+
279+
wantContentBody := "This message is not going to be redacted"
280+
visibleEventID := alice.SendEventSynced(t, roomID, b.Event{
281+
Type: "m.room.message",
282+
Content: map[string]interface{}{
283+
"body": wantContentBody,
284+
"msgtype": "m.text",
285+
},
286+
})
287+
288+
// redact the event
289+
redactBody := client.WithJSONBody(t, map[string]interface{}{"reason": "testing"})
290+
txnID := util.RandomString(8) // random string, as time.Now().Unix() might create the same txnID
291+
resp := alice.MustDoFunc(t, "PUT", []string{"_matrix", "client", "v3", "rooms", roomID, "redact", redactedEventID, txnID}, redactBody)
292+
j := must.ParseJSON(t, resp.Body)
293+
redactionEventID := must.GetJSONFieldStr(t, j, "event_id")
294+
// wait for the redaction to come down sync
295+
alice.MustSyncUntil(t, client.SyncReq{}, client.SyncTimelineHasEventID(roomID, redactionEventID))
296+
297+
searchRequest := client.WithJSONBody(t, map[string]interface{}{
298+
"search_categories": map[string]interface{}{
299+
"room_events": map[string]interface{}{
300+
"keys": []string{"content.body"},
301+
"order_by": ordering,
302+
"search_term": "redacted",
303+
"filter": map[string]interface{}{
304+
"rooms": []string{roomID},
305+
},
306+
},
307+
},
308+
})
309+
310+
resp = alice.MustDoFunc(t, "POST", []string{"_matrix", "client", "v3", "search"}, searchRequest)
311+
sce := "search_categories.room_events"
312+
result0 := sce + ".results.0.result"
313+
must.MatchResponse(t, resp, match.HTTPResponse{
314+
StatusCode: http.StatusOK,
315+
JSON: []match.JSON{
316+
match.JSONKeyPresent(sce + ".count"),
317+
match.JSONKeyPresent(sce + ".results"),
318+
match.JSONKeyArrayOfSize(sce+".results", 1),
319+
match.JSONKeyPresent(result0 + ".content"),
320+
match.JSONKeyPresent(result0 + ".type"),
321+
match.JSONKeyEqual(result0+".event_id", visibleEventID),
322+
match.JSONKeyEqual(result0+".content.body", wantContentBody),
323+
},
324+
})
325+
})
326+
}
327+
328+
})
329+
}
330+
331+
func checkBackpaginateResult(t *testing.T, resp *http.Response, wantCount float64, lastEventID, firstEventID string) (nextBatch string) {
332+
sce := "search_categories.room_events"
333+
result0 := sce + ".results.0.result"
334+
result9 := sce + ".results.9.result"
335+
must.MatchResponse(t, resp, match.HTTPResponse{
336+
StatusCode: http.StatusOK,
337+
JSON: []match.JSON{
338+
match.JSONKeyPresent(sce + ".count"),
339+
match.JSONKeyPresent(sce + ".results"),
340+
match.JSONMapEach(sce, func(k, v gjson.Result) error {
341+
if k.Str == "next_batch" {
342+
nextBatch = v.Str
343+
return nil
344+
}
345+
return nil
346+
}),
347+
match.JSONKeyPresent(sce + ".next_batch"),
348+
match.JSONKeyEqual(sce+".count", wantCount),
349+
match.JSONKeyPresent(result0 + ".content"),
350+
match.JSONKeyPresent(result0 + ".type"),
351+
match.JSONKeyEqual(result0+".event_id", lastEventID),
352+
match.JSONKeyEqual(result9+".event_id", firstEventID),
353+
},
354+
})
355+
return
356+
}

0 commit comments

Comments
 (0)