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