Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions client/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,31 @@ func SyncStateHas(roomID string, check func(gjson.Result) bool) SyncCheckOpt {
}
}

// Check that the `state_after` section for `roomID` has an event which passes the check function.
//
// Note that the `state_after` section of a sync response will not contain the entire
// state of the room for incremental or `lazy_load_members` syncs.
func SyncStateAfterHas(roomID string, check func(gjson.Result) bool) SyncCheckOpt {
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered updating SyncStateHas(...) to include checking for state_after but perhaps someone explicitly wants to check state vs state_after for some specific tests.

return func(clientUserID string, topLevelSyncJSON gjson.Result) error {
// Check the stable field
errStable := checkArrayElements(
topLevelSyncJSON, "rooms.join."+GjsonEscape(roomID)+".state_after.events", check,
)
// Check the unstable field
//
// FIXME: Some implementations haven't stabilized yet (Synapse) so we'll keep this
// here until then.
errUnstable := checkArrayElements(
topLevelSyncJSON, "rooms.join."+GjsonEscape(roomID)+"."+GjsonEscape("org.matrix.msc4222.state_after")+".events", check,
)
// Valid to find it in either place
if errStable == nil || errUnstable == nil {
return nil
}
return fmt.Errorf("SyncStateAfterHas(%s): Tried to check in the stable field: %s - and unstable field: %s", roomID, errStable, errUnstable)
}
}

func SyncEphemeralHas(roomID string, check func(gjson.Result) bool) SyncCheckOpt {
return func(clientUserID string, topLevelSyncJSON gjson.Result) error {
err := checkArrayElements(
Expand Down
20 changes: 5 additions & 15 deletions tests/msc4140/delayed_event_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,6 @@ const (
DelayedEventActionSend = "send"
)

// A filter for `/sync` that excludes timeline events.
//
// This is useful if you want to see `state` in the `/sync` response without the pesky
// de-duplication with `timeline` that traditional `/sync` does.
const NoTimelineSyncFilter = `{
"room": {
"timeline": { "limit": 0 }
}
}`
Comment on lines -33 to -41
Copy link
Copy Markdown
Collaborator Author

@MadLittleMods MadLittleMods May 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding the new standard SyncStateAfterHas(...) check allows us to remove this custom filter and tribal knowledge around how it works.


// TODO: Test pagination of `GET /_matrix/client/v1/delayed_events` once
// it is implemented in a homeserver.

Expand Down Expand Up @@ -195,7 +185,7 @@ func TestDelayedEvents(t *testing.T) {

// Check for the state change from the delayed state event (using `MustSyncUntil` to
// account for any processing or worker replication delays)
user.MustSyncUntil(t, client.SyncReq{Filter: NoTimelineSyncFilter}, client.SyncStateHas(roomID, func(ev gjson.Result) bool {
user.MustSyncUntil(t, client.SyncReq{UseStateAfter: true}, client.SyncStateAfterHas(roomID, func(ev gjson.Result) bool {
return ev.Get("type").Str == eventType && ev.Get("state_key").Str == stateKey
}))
// Make sure the state looks as expected after
Expand Down Expand Up @@ -347,7 +337,7 @@ func TestDelayedEvents(t *testing.T) {

// Check for the state change from the delayed state event (using `MustSyncUntil` to
// account for any processing or worker replication delays)
user.MustSyncUntil(t, client.SyncReq{Filter: NoTimelineSyncFilter}, client.SyncStateHas(roomID, func(ev gjson.Result) bool {
user.MustSyncUntil(t, client.SyncReq{UseStateAfter: true}, client.SyncStateAfterHas(roomID, func(ev gjson.Result) bool {
return ev.Get("type").Str == eventType && ev.Get("state_key").Str == stateKey
}))
// Make sure the state looks as expected after
Expand Down Expand Up @@ -417,7 +407,7 @@ func TestDelayedEvents(t *testing.T) {

// Check for the state change from the delayed state event (using `MustSyncUntil` to
// account for any processing or worker replication delays)
user.MustSyncUntil(t, client.SyncReq{Filter: NoTimelineSyncFilter}, client.SyncStateHas(roomID, func(ev gjson.Result) bool {
user.MustSyncUntil(t, client.SyncReq{UseStateAfter: true}, client.SyncStateAfterHas(roomID, func(ev gjson.Result) bool {
return ev.Get("type").Str == eventType && ev.Get("state_key").Str == stateKey
}))
// Make sure the state looks as expected after
Expand Down Expand Up @@ -512,7 +502,7 @@ func TestDelayedEvents(t *testing.T) {
// Sanity check that the room state was updated correctly with the delayed events
// that were sent. (using `MustSyncUntil` to account for any processing or worker
// replication delays)
user.MustSyncUntil(t, client.SyncReq{Filter: NoTimelineSyncFilter}, client.SyncStateHas(roomID, func(ev gjson.Result) bool {
user.MustSyncUntil(t, client.SyncReq{UseStateAfter: true}, client.SyncStateAfterHas(roomID, func(ev gjson.Result) bool {
return ev.Get("type").Str == eventType && ev.Get("state_key").Str == stateKey1
}))

Expand All @@ -528,7 +518,7 @@ func TestDelayedEvents(t *testing.T) {
// FIXME: Ideally, we'd check specifically for the last one that was sent but it
// will be a bit of a juggle and fiddly to get this right so for now we just check
// one.
user.MustSyncUntil(t, client.SyncReq{Filter: NoTimelineSyncFilter}, client.SyncStateHas(roomID, func(ev gjson.Result) bool {
user.MustSyncUntil(t, client.SyncReq{UseStateAfter: true}, client.SyncStateAfterHas(roomID, func(ev gjson.Result) bool {
return ev.Get("type").Str == eventType && ev.Get("state_key").Str == stateKey2
}))
})
Expand Down
Loading