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