1 // Copyright (c) 2012 The Chromium 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 "jingle/notifier/communicator/single_login_attempt.h"
6
7 #include <cstddef>
8 #include <memory>
9
10 #include "base/compiler_specific.h"
11 #include "base/run_loop.h"
12 #include "base/test/task_environment.h"
13 #include "base/threading/thread_task_runner_handle.h"
14 #include "jingle/glue/network_service_config_test_util.h"
15 #include "jingle/notifier/base/const_communicator.h"
16 #include "jingle/notifier/base/fake_base_task.h"
17 #include "jingle/notifier/communicator/login_settings.h"
18 #include "net/dns/mock_host_resolver.h"
19 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
20 #include "net/url_request/url_request_test_util.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 #include "third_party/libjingle_xmpp/xmllite/xmlelement.h"
23 #include "third_party/libjingle_xmpp/xmpp/constants.h"
24 #include "third_party/libjingle_xmpp/xmpp/xmppengine.h"
25
26 namespace jingle_xmpp {
27 class XmppTaskParentInterface;
28 } // namespace jingle_xmpp
29
30 namespace notifier {
31
32 namespace {
33
34 enum DelegateState {
35 IDLE, CONNECTED, REDIRECTED, CREDENTIALS_REJECTED, SETTINGS_EXHAUSTED
36 };
37
38 class FakeDelegate : public SingleLoginAttempt::Delegate {
39 public:
FakeDelegate()40 FakeDelegate() : state_(IDLE) {}
41
OnConnect(base::WeakPtr<jingle_xmpp::XmppTaskParentInterface> base_task)42 void OnConnect(
43 base::WeakPtr<jingle_xmpp::XmppTaskParentInterface> base_task) override {
44 state_ = CONNECTED;
45 base_task_ = base_task;
46 }
47
OnRedirect(const ServerInformation & redirect_server)48 void OnRedirect(const ServerInformation& redirect_server) override {
49 state_ = REDIRECTED;
50 redirect_server_ = redirect_server;
51 }
52
OnCredentialsRejected()53 void OnCredentialsRejected() override { state_ = CREDENTIALS_REJECTED; }
54
OnSettingsExhausted()55 void OnSettingsExhausted() override { state_ = SETTINGS_EXHAUSTED; }
56
state() const57 DelegateState state() const { return state_; }
58
base_task() const59 base::WeakPtr<jingle_xmpp::XmppTaskParentInterface> base_task() const {
60 return base_task_;
61 }
62
redirect_server() const63 const ServerInformation& redirect_server() const {
64 return redirect_server_;
65 }
66
67 private:
68 DelegateState state_;
69 base::WeakPtr<jingle_xmpp::XmppTaskParentInterface> base_task_;
70 ServerInformation redirect_server_;
71 };
72
73 class MyTestURLRequestContext : public net::TestURLRequestContext {
74 public:
MyTestURLRequestContext()75 MyTestURLRequestContext() : TestURLRequestContext(true) {
76 context_storage_.set_host_resolver(
77 std::unique_ptr<net::HostResolver>(new net::HangingHostResolver()));
78 Init();
79 }
~MyTestURLRequestContext()80 ~MyTestURLRequestContext() override {}
81 };
82
83 class SingleLoginAttemptTest : public ::testing::Test {
84 protected:
SingleLoginAttemptTest()85 SingleLoginAttemptTest()
86 : net_config_helper_(
87 base::MakeRefCounted<net::TestURLRequestContextGetter>(
88 base::ThreadTaskRunnerHandle::Get(),
89 std::unique_ptr<net::TestURLRequestContext>(
90 new MyTestURLRequestContext()))),
91 login_settings_(
92 jingle_xmpp::XmppClientSettings(),
93 net_config_helper_.MakeSocketFactoryCallback(),
94 ServerList(1,
95 ServerInformation(net::HostPortPair("example.com", 100),
96 SUPPORTS_SSLTCP)),
97 false /* try_ssltcp_first */,
98 "auth_mechanism",
99 TRAFFIC_ANNOTATION_FOR_TESTS),
100 attempt_(new SingleLoginAttempt(login_settings_, &fake_delegate_)) {}
101
TearDown()102 void TearDown() override { base::RunLoop().RunUntilIdle(); }
103
FireRedirect(jingle_xmpp::XmlElement * redirect_error)104 void FireRedirect(jingle_xmpp::XmlElement* redirect_error) {
105 attempt_->OnError(jingle_xmpp::XmppEngine::ERROR_STREAM, 0, redirect_error);
106 }
107
~SingleLoginAttemptTest()108 ~SingleLoginAttemptTest() override {
109 attempt_.reset();
110 base::RunLoop().RunUntilIdle();
111 }
112
113 private:
114 base::test::SingleThreadTaskEnvironment task_environment_;
115 jingle_glue::NetworkServiceConfigTestUtil net_config_helper_;
116 const LoginSettings login_settings_;
117
118 protected:
119 std::unique_ptr<SingleLoginAttempt> attempt_;
120 FakeDelegate fake_delegate_;
121 FakeBaseTask fake_base_task_;
122 };
123
124 // Fire OnConnect and make sure the base task gets passed to the
125 // delegate properly.
TEST_F(SingleLoginAttemptTest,Basic)126 TEST_F(SingleLoginAttemptTest, Basic) {
127 attempt_->OnConnect(fake_base_task_.AsWeakPtr());
128 EXPECT_EQ(CONNECTED, fake_delegate_.state());
129 EXPECT_EQ(fake_base_task_.AsWeakPtr().get(),
130 fake_delegate_.base_task().get());
131 }
132
133 // Fire OnErrors and make sure the delegate gets the
134 // OnSettingsExhausted() event.
TEST_F(SingleLoginAttemptTest,Error)135 TEST_F(SingleLoginAttemptTest, Error) {
136 for (int i = 0; i < 2; ++i) {
137 EXPECT_EQ(IDLE, fake_delegate_.state());
138 attempt_->OnError(jingle_xmpp::XmppEngine::ERROR_NONE, 0, NULL);
139 }
140 EXPECT_EQ(SETTINGS_EXHAUSTED, fake_delegate_.state());
141 }
142
143 // Fire OnErrors but replace the last one with OnConnect, and make
144 // sure the delegate still gets the OnConnect message.
TEST_F(SingleLoginAttemptTest,ErrorThenSuccess)145 TEST_F(SingleLoginAttemptTest, ErrorThenSuccess) {
146 attempt_->OnError(jingle_xmpp::XmppEngine::ERROR_NONE, 0, NULL);
147 attempt_->OnConnect(fake_base_task_.AsWeakPtr());
148 EXPECT_EQ(CONNECTED, fake_delegate_.state());
149 EXPECT_EQ(fake_base_task_.AsWeakPtr().get(),
150 fake_delegate_.base_task().get());
151 }
152
MakeRedirectError(const std::string & redirect_server)153 jingle_xmpp::XmlElement* MakeRedirectError(const std::string& redirect_server) {
154 jingle_xmpp::XmlElement* stream_error =
155 new jingle_xmpp::XmlElement(jingle_xmpp::QN_STREAM_ERROR, true);
156 stream_error->AddElement(
157 new jingle_xmpp::XmlElement(jingle_xmpp::QN_XSTREAM_SEE_OTHER_HOST, true));
158 jingle_xmpp::XmlElement* text =
159 new jingle_xmpp::XmlElement(jingle_xmpp::QN_XSTREAM_TEXT, true);
160 stream_error->AddElement(text);
161 text->SetBodyText(redirect_server);
162 return stream_error;
163 }
164
165 // Fire a redirect and make sure the delegate gets the proper redirect
166 // server info.
TEST_F(SingleLoginAttemptTest,Redirect)167 TEST_F(SingleLoginAttemptTest, Redirect) {
168 const ServerInformation redirect_server(
169 net::HostPortPair("example.com", 1000),
170 SUPPORTS_SSLTCP);
171
172 std::unique_ptr<jingle_xmpp::XmlElement> redirect_error(
173 MakeRedirectError(redirect_server.server.ToString()));
174 FireRedirect(redirect_error.get());
175
176 EXPECT_EQ(REDIRECTED, fake_delegate_.state());
177 EXPECT_TRUE(fake_delegate_.redirect_server().Equals(redirect_server));
178 }
179
180 // Fire a redirect with the host only and make sure the delegate gets
181 // the proper redirect server info with the default XMPP port.
TEST_F(SingleLoginAttemptTest,RedirectHostOnly)182 TEST_F(SingleLoginAttemptTest, RedirectHostOnly) {
183 const ServerInformation redirect_server(
184 net::HostPortPair("example.com", kDefaultXmppPort),
185 SUPPORTS_SSLTCP);
186
187 std::unique_ptr<jingle_xmpp::XmlElement> redirect_error(
188 MakeRedirectError(redirect_server.server.host()));
189 FireRedirect(redirect_error.get());
190
191 EXPECT_EQ(REDIRECTED, fake_delegate_.state());
192 EXPECT_TRUE(fake_delegate_.redirect_server().Equals(redirect_server));
193 }
194
195 // Fire a redirect with a zero port and make sure the delegate gets
196 // the proper redirect server info with the default XMPP port.
TEST_F(SingleLoginAttemptTest,RedirectZeroPort)197 TEST_F(SingleLoginAttemptTest, RedirectZeroPort) {
198 const ServerInformation redirect_server(
199 net::HostPortPair("example.com", kDefaultXmppPort),
200 SUPPORTS_SSLTCP);
201
202 std::unique_ptr<jingle_xmpp::XmlElement> redirect_error(
203 MakeRedirectError(redirect_server.server.host() + ":0"));
204 FireRedirect(redirect_error.get());
205
206 EXPECT_EQ(REDIRECTED, fake_delegate_.state());
207 EXPECT_TRUE(fake_delegate_.redirect_server().Equals(redirect_server));
208 }
209
210 // Fire a redirect with an invalid port and make sure the delegate
211 // gets the proper redirect server info with the default XMPP port.
TEST_F(SingleLoginAttemptTest,RedirectInvalidPort)212 TEST_F(SingleLoginAttemptTest, RedirectInvalidPort) {
213 const ServerInformation redirect_server(
214 net::HostPortPair("example.com", kDefaultXmppPort),
215 SUPPORTS_SSLTCP);
216
217 std::unique_ptr<jingle_xmpp::XmlElement> redirect_error(
218 MakeRedirectError(redirect_server.server.host() + ":invalidport"));
219 FireRedirect(redirect_error.get());
220
221 EXPECT_EQ(REDIRECTED, fake_delegate_.state());
222 EXPECT_TRUE(fake_delegate_.redirect_server().Equals(redirect_server));
223 }
224
225 // Fire an empty redirect and make sure the delegate does not get a
226 // redirect.
TEST_F(SingleLoginAttemptTest,RedirectEmpty)227 TEST_F(SingleLoginAttemptTest, RedirectEmpty) {
228 std::unique_ptr<jingle_xmpp::XmlElement> redirect_error(
229 MakeRedirectError(std::string()));
230 FireRedirect(redirect_error.get());
231 EXPECT_EQ(IDLE, fake_delegate_.state());
232 }
233
234 // Fire a redirect with a missing text element and make sure the
235 // delegate does not get a redirect.
TEST_F(SingleLoginAttemptTest,RedirectMissingText)236 TEST_F(SingleLoginAttemptTest, RedirectMissingText) {
237 std::unique_ptr<jingle_xmpp::XmlElement> redirect_error(
238 MakeRedirectError(std::string()));
239 redirect_error->RemoveChildAfter(redirect_error->FirstChild());
240 FireRedirect(redirect_error.get());
241 EXPECT_EQ(IDLE, fake_delegate_.state());
242 }
243
244 // Fire a redirect with a missing see-other-host element and make sure
245 // the delegate does not get a redirect.
TEST_F(SingleLoginAttemptTest,RedirectMissingSeeOtherHost)246 TEST_F(SingleLoginAttemptTest, RedirectMissingSeeOtherHost) {
247 std::unique_ptr<jingle_xmpp::XmlElement> redirect_error(
248 MakeRedirectError(std::string()));
249 redirect_error->RemoveChildAfter(NULL);
250 FireRedirect(redirect_error.get());
251 EXPECT_EQ(IDLE, fake_delegate_.state());
252 }
253
254 // Fire 'Unauthorized' errors and make sure the delegate gets the
255 // OnCredentialsRejected() event.
TEST_F(SingleLoginAttemptTest,CredentialsRejected)256 TEST_F(SingleLoginAttemptTest, CredentialsRejected) {
257 attempt_->OnError(jingle_xmpp::XmppEngine::ERROR_UNAUTHORIZED, 0, NULL);
258 EXPECT_EQ(CREDENTIALS_REJECTED, fake_delegate_.state());
259 }
260
261 } // namespace
262
263 } // namespace notifier
264