events: More relation serde fixes
It seems that we cant count on serde_json::to_value to detect duplicates, because it swallows them instead of returning an error. The only solution then is to serialize to string and try to deserialize again.
This commit is contained in:
parent
862be071d2
commit
f17de39ed4
@ -4,6 +4,8 @@ Bug fixes:
|
||||
|
||||
- Fix deserialization of `AnyGlobalAccountDataEvent` for variants with a type
|
||||
fragment.
|
||||
- Fix serialization of `room::message::Relation` and `room::encrypted::Relation`
|
||||
which could cause duplicate `rel_type` keys.
|
||||
|
||||
Improvements:
|
||||
|
||||
|
@ -165,7 +165,10 @@ mod tests {
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use assert_matches2::assert_matches;
|
||||
use ruma_common::{event_id, serde::Base64};
|
||||
use ruma_common::{
|
||||
event_id,
|
||||
serde::{Base64, Raw},
|
||||
};
|
||||
use serde_json::{
|
||||
from_value as from_json_value, json, to_value as to_json_value, Value as JsonValue,
|
||||
};
|
||||
@ -348,4 +351,26 @@ mod tests {
|
||||
assert_eq!(sas.message_authentication_code, MessageAuthenticationCode::HkdfHmacSha256V2);
|
||||
assert_eq!(sas.short_authentication_string, vec![ShortAuthenticationString::Decimal]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn in_room_serialization_roundtrip() {
|
||||
let event_id = event_id!("$1598361704261elfgc:localhost");
|
||||
|
||||
let content = KeyVerificationAcceptEventContent {
|
||||
relates_to: Reference { event_id: event_id.to_owned() },
|
||||
method: AcceptMethod::SasV1(SasV1Content {
|
||||
hash: HashAlgorithm::Sha256,
|
||||
key_agreement_protocol: KeyAgreementProtocol::Curve25519,
|
||||
message_authentication_code: MessageAuthenticationCode::HkdfHmacSha256V2,
|
||||
short_authentication_string: vec![ShortAuthenticationString::Decimal],
|
||||
commitment: Base64::new(b"hello".to_vec()),
|
||||
}),
|
||||
};
|
||||
|
||||
let json_content = Raw::new(&content).unwrap();
|
||||
let deser_content = json_content.deserialize().unwrap();
|
||||
|
||||
assert_matches!(deser_content.method, AcceptMethod::SasV1(_));
|
||||
assert_eq!(deser_content.relates_to.event_id, event_id);
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ impl From<Annotation> for ReactionEventContent {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use assert_matches2::assert_matches;
|
||||
use ruma_common::owned_event_id;
|
||||
use ruma_common::{owned_event_id, serde::Raw};
|
||||
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
|
||||
|
||||
use super::ReactionEventContent;
|
||||
@ -79,4 +79,18 @@ mod tests {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialization_roundtrip() {
|
||||
let content = ReactionEventContent::new(Annotation::new(
|
||||
owned_event_id!("$my_reaction"),
|
||||
"🏠".to_owned(),
|
||||
));
|
||||
|
||||
let json_content = Raw::new(&content).unwrap();
|
||||
let deser_content = json_content.deserialize().unwrap();
|
||||
|
||||
assert_eq!(deser_content.relates_to.event_id, content.relates_to.event_id);
|
||||
assert_eq!(deser_content.relates_to.key, content.relates_to.key);
|
||||
}
|
||||
}
|
||||
|
@ -84,6 +84,7 @@ impl<C> Replacement<C> {
|
||||
/// [thread]: https://spec.matrix.org/latest/client-server-api/#threading
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[serde(tag = "rel_type", rename = "m.thread")]
|
||||
pub struct Thread {
|
||||
/// The ID of the root message in the thread.
|
||||
pub event_id: OwnedEventId,
|
||||
|
@ -165,6 +165,7 @@ impl<C> From<message::Relation<C>> for Relation {
|
||||
/// [replaces another event]: https://spec.matrix.org/latest/client-server-api/#event-replacements
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[serde(tag = "rel_type", rename = "m.replace")]
|
||||
pub struct Replacement {
|
||||
/// The ID of the event being replaced.
|
||||
pub event_id: OwnedEventId,
|
||||
|
@ -74,27 +74,11 @@ impl Serialize for Relation {
|
||||
st.serialize_field("m.in_reply_to", in_reply_to)?;
|
||||
st.end()
|
||||
}
|
||||
Relation::Replacement(data) => {
|
||||
RelationSerHelper { rel_type: "m.replace", data }.serialize(serializer)
|
||||
}
|
||||
Relation::Reference(data) => {
|
||||
RelationSerHelper { rel_type: "m.reference", data }.serialize(serializer)
|
||||
}
|
||||
Relation::Annotation(data) => {
|
||||
RelationSerHelper { rel_type: "m.annotation", data }.serialize(serializer)
|
||||
}
|
||||
Relation::Thread(data) => {
|
||||
RelationSerHelper { rel_type: "m.thread", data }.serialize(serializer)
|
||||
}
|
||||
Relation::Replacement(data) => data.serialize(serializer),
|
||||
Relation::Reference(data) => data.serialize(serializer),
|
||||
Relation::Annotation(data) => data.serialize(serializer),
|
||||
Relation::Thread(data) => data.serialize(serializer),
|
||||
Relation::_Custom(c) => c.serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct RelationSerHelper<'a, T> {
|
||||
rel_type: &'a str,
|
||||
|
||||
#[serde(flatten)]
|
||||
data: &'a T,
|
||||
}
|
||||
|
@ -157,7 +157,7 @@ pub(super) enum RelationSerHelper {
|
||||
Replacement(ReplacementJsonRepr),
|
||||
|
||||
/// An event that belongs to a thread, with stable names.
|
||||
#[serde(rename = "m.thread")]
|
||||
#[serde(untagged)]
|
||||
Thread(Thread),
|
||||
|
||||
/// An unknown relation type.
|
||||
|
@ -1,7 +1,7 @@
|
||||
use assert_matches2::assert_matches;
|
||||
use ruma_common::{owned_device_id, owned_event_id};
|
||||
use ruma_common::{owned_device_id, owned_event_id, serde::Raw};
|
||||
use ruma_events::{
|
||||
relation::{CustomRelation, InReplyTo, Reference, Thread},
|
||||
relation::{Annotation, CustomRelation, InReplyTo, Reference, Thread},
|
||||
room::encrypted::{
|
||||
EncryptedEventScheme, MegolmV1AesSha2ContentInit, Relation, Replacement,
|
||||
RoomEncryptedEventContent,
|
||||
@ -82,6 +82,17 @@ fn content_no_relation_deserialization() {
|
||||
assert_matches!(content.relates_to, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn content_no_relation_serialization_roundtrip() {
|
||||
let content = RoomEncryptedEventContent::new(encrypted_scheme(), None);
|
||||
|
||||
let json_content = Raw::new(&content).unwrap();
|
||||
let deser_content = json_content.deserialize().unwrap();
|
||||
|
||||
assert_matches!(deser_content.scheme, EncryptedEventScheme::MegolmV1AesSha2(_));
|
||||
assert_matches!(deser_content.relates_to, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn content_reply_serialization() {
|
||||
let content = RoomEncryptedEventContent::new(
|
||||
@ -149,6 +160,22 @@ fn content_reply_deserialization() {
|
||||
assert_eq!(in_reply_to.event_id, "$replied_to_event");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn content_reply_serialization_roundtrip() {
|
||||
let reply = InReplyTo::new(owned_event_id!("$replied_to_event"));
|
||||
let content = RoomEncryptedEventContent::new(
|
||||
encrypted_scheme(),
|
||||
Some(Relation::Reply { in_reply_to: reply.clone() }),
|
||||
);
|
||||
|
||||
let json_content = Raw::new(&content).unwrap();
|
||||
let deser_content = json_content.deserialize().unwrap();
|
||||
|
||||
assert_matches!(deser_content.scheme, EncryptedEventScheme::MegolmV1AesSha2(_));
|
||||
assert_matches!(deser_content.relates_to, Some(Relation::Reply { in_reply_to: deser_reply }));
|
||||
assert_eq!(deser_reply.event_id, reply.event_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn content_replacement_serialization() {
|
||||
let content = RoomEncryptedEventContent::new(
|
||||
@ -214,6 +241,22 @@ fn content_replacement_deserialization() {
|
||||
assert_eq!(replacement.event_id, "$replaced_event");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn content_replacement_serialization_roundtrip() {
|
||||
let replacement = Replacement::new(owned_event_id!("$replaced_event"));
|
||||
let content = RoomEncryptedEventContent::new(
|
||||
encrypted_scheme(),
|
||||
Some(Relation::Replacement(replacement.clone())),
|
||||
);
|
||||
|
||||
let json_content = Raw::new(&content).unwrap();
|
||||
let deser_content = json_content.deserialize().unwrap();
|
||||
|
||||
assert_matches!(deser_content.scheme, EncryptedEventScheme::MegolmV1AesSha2(_));
|
||||
assert_matches!(deser_content.relates_to, Some(Relation::Replacement(deser_replacement)));
|
||||
assert_eq!(deser_replacement.event_id, replacement.event_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn content_reference_serialization() {
|
||||
let content = RoomEncryptedEventContent::new(
|
||||
@ -279,6 +322,22 @@ fn content_reference_deserialization() {
|
||||
assert_eq!(reference.event_id, "$referenced_event");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn content_reference_serialization_roundtrip() {
|
||||
let reference = Reference::new(owned_event_id!("$referenced_event"));
|
||||
let content = RoomEncryptedEventContent::new(
|
||||
encrypted_scheme(),
|
||||
Some(Relation::Reference(reference.clone())),
|
||||
);
|
||||
|
||||
let json_content = Raw::new(&content).unwrap();
|
||||
let deser_content = json_content.deserialize().unwrap();
|
||||
|
||||
assert_matches!(deser_content.scheme, EncryptedEventScheme::MegolmV1AesSha2(_));
|
||||
assert_matches!(deser_content.relates_to, Some(Relation::Reference(deser_reference)));
|
||||
assert_eq!(deser_reference.event_id, reference.event_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn content_thread_serialization() {
|
||||
let content = RoomEncryptedEventContent::new(
|
||||
@ -357,9 +416,23 @@ fn content_thread_deserialization() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn content_annotation_serialization() {
|
||||
use ruma_events::relation::Annotation;
|
||||
fn content_thread_serialization_roundtrip() {
|
||||
let thread = Thread::plain(owned_event_id!("$thread_root"), owned_event_id!("$prev_event"));
|
||||
let content =
|
||||
RoomEncryptedEventContent::new(encrypted_scheme(), Some(Relation::Thread(thread.clone())));
|
||||
|
||||
let json_content = Raw::new(&content).unwrap();
|
||||
let deser_content = json_content.deserialize().unwrap();
|
||||
|
||||
assert_matches!(deser_content.scheme, EncryptedEventScheme::MegolmV1AesSha2(_));
|
||||
assert_matches!(deser_content.relates_to, Some(Relation::Thread(deser_thread)));
|
||||
assert_eq!(deser_thread.event_id, thread.event_id);
|
||||
assert_eq!(deser_thread.in_reply_to.unwrap().event_id, thread.in_reply_to.unwrap().event_id);
|
||||
assert_eq!(deser_thread.is_falling_back, thread.is_falling_back);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn content_annotation_serialization() {
|
||||
let content = RoomEncryptedEventContent::new(
|
||||
encrypted_scheme(),
|
||||
Some(Relation::Annotation(Annotation::new(
|
||||
@ -429,6 +502,23 @@ fn content_annotation_deserialization() {
|
||||
assert_eq!(annotation.key, "some_key");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn content_annotation_serialization_roundtrip() {
|
||||
let annotation = Annotation::new(owned_event_id!("$annotated_event"), "some_key".to_owned());
|
||||
let content = RoomEncryptedEventContent::new(
|
||||
encrypted_scheme(),
|
||||
Some(Relation::Annotation(annotation.clone())),
|
||||
);
|
||||
|
||||
let json_content = Raw::new(&content).unwrap();
|
||||
let deser_content = json_content.deserialize().unwrap();
|
||||
|
||||
assert_matches!(deser_content.scheme, EncryptedEventScheme::MegolmV1AesSha2(_));
|
||||
assert_matches!(deser_content.relates_to, Some(Relation::Annotation(deser_annotation)));
|
||||
assert_eq!(deser_annotation.event_id, annotation.event_id);
|
||||
assert_eq!(deser_annotation.key, annotation.key);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_relation_deserialization() {
|
||||
let relation_json = json!({
|
||||
@ -502,3 +592,31 @@ fn custom_relation_serialization() {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_serialization_roundtrip() {
|
||||
let rel_type = "io.ruma.unknown";
|
||||
let event_id = "$related_event";
|
||||
let key = "value";
|
||||
let json_relation = json!({
|
||||
"rel_type": rel_type,
|
||||
"event_id": event_id,
|
||||
"key": key,
|
||||
});
|
||||
let relation = from_json_value::<CustomRelation>(json_relation).unwrap();
|
||||
|
||||
let content =
|
||||
RoomEncryptedEventContent::new(encrypted_scheme(), Some(Relation::_Custom(relation)));
|
||||
|
||||
let json_content = Raw::new(&content).unwrap();
|
||||
let deser_content = json_content.deserialize().unwrap();
|
||||
|
||||
assert_matches!(content.scheme, EncryptedEventScheme::MegolmV1AesSha2(_));
|
||||
let deser_relates_to = deser_content.relates_to.unwrap();
|
||||
assert_matches!(&deser_relates_to, Relation::_Custom(_));
|
||||
assert_eq!(deser_relates_to.rel_type().unwrap().as_str(), rel_type);
|
||||
let deser_relation = deser_relates_to.data();
|
||||
assert_eq!(deser_relation.get("rel_type").unwrap().as_str().unwrap(), rel_type);
|
||||
assert_eq!(deser_relation.get("event_id").unwrap().as_str().unwrap(), event_id);
|
||||
assert_eq!(deser_relation.get("key").unwrap().as_str().unwrap(), key);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use assert_matches2::assert_matches;
|
||||
use assign::assign;
|
||||
use ruma_common::owned_event_id;
|
||||
use ruma_common::{owned_event_id, serde::Raw};
|
||||
use ruma_events::{
|
||||
relation::{CustomRelation, InReplyTo, Replacement, Thread},
|
||||
room::message::{MessageType, Relation, RoomMessageEventContent},
|
||||
@ -52,6 +52,22 @@ fn reply_serialize() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reply_serialization_roundtrip() {
|
||||
let body = "This is a reply";
|
||||
let mut content = RoomMessageEventContent::text_plain(body);
|
||||
let reply = InReplyTo::new(owned_event_id!("$1598361704261elfgc"));
|
||||
content.relates_to = Some(Relation::Reply { in_reply_to: reply.clone() });
|
||||
|
||||
let json_content = Raw::new(&content).unwrap();
|
||||
let deser_content = json_content.deserialize().unwrap();
|
||||
|
||||
assert_matches!(deser_content.msgtype, MessageType::Text(deser_msg));
|
||||
assert_eq!(deser_msg.body, body);
|
||||
assert_matches!(content.relates_to.unwrap(), Relation::Reply { in_reply_to: deser_reply });
|
||||
assert_eq!(deser_reply.event_id, reply.event_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn replacement_serialize() {
|
||||
let content = assign!(
|
||||
@ -111,6 +127,28 @@ fn replacement_deserialize() {
|
||||
assert_eq!(text.body, "Hello! My name is bar");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn replacement_serialization_roundtrip() {
|
||||
let body = "<text msg>";
|
||||
let mut content = RoomMessageEventContent::text_plain(body);
|
||||
let new_body = "This is the new content.";
|
||||
let replacement = Replacement::new(
|
||||
owned_event_id!("$1598361704261elfgc"),
|
||||
RoomMessageEventContent::text_plain(new_body).into(),
|
||||
);
|
||||
content.relates_to = Some(Relation::Replacement(replacement.clone()));
|
||||
|
||||
let json_content = Raw::new(&content).unwrap();
|
||||
let deser_content = json_content.deserialize().unwrap();
|
||||
|
||||
assert_matches!(deser_content.msgtype, MessageType::Text(deser_msg));
|
||||
assert_eq!(deser_msg.body, body);
|
||||
assert_matches!(content.relates_to.unwrap(), Relation::Replacement(deser_replacement));
|
||||
assert_eq!(deser_replacement.event_id, replacement.event_id);
|
||||
assert_matches!(deser_replacement.new_content.msgtype, MessageType::Text(deser_new_msg));
|
||||
assert_eq!(deser_new_msg.body, new_body);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn thread_plain_serialize() {
|
||||
let content = assign!(
|
||||
@ -250,6 +288,25 @@ fn thread_unstable_deserialize() {
|
||||
assert!(!thread.is_falling_back);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn thread_serialization_roundtrip() {
|
||||
let body = "<text msg>";
|
||||
let mut content = RoomMessageEventContent::text_plain(body);
|
||||
let thread =
|
||||
Thread::plain(owned_event_id!("$1598361704261elfgc"), owned_event_id!("$latesteventid"));
|
||||
content.relates_to = Some(Relation::Thread(thread.clone()));
|
||||
|
||||
let json_content = Raw::new(&content).unwrap();
|
||||
let deser_content = json_content.deserialize().unwrap();
|
||||
|
||||
assert_matches!(deser_content.msgtype, MessageType::Text(deser_msg));
|
||||
assert_eq!(deser_msg.body, body);
|
||||
assert_matches!(content.relates_to.unwrap(), Relation::Thread(deser_thread));
|
||||
assert_eq!(deser_thread.event_id, thread.event_id);
|
||||
assert_eq!(deser_thread.in_reply_to.unwrap().event_id, thread.in_reply_to.unwrap().event_id);
|
||||
assert_eq!(deser_thread.is_falling_back, thread.is_falling_back);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_deserialize() {
|
||||
let relation_json = json!({
|
||||
@ -300,3 +357,33 @@ fn custom_serialize() {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_serialization_roundtrip() {
|
||||
let rel_type = "io.ruma.unknown";
|
||||
let event_id = "$related_event";
|
||||
let key = "value";
|
||||
let json_relation = json!({
|
||||
"rel_type": rel_type,
|
||||
"event_id": event_id,
|
||||
"key": key,
|
||||
});
|
||||
let relation = from_json_value::<CustomRelation>(json_relation).unwrap();
|
||||
|
||||
let body = "<text msg>";
|
||||
let mut content = RoomMessageEventContent::text_plain(body);
|
||||
content.relates_to = Some(Relation::_Custom(relation));
|
||||
|
||||
let json_content = Raw::new(&content).unwrap();
|
||||
let deser_content = json_content.deserialize().unwrap();
|
||||
|
||||
assert_matches!(deser_content.msgtype, MessageType::Text(deser_msg));
|
||||
assert_eq!(deser_msg.body, body);
|
||||
let deser_relates_to = deser_content.relates_to.unwrap();
|
||||
assert_matches!(&deser_relates_to, Relation::_Custom(_));
|
||||
assert_eq!(deser_relates_to.rel_type().unwrap().as_str(), rel_type);
|
||||
let deser_relation = deser_relates_to.data();
|
||||
assert_eq!(deser_relation.get("rel_type").unwrap().as_str().unwrap(), rel_type);
|
||||
assert_eq!(deser_relation.get("event_id").unwrap().as_str().unwrap(), event_id);
|
||||
assert_eq!(deser_relation.get("key").unwrap().as_str().unwrap(), key);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user