| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "remoting/test/access_token_fetcher.h" |
| |
| #include <string> |
| |
| #include "base/bind.h" |
| #include "base/macros.h" |
| #include "base/run_loop.h" |
| #include "base/test/scoped_task_environment.h" |
| #include "google_apis/gaia/gaia_urls.h" |
| #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" |
| #include "services/network/test/test_url_loader_factory.h" |
| #include "services/network/test/test_utils.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace { |
| const char kAuthCodeValue[] = "test_auth_code_value"; |
| const char kAccessTokenValue[] = "test_access_token_value"; |
| const char kRefreshTokenValue[] = "test_refresh_token_value"; |
| const char kAuthCodeExchangeValidResponse[] = |
| "{" |
| " \"refresh_token\": \"test_refresh_token_value\"," |
| " \"access_token\": \"test_access_token_value\"," |
| " \"expires_in\": 3600," |
| " \"token_type\": \"Bearer\"" |
| "}"; |
| const char kAuthCodeExchangeEmptyResponse[] = "{}"; |
| const char kRefreshTokenExchangeValidResponse[] = |
| "{" |
| " \"access_token\": \"test_access_token_value\"," |
| " \"expires_in\": 3600," |
| " \"token_type\": \"Bearer\"" |
| "}"; |
| const char kRefreshTokenExchangeEmptyResponse[] = "{}"; |
| const char kValidTokenInfoResponse[] = |
| "{" |
| " \"audience\": \"blah.apps.googleusercontent.blah.com\"," |
| " \"used_id\": \"1234567890\"," |
| " \"scope\": \"all the things\"," |
| " \"expires_in\": \"1800\"," |
| " \"token_type\": \"Bearer\"" |
| "}"; |
| const char kInvalidTokenInfoResponse[] = |
| "{" |
| " \"error\": \"invalid_token\"" |
| "}"; |
| } // namespace |
| |
| namespace remoting { |
| namespace test { |
| |
| // Provides base functionality for the AccessTokenFetcher Tests below. The |
| // FakeURLFetcherFactory allows us to override the response data and payload for |
| // specified URLs. We use this to stub out network calls made by the |
| // AccessTokenFetcher. This fixture also creates an IO MessageLoop, if |
| // necessary, for use by the AccessTokenFetcher. |
| class AccessTokenFetcherTest : public ::testing::Test { |
| public: |
| AccessTokenFetcherTest(); |
| ~AccessTokenFetcherTest() override; |
| |
| void OnAccessTokenRetrieved(base::Closure done_closure, |
| const std::string& access_token, |
| const std::string& refresh_token); |
| |
| protected: |
| void SetFakeResponse(const GURL& url, |
| const std::string& data, |
| net::HttpStatusCode code, |
| int net_error); |
| |
| scoped_refptr<network::SharedURLLoaderFactory> shared_factory() { |
| return shared_factory_; |
| } |
| |
| // Used for result verification |
| std::string access_token_retrieved_; |
| std::string refresh_token_retrieved_; |
| |
| private: |
| base::test::ScopedTaskEnvironment scoped_task_environment_; |
| network::TestURLLoaderFactory test_url_loader_factory_; |
| scoped_refptr<network::SharedURLLoaderFactory> shared_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(AccessTokenFetcherTest); |
| }; |
| |
| AccessTokenFetcherTest::AccessTokenFetcherTest() |
| : scoped_task_environment_( |
| base::test::ScopedTaskEnvironment::MainThreadType::IO), |
| shared_factory_( |
| base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( |
| &test_url_loader_factory_)) {} |
| |
| AccessTokenFetcherTest::~AccessTokenFetcherTest() = default; |
| |
| void AccessTokenFetcherTest::OnAccessTokenRetrieved( |
| base::Closure done_closure, |
| const std::string& access_token, |
| const std::string& refresh_token) { |
| access_token_retrieved_ = access_token; |
| refresh_token_retrieved_ = refresh_token; |
| |
| done_closure.Run(); |
| } |
| |
| void AccessTokenFetcherTest::SetFakeResponse(const GURL& url, |
| const std::string& data, |
| net::HttpStatusCode code, |
| int net_error) { |
| test_url_loader_factory_.AddResponse( |
| url, network::CreateResourceResponseHead(code), data, |
| network::URLLoaderCompletionStatus(net_error)); |
| } |
| |
| TEST_F(AccessTokenFetcherTest, ExchangeAuthCodeForAccessToken) { |
| SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(), |
| kAuthCodeExchangeValidResponse, net::HTTP_OK, net::OK); |
| |
| SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_info_url(), |
| kValidTokenInfoResponse, net::HTTP_OK, net::OK); |
| |
| base::RunLoop run_loop; |
| AccessTokenCallback access_token_callback = |
| base::Bind(&AccessTokenFetcherTest::OnAccessTokenRetrieved, |
| base::Unretained(this), run_loop.QuitClosure()); |
| |
| AccessTokenFetcher access_token_fetcher; |
| access_token_fetcher.SetURLLoaderFactoryForTesting(shared_factory()); |
| access_token_fetcher.GetAccessTokenFromAuthCode(kAuthCodeValue, |
| access_token_callback); |
| |
| run_loop.Run(); |
| |
| EXPECT_EQ(access_token_retrieved_.compare(kAccessTokenValue), 0); |
| EXPECT_EQ(refresh_token_retrieved_.compare(kRefreshTokenValue), 0); |
| } |
| |
| TEST_F(AccessTokenFetcherTest, ExchangeRefreshTokenForAccessToken) { |
| SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(), |
| kRefreshTokenExchangeValidResponse, net::HTTP_OK, net::OK); |
| |
| SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_info_url(), |
| kValidTokenInfoResponse, net::HTTP_OK, net::OK); |
| |
| base::RunLoop run_loop; |
| AccessTokenCallback access_token_callback = |
| base::Bind(&AccessTokenFetcherTest::OnAccessTokenRetrieved, |
| base::Unretained(this), run_loop.QuitClosure()); |
| |
| AccessTokenFetcher access_token_fetcher; |
| access_token_fetcher.SetURLLoaderFactoryForTesting(shared_factory()); |
| access_token_fetcher.GetAccessTokenFromRefreshToken(kRefreshTokenValue, |
| access_token_callback); |
| |
| run_loop.Run(); |
| |
| EXPECT_EQ(access_token_retrieved_.compare(kAccessTokenValue), 0); |
| EXPECT_EQ(refresh_token_retrieved_.compare(kRefreshTokenValue), 0); |
| } |
| |
| TEST_F(AccessTokenFetcherTest, MultipleAccessTokenCalls) { |
| SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(), |
| kAuthCodeExchangeValidResponse, net::HTTP_OK, net::OK); |
| |
| SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_info_url(), |
| kValidTokenInfoResponse, net::HTTP_OK, net::OK); |
| |
| std::unique_ptr<base::RunLoop> run_loop; |
| run_loop.reset(new base::RunLoop()); |
| AccessTokenCallback access_token_callback = |
| base::Bind(&AccessTokenFetcherTest::OnAccessTokenRetrieved, |
| base::Unretained(this), run_loop->QuitClosure()); |
| |
| AccessTokenFetcher access_token_fetcher; |
| access_token_fetcher.SetURLLoaderFactoryForTesting(shared_factory()); |
| access_token_fetcher.GetAccessTokenFromAuthCode(kAuthCodeValue, |
| access_token_callback); |
| |
| run_loop->Run(); |
| |
| EXPECT_EQ(access_token_retrieved_.compare(kAccessTokenValue), 0); |
| EXPECT_EQ(refresh_token_retrieved_.compare(kRefreshTokenValue), 0); |
| |
| // Reset our token data for the next iteration. |
| access_token_retrieved_.clear(); |
| refresh_token_retrieved_.clear(); |
| |
| // Update the response since we will call the refresh token method next. |
| SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(), |
| kRefreshTokenExchangeValidResponse, net::HTTP_OK, net::OK); |
| |
| run_loop.reset(new base::RunLoop()); |
| access_token_callback = |
| base::Bind(&AccessTokenFetcherTest::OnAccessTokenRetrieved, |
| base::Unretained(this), run_loop->QuitClosure()); |
| |
| access_token_fetcher.GetAccessTokenFromRefreshToken(kRefreshTokenValue, |
| access_token_callback); |
| |
| run_loop->Run(); |
| |
| EXPECT_EQ(access_token_retrieved_.compare(kAccessTokenValue), 0); |
| EXPECT_EQ(refresh_token_retrieved_.compare(kRefreshTokenValue), 0); |
| |
| run_loop.reset(new base::RunLoop()); |
| access_token_callback = |
| base::Bind(&AccessTokenFetcherTest::OnAccessTokenRetrieved, |
| base::Unretained(this), run_loop->QuitClosure()); |
| |
| // Reset our token data for the next iteration. |
| access_token_retrieved_.clear(); |
| refresh_token_retrieved_.clear(); |
| |
| access_token_fetcher.GetAccessTokenFromRefreshToken(kRefreshTokenValue, |
| access_token_callback); |
| |
| run_loop->Run(); |
| |
| EXPECT_EQ(access_token_retrieved_.compare(kAccessTokenValue), 0); |
| EXPECT_EQ(refresh_token_retrieved_.compare(kRefreshTokenValue), 0); |
| } |
| |
| TEST_F(AccessTokenFetcherTest, ExchangeAuthCode_Unauthorized_Error) { |
| SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(), |
| kAuthCodeExchangeValidResponse, net::HTTP_UNAUTHORIZED, |
| net::ERR_FAILED); |
| |
| base::RunLoop run_loop; |
| AccessTokenCallback access_token_callback = |
| base::Bind(&AccessTokenFetcherTest::OnAccessTokenRetrieved, |
| base::Unretained(this), run_loop.QuitClosure()); |
| |
| AccessTokenFetcher access_token_fetcher; |
| access_token_fetcher.SetURLLoaderFactoryForTesting(shared_factory()); |
| access_token_fetcher.GetAccessTokenFromAuthCode(kAuthCodeValue, |
| access_token_callback); |
| |
| run_loop.Run(); |
| |
| // Our callback should have been called with empty strings. |
| EXPECT_TRUE(access_token_retrieved_.empty()); |
| EXPECT_TRUE(refresh_token_retrieved_.empty()); |
| } |
| |
| TEST_F(AccessTokenFetcherTest, ExchangeRefreshToken_Unauthorized_Error) { |
| SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(), |
| kRefreshTokenExchangeValidResponse, net::HTTP_UNAUTHORIZED, |
| net::ERR_FAILED); |
| |
| base::RunLoop run_loop; |
| AccessTokenCallback access_token_callback = |
| base::Bind(&AccessTokenFetcherTest::OnAccessTokenRetrieved, |
| base::Unretained(this), run_loop.QuitClosure()); |
| |
| AccessTokenFetcher access_token_fetcher; |
| access_token_fetcher.SetURLLoaderFactoryForTesting(shared_factory()); |
| access_token_fetcher.GetAccessTokenFromRefreshToken(kRefreshTokenValue, |
| access_token_callback); |
| |
| run_loop.Run(); |
| |
| // Our callback should have been called with empty strings. |
| EXPECT_TRUE(access_token_retrieved_.empty()); |
| EXPECT_TRUE(refresh_token_retrieved_.empty()); |
| } |
| |
| TEST_F(AccessTokenFetcherTest, ExchangeAuthCode_NetworkError) { |
| SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(), |
| kAuthCodeExchangeValidResponse, net::HTTP_NOT_FOUND, |
| net::ERR_FAILED); |
| |
| base::RunLoop run_loop; |
| AccessTokenCallback access_token_callback = |
| base::Bind(&AccessTokenFetcherTest::OnAccessTokenRetrieved, |
| base::Unretained(this), run_loop.QuitClosure()); |
| |
| AccessTokenFetcher access_token_fetcher; |
| access_token_fetcher.SetURLLoaderFactoryForTesting(shared_factory()); |
| access_token_fetcher.GetAccessTokenFromAuthCode(kAuthCodeValue, |
| access_token_callback); |
| |
| run_loop.Run(); |
| |
| // Our callback should have been called with empty strings. |
| EXPECT_TRUE(access_token_retrieved_.empty()); |
| EXPECT_TRUE(refresh_token_retrieved_.empty()); |
| } |
| |
| TEST_F(AccessTokenFetcherTest, ExchangeRefreshToken_NetworkError) { |
| SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(), |
| kRefreshTokenExchangeValidResponse, net::HTTP_NOT_FOUND, |
| net::ERR_FAILED); |
| |
| base::RunLoop run_loop; |
| AccessTokenCallback access_token_callback = |
| base::Bind(&AccessTokenFetcherTest::OnAccessTokenRetrieved, |
| base::Unretained(this), run_loop.QuitClosure()); |
| |
| AccessTokenFetcher access_token_fetcher; |
| access_token_fetcher.SetURLLoaderFactoryForTesting(shared_factory()); |
| access_token_fetcher.GetAccessTokenFromRefreshToken(kRefreshTokenValue, |
| access_token_callback); |
| |
| run_loop.Run(); |
| |
| // Our callback should have been called with empty strings. |
| EXPECT_TRUE(access_token_retrieved_.empty()); |
| EXPECT_TRUE(refresh_token_retrieved_.empty()); |
| } |
| |
| TEST_F(AccessTokenFetcherTest, AuthCode_GetTokenInfoResponse_InvalidToken) { |
| SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(), |
| kAuthCodeExchangeValidResponse, net::HTTP_OK, net::OK); |
| |
| SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_info_url(), |
| kInvalidTokenInfoResponse, net::HTTP_OK, net::OK); |
| |
| base::RunLoop run_loop; |
| AccessTokenCallback access_token_callback = |
| base::Bind(&AccessTokenFetcherTest::OnAccessTokenRetrieved, |
| base::Unretained(this), run_loop.QuitClosure()); |
| |
| AccessTokenFetcher access_token_fetcher; |
| access_token_fetcher.SetURLLoaderFactoryForTesting(shared_factory()); |
| access_token_fetcher.GetAccessTokenFromAuthCode(kAuthCodeValue, |
| access_token_callback); |
| |
| run_loop.Run(); |
| |
| // Our callback should have been called with empty strings. |
| EXPECT_TRUE(access_token_retrieved_.empty()); |
| EXPECT_TRUE(refresh_token_retrieved_.empty()); |
| } |
| |
| TEST_F(AccessTokenFetcherTest, ExchangeAuthCodeForAccessToken_EmptyToken) { |
| SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(), |
| kAuthCodeExchangeEmptyResponse, net::HTTP_OK, net::OK); |
| |
| base::RunLoop run_loop; |
| AccessTokenCallback access_token_callback = |
| base::Bind(&AccessTokenFetcherTest::OnAccessTokenRetrieved, |
| base::Unretained(this), run_loop.QuitClosure()); |
| |
| AccessTokenFetcher access_token_fetcher; |
| access_token_fetcher.SetURLLoaderFactoryForTesting(shared_factory()); |
| access_token_fetcher.GetAccessTokenFromAuthCode(kAuthCodeValue, |
| access_token_callback); |
| |
| run_loop.Run(); |
| |
| // Our callback should have been called with empty strings. |
| EXPECT_TRUE(access_token_retrieved_.empty()); |
| EXPECT_TRUE(refresh_token_retrieved_.empty()); |
| } |
| |
| TEST_F(AccessTokenFetcherTest, RefreshToken_GetTokenInfoResponse_InvalidToken) { |
| SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(), |
| kRefreshTokenExchangeValidResponse, net::HTTP_OK, net::OK); |
| |
| SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_info_url(), |
| kInvalidTokenInfoResponse, net::HTTP_OK, net::OK); |
| |
| base::RunLoop run_loop; |
| AccessTokenCallback access_token_callback = |
| base::Bind(&AccessTokenFetcherTest::OnAccessTokenRetrieved, |
| base::Unretained(this), run_loop.QuitClosure()); |
| |
| AccessTokenFetcher access_token_fetcher; |
| access_token_fetcher.SetURLLoaderFactoryForTesting(shared_factory()); |
| access_token_fetcher.GetAccessTokenFromRefreshToken(kRefreshTokenValue, |
| access_token_callback); |
| |
| run_loop.Run(); |
| |
| // Our callback should have been called with empty strings. |
| EXPECT_TRUE(access_token_retrieved_.empty()); |
| EXPECT_TRUE(refresh_token_retrieved_.empty()); |
| } |
| |
| TEST_F(AccessTokenFetcherTest, ExchangeRefreshTokenForAccessToken_EmptyToken) { |
| SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(), |
| kRefreshTokenExchangeEmptyResponse, net::HTTP_OK, net::OK); |
| |
| base::RunLoop run_loop; |
| AccessTokenCallback access_token_callback = |
| base::Bind(&AccessTokenFetcherTest::OnAccessTokenRetrieved, |
| base::Unretained(this), run_loop.QuitClosure()); |
| |
| AccessTokenFetcher access_token_fetcher; |
| access_token_fetcher.SetURLLoaderFactoryForTesting(shared_factory()); |
| access_token_fetcher.GetAccessTokenFromRefreshToken(kRefreshTokenValue, |
| access_token_callback); |
| |
| run_loop.Run(); |
| |
| // Our callback should have been called with empty strings. |
| EXPECT_TRUE(access_token_retrieved_.empty()); |
| EXPECT_TRUE(refresh_token_retrieved_.empty()); |
| } |
| |
| } // namespace test |
| } // namespace remoting |