Home | History | Annotate | Download | only in gcm
      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 "chrome/browser/services/gcm/gcm_account_tracker.h"
      6 
      7 #include <map>
      8 #include <string>
      9 
     10 #include "base/memory/scoped_ptr.h"
     11 #include "base/message_loop/message_loop.h"
     12 #include "google_apis/gaia/fake_identity_provider.h"
     13 #include "google_apis/gaia/fake_oauth2_token_service.h"
     14 #include "google_apis/gaia/google_service_auth_error.h"
     15 #include "net/http/http_status_code.h"
     16 #include "net/url_request/test_url_fetcher_factory.h"
     17 #include "net/url_request/url_request_test_util.h"
     18 #include "testing/gtest/include/gtest/gtest.h"
     19 
     20 namespace gcm {
     21 
     22 namespace {
     23 
     24 const char kAccountId1[] = "account_1";
     25 const char kAccountId2[] = "account_2";
     26 
     27 std::string AccountKeyToObfuscatedId(const std::string email) {
     28   return "obfid-" + email;
     29 }
     30 
     31 std::string GetValidTokenInfoResponse(const std::string account_key) {
     32   return std::string("{ \"id\": \"") + AccountKeyToObfuscatedId(account_key) +
     33          "\" }";
     34 }
     35 
     36 std::string MakeAccessToken(const std::string& account_key) {
     37   return "access_token-" + account_key;
     38 }
     39 
     40 }  // namespace
     41 
     42 class GCMAccountTrackerTest : public testing::Test {
     43  public:
     44   GCMAccountTrackerTest();
     45   virtual ~GCMAccountTrackerTest();
     46 
     47   // Callback for the account tracker.
     48   void UpdateAccounts(const std::map<std::string, std::string>& accounts);
     49 
     50   // Helpers to pass fake events to the tracker. Tests should have either a pair
     51   // of Start/FinishAccountSignIn or SignInAccount per account. Don't mix.
     52   // Call to SignOutAccount is not mandatory.
     53   void StartAccountSignIn(const std::string& account_key);
     54   void FinishAccountSignIn(const std::string& account_key);
     55   void SignInAccount(const std::string& account_key);
     56   void SignOutAccount(const std::string& account_key);
     57 
     58   // Helpers for dealing with OAuth2 access token requests.
     59   void IssueAccessToken(const std::string& account_key);
     60   void IssueError(const std::string& account_key);
     61 
     62   // Test results and helpers.
     63   void ResetResults();
     64   bool update_accounts_called() const { return update_accounts_called_; }
     65   const std::map<std::string, std::string>& accounts() const {
     66     return accounts_;
     67   }
     68 
     69   // Accessor to account tracker.
     70   GCMAccountTracker* tracker() { return tracker_.get(); }
     71 
     72  private:
     73   std::map<std::string, std::string> accounts_;
     74   bool update_accounts_called_;
     75 
     76   base::MessageLoop message_loop_;
     77   net::TestURLFetcherFactory test_fetcher_factory_;
     78   scoped_ptr<FakeOAuth2TokenService> fake_token_service_;
     79   scoped_ptr<FakeIdentityProvider> fake_identity_provider_;
     80   scoped_ptr<GCMAccountTracker> tracker_;
     81 };
     82 
     83 GCMAccountTrackerTest::GCMAccountTrackerTest()
     84     : update_accounts_called_(false) {
     85   fake_token_service_.reset(new FakeOAuth2TokenService());
     86 
     87   fake_identity_provider_.reset(
     88       new FakeIdentityProvider(fake_token_service_.get()));
     89 
     90   scoped_ptr<gaia::AccountTracker> gaia_account_tracker(
     91       new gaia::AccountTracker(fake_identity_provider_.get(),
     92                                new net::TestURLRequestContextGetter(
     93                                    message_loop_.message_loop_proxy())));
     94 
     95   tracker_.reset(new GCMAccountTracker(
     96       gaia_account_tracker.Pass(),
     97       base::Bind(&GCMAccountTrackerTest::UpdateAccounts,
     98                  base::Unretained(this))));
     99 }
    100 
    101 GCMAccountTrackerTest::~GCMAccountTrackerTest() {
    102   if (tracker_)
    103     tracker_->Shutdown();
    104 }
    105 
    106 void GCMAccountTrackerTest::UpdateAccounts(
    107     const std::map<std::string, std::string>& accounts) {
    108   update_accounts_called_ = true;
    109   accounts_ = accounts;
    110 }
    111 
    112 void GCMAccountTrackerTest::ResetResults() {
    113   accounts_.clear();
    114   update_accounts_called_ = false;
    115 }
    116 
    117 void GCMAccountTrackerTest::StartAccountSignIn(const std::string& account_key) {
    118   fake_identity_provider_->LogIn(account_key);
    119   fake_token_service_->AddAccount(account_key);
    120 }
    121 
    122 void GCMAccountTrackerTest::FinishAccountSignIn(
    123     const std::string& account_key) {
    124   IssueAccessToken(account_key);
    125 
    126   net::TestURLFetcher* fetcher = test_fetcher_factory_.GetFetcherByID(
    127       gaia::GaiaOAuthClient::kUrlFetcherId);
    128   ASSERT_TRUE(fetcher);
    129   fetcher->set_response_code(net::HTTP_OK);
    130   fetcher->SetResponseString(GetValidTokenInfoResponse(account_key));
    131   fetcher->delegate()->OnURLFetchComplete(fetcher);
    132 }
    133 
    134 void GCMAccountTrackerTest::SignInAccount(const std::string& account_key) {
    135   StartAccountSignIn(account_key);
    136   FinishAccountSignIn(account_key);
    137 }
    138 
    139 void GCMAccountTrackerTest::SignOutAccount(const std::string& account_key) {
    140   fake_token_service_->RemoveAccount(account_key);
    141 }
    142 
    143 void GCMAccountTrackerTest::IssueAccessToken(const std::string& account_key) {
    144   fake_token_service_->IssueAllTokensForAccount(
    145       account_key, MakeAccessToken(account_key), base::Time::Max());
    146 }
    147 
    148 void GCMAccountTrackerTest::IssueError(const std::string& account_key) {
    149   fake_token_service_->IssueErrorForAllPendingRequestsForAccount(
    150       account_key,
    151       GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE));
    152 }
    153 
    154 TEST_F(GCMAccountTrackerTest, NoAccounts) {
    155   EXPECT_FALSE(update_accounts_called());
    156   tracker()->Start();
    157   // Callback should not be called if there where no accounts provided.
    158   EXPECT_FALSE(update_accounts_called());
    159   EXPECT_TRUE(accounts().empty());
    160   tracker()->Stop();
    161 }
    162 
    163 // Verifies that callback is called after a token is issued for a single account
    164 // with a specific scope. In this scenario, the underlying account tracker is
    165 // still working when the CompleteCollectingTokens is called for the first time.
    166 TEST_F(GCMAccountTrackerTest, SingleAccount) {
    167   StartAccountSignIn(kAccountId1);
    168 
    169   tracker()->Start();
    170   // We don't have any accounts to report, but given the inner account tracker
    171   // is still working we don't make a call with empty accounts list.
    172   EXPECT_FALSE(update_accounts_called());
    173 
    174   // This concludes the work of inner account tracker.
    175   FinishAccountSignIn(kAccountId1);
    176   IssueAccessToken(kAccountId1);
    177 
    178   EXPECT_TRUE(update_accounts_called());
    179 
    180   std::map<std::string, std::string> expected_accounts;
    181   expected_accounts[kAccountId1] = MakeAccessToken(kAccountId1);
    182   EXPECT_EQ(expected_accounts, accounts());
    183   tracker()->Stop();
    184 }
    185 
    186 TEST_F(GCMAccountTrackerTest, MultipleAccounts) {
    187   StartAccountSignIn(kAccountId1);
    188   StartAccountSignIn(kAccountId2);
    189 
    190   tracker()->Start();
    191   EXPECT_FALSE(update_accounts_called());
    192 
    193   FinishAccountSignIn(kAccountId1);
    194   IssueAccessToken(kAccountId1);
    195   EXPECT_FALSE(update_accounts_called());
    196 
    197   FinishAccountSignIn(kAccountId2);
    198   IssueAccessToken(kAccountId2);
    199   EXPECT_TRUE(update_accounts_called());
    200 
    201   std::map<std::string, std::string> expected_accounts;
    202   expected_accounts[kAccountId1] = MakeAccessToken(kAccountId1);
    203   expected_accounts[kAccountId2] = MakeAccessToken(kAccountId2);
    204   EXPECT_EQ(expected_accounts, accounts());
    205 
    206   tracker()->Stop();
    207 }
    208 
    209 TEST_F(GCMAccountTrackerTest, AccountAdded) {
    210   tracker()->Start();
    211   ResetResults();
    212 
    213   SignInAccount(kAccountId1);
    214   EXPECT_FALSE(update_accounts_called());
    215 
    216   IssueAccessToken(kAccountId1);
    217   EXPECT_TRUE(update_accounts_called());
    218 
    219   std::map<std::string, std::string> expected_accounts;
    220   expected_accounts[kAccountId1] = MakeAccessToken(kAccountId1);
    221   EXPECT_EQ(expected_accounts, accounts());
    222 
    223   tracker()->Stop();
    224 }
    225 
    226 TEST_F(GCMAccountTrackerTest, AccountRemoved) {
    227   SignInAccount(kAccountId1);
    228   SignInAccount(kAccountId2);
    229 
    230   tracker()->Start();
    231   IssueAccessToken(kAccountId1);
    232   IssueAccessToken(kAccountId2);
    233   EXPECT_TRUE(update_accounts_called());
    234 
    235   ResetResults();
    236   EXPECT_FALSE(update_accounts_called());
    237 
    238   SignOutAccount(kAccountId2);
    239   EXPECT_TRUE(update_accounts_called());
    240 
    241   std::map<std::string, std::string> expected_accounts;
    242   expected_accounts[kAccountId1] = MakeAccessToken(kAccountId1);
    243   EXPECT_EQ(expected_accounts, accounts());
    244 
    245   tracker()->Stop();
    246 }
    247 
    248 TEST_F(GCMAccountTrackerTest, GetTokenFailed) {
    249   SignInAccount(kAccountId1);
    250   SignInAccount(kAccountId2);
    251 
    252   tracker()->Start();
    253   IssueAccessToken(kAccountId1);
    254   EXPECT_FALSE(update_accounts_called());
    255 
    256   IssueError(kAccountId2);
    257   EXPECT_TRUE(update_accounts_called());
    258 
    259   std::map<std::string, std::string> expected_accounts;
    260   expected_accounts[kAccountId1] = MakeAccessToken(kAccountId1);
    261   EXPECT_EQ(expected_accounts, accounts());
    262 
    263   tracker()->Stop();
    264 }
    265 
    266 TEST_F(GCMAccountTrackerTest, GetTokenFailedAccountRemoved) {
    267   SignInAccount(kAccountId1);
    268   SignInAccount(kAccountId2);
    269 
    270   tracker()->Start();
    271   IssueAccessToken(kAccountId1);
    272   IssueError(kAccountId2);
    273 
    274   ResetResults();
    275   SignOutAccount(kAccountId2);
    276   EXPECT_TRUE(update_accounts_called());
    277 
    278   std::map<std::string, std::string> expected_accounts;
    279   expected_accounts[kAccountId1] = MakeAccessToken(kAccountId1);
    280   EXPECT_EQ(expected_accounts, accounts());
    281 
    282   tracker()->Stop();
    283 }
    284 
    285 TEST_F(GCMAccountTrackerTest, AccountRemovedWhileRequestsPending) {
    286   SignInAccount(kAccountId1);
    287   SignInAccount(kAccountId2);
    288 
    289   tracker()->Start();
    290   IssueAccessToken(kAccountId1);
    291   EXPECT_FALSE(update_accounts_called());
    292 
    293   SignOutAccount(kAccountId2);
    294   IssueAccessToken(kAccountId2);
    295   EXPECT_TRUE(update_accounts_called());
    296 
    297   std::map<std::string, std::string> expected_accounts;
    298   expected_accounts[kAccountId1] = MakeAccessToken(kAccountId1);
    299   EXPECT_EQ(expected_accounts, accounts());
    300 
    301   tracker()->Stop();
    302 }
    303 
    304 // TODO(fgorski): Add test for adding account after removal >> make sure it does
    305 // not mark removal.
    306 
    307 }  // namespace gcm
    308