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

** DO NOT MERGE ** Test CI for unreviewed insta test #4441

Closed
wants to merge 10 commits into from
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ concurrency:

env:
CARGO_TERM_COLOR: always
# Insta.rs is run directly via cargo test. We don't want insta.rs to create new snapshots files.
# Just want it to run the tests (option `no` instead of `auto`).
INSTA_UPDATE: no

jobs:
xtask:
Expand Down
27 changes: 27 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,33 @@ integration tests that need a running synapse instance. These tests reside in
synapse for testing purposes.


### Snapshot Testing

You can add/review snapshot tests using [insta.rs](https://insta.rs)

Every new struct/enum that derives `Serialize` `Deserialise` should have a snapshot test for it.
Any code change that breaks serialisation will then break a test, the author will then have to decide
how to handle migration and test it if needed.


And for an improved review experience it's recommended (but not necessary) to install the cargo-insta tool:

Unix:
```
curl -LsSf https://insta.rs/install.sh | sh
```

Windows:
```
powershell -c "irm https://insta.rs/install.ps1 | iex"
```

Usual flow is to first run the test, then review them.
```
cargo insta test
cargo insta review
```

## Pull requests

Ideally, a PR should have a *proper title*, with *atomic logical commits*, and
Expand Down
20 changes: 20 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ hmac = "0.12.1"
http = "1.1.0"
imbl = "3.0.0"
indexmap = "2.6.0"
insta = { version = "1.41.1", features = ["json"] }
itertools = "0.13.0"
js-sys = "0.3.69"
mime = "0.3.17"
Expand Down Expand Up @@ -124,6 +125,9 @@ debug = 0
# for the extra time of optimizing it for a clean build of matrix-sdk-ffi.
quote = { opt-level = 2 }
sha2 = { opt-level = 2 }
# faster runs for insta.rs snapshot testing
insta.opt-level = 3
similar.opt-level = 3

# Custom profile with full debugging info, use `--profile dbg` to select
[profile.dbg]
Expand Down
1 change: 1 addition & 0 deletions crates/matrix-sdk-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ assert_matches = { workspace = true }
proptest = { workspace = true }
matrix-sdk-test-macros = { path = "../../testing/matrix-sdk-test-macros" }
wasm-bindgen-test = { workspace = true }
insta = { workspace = true }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
# Enable the test macro.
Expand Down
219 changes: 212 additions & 7 deletions crates/matrix-sdk-common/src/deserialized_responses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -906,24 +906,25 @@ impl From<SyncTimelineEventDeserializationHelperV0> for SyncTimelineEvent {

#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use std::{collections::BTreeMap, vec};

use assert_matches::assert_matches;
use insta::{assert_json_snapshot, with_settings};
use ruma::{
event_id,
device_id, event_id,
events::{room::message::RoomMessageEventContent, AnySyncTimelineEvent},
serde::Raw,
user_id,
user_id, DeviceKeyAlgorithm,
};
use serde::Deserialize;
use serde_json::json;

use super::{
AlgorithmInfo, DecryptedRoomEvent, EncryptionInfo, SyncTimelineEvent, TimelineEvent,
TimelineEventKind, UnableToDecryptInfo, UnableToDecryptReason, UnsignedDecryptionResult,
UnsignedEventLocation, VerificationState, WithheldCode,
AlgorithmInfo, DecryptedRoomEvent, DeviceLinkProblem, EncryptionInfo, ShieldState,
ShieldStateCode, SyncTimelineEvent, TimelineEvent, TimelineEventKind, UnableToDecryptInfo,
UnableToDecryptReason, UnsignedDecryptionResult, UnsignedEventLocation, VerificationLevel,
VerificationState, WithheldCode,
};
use crate::deserialized_responses::{DeviceLinkProblem, ShieldStateCode, VerificationLevel};

fn example_event() -> serde_json::Value {
json!({
Expand Down Expand Up @@ -1317,4 +1318,208 @@ mod tests {
let reason = UnableToDecryptReason::UnknownMegolmMessageIndex;
assert!(reason.is_missing_room_key());
}

#[test]
fn snapshot_test_verification_level() {
let level = VerificationLevel::VerificationViolation;
assert_json_snapshot! {
serde_json::to_value(&level).unwrap(),
}

let level = VerificationLevel::UnsignedDevice;
assert_json_snapshot! {
serde_json::to_value(&level).unwrap(),
}

let level = VerificationLevel::None(DeviceLinkProblem::InsecureSource);
assert_json_snapshot! {
serde_json::to_value(&level).unwrap(),
}

let level = VerificationLevel::None(DeviceLinkProblem::MissingDevice);
assert_json_snapshot! {
serde_json::to_value(&level).unwrap(),
}

let level = VerificationLevel::UnverifiedIdentity;
assert_json_snapshot! {
serde_json::to_value(&level).unwrap()
}
}

#[test]
fn snapshot_test_verification_states() {
let states = vec![
VerificationState::Unverified(VerificationLevel::UnsignedDevice),
VerificationState::Unverified(VerificationLevel::VerificationViolation),
VerificationState::Unverified(VerificationLevel::None(
DeviceLinkProblem::InsecureSource,
)),
VerificationState::Unverified(VerificationLevel::None(
DeviceLinkProblem::MissingDevice,
)),
VerificationState::Verified,
];

for state in states {
assert_json_snapshot! {
serde_json::to_value(&state).unwrap(),
}
}
}

#[test]
fn snapshot_test_shield_states() {
let state = ShieldState::None;
assert_json_snapshot! {
serde_json::to_value(&state).unwrap(),
}

let state =
ShieldState::Red { code: ShieldStateCode::UnverifiedIdentity, message: "a message" };
assert_json_snapshot! {
serde_json::to_value(&state).unwrap(),
}

let state = ShieldState::Grey {
code: ShieldStateCode::AuthenticityNotGuaranteed,
message: "authenticity of this message cannot be guaranteed",
};
assert_json_snapshot! {
serde_json::to_value(&state).unwrap(),
}
}

#[test]
fn snapshot_test_shield_codes() {
let codes = vec![
ShieldStateCode::AuthenticityNotGuaranteed,
ShieldStateCode::UnknownDevice,
ShieldStateCode::UnsignedDevice,
ShieldStateCode::UnverifiedIdentity,
ShieldStateCode::SentInClear,
ShieldStateCode::VerificationViolation,
];

for code in codes {
assert_json_snapshot! {
serde_json::to_value(code).unwrap(),
}
}
}

#[test]
fn snapshot_test_algorithm_info() {
let mut map = BTreeMap::new();
map.insert(DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned());
map.insert(DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned());
let info = AlgorithmInfo::MegolmV1AesSha2 {
curve25519_key: "curvecurvecurve".into(),
sender_claimed_keys: map,
};

with_settings!({sort_maps =>true}, {
assert_json_snapshot! {
serde_json::to_value(&info).unwrap(),
}
})
}

#[test]
fn snapshot_test_encryption_info() {
let info = EncryptionInfo {
sender: user_id!("@alice:localhost").to_owned(),
sender_device: Some(device_id!("ABCDEFGH").to_owned()),
algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
curve25519_key: "curvecurvecurve".into(),
sender_claimed_keys: Default::default(),
},
verification_state: VerificationState::Verified,
};

with_settings!({sort_maps =>true}, {
assert_json_snapshot! {
serde_json::to_value(&info).unwrap(),
}
});
}

#[test]
fn snapshot_test_sync_timeline_event() {
let room_event = SyncTimelineEvent {
kind: TimelineEventKind::Decrypted(DecryptedRoomEvent {
event: Raw::new(&example_event()).unwrap().cast(),
encryption_info: EncryptionInfo {
sender: user_id!("@sender:example.com").to_owned(),
sender_device: Some(device_id!("ABCDEFGHIJ").to_owned()),
algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
curve25519_key: "xxx".to_owned(),
sender_claimed_keys: BTreeMap::from([
(
DeviceKeyAlgorithm::Ed25519,
"I3YsPwqMZQXHkSQbjFNEs7b529uac2xBpI83eN3LUXo".to_owned(),
),
(
DeviceKeyAlgorithm::Curve25519,
"qzdW3F5IMPFl0HQgz5w/L5Oi/npKUFn8Um84acIHfPY".to_owned(),
),
]),
},
verification_state: VerificationState::Verified,
},
unsigned_encryption_info: Some(BTreeMap::from([(
UnsignedEventLocation::RelationsThreadLatestEvent,
UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
session_id: Some("xyz".to_owned()),
reason: UnableToDecryptReason::MissingMegolmSession {
withheld_code: Some(WithheldCode::Unverified),
},
}),
)])),
}),
push_actions: Default::default(),
};

with_settings!({sort_maps =>true}, {
assert_json_snapshot! {
serde_json::to_value(&room_event).unwrap(),
}
});
}

#[test]
fn snapshot_old_to_remove() {
let serialized = json!({
"kind": {
"UnableToDecrypt": {
"event": {
"content": {
"algorithm": "m.megolm.v1.aes-sha2",
"ciphertext": "AwgAEoABzL1JYhqhjW9jXrlT3M6H8mJ4qffYtOQOnPuAPNxsuG20oiD/Fnpv6jnQGhU6YbV9pNM+1mRnTvxW3CbWOPjLKqCWTJTc7Q0vDEVtYePg38ncXNcwMmfhgnNAoW9S7vNs8C003x3yUl6NeZ8bH+ci870BZL+kWM/lMl10tn6U7snNmSjnE3ckvRdO+11/R4//5VzFQpZdf4j036lNSls/WIiI67Fk9iFpinz9xdRVWJFVdrAiPFwb8L5xRZ8aX+e2JDMlc1eW8gk",
"device_id": "SKCGPNUWAU",
"sender_key": "Gim/c7uQdSXyrrUbmUOrBT6sMC0gO7QSLmOK6B7NOm0",
"session_id": "hgLyeSqXfb8vc5AjQLsg6TSHVu0HJ7HZ4B6jgMvxkrs"
},
"event_id": "$xxxxx:example.org",
"origin_server_ts": 2189,
"room_id": "!someroom:example.com",
"sender": "@carl:example.com",
"type": "m.room.message"
},
"utd_info": {
"reason": "MissingMegolmSession",
"session_id": "session0001"
}
}
}
});

let result: SyncTimelineEvent = serde_json::from_value(serialized).unwrap();

with_settings!({sort_maps =>true}, {
assert_json_snapshot! {
serde_json::to_value(&result).unwrap(),
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
source: crates/matrix-sdk-common/src/deserialized_responses.rs
expression: "serde_json::to_value(&info).unwrap()"
---
{
"MegolmV1AesSha2": {
"curve25519_key": "curvecurvecurve",
"sender_claimed_keys": {
"curve25519": "claimedclaimedcurve25519",
"ed25519": "claimedclaimeded25519"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
source: crates/matrix-sdk-common/src/deserialized_responses.rs
expression: "serde_json::to_value(&info).unwrap()"
---
{
"algorithm_info": {
"MegolmV1AesSha2": {
"curve25519_key": "curvecurvecurve",
"sender_claimed_keys": {}
}
},
"sender": "@alice:localhost",
"sender_device": "ABCDEFGH",
"verification_state": "Verified"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
source: crates/matrix-sdk-common/src/deserialized_responses.rs
expression: "serde_json::to_value(&code).unwrap()"
---
"UnknownDevice"
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
source: crates/matrix-sdk-common/src/deserialized_responses.rs
expression: "serde_json::to_value(&code).unwrap()"
---
"UnsignedDevice"
Loading
Loading