events: Allow replacement unstable poll start events to have no fallback

This commit is contained in:
Kévin Commaille 2023-08-24 11:32:09 +02:00 committed by Kévin Commaille
parent a70f99a233
commit 18195e0a6e
5 changed files with 505 additions and 55 deletions

View File

@ -6,10 +6,11 @@ use js_int::UInt;
use ruma_macros::EventContent;
use serde::{Deserialize, Serialize};
mod content_serde;
mod unstable_poll_answers_serde;
mod unstable_poll_kind_serde;
use ruma_common::MilliSecondsSinceUnixEpoch;
use ruma_common::{MilliSecondsSinceUnixEpoch, OwnedEventId};
use self::unstable_poll_answers_serde::UnstablePollAnswersDeHelper;
use super::{
@ -18,7 +19,11 @@ use super::{
unstable_end::UnstablePollEndEventContent,
PollResponseData,
};
use crate::room::message::Relation;
use crate::{
relation::Replacement, room::message::RelationWithoutReplacement, EventContent,
MessageLikeEventContent, MessageLikeEventType, RedactContent, RedactedMessageLikeEventContent,
StaticEventContent,
};
/// The payload for an unstable poll start event.
///
@ -30,37 +35,46 @@ use crate::room::message::Relation;
/// [`PollStartEventContent`].
///
/// [`PollStartEventContent`]: super::start::PollStartEventContent
#[derive(Clone, Debug, Serialize, Deserialize, EventContent)]
#[derive(Clone, Debug, Serialize, EventContent)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[ruma_event(type = "org.matrix.msc3381.poll.start", kind = MessageLike, without_relation)]
pub struct UnstablePollStartEventContent {
/// The poll content of the message.
#[serde(rename = "org.matrix.msc3381.poll.start")]
pub poll_start: UnstablePollStartContentBlock,
#[ruma_event(type = "org.matrix.msc3381.poll.start", kind = MessageLike, custom_redacted)]
#[serde(untagged)]
#[allow(clippy::large_enum_variant)]
pub enum UnstablePollStartEventContent {
/// A new poll start event.
New(NewUnstablePollStartEventContent),
/// Text representation of the message, for clients that don't support polls.
#[serde(rename = "org.matrix.msc1767.text")]
pub text: Option<String>,
/// Information about related messages.
#[serde(
flatten,
skip_serializing_if = "Option::is_none",
deserialize_with = "crate::room::message::relation_serde::deserialize_relation"
)]
pub relates_to: Option<Relation<UnstablePollStartEventContentWithoutRelation>>,
/// A replacement poll start event.
Replacement(ReplacementUnstablePollStartEventContent),
}
impl UnstablePollStartEventContent {
/// Creates a new `PollStartEventContent` with the given poll content.
pub fn new(poll_start: UnstablePollStartContentBlock) -> Self {
Self { poll_start, text: None, relates_to: None }
/// Get the poll start content of this event content.
pub fn poll_start(&self) -> &UnstablePollStartContentBlock {
match self {
Self::New(c) => &c.poll_start,
Self::Replacement(c) => &c.relates_to.new_content.poll_start,
}
}
}
/// Creates a new `PollStartEventContent` with the given plain text fallback
/// representation and poll content.
pub fn plain_text(text: impl Into<String>, poll_start: UnstablePollStartContentBlock) -> Self {
Self { poll_start, text: Some(text.into()), relates_to: None }
impl RedactContent for UnstablePollStartEventContent {
type Redacted = RedactedUnstablePollStartEventContent;
fn redact(self, _version: &crate::RoomVersionId) -> Self::Redacted {
RedactedUnstablePollStartEventContent::default()
}
}
impl From<NewUnstablePollStartEventContent> for UnstablePollStartEventContent {
fn from(value: NewUnstablePollStartEventContent) -> Self {
Self::New(value)
}
}
impl From<ReplacementUnstablePollStartEventContent> for UnstablePollStartEventContent {
fn from(value: ReplacementUnstablePollStartEventContent) -> Self {
Self::Replacement(value)
}
}
@ -75,8 +89,10 @@ impl OriginalSyncUnstablePollStartEvent {
&'a self,
responses: impl IntoIterator<Item = PollResponseData<'a>>,
) -> UnstablePollEndEventContent {
let poll_start = self.content.poll_start();
let full_results = compile_unstable_poll_results(
&self.content.poll_start,
poll_start,
responses,
Some(MilliSecondsSinceUnixEpoch::now()),
);
@ -84,19 +100,171 @@ impl OriginalSyncUnstablePollStartEvent {
full_results.into_iter().map(|(id, users)| (id, users.len())).collect::<Vec<_>>();
// Get the text representation of the best answers.
let answers = self
.content
.poll_start
.answers
.iter()
.map(|a| (a.id.as_str(), a.text.as_str()))
.collect::<Vec<_>>();
let answers =
poll_start.answers.iter().map(|a| (a.id.as_str(), a.text.as_str())).collect::<Vec<_>>();
let plain_text = generate_poll_end_fallback_text(&answers, results.into_iter());
UnstablePollEndEventContent::new(plain_text, self.event_id.clone())
}
}
/// A new unstable poll start event.
#[derive(Clone, Debug, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct NewUnstablePollStartEventContent {
/// The poll content of the message.
#[serde(rename = "org.matrix.msc3381.poll.start")]
pub poll_start: UnstablePollStartContentBlock,
/// Text representation of the message, for clients that don't support polls.
#[serde(rename = "org.matrix.msc1767.text")]
pub text: Option<String>,
/// Information about related messages.
#[serde(rename = "m.relates_to", skip_serializing_if = "Option::is_none")]
pub relates_to: Option<RelationWithoutReplacement>,
}
impl NewUnstablePollStartEventContent {
/// Creates a `NewUnstablePollStartEventContent` with the given poll content.
pub fn new(poll_start: UnstablePollStartContentBlock) -> Self {
Self { poll_start, text: None, relates_to: None }
}
/// Creates a `NewUnstablePollStartEventContent` with the given plain text fallback
/// representation and poll content.
pub fn plain_text(text: impl Into<String>, poll_start: UnstablePollStartContentBlock) -> Self {
Self { poll_start, text: Some(text.into()), relates_to: None }
}
}
impl EventContent for NewUnstablePollStartEventContent {
type EventType = MessageLikeEventType;
fn event_type(&self) -> Self::EventType {
MessageLikeEventType::UnstablePollStart
}
}
impl StaticEventContent for NewUnstablePollStartEventContent {
const TYPE: &'static str = "org.matrix.msc3381.poll.start";
}
impl MessageLikeEventContent for NewUnstablePollStartEventContent {}
/// Form of [`NewUnstablePollStartEventContent`] without relation.
///
/// To construct this type, construct a [`NewUnstablePollStartEventContent`] and then use one of its
/// `::from()` / `.into()` methods.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct NewUnstablePollStartEventContentWithoutRelation {
/// The poll content of the message.
#[serde(rename = "org.matrix.msc3381.poll.start")]
pub poll_start: UnstablePollStartContentBlock,
/// Text representation of the message, for clients that don't support polls.
#[serde(rename = "org.matrix.msc1767.text")]
pub text: Option<String>,
}
impl From<NewUnstablePollStartEventContent> for NewUnstablePollStartEventContentWithoutRelation {
fn from(value: NewUnstablePollStartEventContent) -> Self {
let NewUnstablePollStartEventContent { poll_start, text, .. } = value;
Self { poll_start, text }
}
}
/// A replacement unstable poll start event.
#[derive(Clone, Debug)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct ReplacementUnstablePollStartEventContent {
/// The poll content of the message.
pub poll_start: Option<UnstablePollStartContentBlock>,
/// Text representation of the message, for clients that don't support polls.
pub text: Option<String>,
/// Information about related messages.
pub relates_to: Replacement<NewUnstablePollStartEventContentWithoutRelation>,
}
impl ReplacementUnstablePollStartEventContent {
/// Creates a `ReplacementUnstablePollStartEventContent` with the given poll content that
/// replaces the event with the given ID.
///
/// The constructed content does not have a fallback by default.
pub fn new(poll_start: UnstablePollStartContentBlock, replaces: OwnedEventId) -> Self {
Self {
poll_start: None,
text: None,
relates_to: Replacement {
event_id: replaces,
new_content: NewUnstablePollStartEventContent::new(poll_start).into(),
},
}
}
/// Creates a `ReplacementUnstablePollStartEventContent` with the given plain text fallback
/// representation and poll content that replaces the event with the given ID.
///
/// The constructed content does not have a fallback by default.
pub fn plain_text(
text: impl Into<String>,
poll_start: UnstablePollStartContentBlock,
replaces: OwnedEventId,
) -> Self {
Self {
poll_start: None,
text: None,
relates_to: Replacement {
event_id: replaces,
new_content: NewUnstablePollStartEventContent::plain_text(text, poll_start).into(),
},
}
}
}
impl EventContent for ReplacementUnstablePollStartEventContent {
type EventType = MessageLikeEventType;
fn event_type(&self) -> Self::EventType {
MessageLikeEventType::UnstablePollStart
}
}
impl StaticEventContent for ReplacementUnstablePollStartEventContent {
const TYPE: &'static str = "org.matrix.msc3381.poll.start";
}
impl MessageLikeEventContent for ReplacementUnstablePollStartEventContent {}
/// Redacted form of UnstablePollStartEventContent
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct RedactedUnstablePollStartEventContent {}
impl RedactedUnstablePollStartEventContent {
/// Creates an empty RedactedUnstablePollStartEventContent.
pub fn new() -> RedactedUnstablePollStartEventContent {
Self::default()
}
}
impl EventContent for RedactedUnstablePollStartEventContent {
type EventType = MessageLikeEventType;
fn event_type(&self) -> Self::EventType {
MessageLikeEventType::UnstablePollStart
}
}
impl StaticEventContent for RedactedUnstablePollStartEventContent {
const TYPE: &'static str = "org.matrix.msc3381.poll.start";
}
impl RedactedMessageLikeEventContent for RedactedUnstablePollStartEventContent {}
/// An unstable block for poll start content.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]

View File

@ -0,0 +1,82 @@
use ruma_common::{serde::from_raw_json_value, EventId};
use serde::{de, ser::SerializeStruct, Deserialize, Deserializer, Serialize};
use serde_json::value::RawValue as RawJsonValue;
use super::{
NewUnstablePollStartEventContent, NewUnstablePollStartEventContentWithoutRelation,
ReplacementUnstablePollStartEventContent, UnstablePollStartContentBlock,
UnstablePollStartEventContent,
};
use crate::room::message::{deserialize_relation, Relation};
impl<'de> Deserialize<'de> for UnstablePollStartEventContent {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let json = Box::<RawJsonValue>::deserialize(deserializer)?;
let mut deserializer = serde_json::Deserializer::from_str(json.get());
let relates_to: Option<Relation<NewUnstablePollStartEventContentWithoutRelation>> =
deserialize_relation(&mut deserializer).map_err(de::Error::custom)?;
let UnstablePollStartEventContentDeHelper { poll_start, text } =
from_raw_json_value(&json)?;
let c = match relates_to {
Some(Relation::Replacement(relates_to)) => {
ReplacementUnstablePollStartEventContent { poll_start, text, relates_to }.into()
}
rel => {
let poll_start = poll_start
.ok_or_else(|| de::Error::missing_field("org.matrix.msc3381.poll.start"))?;
let relates_to = rel
.map(|r| r.try_into().expect("Relation::Replacement has already been handled"));
NewUnstablePollStartEventContent { poll_start, text, relates_to }.into()
}
};
Ok(c)
}
}
#[derive(Debug, Deserialize)]
struct UnstablePollStartEventContentDeHelper {
#[serde(rename = "org.matrix.msc3381.poll.start")]
poll_start: Option<UnstablePollStartContentBlock>,
#[serde(rename = "org.matrix.msc1767.text")]
text: Option<String>,
}
impl Serialize for ReplacementUnstablePollStartEventContent {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let len = 2 + self.poll_start.is_some() as usize + self.text.is_some() as usize;
let mut state =
serializer.serialize_struct("ReplacementUnstablePollStartEventContent", len)?;
if let Some(poll_start) = &self.poll_start {
state.serialize_field("org.matrix.msc3381.poll.start", poll_start)?;
}
if let Some(text) = &self.text {
state.serialize_field("org.matrix.msc1767.text", text)?;
}
state.serialize_field("m.new_content", &self.relates_to.new_content)?;
state.serialize_field(
"m.relates_to",
&ReplacementRelatesTo { event_id: &self.relates_to.event_id },
)?;
state.end()
}
}
#[derive(Debug, Serialize)]
#[serde(tag = "rel_type", rename = "m.replace")]
struct ReplacementRelatesTo<'a> {
event_id: &'a EventId,
}

View File

@ -866,6 +866,79 @@ impl<C> Relation<C> {
}
}
/// Message event relationship, except a replacement.
#[derive(Clone, Debug)]
#[allow(clippy::manual_non_exhaustive)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum RelationWithoutReplacement {
/// An `m.in_reply_to` relation indicating that the event is a reply to another event.
Reply {
/// Information about another message being replied to.
in_reply_to: InReplyTo,
},
/// An event that belongs to a thread.
Thread(Thread),
#[doc(hidden)]
_Custom(CustomRelation),
}
impl RelationWithoutReplacement {
/// The type of this `Relation`.
///
/// Returns an `Option` because the `Reply` relation does not have a`rel_type` field.
pub fn rel_type(&self) -> Option<RelationType> {
match self {
Self::Reply { .. } => None,
Self::Thread(_) => Some(RelationType::Thread),
Self::_Custom(c) => Some(c.rel_type.as_str().into()),
}
}
/// The ID of the event this relates to.
///
/// This is the `event_id` field at the root of an `m.relates_to` object, except in the case of
/// a reply relation where it's the `event_id` field in the `m.in_reply_to` object.
pub fn event_id(&self) -> &EventId {
match self {
Self::Reply { in_reply_to } => &in_reply_to.event_id,
Self::Thread(t) => &t.event_id,
Self::_Custom(c) => &c.event_id,
}
}
/// The associated data.
///
/// The returned JSON object won't contain the `rel_type` field, use
/// [`.rel_type()`][Self::rel_type] to access it.
///
/// Prefer to use the public variants of `Relation` where possible; this method is meant to
/// be used for custom relations only.
pub fn data(&self) -> Cow<'_, JsonObject> {
if let Self::_Custom(c) = self {
Cow::Borrowed(&c.data)
} else {
Cow::Owned(self.serialize_data())
}
}
}
impl<C> TryFrom<Relation<C>> for RelationWithoutReplacement {
type Error = Replacement<C>;
fn try_from(value: Relation<C>) -> Result<Self, Self::Error> {
let rel = match value {
Relation::Reply { in_reply_to } => Self::Reply { in_reply_to },
Relation::Replacement(r) => return Err(r),
Relation::Thread(t) => Self::Thread(t),
Relation::_Custom(c) => Self::_Custom(c),
};
Ok(rel)
}
}
/// The format for the formatted representation of a message body.
#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
#[derive(Clone, PartialEq, Eq, StringEnum)]

View File

@ -2,7 +2,7 @@ use ruma_common::{serde::JsonObject, OwnedEventId};
use serde::{de, Deserialize, Deserializer, Serialize};
use serde_json::Value as JsonValue;
use super::{InReplyTo, Relation, Replacement, Thread};
use super::{InReplyTo, Relation, RelationWithoutReplacement, Replacement, Thread};
use crate::relation::CustomRelation;
/// Deserialize an event's `relates_to` field.
@ -77,7 +77,7 @@ where
}
#[derive(Deserialize)]
struct EventWithRelatesToDeHelper<C> {
pub(crate) struct EventWithRelatesToDeHelper<C> {
#[serde(rename = "m.relates_to")]
relates_to: Option<RelatesToDeHelper>,
@ -86,7 +86,7 @@ struct EventWithRelatesToDeHelper<C> {
}
#[derive(Deserialize)]
struct RelatesToDeHelper {
pub(crate) struct RelatesToDeHelper {
#[serde(rename = "m.in_reply_to")]
in_reply_to: Option<InReplyTo>,
@ -96,14 +96,14 @@ struct RelatesToDeHelper {
#[derive(Deserialize)]
#[serde(untagged)]
enum RelationDeHelper {
pub(crate) enum RelationDeHelper {
Known(KnownRelationDeHelper),
Unknown(CustomRelation),
}
#[derive(Deserialize)]
#[serde(tag = "rel_type")]
enum KnownRelationDeHelper {
pub(crate) enum KnownRelationDeHelper {
#[serde(rename = "m.replace")]
Replacement(ReplacementJsonRepr),
@ -116,13 +116,13 @@ enum KnownRelationDeHelper {
/// A replacement relation without `m.new_content`.
#[derive(Deserialize, Serialize)]
pub(super) struct ReplacementJsonRepr {
pub(crate) struct ReplacementJsonRepr {
event_id: OwnedEventId,
}
/// A thread relation without the reply fallback, with stable names.
#[derive(Deserialize)]
struct ThreadDeHelper {
pub(crate) struct ThreadDeHelper {
event_id: OwnedEventId,
#[serde(default)]
@ -131,7 +131,7 @@ struct ThreadDeHelper {
/// A thread relation without the reply fallback, with unstable names.
#[derive(Deserialize)]
struct ThreadUnstableDeHelper {
pub(crate) struct ThreadUnstableDeHelper {
event_id: OwnedEventId,
#[serde(rename = "io.element.show_reply", default)]
@ -222,3 +222,38 @@ impl From<CustomRelation> for CustomSerHelper {
Self { rel_type: Some(rel_type), event_id: Some(event_id), data, ..Default::default() }
}
}
impl From<&RelationWithoutReplacement> for RelationSerHelper {
fn from(value: &RelationWithoutReplacement) -> Self {
match value.clone() {
RelationWithoutReplacement::Reply { in_reply_to } => {
RelationSerHelper::Custom(in_reply_to.into())
}
RelationWithoutReplacement::Thread(t) => RelationSerHelper::Thread(t),
RelationWithoutReplacement::_Custom(c) => RelationSerHelper::Custom(c.into()),
}
}
}
impl Serialize for RelationWithoutReplacement {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
RelationSerHelper::from(self).serialize(serializer)
}
}
impl RelationWithoutReplacement {
pub(super) fn serialize_data(&self) -> JsonObject {
let helper = RelationSerHelper::from(self);
match serde_json::to_value(helper).expect("relation serialization to succeed") {
JsonValue::Object(mut obj) => {
obj.remove("rel_type");
obj
}
_ => panic!("all relations must serialize to objects"),
}
}
}

View File

@ -20,12 +20,13 @@ use ruma_events::{
OriginalSyncUnstablePollResponseEvent, UnstablePollResponseEventContent,
},
unstable_start::{
OriginalSyncUnstablePollStartEvent, UnstablePollAnswer, UnstablePollStartContentBlock,
UnstablePollStartEventContent,
NewUnstablePollStartEventContent, OriginalSyncUnstablePollStartEvent,
ReplacementUnstablePollStartEventContent, UnstablePollAnswer,
UnstablePollStartContentBlock, UnstablePollStartEventContent,
},
},
relation::Reference,
room::message::Relation,
room::message::{Relation, RelationWithoutReplacement},
AnyMessageLikeEvent, MessageLikeEvent,
};
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
@ -384,8 +385,8 @@ fn end_event_deserialization() {
}
#[test]
fn unstable_start_content_serialization() {
let event_content = UnstablePollStartEventContent::plain_text(
fn new_unstable_start_content_serialization() {
let event_content = NewUnstablePollStartEventContent::plain_text(
"How's the weather?\n1. Not bad…\n2. Fine.\n3. Amazing!",
UnstablePollStartContentBlock::new(
"How's the weather?",
@ -418,7 +419,58 @@ fn unstable_start_content_serialization() {
}
#[test]
fn unstable_start_event_deserialization() {
fn replacement_unstable_start_content_serialization() {
let replaces = owned_event_id!("$replacedevent");
let event_content = ReplacementUnstablePollStartEventContent::plain_text(
"How's the weather?\n1. Not bad…\n2. Fine.\n3. Amazing!",
UnstablePollStartContentBlock::new(
"How's the weather?",
vec![
UnstablePollAnswer::new("not-bad", "Not bad…"),
UnstablePollAnswer::new("fine", "Fine."),
UnstablePollAnswer::new("amazing", "Amazing!"),
]
.try_into()
.unwrap(),
),
replaces.clone(),
);
assert_eq!(
to_json_value(&event_content).unwrap(),
json!({
"m.new_content": {
"org.matrix.msc1767.text": "How's the weather?\n1. Not bad…\n2. Fine.\n3. Amazing!",
"org.matrix.msc3381.poll.start": {
"kind": "org.matrix.msc3381.poll.undisclosed",
"max_selections": 1,
"question": { "org.matrix.msc1767.text": "How's the weather?" },
"answers": [
{ "id": "not-bad", "org.matrix.msc1767.text": "Not bad…" },
{ "id": "fine", "org.matrix.msc1767.text": "Fine." },
{ "id": "amazing", "org.matrix.msc1767.text": "Amazing!" },
],
},
},
"m.relates_to": {
"rel_type": "m.replace",
"event_id": replaces,
},
})
);
}
#[test]
fn unstable_start_event_content_deserialization_missing_poll_start() {
let json_data = json!({
"org.matrix.msc1767.text": "How's the weather?\n1. Not bad…\n2. Fine.\n3. Amazing!",
});
from_json_value::<UnstablePollStartEventContent>(json_data).unwrap_err();
}
#[test]
fn new_unstable_start_event_deserialization() {
let json_data = json!({
"content": {
"org.matrix.msc1767.text": "How's the weather?\n1. Not bad…\n2. Fine.\n3. Amazing!",
@ -440,6 +492,45 @@ fn unstable_start_event_deserialization() {
},
]
},
"m.relates_to": {
"rel_type": "m.thread",
"event_id": "$previous_event_id",
},
},
"event_id": "$event:notareal.hs",
"origin_server_ts": 134_829_848,
"room_id": "!roomid:notareal.hs",
"sender": "@user:notareal.hs",
"type": "org.matrix.msc3381.poll.start",
});
let event = from_json_value::<AnyMessageLikeEvent>(json_data).unwrap();
assert_matches!(
event,
AnyMessageLikeEvent::UnstablePollStart(MessageLikeEvent::Original(message_event))
);
assert_matches!(message_event.content, UnstablePollStartEventContent::New(content));
assert_eq!(content.text.unwrap(), "How's the weather?\n1. Not bad…\n2. Fine.\n3. Amazing!");
let poll = content.poll_start;
assert_eq!(poll.question.text, "How's the weather?");
assert_eq!(poll.kind, PollKind::Undisclosed);
assert_eq!(poll.max_selections, uint!(2));
let answers = poll.answers;
assert_eq!(answers.len(), 3);
assert_eq!(answers[0].id, "not-bad");
assert_eq!(answers[0].text, "Not bad…");
assert_eq!(answers[1].id, "fine");
assert_eq!(answers[1].text, "Fine.");
assert_eq!(answers[2].id, "amazing");
assert_eq!(answers[2].text, "Amazing!");
assert_matches!(content.relates_to, Some(RelationWithoutReplacement::Thread(_)));
}
#[test]
fn replacement_unstable_start_event_deserialization() {
let json_data = json!({
"content": {
"m.new_content": {
"org.matrix.msc1767.text": "How's the weather?\n1. Not bad…\n2. Fine.\n3. Amazing!",
"org.matrix.msc3381.poll.start": {
@ -478,11 +569,13 @@ fn unstable_start_event_deserialization() {
event,
AnyMessageLikeEvent::UnstablePollStart(MessageLikeEvent::Original(message_event))
);
assert_eq!(
message_event.content.text.unwrap(),
"How's the weather?\n1. Not bad…\n2. Fine.\n3. Amazing!"
);
let poll = message_event.content.poll_start;
assert_matches!(message_event.content, UnstablePollStartEventContent::Replacement(content));
assert!(content.text.is_none());
assert!(content.poll_start.is_none());
let new_content = content.relates_to.new_content;
assert_eq!(new_content.text.unwrap(), "How's the weather?\n1. Not bad…\n2. Fine.\n3. Amazing!");
let poll = new_content.poll_start;
assert_eq!(poll.question.text, "How's the weather?");
assert_eq!(poll.kind, PollKind::Undisclosed);
assert_eq!(poll.max_selections, uint!(2));
@ -494,7 +587,6 @@ fn unstable_start_event_deserialization() {
assert_eq!(answers[1].text, "Fine.");
assert_eq!(answers[2].id, "amazing");
assert_eq!(answers[2].text, "Amazing!");
assert_matches!(message_event.content.relates_to, Some(Relation::Replacement(_)));
}
#[test]
@ -985,7 +1077,7 @@ fn compute_unstable_results() {
responses.extend(generate_unstable_poll_responses(8..11, &["wings"]));
let counted = compile_unstable_poll_results(
&poll.content.poll_start,
poll.content.poll_start(),
responses.iter().map(|r| r.data()),
None,
);