Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

task(tests): introduce prebuilt mocks and mocking helpers #4197

Merged
merged 3 commits into from
Nov 7, 2024

Conversation

bnjbvr
Copy link
Member

@bnjbvr bnjbvr commented Oct 31, 2024

This introduces new test utilities, starting with MatrixMockServer. This is a pair of a MockServer and a Client (one can retrieve them respectively with .server() and .client()).

It implements a few mock endpoints, expanding and limited the shared code as much as possible, so the mocks are still flexible to use as scoped/unscoped mounts, named, and so on.

It works like this:

  • start with telling which endpoint you'd like to mock, e.g. mock_room_send(). This returns a specialized MockSomething data structure, with its own impl. For this example, it's MockRoomSend.
  • configure the response on the endpoint-specific mock data structure. For instance, if you want the sending to result in a transient failure, call MockRoomSend::error500(); if you want it to succeed and return the event $42, call MockRoomSend::ok(event_id!("$42")). It's still possible to call respond_with(), as we do with wiremock MockBuilder, for maximum flexibility when the helpers aren't sufficient.
  • once the endpoint's response is configured, for any mock builder, you get a MatrixMock; this is a plain wiremock::Mock with the server curried, so one doesn't have to pass it around when calling .mount() or .mount_as_scoped(). As such, it mostly defers its implementations to wiremock::Mock under the hood.

Example

With this, we can go from that code:

#[async_test]
async fn test_make_reply_event_doesnt_require_event_cache() {
    let (client, server) = logged_in_client_with_server().await;

    let event_id = event_id!("$1");
    let resp_event_id = event_id!("$resp");
    let room_id = room_id!("!galette:saucisse.bzh");

    let f = EventFactory::new();

    let raw_original_event = f
        .text_msg("hi")
        .event_id(event_id)
        .sender(client.user_id().unwrap())
        .room(room_id)
        .into_raw_timeline();

    mock_sync_with_new_room(
        |builder| {
            builder.add_joined_room(JoinedRoomBuilder::new(room_id));
        },
        &client,
        &server,
        room_id,
    )
    .await;

    Mock::given(method("GET"))
        .and(path_regex(r"^/_matrix/client/r0/rooms/.*/event/"))
        .and(header("authorization", "Bearer 1234"))
        .respond_with(ResponseTemplate::new(200).set_body_json(raw_original_event.json()))
        .expect(1)
        .named("/event")
        .mount(&server)
        .await;

    let new_content = RoomMessageEventContentWithoutRelation::text_plain("uh i mean bonjour");

    let room = client.get_room(room_id).unwrap();

    // make_edit_event works, even if the event cache hasn't been enabled.
    room.make_edit_event(resp_event_id, EditedContent::RoomMessage(new_content)).await.unwrap();
}

To that code:

#[async_test]
async fn test_make_reply_event_doesnt_require_event_cache() {
    let mock = MatrixMockServer::new().await;
    let user_id = mock.client().user_id().unwrap().to_owned();

    let room_id = room_id!("!galette:saucisse.bzh");
    let room = mock.sync_joined_room(room_id).await;

    let event_id = event_id!("$1");
    let f = EventFactory::new();
    mock.mock_room_event()
        .ok(f.text_msg("hi").event_id(event_id).sender(&user_id).room(room_id).into_timeline())
        .expect(1)
        .named("/event")
        .mount()
        .await;

    let new_content = RoomMessageEventContentWithoutRelation::text_plain("uh i mean bonjour");

    // make_edit_event works, even if the event cache hasn't been enabled.
    room.make_edit_event(event_id, EditedContent::RoomMessage(new_content)).await.unwrap();
}

@bnjbvr bnjbvr requested a review from a team as a code owner October 31, 2024 13:30
@bnjbvr bnjbvr requested review from andybalaam and removed request for a team October 31, 2024 13:30
Copy link

codecov bot commented Oct 31, 2024

Codecov Report

Attention: Patch coverage is 94.59459% with 8 lines in your changes missing coverage. Please review.

Please upload report for BASE (main@5d83808). Learn more about missing BASE report.
Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
crates/matrix-sdk/src/test_utils/mocks.rs 94.48% 8 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #4197   +/-   ##
=======================================
  Coverage        ?   84.90%           
=======================================
  Files           ?      274           
  Lines           ?    29677           
  Branches        ?        0           
=======================================
  Hits            ?    25196           
  Misses          ?     4481           
  Partials        ?        0           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@bnjbvr bnjbvr force-pushed the bnjbvr/prebuilt-mock-test-system branch from 2c8e654 to e9016b2 Compare October 31, 2024 14:17
Copy link
Member

@andybalaam andybalaam left a comment

Choose a reason for hiding this comment

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

This is great. Passing on to @richvdh because a) I think he will have helpful feedback and b) I'm not available next week.

crates/matrix-sdk/src/test_utils/mocks.rs Outdated Show resolved Hide resolved
crates/matrix-sdk/src/test_utils/mocks.rs Outdated Show resolved Hide resolved
/// Overrides the sync/ endpoint with knowledge that the given
/// invited/joined/knocked/left room exists, runs a sync and returns the
/// given room.
pub async fn sync_room(&self, room_id: &RoomId, room_data: impl Into<AnyRoomBuilder>) -> Room {
Copy link
Member

Choose a reason for hiding this comment

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

Trying to think of a more informative name for this...

Maybe create_or_change_room ? yuck.

Copy link
Member Author

Choose a reason for hiding this comment

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

mock_and_run_sync_for_room? 🥴

crates/matrix-sdk/src/test_utils/mocks.rs Show resolved Hide resolved
crates/matrix-sdk/src/test_utils/mocks.rs Outdated Show resolved Hide resolved
@andybalaam andybalaam requested a review from richvdh November 1, 2024 12:43
Copy link
Member

@richvdh richvdh left a comment

Choose a reason for hiding this comment

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

some thoughts. Looks like a great improvement.

crates/matrix-sdk/src/test_utils/mocks.rs Show resolved Hide resolved
crates/matrix-sdk/src/test_utils/mocks.rs Show resolved Hide resolved
crates/matrix-sdk/src/test_utils/mocks.rs Outdated Show resolved Hide resolved
@bnjbvr bnjbvr force-pushed the bnjbvr/prebuilt-mock-test-system branch from e9016b2 to cfc43f4 Compare November 6, 2024 16:28
@bnjbvr bnjbvr requested a review from richvdh November 6, 2024 16:37
@bnjbvr
Copy link
Member Author

bnjbvr commented Nov 6, 2024

Should be ready for another round of review! I'm planning to use this for writing more tests for media uploads.

crates/matrix-sdk/src/test_utils/mocks.rs Outdated Show resolved Hide resolved
crates/matrix-sdk/src/test_utils/mocks.rs Outdated Show resolved Hide resolved
crates/matrix-sdk/src/test_utils/mocks.rs Outdated Show resolved Hide resolved
crates/matrix-sdk/src/test_utils/mocks.rs Outdated Show resolved Hide resolved
crates/matrix-sdk/src/test_utils/mocks.rs Outdated Show resolved Hide resolved
/// Mocks a sync endpoint.
pub fn mock_sync(&self) -> MockSync<'_> {
let mock = Mock::given(method("GET"))
.and(path("/_matrix/client/r0/sync"))
Copy link
Member

Choose a reason for hiding this comment

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

aside: shouldn't we be using /v3/ endpoints? /r0/ is supported by synapse for compatibility with old applications, but new codebases shouldn't be using it.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah the default Client is configured with no information about matrix versions, so it supposes the first version by default. It usually doesn't matter, except in the few cases where it does, of course. I'll ask about this in the Rust room, but i'm tempted to keep it like this right now.

Copy link
Contributor

Choose a reason for hiding this comment

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

In reality we do use the /v3 endpoints, but in tests we don't bother filling out the supported version in the Client and as @bnjbvr says, it falls back to a default version.

We could modify the constructor of the test Client object to have the versions prefilled, but that will automatically mean that you need to update all tests that are using this test client.

I would say that we should do that, but not as part of this PR.

@bnjbvr bnjbvr requested a review from richvdh November 7, 2024 14:54
Copy link
Member

@richvdh richvdh left a comment

Choose a reason for hiding this comment

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

lgtm 🎉

@bnjbvr bnjbvr force-pushed the bnjbvr/prebuilt-mock-test-system branch from a9f3928 to 8e73592 Compare November 7, 2024 16:21
@bnjbvr bnjbvr enabled auto-merge (rebase) November 7, 2024 16:21
@bnjbvr bnjbvr merged commit 965a59d into main Nov 7, 2024
39 checks passed
@bnjbvr bnjbvr deleted the bnjbvr/prebuilt-mock-test-system branch November 7, 2024 16:37
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.

4 participants