Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 1 | // Copyright 2013 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 "base/bind.h" |
| 6 | #include "base/memory/scoped_ptr.h" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 7 | #include "base/strings/stringprintf.h" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 8 | #include "chrome/browser/managed_mode/managed_user_refresh_token_fetcher.h" |
| 9 | #include "chrome/browser/signin/oauth2_token_service.h" |
| 10 | #include "chrome/test/base/testing_profile.h" |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 11 | #include "content/public/test/test_browser_thread_bundle.h" |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 12 | #include "google_apis/gaia/gaia_oauth_client.h" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 13 | #include "google_apis/gaia/gaia_urls.h" |
| 14 | #include "google_apis/gaia/google_service_auth_error.h" |
| 15 | #include "net/base/net_errors.h" |
| 16 | #include "net/base/url_util.h" |
| 17 | #include "net/http/http_request_headers.h" |
| 18 | #include "net/http/http_status_code.h" |
| 19 | #include "net/url_request/test_url_fetcher_factory.h" |
| 20 | #include "net/url_request/url_fetcher_delegate.h" |
| 21 | #include "testing/gtest/include/gtest/gtest.h" |
| 22 | |
| 23 | namespace { |
| 24 | |
| 25 | const char kManagedUserId[] = "abcdef"; |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 26 | const char kDeviceName[] = "Compy"; |
| 27 | |
| 28 | const char kAccessToken[] = "accesstoken"; |
| 29 | const char kAuthorizationCode[] = "authorizationcode"; |
| 30 | const char kManagedUserToken[] = "managedusertoken"; |
| 31 | |
| 32 | const char kIssueTokenResponseFormat[] = |
| 33 | "{" |
| 34 | " \"code\": \"%s\"" |
| 35 | "}"; |
| 36 | |
| 37 | const char kGetRefreshTokenResponseFormat[] = |
| 38 | "{" |
| 39 | " \"access_token\": \"<ignored>\"," |
| 40 | " \"expires_in\": 12345," |
| 41 | " \"refresh_token\": \"%s\"" |
| 42 | "}"; |
| 43 | |
| 44 | // MockOAuth2TokenService --------------------------------------------- |
| 45 | |
| 46 | class MockOAuth2TokenService : public OAuth2TokenService { |
| 47 | public: |
| 48 | class Request : public OAuth2TokenService::Request { |
| 49 | public: |
| 50 | Request(const OAuth2TokenService::ScopeSet& scopes, |
| 51 | OAuth2TokenService::Consumer* consumer, |
| 52 | MockOAuth2TokenService* owner); |
| 53 | virtual ~Request(); |
| 54 | |
| 55 | void Succeed(); |
| 56 | void Fail(GoogleServiceAuthError::State error); |
| 57 | |
| 58 | const OAuth2TokenService::ScopeSet& scopes() const { return scopes_; } |
| 59 | |
| 60 | private: |
| 61 | OAuth2TokenService::ScopeSet scopes_; |
| 62 | |
| 63 | OAuth2TokenService::Consumer* consumer_; |
| 64 | |
| 65 | MockOAuth2TokenService* owner_; |
| 66 | }; |
| 67 | |
| 68 | MockOAuth2TokenService(); |
| 69 | virtual ~MockOAuth2TokenService(); |
| 70 | |
| 71 | Request* request() const { return request_; } |
| 72 | |
| 73 | void ClearRequest(Request* request); |
| 74 | |
| 75 | private: |
| 76 | // OAuth2TokenService overrides: |
| 77 | virtual scoped_ptr<OAuth2TokenService::Request> StartRequest( |
| 78 | const OAuth2TokenService::ScopeSet& scopes, |
| 79 | OAuth2TokenService::Consumer* consumer) OVERRIDE; |
| 80 | virtual std::string GetRefreshToken() OVERRIDE; |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 81 | virtual net::URLRequestContextGetter* GetRequestContext() OVERRIDE { |
| 82 | return NULL; |
| 83 | } |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 84 | |
| 85 | Request* request_; |
| 86 | |
| 87 | DISALLOW_COPY_AND_ASSIGN(MockOAuth2TokenService); |
| 88 | }; |
| 89 | |
| 90 | MockOAuth2TokenService::Request::Request( |
| 91 | const OAuth2TokenService::ScopeSet& scopes, |
| 92 | OAuth2TokenService::Consumer* consumer, |
| 93 | MockOAuth2TokenService* owner) |
| 94 | : scopes_(scopes), |
| 95 | consumer_(consumer), |
| 96 | owner_(owner) {} |
| 97 | |
| 98 | MockOAuth2TokenService::Request::~Request() { |
| 99 | owner_->ClearRequest(this); |
| 100 | } |
| 101 | |
| 102 | void MockOAuth2TokenService::Request::Succeed() { |
| 103 | base::Time expiration_date = base::Time::Now() + |
| 104 | base::TimeDelta::FromHours(1); |
| 105 | consumer_->OnGetTokenSuccess(this, kAccessToken, expiration_date); |
| 106 | } |
| 107 | |
| 108 | void MockOAuth2TokenService::Request::Fail( |
| 109 | GoogleServiceAuthError::State error) { |
| 110 | consumer_->OnGetTokenFailure(this, GoogleServiceAuthError(error)); |
| 111 | } |
| 112 | |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 113 | MockOAuth2TokenService::MockOAuth2TokenService() : request_(NULL) {} |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 114 | |
| 115 | MockOAuth2TokenService::~MockOAuth2TokenService() { |
| 116 | EXPECT_FALSE(request_); |
| 117 | } |
| 118 | |
| 119 | void MockOAuth2TokenService::ClearRequest( |
| 120 | MockOAuth2TokenService::Request* request) { |
| 121 | if (request_ == request) |
| 122 | request_ = NULL; |
| 123 | } |
| 124 | |
| 125 | scoped_ptr<OAuth2TokenService::Request> MockOAuth2TokenService::StartRequest( |
| 126 | const OAuth2TokenService::ScopeSet& scopes, |
| 127 | OAuth2TokenService::Consumer* consumer) { |
| 128 | scoped_ptr<Request> request(new Request(scopes, consumer, this)); |
| 129 | request_ = request.get(); |
| 130 | return request.PassAs<OAuth2TokenService::Request>(); |
| 131 | } |
| 132 | |
| 133 | std::string MockOAuth2TokenService::GetRefreshToken() { |
| 134 | NOTREACHED(); |
| 135 | return std::string(); |
| 136 | } |
| 137 | |
| 138 | // Utility methods -------------------------------------------------- |
| 139 | |
| 140 | // Slightly hacky way to extract a value from a URL-encoded POST request body. |
| 141 | bool GetValueForKey(const std::string& encoded_string, |
| 142 | const std::string& key, |
| 143 | std::string* value) { |
| 144 | GURL url("http://example.com/?" + encoded_string); |
| 145 | return net::GetValueForKeyInQuery(url, key, value); |
| 146 | } |
| 147 | |
| 148 | void SendResponse(net::TestURLFetcher* url_fetcher, |
| 149 | const std::string& response) { |
| 150 | url_fetcher->set_status( |
| 151 | net::URLRequestStatus(net::URLRequestStatus::SUCCESS, 0)); |
| 152 | url_fetcher->set_response_code(net::HTTP_OK); |
| 153 | url_fetcher->SetResponseString(response); |
| 154 | url_fetcher->delegate()->OnURLFetchComplete(url_fetcher); |
| 155 | } |
| 156 | |
| 157 | void SetNetworkError(net::TestURLFetcher* url_fetcher, int error) { |
| 158 | url_fetcher->set_status( |
| 159 | net::URLRequestStatus(net::URLRequestStatus::FAILED, error)); |
| 160 | url_fetcher->delegate()->OnURLFetchComplete(url_fetcher); |
| 161 | } |
| 162 | |
| 163 | void SetHttpError(net::TestURLFetcher* url_fetcher, int error) { |
| 164 | url_fetcher->set_status(net::URLRequestStatus()); |
| 165 | url_fetcher->set_response_code(error); |
| 166 | url_fetcher->delegate()->OnURLFetchComplete(url_fetcher); |
| 167 | } |
| 168 | |
| 169 | } // namespace |
| 170 | |
| 171 | class ManagedUserRefreshTokenFetcherTest : public testing::Test { |
| 172 | public: |
| 173 | ManagedUserRefreshTokenFetcherTest(); |
| 174 | virtual ~ManagedUserRefreshTokenFetcherTest() {} |
| 175 | |
| 176 | protected: |
| 177 | void StartFetching(); |
| 178 | |
| 179 | MockOAuth2TokenService::Request* GetOAuth2TokenServiceRequest(); |
| 180 | net::TestURLFetcher* GetIssueTokenRequest(); |
| 181 | net::TestURLFetcher* GetRefreshTokenRequest(); |
| 182 | |
| 183 | void MakeIssueTokenRequestSucceed(); |
| 184 | void MakeRefreshTokenFetchSucceed(); |
| 185 | |
| 186 | void Reset(); |
| 187 | |
| 188 | const GoogleServiceAuthError& error() const { return error_; } |
| 189 | const std::string& token() const { return token_; } |
| 190 | |
| 191 | private: |
| 192 | void OnTokenFetched(const GoogleServiceAuthError& error, |
| 193 | const std::string& token); |
| 194 | |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 195 | content::TestBrowserThreadBundle thread_bundle_; |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 196 | TestingProfile profile_; |
| 197 | MockOAuth2TokenService oauth2_token_service_; |
| 198 | net::TestURLFetcherFactory url_fetcher_factory_; |
| 199 | scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher_; |
| 200 | |
| 201 | GoogleServiceAuthError error_; |
| 202 | std::string token_; |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 203 | base::WeakPtrFactory<ManagedUserRefreshTokenFetcherTest> weak_ptr_factory_; |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 204 | }; |
| 205 | |
| 206 | ManagedUserRefreshTokenFetcherTest::ManagedUserRefreshTokenFetcherTest() |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 207 | : token_fetcher_( |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 208 | ManagedUserRefreshTokenFetcher::Create(&oauth2_token_service_, |
| 209 | profile_.GetRequestContext())), |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 210 | error_(GoogleServiceAuthError::NONE), |
| 211 | weak_ptr_factory_(this) {} |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 212 | |
| 213 | void ManagedUserRefreshTokenFetcherTest::StartFetching() { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 214 | token_fetcher_->Start(kManagedUserId, kDeviceName, |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 215 | base::Bind( |
| 216 | &ManagedUserRefreshTokenFetcherTest::OnTokenFetched, |
| 217 | weak_ptr_factory_.GetWeakPtr())); |
| 218 | } |
| 219 | |
| 220 | MockOAuth2TokenService::Request* |
| 221 | ManagedUserRefreshTokenFetcherTest::GetOAuth2TokenServiceRequest() { |
| 222 | MockOAuth2TokenService::Request* request = oauth2_token_service_.request(); |
| 223 | |
| 224 | OAuth2TokenService::ScopeSet scopes = request->scopes(); |
| 225 | EXPECT_EQ(1u, scopes.size()); |
| 226 | EXPECT_EQ(1u, scopes.count(GaiaUrls::GetInstance()->oauth1_login_scope())); |
| 227 | return request; |
| 228 | } |
| 229 | |
| 230 | net::TestURLFetcher* |
| 231 | ManagedUserRefreshTokenFetcherTest::GetIssueTokenRequest() { |
| 232 | net::TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(1); |
| 233 | if (!url_fetcher) |
| 234 | return NULL; |
| 235 | |
| 236 | EXPECT_EQ(GaiaUrls::GetInstance()->oauth2_issue_token_url(), |
| 237 | url_fetcher->GetOriginalURL().spec()); |
| 238 | std::string access_token; |
| 239 | net::HttpRequestHeaders headers; |
| 240 | url_fetcher->GetExtraRequestHeaders(&headers); |
| 241 | EXPECT_TRUE(headers.GetHeader("Authorization", &access_token)); |
| 242 | EXPECT_EQ(std::string("Bearer ") + kAccessToken, access_token); |
| 243 | const std::string upload_data = url_fetcher->upload_data(); |
| 244 | std::string managed_user_id; |
| 245 | EXPECT_TRUE(GetValueForKey(upload_data, "profile_id", &managed_user_id)); |
| 246 | EXPECT_EQ(kManagedUserId, managed_user_id); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 247 | std::string device_name; |
| 248 | EXPECT_TRUE(GetValueForKey(upload_data, "device_name", &device_name)); |
| 249 | EXPECT_EQ(kDeviceName, device_name); |
| 250 | return url_fetcher; |
| 251 | } |
| 252 | |
| 253 | net::TestURLFetcher* |
| 254 | ManagedUserRefreshTokenFetcherTest::GetRefreshTokenRequest() { |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 255 | net::TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID( |
| 256 | gaia::GaiaOAuthClient::kUrlFetcherId); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 257 | if (!url_fetcher) |
| 258 | return NULL; |
| 259 | |
| 260 | EXPECT_EQ(GaiaUrls::GetInstance()->oauth2_token_url(), |
| 261 | url_fetcher->GetOriginalURL().spec()); |
| 262 | std::string auth_code; |
| 263 | EXPECT_TRUE(GetValueForKey(url_fetcher->upload_data(), "code", &auth_code)); |
| 264 | EXPECT_EQ(kAuthorizationCode, auth_code); |
| 265 | return url_fetcher; |
| 266 | } |
| 267 | |
| 268 | void ManagedUserRefreshTokenFetcherTest::MakeIssueTokenRequestSucceed() { |
| 269 | SendResponse(GetIssueTokenRequest(), |
| 270 | base::StringPrintf(kIssueTokenResponseFormat, |
| 271 | kAuthorizationCode)); |
| 272 | } |
| 273 | |
| 274 | void ManagedUserRefreshTokenFetcherTest::MakeRefreshTokenFetchSucceed() { |
| 275 | SendResponse(GetRefreshTokenRequest(), |
| 276 | base::StringPrintf(kGetRefreshTokenResponseFormat, |
| 277 | kManagedUserToken)); |
| 278 | } |
| 279 | |
| 280 | void ManagedUserRefreshTokenFetcherTest::Reset() { |
| 281 | token_fetcher_.reset(); |
| 282 | } |
| 283 | |
| 284 | void ManagedUserRefreshTokenFetcherTest::OnTokenFetched( |
| 285 | const GoogleServiceAuthError& error, |
| 286 | const std::string& token) { |
| 287 | error_ = error; |
| 288 | token_ = token; |
| 289 | } |
| 290 | |
| 291 | // Tests -------------------------------------------------------- |
| 292 | |
| 293 | TEST_F(ManagedUserRefreshTokenFetcherTest, Success) { |
| 294 | StartFetching(); |
| 295 | GetOAuth2TokenServiceRequest()->Succeed(); |
| 296 | MakeIssueTokenRequestSucceed(); |
| 297 | MakeRefreshTokenFetchSucceed(); |
| 298 | |
| 299 | EXPECT_EQ(GoogleServiceAuthError::NONE, error().state()); |
| 300 | EXPECT_EQ(kManagedUserToken, token()); |
| 301 | } |
| 302 | |
| 303 | TEST_F(ManagedUserRefreshTokenFetcherTest, ExpiredAccessToken) { |
| 304 | StartFetching(); |
| 305 | GetOAuth2TokenServiceRequest()->Succeed(); |
| 306 | SetHttpError(GetIssueTokenRequest(), net::HTTP_UNAUTHORIZED); |
| 307 | GetOAuth2TokenServiceRequest()->Succeed(); |
| 308 | MakeIssueTokenRequestSucceed(); |
| 309 | MakeRefreshTokenFetchSucceed(); |
| 310 | |
| 311 | EXPECT_EQ(GoogleServiceAuthError::NONE, error().state()); |
| 312 | EXPECT_EQ(kManagedUserToken, token()); |
| 313 | } |
| 314 | |
| 315 | TEST_F(ManagedUserRefreshTokenFetcherTest, ExpiredAccessTokenRetry) { |
| 316 | // If we get a 401 error for the second time, we should give up instead of |
| 317 | // retrying again. |
| 318 | StartFetching(); |
| 319 | GetOAuth2TokenServiceRequest()->Succeed(); |
| 320 | SetHttpError(GetIssueTokenRequest(), net::HTTP_UNAUTHORIZED); |
| 321 | GetOAuth2TokenServiceRequest()->Succeed(); |
| 322 | SetHttpError(GetIssueTokenRequest(), net::HTTP_UNAUTHORIZED); |
| 323 | |
| 324 | EXPECT_EQ(GoogleServiceAuthError::CONNECTION_FAILED, error().state()); |
| 325 | EXPECT_EQ(net::ERR_FAILED, error().network_error()); |
| 326 | EXPECT_EQ(std::string(), token()); |
| 327 | } |
| 328 | |
| 329 | TEST_F(ManagedUserRefreshTokenFetcherTest, MalformedIssueTokenResponse) { |
| 330 | StartFetching(); |
| 331 | GetOAuth2TokenServiceRequest()->Succeed(); |
| 332 | SendResponse(GetIssueTokenRequest(), "choke"); |
| 333 | |
| 334 | EXPECT_EQ(GoogleServiceAuthError::CONNECTION_FAILED, error().state()); |
| 335 | EXPECT_EQ(net::ERR_INVALID_RESPONSE, error().network_error()); |
| 336 | EXPECT_EQ(std::string(), token()); |
| 337 | } |
| 338 | |
| 339 | TEST_F(ManagedUserRefreshTokenFetcherTest, FetchAccessTokenFailure) { |
| 340 | StartFetching(); |
| 341 | GetOAuth2TokenServiceRequest()->Fail( |
| 342 | GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); |
| 343 | |
| 344 | EXPECT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS, error().state()); |
| 345 | EXPECT_EQ(std::string(), token()); |
| 346 | } |
| 347 | |
| 348 | TEST_F(ManagedUserRefreshTokenFetcherTest, IssueTokenNetworkError) { |
| 349 | StartFetching(); |
| 350 | GetOAuth2TokenServiceRequest()->Succeed(); |
| 351 | SetNetworkError(GetIssueTokenRequest(), net::ERR_SSL_PROTOCOL_ERROR); |
| 352 | |
| 353 | EXPECT_EQ(GoogleServiceAuthError::CONNECTION_FAILED, error().state()); |
| 354 | EXPECT_EQ(net::ERR_SSL_PROTOCOL_ERROR, error().network_error()); |
| 355 | EXPECT_EQ(std::string(), token()); |
| 356 | } |
| 357 | |
| 358 | TEST_F(ManagedUserRefreshTokenFetcherTest, FetchRefreshTokenNetworkError) { |
| 359 | StartFetching(); |
| 360 | GetOAuth2TokenServiceRequest()->Succeed(); |
| 361 | MakeIssueTokenRequestSucceed(); |
| 362 | SetNetworkError(GetRefreshTokenRequest(), net::ERR_CONNECTION_REFUSED); |
| 363 | EXPECT_EQ(GoogleServiceAuthError::NONE, error().state()); |
| 364 | SetNetworkError(GetRefreshTokenRequest(), net::ERR_CONNECTION_REFUSED); |
| 365 | |
| 366 | EXPECT_EQ(GoogleServiceAuthError::CONNECTION_FAILED, error().state()); |
| 367 | EXPECT_EQ(net::ERR_FAILED, error().network_error()); |
| 368 | EXPECT_EQ(std::string(), token()); |
| 369 | } |
| 370 | |
| 371 | TEST_F(ManagedUserRefreshTokenFetcherTest, |
| 372 | FetchRefreshTokenTransientNetworkError) { |
| 373 | StartFetching(); |
| 374 | GetOAuth2TokenServiceRequest()->Succeed(); |
| 375 | MakeIssueTokenRequestSucceed(); |
| 376 | SetNetworkError(GetRefreshTokenRequest(), net::ERR_CONNECTION_REFUSED); |
| 377 | |
| 378 | EXPECT_EQ(GoogleServiceAuthError::NONE, error().state()); |
| 379 | MakeRefreshTokenFetchSucceed(); |
| 380 | |
| 381 | EXPECT_EQ(GoogleServiceAuthError::NONE, error().state()); |
| 382 | EXPECT_EQ(kManagedUserToken, token()); |
| 383 | } |
| 384 | |
| 385 | TEST_F(ManagedUserRefreshTokenFetcherTest, FetchRefreshTokenBadRequest) { |
| 386 | StartFetching(); |
| 387 | GetOAuth2TokenServiceRequest()->Succeed(); |
| 388 | MakeIssueTokenRequestSucceed(); |
| 389 | SetHttpError(GetRefreshTokenRequest(), net::HTTP_BAD_REQUEST); |
| 390 | |
| 391 | EXPECT_EQ(GoogleServiceAuthError::CONNECTION_FAILED, error().state()); |
| 392 | EXPECT_EQ(net::ERR_FAILED, error().network_error()); |
| 393 | EXPECT_EQ(std::string(), token()); |
| 394 | } |
| 395 | |
| 396 | TEST_F(ManagedUserRefreshTokenFetcherTest, CancelWhileFetchingAccessToken) { |
| 397 | StartFetching(); |
| 398 | Reset(); |
| 399 | |
| 400 | EXPECT_EQ(GoogleServiceAuthError::NONE, error().state()); |
| 401 | EXPECT_EQ(std::string(), token()); |
| 402 | } |
| 403 | |
| 404 | TEST_F(ManagedUserRefreshTokenFetcherTest, CancelWhileCallingIssueToken) { |
| 405 | StartFetching(); |
| 406 | GetOAuth2TokenServiceRequest()->Succeed(); |
| 407 | Reset(); |
| 408 | |
| 409 | EXPECT_EQ(GoogleServiceAuthError::NONE, error().state()); |
| 410 | EXPECT_EQ(std::string(), token()); |
| 411 | } |
| 412 | |
| 413 | TEST_F(ManagedUserRefreshTokenFetcherTest, CancelWhileFetchingRefreshToken) { |
| 414 | StartFetching(); |
| 415 | GetOAuth2TokenServiceRequest()->Succeed(); |
| 416 | MakeIssueTokenRequestSucceed(); |
| 417 | Reset(); |
| 418 | |
| 419 | EXPECT_EQ(GoogleServiceAuthError::NONE, error().state()); |
| 420 | EXPECT_EQ(std::string(), token()); |
| 421 | } |