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