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