1/*
2 *  Copyright 2011 The WebRTC Project Authors. All rights reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include <string>
12
13#include "webrtc/libjingle/xmllite/qname.h"
14#include "webrtc/libjingle/xmllite/xmlelement.h"
15#include "webrtc/libjingle/xmpp/constants.h"
16#include "webrtc/libjingle/xmpp/fakexmppclient.h"
17#include "webrtc/libjingle/xmpp/jid.h"
18#include "webrtc/libjingle/xmpp/pubsubclient.h"
19#include "webrtc/base/faketaskrunner.h"
20#include "webrtc/base/gunit.h"
21#include "webrtc/base/sigslot.h"
22
23struct HandledPubSubItem {
24  std::string itemid;
25  std::string payload;
26};
27
28class TestPubSubItemsListener : public sigslot::has_slots<> {
29 public:
30  TestPubSubItemsListener() : error_count(0) {}
31
32  void OnItems(buzz::PubSubClient*,
33               const std::vector<buzz::PubSubItem>& items) {
34    for (std::vector<buzz::PubSubItem>::const_iterator item = items.begin();
35         item != items.end(); ++item) {
36      HandledPubSubItem handled_item;
37      handled_item.itemid = item->itemid;
38      if (item->elem->FirstElement() != NULL) {
39        handled_item.payload = item->elem->FirstElement()->Str();
40      }
41      this->items.push_back(handled_item);
42    }
43  }
44
45  void OnRequestError(buzz::PubSubClient* client,
46                      const buzz::XmlElement* stanza) {
47    error_count++;
48  }
49
50  void OnPublishResult(buzz::PubSubClient* client,
51                       const std::string& task_id,
52                       const buzz::XmlElement* item) {
53    result_task_id = task_id;
54  }
55
56  void OnPublishError(buzz::PubSubClient* client,
57                      const std::string& task_id,
58                      const buzz::XmlElement* item,
59                      const buzz::XmlElement* stanza) {
60    error_count++;
61    error_task_id = task_id;
62  }
63
64  void OnRetractResult(buzz::PubSubClient* client,
65                       const std::string& task_id) {
66    result_task_id = task_id;
67  }
68
69  void OnRetractError(buzz::PubSubClient* client,
70                      const std::string& task_id,
71                      const buzz::XmlElement* stanza) {
72    error_count++;
73    error_task_id = task_id;
74  }
75
76  std::vector<HandledPubSubItem> items;
77  int error_count;
78  std::string error_task_id;
79  std::string result_task_id;
80};
81
82class PubSubClientTest : public testing::Test {
83 public:
84  PubSubClientTest() :
85      pubsubjid("room@domain.com"),
86      node("topic"),
87      itemid("key") {
88    runner.reset(new rtc::FakeTaskRunner());
89    xmpp_client = new buzz::FakeXmppClient(runner.get());
90    client.reset(new buzz::PubSubClient(xmpp_client, pubsubjid, node));
91    listener.reset(new TestPubSubItemsListener());
92    client->SignalItems.connect(
93        listener.get(), &TestPubSubItemsListener::OnItems);
94    client->SignalRequestError.connect(
95        listener.get(), &TestPubSubItemsListener::OnRequestError);
96    client->SignalPublishResult.connect(
97        listener.get(), &TestPubSubItemsListener::OnPublishResult);
98    client->SignalPublishError.connect(
99        listener.get(), &TestPubSubItemsListener::OnPublishError);
100    client->SignalRetractResult.connect(
101        listener.get(), &TestPubSubItemsListener::OnRetractResult);
102    client->SignalRetractError.connect(
103        listener.get(), &TestPubSubItemsListener::OnRetractError);
104  }
105
106  rtc::scoped_ptr<rtc::FakeTaskRunner> runner;
107  // xmpp_client deleted by deleting runner.
108  buzz::FakeXmppClient* xmpp_client;
109  rtc::scoped_ptr<buzz::PubSubClient> client;
110  rtc::scoped_ptr<TestPubSubItemsListener> listener;
111  buzz::Jid pubsubjid;
112  std::string node;
113  std::string itemid;
114};
115
116TEST_F(PubSubClientTest, TestRequest) {
117  client->RequestItems();
118
119  std::string expected_iq =
120      "<cli:iq type=\"get\" to=\"room@domain.com\" id=\"0\" "
121        "xmlns:cli=\"jabber:client\">"
122        "<pub:pubsub xmlns:pub=\"http://jabber.org/protocol/pubsub\">"
123          "<pub:items node=\"topic\"/>"
124        "</pub:pubsub>"
125      "</cli:iq>";
126
127  ASSERT_EQ(1U, xmpp_client->sent_stanzas().size());
128  EXPECT_EQ(expected_iq, xmpp_client->sent_stanzas()[0]->Str());
129
130  std::string result_iq =
131      "<iq xmlns='jabber:client' id='0' type='result' from='room@domain.com'>"
132      "  <pubsub xmlns='http://jabber.org/protocol/pubsub'>"
133      "    <items node='topic'>"
134      "      <item id='key0'>"
135      "        <value0a/>"
136      "      </item>"
137      "      <item id='key1'>"
138      "        <value1a/>"
139      "      </item>"
140      "    </items>"
141      "  </pubsub>"
142      "</iq>";
143
144  xmpp_client->HandleStanza(buzz::XmlElement::ForStr(result_iq));
145  ASSERT_EQ(2U, listener->items.size());
146  EXPECT_EQ("key0", listener->items[0].itemid);
147  EXPECT_EQ("<pub:value0a xmlns:pub=\"http://jabber.org/protocol/pubsub\"/>",
148            listener->items[0].payload);
149  EXPECT_EQ("key1", listener->items[1].itemid);
150  EXPECT_EQ("<pub:value1a xmlns:pub=\"http://jabber.org/protocol/pubsub\"/>",
151            listener->items[1].payload);
152
153  std::string items_message =
154      "<message xmlns='jabber:client' from='room@domain.com'>"
155      "  <event xmlns='http://jabber.org/protocol/pubsub#event'>"
156      "    <items node='topic'>"
157      "      <item id='key0'>"
158      "        <value0b/>"
159      "      </item>"
160      "      <item id='key1'>"
161      "        <value1b/>"
162      "      </item>"
163      "    </items>"
164      "  </event>"
165      "</message>";
166
167  xmpp_client->HandleStanza(buzz::XmlElement::ForStr(items_message));
168  ASSERT_EQ(4U, listener->items.size());
169  EXPECT_EQ("key0", listener->items[2].itemid);
170  EXPECT_EQ("<eve:value0b"
171            " xmlns:eve=\"http://jabber.org/protocol/pubsub#event\"/>",
172            listener->items[2].payload);
173  EXPECT_EQ("key1", listener->items[3].itemid);
174  EXPECT_EQ("<eve:value1b"
175            " xmlns:eve=\"http://jabber.org/protocol/pubsub#event\"/>",
176            listener->items[3].payload);
177}
178
179TEST_F(PubSubClientTest, TestRequestError) {
180  std::string result_iq =
181      "<iq xmlns='jabber:client' id='0' type='error' from='room@domain.com'>"
182      "  <error type='auth'>"
183      "    <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>"
184      "  </error>"
185      "</iq>";
186
187  client->RequestItems();
188  xmpp_client->HandleStanza(buzz::XmlElement::ForStr(result_iq));
189  EXPECT_EQ(1, listener->error_count);
190}
191
192TEST_F(PubSubClientTest, TestPublish) {
193  buzz::XmlElement* payload =
194      new buzz::XmlElement(buzz::QName(buzz::NS_PUBSUB, "value"));
195
196  std::string task_id;
197  client->PublishItem(itemid, payload, &task_id);
198
199  std::string expected_iq =
200      "<cli:iq type=\"set\" to=\"room@domain.com\" id=\"0\" "
201        "xmlns:cli=\"jabber:client\">"
202        "<pubsub xmlns=\"http://jabber.org/protocol/pubsub\">"
203          "<publish node=\"topic\">"
204            "<item id=\"key\">"
205              "<value/>"
206            "</item>"
207          "</publish>"
208        "</pubsub>"
209      "</cli:iq>";
210
211  ASSERT_EQ(1U, xmpp_client->sent_stanzas().size());
212  EXPECT_EQ(expected_iq, xmpp_client->sent_stanzas()[0]->Str());
213
214  std::string result_iq =
215      "<iq xmlns='jabber:client' id='0' type='result' from='room@domain.com'/>";
216
217  xmpp_client->HandleStanza(buzz::XmlElement::ForStr(result_iq));
218  EXPECT_EQ(task_id, listener->result_task_id);
219}
220
221TEST_F(PubSubClientTest, TestPublishError) {
222  buzz::XmlElement* payload =
223      new buzz::XmlElement(buzz::QName(buzz::NS_PUBSUB, "value"));
224
225  std::string task_id;
226  client->PublishItem(itemid, payload, &task_id);
227
228  std::string result_iq =
229      "<iq xmlns='jabber:client' id='0' type='error' from='room@domain.com'>"
230      "  <error type='auth'>"
231      "    <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>"
232      "  </error>"
233      "</iq>";
234
235  xmpp_client->HandleStanza(buzz::XmlElement::ForStr(result_iq));
236  EXPECT_EQ(1, listener->error_count);
237  EXPECT_EQ(task_id, listener->error_task_id);
238}
239
240TEST_F(PubSubClientTest, TestRetract) {
241  std::string task_id;
242  client->RetractItem(itemid, &task_id);
243
244  std::string expected_iq =
245      "<cli:iq type=\"set\" to=\"room@domain.com\" id=\"0\" "
246        "xmlns:cli=\"jabber:client\">"
247        "<pubsub xmlns=\"http://jabber.org/protocol/pubsub\">"
248          "<retract node=\"topic\" notify=\"true\">"
249            "<item id=\"key\"/>"
250          "</retract>"
251        "</pubsub>"
252      "</cli:iq>";
253
254  ASSERT_EQ(1U, xmpp_client->sent_stanzas().size());
255  EXPECT_EQ(expected_iq, xmpp_client->sent_stanzas()[0]->Str());
256
257  std::string result_iq =
258      "<iq xmlns='jabber:client' id='0' type='result' from='room@domain.com'/>";
259
260  xmpp_client->HandleStanza(buzz::XmlElement::ForStr(result_iq));
261  EXPECT_EQ(task_id, listener->result_task_id);
262}
263
264TEST_F(PubSubClientTest, TestRetractError) {
265  std::string task_id;
266  client->RetractItem(itemid, &task_id);
267
268  std::string result_iq =
269      "<iq xmlns='jabber:client' id='0' type='error' from='room@domain.com'>"
270      "  <error type='auth'>"
271      "    <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>"
272      "  </error>"
273      "</iq>";
274
275  xmpp_client->HandleStanza(buzz::XmlElement::ForStr(result_iq));
276  EXPECT_EQ(1, listener->error_count);
277  EXPECT_EQ(task_id, listener->error_task_id);
278}
279