1// Copyright 2014 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 <string>
6
7#include "base/bind.h"
8#include "base/location.h"
9#include "base/memory/ref_counted.h"
10#include "base/run_loop.h"
11#include "base/test/null_task_runner.h"
12#include "chrome/test/base/testing_profile.h"
13#include "chromeos/login/auth/auth_attempt_state.h"
14#include "chromeos/login/auth/mock_auth_attempt_state_resolver.h"
15#include "chromeos/login/auth/mock_url_fetchers.h"
16#include "chromeos/login/auth/online_attempt.h"
17#include "chromeos/login/auth/test_attempt_state.h"
18#include "chromeos/login/auth/user_context.h"
19#include "google_apis/gaia/gaia_auth_consumer.h"
20#include "google_apis/gaia/mock_url_fetcher_factory.h"
21#include "net/url_request/url_request_context.h"
22#include "net/url_request/url_request_context_getter.h"
23#include "net/url_request/url_request_test_util.h"
24#include "testing/gmock/include/gmock/gmock.h"
25#include "testing/gtest/include/gtest/gtest.h"
26#include "url/gurl.h"
27
28using ::testing::AnyNumber;
29using ::testing::Invoke;
30using ::testing::Return;
31using ::testing::_;
32
33namespace {
34
35class TestContextURLRequestContextGetter : public net::URLRequestContextGetter {
36 public:
37  TestContextURLRequestContextGetter()
38      : null_task_runner_(new base::NullTaskRunner) {}
39
40  virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE {
41    return &context_;
42  }
43
44  virtual scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner()
45      const OVERRIDE {
46    return null_task_runner_;
47  }
48
49 private:
50  virtual ~TestContextURLRequestContextGetter() {}
51
52  net::TestURLRequestContext context_;
53  scoped_refptr<base::SingleThreadTaskRunner> null_task_runner_;
54};
55
56}  // namespace
57
58namespace chromeos {
59
60class OnlineAttemptTest : public testing::Test {
61 public:
62  OnlineAttemptTest()
63      : state_(UserContext(), false),
64        attempt_(new OnlineAttempt(&state_, &resolver_)) {}
65
66  virtual void SetUp() OVERRIDE {
67    message_loop_ = base::MessageLoopProxy::current();
68    request_context_ = new TestContextURLRequestContextGetter();
69  }
70
71  void RunFailureTest(const GoogleServiceAuthError& error) {
72    EXPECT_CALL(resolver_, Resolve()).Times(1).RetiresOnSaturation();
73
74    message_loop_->PostTask(FROM_HERE,
75                            base::Bind(&OnlineAttempt::OnClientLoginFailure,
76                                       attempt_->weak_factory_.GetWeakPtr(),
77                                       error));
78    // Force UI thread to finish tasks so I can verify |state_|.
79    base::RunLoop().RunUntilIdle();
80    EXPECT_TRUE(error == state_.online_outcome().error());
81  }
82
83  void CancelLogin(OnlineAttempt* auth) {
84    message_loop_->PostTask(FROM_HERE,
85                            base::Bind(&OnlineAttempt::CancelClientLogin,
86                                       auth->weak_factory_.GetWeakPtr()));
87  }
88
89  scoped_refptr<base::MessageLoopProxy> message_loop_;
90  scoped_refptr<net::URLRequestContextGetter> request_context_;
91  base::MessageLoop loop_;
92  TestAttemptState state_;
93  MockAuthAttemptStateResolver resolver_;
94  scoped_ptr<OnlineAttempt> attempt_;
95};
96
97TEST_F(OnlineAttemptTest, LoginSuccess) {
98  EXPECT_CALL(resolver_, Resolve()).Times(1).RetiresOnSaturation();
99
100  message_loop_->PostTask(FROM_HERE,
101                          base::Bind(&OnlineAttempt::OnClientLoginSuccess,
102                                     attempt_->weak_factory_.GetWeakPtr(),
103                                     GaiaAuthConsumer::ClientLoginResult()));
104  // Force UI thread to finish tasks so I can verify |state_|.
105  base::RunLoop().RunUntilIdle();
106}
107
108TEST_F(OnlineAttemptTest, LoginCancelRetry) {
109  GoogleServiceAuthError error(GoogleServiceAuthError::REQUEST_CANCELED);
110  TestingProfile profile;
111
112  base::RunLoop run_loop;
113  EXPECT_CALL(resolver_, Resolve())
114      .WillOnce(Invoke(&run_loop, &base::RunLoop::Quit))
115      .RetiresOnSaturation();
116
117  // This is how we inject fake URLFetcher objects, with a factory.
118  // This factory creates fake URLFetchers that Start() a fake fetch attempt
119  // and then come back on the UI thread saying they've been canceled.
120  MockURLFetcherFactory<GotCanceledFetcher> factory;
121
122  attempt_->Initiate(request_context_.get());
123
124  run_loop.Run();
125
126  EXPECT_TRUE(error == state_.online_outcome().error());
127  EXPECT_EQ(AuthFailure::NETWORK_AUTH_FAILED, state_.online_outcome().reason());
128}
129
130TEST_F(OnlineAttemptTest, LoginTimeout) {
131  AuthFailure error(AuthFailure::LOGIN_TIMED_OUT);
132  TestingProfile profile;
133
134  base::RunLoop run_loop;
135  EXPECT_CALL(resolver_, Resolve())
136      .WillOnce(Invoke(&run_loop, &base::RunLoop::Quit))
137      .RetiresOnSaturation();
138
139  // This is how we inject fake URLFetcher objects, with a factory.
140  // This factory creates fake URLFetchers that Start() a fake fetch attempt
141  // and then come back on the UI thread saying they've been canceled.
142  MockURLFetcherFactory<ExpectCanceledFetcher> factory;
143
144  attempt_->Initiate(request_context_.get());
145
146  // Post a task to cancel the login attempt.
147  CancelLogin(attempt_.get());
148
149  run_loop.Run();
150
151  EXPECT_EQ(AuthFailure::LOGIN_TIMED_OUT, state_.online_outcome().reason());
152}
153
154TEST_F(OnlineAttemptTest, HostedLoginRejected) {
155  AuthFailure error(AuthFailure::FromNetworkAuthFailure(
156      GoogleServiceAuthError(GoogleServiceAuthError::HOSTED_NOT_ALLOWED)));
157  TestingProfile profile;
158
159  base::RunLoop run_loop;
160  EXPECT_CALL(resolver_, Resolve())
161      .WillOnce(Invoke(&run_loop, &base::RunLoop::Quit))
162      .RetiresOnSaturation();
163
164  // This is how we inject fake URLFetcher objects, with a factory.
165  MockURLFetcherFactory<HostedFetcher> factory;
166
167  TestAttemptState local_state(UserContext(), true);
168  attempt_.reset(new OnlineAttempt(&local_state, &resolver_));
169  attempt_->Initiate(request_context_.get());
170
171  run_loop.Run();
172
173  EXPECT_EQ(error, local_state.online_outcome());
174  EXPECT_EQ(AuthFailure::NETWORK_AUTH_FAILED,
175            local_state.online_outcome().reason());
176}
177
178TEST_F(OnlineAttemptTest, FullLogin) {
179  TestingProfile profile;
180
181  base::RunLoop run_loop;
182  EXPECT_CALL(resolver_, Resolve())
183      .WillOnce(Invoke(&run_loop, &base::RunLoop::Quit))
184      .RetiresOnSaturation();
185
186  // This is how we inject fake URLFetcher objects, with a factory.
187  MockURLFetcherFactory<SuccessFetcher> factory;
188
189  TestAttemptState local_state(UserContext(), true);
190  attempt_.reset(new OnlineAttempt(&local_state, &resolver_));
191  attempt_->Initiate(request_context_.get());
192
193  run_loop.Run();
194
195  EXPECT_EQ(AuthFailure::AuthFailureNone(), local_state.online_outcome());
196}
197
198TEST_F(OnlineAttemptTest, LoginNetFailure) {
199  RunFailureTest(
200      GoogleServiceAuthError::FromConnectionError(net::ERR_CONNECTION_RESET));
201}
202
203TEST_F(OnlineAttemptTest, LoginDenied) {
204  RunFailureTest(
205      GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
206}
207
208TEST_F(OnlineAttemptTest, LoginAccountDisabled) {
209  RunFailureTest(
210      GoogleServiceAuthError(GoogleServiceAuthError::ACCOUNT_DISABLED));
211}
212
213TEST_F(OnlineAttemptTest, LoginAccountDeleted) {
214  RunFailureTest(
215      GoogleServiceAuthError(GoogleServiceAuthError::ACCOUNT_DELETED));
216}
217
218TEST_F(OnlineAttemptTest, LoginServiceUnavailable) {
219  RunFailureTest(
220      GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE));
221}
222
223TEST_F(OnlineAttemptTest, CaptchaErrorOutputted) {
224  GoogleServiceAuthError auth_error =
225      GoogleServiceAuthError::FromClientLoginCaptchaChallenge(
226          "CCTOKEN",
227          GURL("http://accounts.google.com/Captcha?ctoken=CCTOKEN"),
228          GURL("http://www.google.com/login/captcha"));
229  RunFailureTest(auth_error);
230}
231
232TEST_F(OnlineAttemptTest, TwoFactorSuccess) {
233  EXPECT_CALL(resolver_, Resolve()).Times(1).RetiresOnSaturation();
234  GoogleServiceAuthError error(GoogleServiceAuthError::TWO_FACTOR);
235  message_loop_->PostTask(FROM_HERE,
236                          base::Bind(&OnlineAttempt::OnClientLoginFailure,
237                                     attempt_->weak_factory_.GetWeakPtr(),
238                                     error));
239
240  // Force UI thread to finish tasks so I can verify |state_|.
241  base::RunLoop().RunUntilIdle();
242  EXPECT_TRUE(GoogleServiceAuthError::AuthErrorNone() ==
243              state_.online_outcome().error());
244}
245
246}  // namespace chromeos
247