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