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