roster.rs

  1// Copyright (c) 2017 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
  2//
  3// This Source Code Form is subject to the terms of the Mozilla Public
  4// License, v. 2.0. If a copy of the MPL was not distributed with this
  5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6
  7use jid::Jid;
  8use iq::{IqGetPayload, IqSetPayload, IqResultPayload};
  9
 10generate_elem_id!(Group, "group", ROSTER);
 11
 12generate_attribute!(Subscription, "subscription", {
 13    None => "none",
 14    From => "from",
 15    To => "to",
 16    Both => "both",
 17    Remove => "remove",
 18}, Default = None);
 19
 20generate_element_with_children!(
 21    /// Contact from the user’s contact list.
 22    #[derive(PartialEq)]
 23    Item, "item", ROSTER,
 24    attributes: [
 25        /// JID of this contact.
 26        jid: Jid = "jid" => required,
 27
 28        /// Name of this contact.
 29        name: Option<String> = "name" => optional_empty,
 30
 31        /// Subscription status of this contact.
 32        subscription: Subscription = "subscription" => default
 33    ],
 34
 35    children: [
 36        /// Groups this contact is part of.
 37        groups: Vec<Group> = ("group", ROSTER) => Group
 38    ]
 39);
 40
 41generate_element_with_children!(
 42    /// The contact list of the user.
 43    Roster, "query", ROSTER,
 44    attributes: [
 45        /// Version of the contact list.
 46        ///
 47        /// This is an opaque string that should only be sent back to the server on
 48        /// a new connection, if this client is storing the contact list between
 49        /// connections.
 50        ver: Option<String> = "ver" => optional
 51    ],
 52    children: [
 53        /// List of the contacts of the user.
 54        items: Vec<Item> = ("item", ROSTER) => Item
 55    ]
 56);
 57
 58impl IqGetPayload for Roster {}
 59impl IqSetPayload for Roster {}
 60impl IqResultPayload for Roster {}
 61
 62#[cfg(test)]
 63mod tests {
 64    use super::*;
 65    use try_from::TryFrom;
 66    use minidom::Element;
 67    use error::Error;
 68    use std::str::FromStr;
 69    use compare_elements::NamespaceAwareCompare;
 70
 71    #[test]
 72    fn test_get() {
 73        let elem: Element = "<query xmlns='jabber:iq:roster'/>".parse().unwrap();
 74        let roster = Roster::try_from(elem).unwrap();
 75        assert!(roster.ver.is_none());
 76        assert!(roster.items.is_empty());
 77    }
 78
 79    #[test]
 80    fn test_result() {
 81        let elem: Element = "<query xmlns='jabber:iq:roster' ver='ver7'><item jid='nurse@example.com'/><item jid='romeo@example.net'/></query>".parse().unwrap();
 82        let roster = Roster::try_from(elem).unwrap();
 83        assert_eq!(roster.ver, Some(String::from("ver7")));
 84        assert_eq!(roster.items.len(), 2);
 85
 86        let elem2: Element = "<query xmlns='jabber:iq:roster' ver='ver7'><item jid='nurse@example.com'/><item jid='romeo@example.net' name=''/></query>".parse().unwrap();
 87        let roster2 = Roster::try_from(elem2).unwrap();
 88        assert_eq!(roster.items, roster2.items);
 89
 90        let elem: Element = "<query xmlns='jabber:iq:roster' ver='ver9'/>".parse().unwrap();
 91        let roster = Roster::try_from(elem).unwrap();
 92        assert_eq!(roster.ver, Some(String::from("ver9")));
 93        assert!(roster.items.is_empty());
 94
 95        let elem: Element = r#"
 96<query xmlns='jabber:iq:roster' ver='ver11'>
 97  <item jid='romeo@example.net'
 98        name='Romeo'
 99        subscription='both'>
100    <group>Friends</group>
101  </item>
102  <item jid='mercutio@example.com'
103        name='Mercutio'
104        subscription='from'/>
105  <item jid='benvolio@example.net'
106        name='Benvolio'
107        subscription='both'/>
108</query>
109"#.parse().unwrap();
110        let roster = Roster::try_from(elem).unwrap();
111        assert_eq!(roster.ver, Some(String::from("ver11")));
112        assert_eq!(roster.items.len(), 3);
113        assert_eq!(roster.items[0].jid, Jid::from_str("romeo@example.net").unwrap());
114        assert_eq!(roster.items[0].name, Some(String::from("Romeo")));
115        assert_eq!(roster.items[0].subscription, Subscription::Both);
116        assert_eq!(roster.items[0].groups, vec!(Group::from_str("Friends").unwrap()));
117    }
118
119    #[test]
120    fn test_multiple_groups() {
121        let elem: Element = r#"
122<query xmlns='jabber:iq:roster'>
123  <item jid='test@example.org'>
124    <group>A</group>
125    <group>B</group>
126  </item>
127</query>
128"#.parse().unwrap();
129        let elem1 = elem.clone();
130        let roster = Roster::try_from(elem).unwrap();
131        assert!(roster.ver.is_none());
132        assert_eq!(roster.items.len(), 1);
133        assert_eq!(roster.items[0].jid, Jid::from_str("test@example.org").unwrap());
134        assert_eq!(roster.items[0].name, None);
135        assert_eq!(roster.items[0].groups.len(), 2);
136        assert_eq!(roster.items[0].groups[0], Group::from_str("A").unwrap());
137        assert_eq!(roster.items[0].groups[1], Group::from_str("B").unwrap());
138        let elem2 = roster.into();
139        assert!(elem1.compare_to(&elem2));
140    }
141
142    #[test]
143    fn test_set() {
144        let elem: Element = "<query xmlns='jabber:iq:roster'><item jid='nurse@example.com'/></query>".parse().unwrap();
145        let roster = Roster::try_from(elem).unwrap();
146        assert!(roster.ver.is_none());
147        assert_eq!(roster.items.len(), 1);
148
149        let elem: Element = r#"
150<query xmlns='jabber:iq:roster'>
151  <item jid='nurse@example.com'
152        name='Nurse'>
153    <group>Servants</group>
154  </item>
155</query>
156"#.parse().unwrap();
157        let roster = Roster::try_from(elem).unwrap();
158        assert!(roster.ver.is_none());
159        assert_eq!(roster.items.len(), 1);
160        assert_eq!(roster.items[0].jid, Jid::from_str("nurse@example.com").unwrap());
161        assert_eq!(roster.items[0].name, Some(String::from("Nurse")));
162        assert_eq!(roster.items[0].groups.len(), 1);
163        assert_eq!(roster.items[0].groups[0], Group::from_str("Servants").unwrap());
164
165        let elem: Element = r#"
166<query xmlns='jabber:iq:roster'>
167  <item jid='nurse@example.com'
168        subscription='remove'/>
169</query>
170"#.parse().unwrap();
171        let roster = Roster::try_from(elem).unwrap();
172        assert!(roster.ver.is_none());
173        assert_eq!(roster.items.len(), 1);
174        assert_eq!(roster.items[0].jid, Jid::from_str("nurse@example.com").unwrap());
175        assert!(roster.items[0].name.is_none());
176        assert!(roster.items[0].groups.is_empty());
177        assert_eq!(roster.items[0].subscription, Subscription::Remove);
178    }
179
180    #[test]
181    fn test_invalid() {
182        let elem: Element = "<query xmlns='jabber:iq:roster'><coucou/></query>".parse().unwrap();
183        let error = Roster::try_from(elem).unwrap_err();
184        let message = match error {
185            Error::ParseError(string) => string,
186            _ => panic!(),
187        };
188        assert_eq!(message, "Unknown child in query element.");
189
190        let elem: Element = "<query xmlns='jabber:iq:roster' coucou=''/>".parse().unwrap();
191        let error = Roster::try_from(elem).unwrap_err();
192        let message = match error {
193            Error::ParseError(string) => string,
194            _ => panic!(),
195        };
196        assert_eq!(message, "Unknown attribute in query element.");
197    }
198
199    #[test]
200    fn test_invalid_item() {
201        let elem: Element = "<query xmlns='jabber:iq:roster'><item/></query>".parse().unwrap();
202        let error = Roster::try_from(elem).unwrap_err();
203        let message = match error {
204            Error::ParseError(string) => string,
205            _ => panic!(),
206        };
207        assert_eq!(message, "Required attribute 'jid' missing.");
208
209        /*
210        let elem: Element = "<query xmlns='jabber:iq:roster'><item jid=''/></query>".parse().unwrap();
211        let error = Roster::try_from(elem).unwrap_err();
212        let error = match error {
213            Error::JidParseError(error) => error,
214            _ => panic!(),
215        };
216        assert_eq!(error.description(), "Invalid JID, I guess?");
217        */
218
219        let elem: Element = "<query xmlns='jabber:iq:roster'><item jid='coucou'><coucou/></item></query>".parse().unwrap();
220        let error = Roster::try_from(elem).unwrap_err();
221        let message = match error {
222            Error::ParseError(string) => string,
223            _ => panic!(),
224        };
225        assert_eq!(message, "Unknown child in item element.");
226    }
227}