Skip to content

Add tests for MSC4429: Profile Updates for Legacy Sync#849

Open
anoadragon453 wants to merge 9 commits into
mainfrom
anoa/msc4429
Open

Add tests for MSC4429: Profile Updates for Legacy Sync#849
anoadragon453 wants to merge 9 commits into
mainfrom
anoa/msc4429

Conversation

@anoadragon453

@anoadragon453 anoadragon453 commented Mar 12, 2026

Copy link
Copy Markdown
Member

This PR introduces some initial Complement tests for the current draft of MSC4429: Profile Updates for Legacy Sync.

Note: This PR was almost entirely written by OpenAI Codex, but has been reviewed manually. Extra review scrutiny is advised.

Tests are expected to fail until the associated homeserver implementation has been written.

Pull Request Checklist

@anoadragon453 anoadragon453 marked this pull request as ready for review March 18, 2026 15:58
@anoadragon453 anoadragon453 requested review from a team as code owners March 18, 2026 15:59
@MadLittleMods MadLittleMods changed the title Add tests for MSC4429 Add tests for MSC4429: Profile Updates for Legacy Sync Mar 19, 2026
Comment thread tests/msc4429/msc4429_test.go Outdated
Comment thread tests/msc4429/msc4429_test.go
Comment thread tests/msc4429/msc4429_test.go
Comment thread tests/msc4429/msc4429_test.go Outdated
Comment on lines +59 to +63
// No filter = no profile fields returned.
_, since := alice.MustSync(t, client.SyncReq{})
mustSetProfileField(t, bob, "m.status", map[string]interface{}{
"text": "away",
})

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

As a sanity check, we should have another observer user that sees the m.status with a filter, and then alice syncs without a filter

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Addressed in fd4b5fd84fd325aa9c922ff70fe534ba0c1e7be4.

Comment thread tests/msc4429/msc4429_test.go
Comment thread tests/msc4429/msc4429_test.go Outdated
Comment on lines +59 to +60
// No filter = no profile fields returned.
_, since := alice.MustSync(t, client.SyncReq{})

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We don't actually assert anything for the initial sync request

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I believe this is just to get a since token, so we can perform an incremental sync afterwards.

Clarified the comments in 4b93b71a59ca431a1f10cefe714c44f2384055bc.

assertNoProfileUpdate(t, res, bob.UserID, "m.status")
})

t.Run("Incremental sync returns the latest update", func(t *testing.T) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Perhaps we should have another test to make sure more subsequent incremental /sync include new updates for the new user.

This ensures that any caching (remembering which users were sent down before) is correct.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I guess, we kinda have this already with "Cleared profile field is returned as null"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I guess, we kinda have this already with "Cleared profile field is returned as null"

Sort of. The data structure of the /sync response JSON doesn't allow to return two subsequent status updates for the same user and field:

{
  "users": {
    "@user:example.org": {
      "profile_updates": {
        "m.status": {
          "text": "Swimming in the Great Lakes!",
          "emoji": "🏊️"
        }
      }
    }
  }
}

What might be useful is an additional test with:

  • Bob updating their m.status field
  • Alice syncing without a sync filter which includes the field
  • Bob updating their displayname field
  • Alice syncing with a m.status and displayname filter
  • Alice should only receive the displayname update

Comment thread tests/msc4429/msc4429_test.go Outdated
Comment thread tests/msc4429/msc4429_test.go
@anoadragon453

Copy link
Copy Markdown
Member Author

The tests in this PR weren't actually running in CI 🤦 I've fixed that in 4cddfea.

All were expected to succeed (despite all failing), other than the newly added A user leaving the last shared room returns a profile update of null which @jaywink is currently working on. Presumably the Synapse implementation needs adapting to fix the rest of the tests as well.

Still, the tests here should be ready for another round of review.

Comment thread .github/workflows/ci.yaml
repo: element-hq/synapse
tags: synapse_blacklist
packages: ./tests/msc3874 ./tests/msc3902 ./tests/msc4306
packages: ./tests/msc3874 ./tests/msc3902 ./tests/msc4306 ./tests/msc4429

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The tests in this PR weren't actually running in CI 🤦 I've fixed that in 4cddfea.

I assume you meant 2b67137 but in any case, we don't really worry about updating this list.

It's already heavily out of sync with the list we run in the Synapse project, element-hq/synapse -> scripts-dev/complement.sh#L272-L289

At this point, I think we just rely on it as a general sanity check that Complement is still working with Synapse.

Comment on lines +72 to +78
// Assert that charlie receives bob's profile update in an incremental sync
// with the appropriate filter set.
filter := mustBuildMSC4429Filter(t, []string{"m.status"})
charlie.MustSyncUntil(t, client.SyncReq{Filter: filter}, syncHasProfileUpdate(bob.UserID, "m.status", map[string]interface{}{
"text": "away",
"emoji": "🟡",
}))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Kinda feels like this should also be an incremental sync to better demonstrate the data is there with the right filter.


// syncHasProfileUpdatesNull checks whether a sync response contains a null
// profile_updates value for the given user.
func syncHasProfileUpdatesNull(userID string) client.SyncCheckOpt {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Seems like the usage could be replaced by syncHasProfileUpdate(...)

Or at-least this function re-uses syncHasProfileUpdate(...)

// Join all of the given users to the room.
for _, user := range users {
user.MustSyncUntil(t, client.SyncReq{}, client.SyncJoinedTo(user.UserID, roomID))
user.MustJoinRoom(t, roomID, nil)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This should be before the user.MustSyncUntil afaict.

res, _ := alice.MustSync(t, client.SyncReq{Filter: filter})

// We should see the m.status profile update.
alice.MustSyncUntil(t, client.SyncReq{Filter: filter}, syncHasProfileUpdate(alice.UserID, "m.status", map[string]interface{}{

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
alice.MustSyncUntil(t, client.SyncReq{Filter: filter}, syncHasProfileUpdate(alice.UserID, "m.status", map[string]interface{}{
alice.MustSyncUntil(t, client.SyncReq{Filter: filter}, syncHasProfileUpdate(bob.UserID, "m.status", map[string]interface{}{

This should check for bob's update, not alice's.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants