1 // Copyright 2015 The Weave Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/notification/xmpp_iq_stanza_handler.h"
6 
7 #include <map>
8 #include <memory>
9 
10 #include <gmock/gmock.h>
11 #include <gtest/gtest.h>
12 #include <weave/provider/test/fake_task_runner.h>
13 
14 #include "src/bind_lambda.h"
15 #include "src/notification/xml_node.h"
16 #include "src/notification/xmpp_channel.h"
17 #include "src/notification/xmpp_stream_parser.h"
18 
19 using testing::_;
20 
21 namespace weave {
22 namespace {
23 
24 class MockXmppChannelInterface : public XmppChannelInterface {
25  public:
26   MockXmppChannelInterface() = default;
27 
28   MOCK_METHOD1(SendMessage, void(const std::string&));
29 
30  private:
31   DISALLOW_COPY_AND_ASSIGN(MockXmppChannelInterface);
32 };
33 
34 // Simple class that allows to parse XML from string to XmlNode.
35 class XmlParser : public XmppStreamParser::Delegate {
36  public:
Parse(const std::string & xml)37   std::unique_ptr<XmlNode> Parse(const std::string& xml) {
38     parser_.ParseData(xml);
39     return std::move(node_);
40   }
41 
42  private:
43   // Overrides from XmppStreamParser::Delegate.
OnStreamStart(const std::string & node_name,std::map<std::string,std::string> attributes)44   void OnStreamStart(const std::string& node_name,
45                      std::map<std::string, std::string> attributes) override {
46     node_.reset(new XmlNode{node_name, std::move(attributes)});
47   }
48 
OnStreamEnd(const std::string & node_name)49   void OnStreamEnd(const std::string& node_name) override {}
50 
OnStanza(std::unique_ptr<XmlNode> stanza)51   void OnStanza(std::unique_ptr<XmlNode> stanza) override {
52     node_->AddChild(std::move(stanza));
53   }
54 
55   std::unique_ptr<XmlNode> node_;
56   XmppStreamParser parser_{this};
57 };
58 
59 class MockResponseReceiver {
60  public:
61   MOCK_METHOD2(OnResponse, void(int id, const std::string&));
62 
callback(int id)63   IqStanzaHandler::ResponseCallback callback(int id) {
64     return base::Bind(&MockResponseReceiver::OnResponseCallback,
65                       base::Unretained(this), id);
66   }
67 
68  private:
OnResponseCallback(int id,std::unique_ptr<XmlNode> response)69   void OnResponseCallback(int id, std::unique_ptr<XmlNode> response) {
70     OnResponse(id, response->children().front()->name());
71   }
72 };
73 
74 }  // anonymous namespace
75 
76 class IqStanzaHandlerTest : public testing::Test {
77  public:
78   testing::StrictMock<MockXmppChannelInterface> mock_xmpp_channel_;
79   provider::test::FakeTaskRunner task_runner_;
80   IqStanzaHandler iq_stanza_handler_{&mock_xmpp_channel_, &task_runner_};
81   MockResponseReceiver receiver_;
82 };
83 
TEST_F(IqStanzaHandlerTest,SendRequest)84 TEST_F(IqStanzaHandlerTest, SendRequest) {
85   std::string expected_msg = "<iq id='1' type='set'><body/></iq>";
86   EXPECT_CALL(mock_xmpp_channel_, SendMessage(expected_msg)).Times(1);
87   iq_stanza_handler_.SendRequest("set", "", "", "<body/>", {}, {});
88 
89   expected_msg = "<iq id='2' type='get'><body/></iq>";
90   EXPECT_CALL(mock_xmpp_channel_, SendMessage(expected_msg)).Times(1);
91   iq_stanza_handler_.SendRequest("get", "", "", "<body/>", {}, {});
92 
93   expected_msg = "<iq id='3' type='query' from='foo@bar'><body/></iq>";
94   EXPECT_CALL(mock_xmpp_channel_, SendMessage(expected_msg)).Times(1);
95   iq_stanza_handler_.SendRequest("query", "foo@bar", "", "<body/>", {}, {});
96 
97   expected_msg = "<iq id='4' type='query' to='foo@bar'><body/></iq>";
98   EXPECT_CALL(mock_xmpp_channel_, SendMessage(expected_msg)).Times(1);
99   iq_stanza_handler_.SendRequest("query", "", "foo@bar", "<body/>", {}, {});
100 
101   expected_msg = "<iq id='5' type='query' from='foo@bar' to='baz'><body/></iq>";
102   EXPECT_CALL(mock_xmpp_channel_, SendMessage(expected_msg)).Times(1);
103   iq_stanza_handler_.SendRequest("query", "foo@bar", "baz", "<body/>", {}, {});
104   // This test ignores all the posted callbacks.
105 }
106 
TEST_F(IqStanzaHandlerTest,UnsupportedIqRequest)107 TEST_F(IqStanzaHandlerTest, UnsupportedIqRequest) {
108   // Server IQ requests are not supported for now. Expect an error response.
109   std::string expected_msg =
110       "<iq id='1' type='error'><error type='modify'>"
111       "<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>"
112       "</error></iq>";
113   EXPECT_CALL(mock_xmpp_channel_, SendMessage(expected_msg)).Times(1);
114   auto request = XmlParser{}.Parse("<iq id='1' type='set'><foo/></iq>");
115   EXPECT_TRUE(iq_stanza_handler_.HandleIqStanza(std::move(request)));
116 }
117 
TEST_F(IqStanzaHandlerTest,UnknownResponseId)118 TEST_F(IqStanzaHandlerTest, UnknownResponseId) {
119   // No requests with ID=100 have been previously sent.
120   auto request = XmlParser{}.Parse("<iq id='100' type='result'><foo/></iq>");
121   EXPECT_TRUE(iq_stanza_handler_.HandleIqStanza(std::move(request)));
122 }
123 
TEST_F(IqStanzaHandlerTest,SequentialResponses)124 TEST_F(IqStanzaHandlerTest, SequentialResponses) {
125   EXPECT_CALL(mock_xmpp_channel_, SendMessage(_)).Times(2);
126   iq_stanza_handler_.SendRequest("set", "", "", "<body/>",
127                                  receiver_.callback(1), {});
128   iq_stanza_handler_.SendRequest("get", "", "", "<body/>",
129                                  receiver_.callback(2), {});
130 
131   EXPECT_CALL(receiver_, OnResponse(1, "foo"));
132   auto request = XmlParser{}.Parse("<iq id='1' type='result'><foo/></iq>");
133   EXPECT_TRUE(iq_stanza_handler_.HandleIqStanza(std::move(request)));
134 
135   EXPECT_CALL(receiver_, OnResponse(2, "bar"));
136   request = XmlParser{}.Parse("<iq id='2' type='result'><bar/></iq>");
137   EXPECT_TRUE(iq_stanza_handler_.HandleIqStanza(std::move(request)));
138 
139   task_runner_.Run();
140 }
141 
TEST_F(IqStanzaHandlerTest,OutOfOrderResponses)142 TEST_F(IqStanzaHandlerTest, OutOfOrderResponses) {
143   EXPECT_CALL(mock_xmpp_channel_, SendMessage(_)).Times(2);
144   iq_stanza_handler_.SendRequest("set", "", "", "<body/>",
145                                  receiver_.callback(1), {});
146   iq_stanza_handler_.SendRequest("get", "", "", "<body/>",
147                                  receiver_.callback(2), {});
148 
149   EXPECT_CALL(receiver_, OnResponse(2, "bar"));
150   auto request = XmlParser{}.Parse("<iq id='2' type='result'><bar/></iq>");
151   EXPECT_TRUE(iq_stanza_handler_.HandleIqStanza(std::move(request)));
152 
153   EXPECT_CALL(receiver_, OnResponse(1, "foo"));
154   request = XmlParser{}.Parse("<iq id='1' type='result'><foo/></iq>");
155   EXPECT_TRUE(iq_stanza_handler_.HandleIqStanza(std::move(request)));
156 
157   task_runner_.Run();
158 }
159 
TEST_F(IqStanzaHandlerTest,RequestTimeout)160 TEST_F(IqStanzaHandlerTest, RequestTimeout) {
161   bool called = false;
162   auto on_timeout = [&called]() { called = true; };
163 
164   EXPECT_CALL(mock_xmpp_channel_, SendMessage(_)).Times(1);
165   EXPECT_FALSE(called);
166   iq_stanza_handler_.SendRequest("set", "", "", "<body/>", {},
167                                  base::Bind(on_timeout));
168   task_runner_.Run();
169   EXPECT_TRUE(called);
170 }
171 
172 }  // namespace weave
173