| OLD | NEW |
| (Empty) | |
| 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 #include <vector> |
| 7 |
| 8 #include "base/json/json_writer.h" |
| 9 #include "base/memory/scoped_ptr.h" |
| 10 #include "base/message_loop/message_loop.h" |
| 11 #include "base/message_loop/message_loop_proxy.h" |
| 12 #include "base/values.h" |
| 13 #include "chrome/browser/signin/fake_profile_oauth2_token_service.h" |
| 14 #include "chrome/browser/supervised_user/child_accounts/family_info_fetcher.h" |
| 15 #include "net/url_request/test_url_fetcher_factory.h" |
| 16 #include "net/url_request/url_request_test_util.h" |
| 17 #include "testing/gmock/include/gmock/gmock.h" |
| 18 #include "testing/gtest/include/gtest/gtest.h" |
| 19 |
| 20 const char kAccountId[] = "user@gmail.com"; |
| 21 const char kDifferentAccountId[] = "some_other_user@gmail.com"; |
| 22 const int kFamilyInfoFetcherURLFetcherID = 0; |
| 23 |
| 24 bool operator==(const FamilyInfoFetcher::FamilyProfile& family1, |
| 25 const FamilyInfoFetcher::FamilyProfile& family2) { |
| 26 return family1.id == family2.id && |
| 27 family1.name == family2.name; |
| 28 } |
| 29 |
| 30 bool operator==(const FamilyInfoFetcher::FamilyMember& account1, |
| 31 const FamilyInfoFetcher::FamilyMember& account2) { |
| 32 return account1.obfuscated_gaia_id == account2.obfuscated_gaia_id && |
| 33 account1.role == account2.role && |
| 34 account1.display_name == account2.display_name && |
| 35 account1.email == account2.email && |
| 36 account1.profile_url == account2.profile_url && |
| 37 account1.profile_image_url == account2.profile_image_url; |
| 38 } |
| 39 |
| 40 namespace { |
| 41 |
| 42 std::string BuildGetFamilyProfileResponse( |
| 43 const FamilyInfoFetcher::FamilyProfile& family) { |
| 44 base::DictionaryValue dict; |
| 45 base::DictionaryValue* family_dict = new base::DictionaryValue; |
| 46 family_dict->SetStringWithoutPathExpansion("familyId", family.id); |
| 47 base::DictionaryValue* profile_dict = new base::DictionaryValue; |
| 48 profile_dict->SetStringWithoutPathExpansion("name", family.name); |
| 49 family_dict->SetWithoutPathExpansion("profile", profile_dict); |
| 50 dict.SetWithoutPathExpansion("family", family_dict); |
| 51 std::string result; |
| 52 base::JSONWriter::Write(&dict, &result); |
| 53 return result; |
| 54 } |
| 55 |
| 56 std::string BuildEmptyGetFamilyProfileResponse() { |
| 57 base::DictionaryValue dict; |
| 58 base::DictionaryValue* family_dict = new base::DictionaryValue; |
| 59 dict.SetWithoutPathExpansion("family", family_dict); |
| 60 std::string result; |
| 61 base::JSONWriter::Write(&dict, &result); |
| 62 return result; |
| 63 } |
| 64 |
| 65 std::string BuildGetFamilyMembersResponse( |
| 66 const std::vector<FamilyInfoFetcher::FamilyMember>& members) { |
| 67 base::DictionaryValue dict; |
| 68 base::ListValue* list = new base::ListValue; |
| 69 for (size_t i = 0; i < members.size(); i++) { |
| 70 const FamilyInfoFetcher::FamilyMember& member = members[i]; |
| 71 base::DictionaryValue* member_dict = new base::DictionaryValue; |
| 72 member_dict->SetStringWithoutPathExpansion("userId", |
| 73 member.obfuscated_gaia_id); |
| 74 member_dict->SetStringWithoutPathExpansion( |
| 75 "role", FamilyInfoFetcher::RoleToString(member.role)); |
| 76 if (!member.display_name.empty() || |
| 77 !member.email.empty() || |
| 78 !member.profile_url.empty() || |
| 79 !member.profile_image_url.empty()) { |
| 80 base::DictionaryValue* profile_dict = new base::DictionaryValue; |
| 81 if (!member.display_name.empty()) |
| 82 profile_dict->SetStringWithoutPathExpansion("displayName", |
| 83 member.display_name); |
| 84 if (!member.email.empty()) |
| 85 profile_dict->SetStringWithoutPathExpansion("email", |
| 86 member.email); |
| 87 if (!member.profile_url.empty()) |
| 88 profile_dict->SetStringWithoutPathExpansion("profileUrl", |
| 89 member.profile_url); |
| 90 if (!member.profile_image_url.empty()) |
| 91 profile_dict->SetStringWithoutPathExpansion("profileImageUrl", |
| 92 member.profile_image_url); |
| 93 |
| 94 member_dict->SetWithoutPathExpansion("profile", profile_dict); |
| 95 } |
| 96 list->Append(member_dict); |
| 97 } |
| 98 dict.SetWithoutPathExpansion("members", list); |
| 99 std::string result; |
| 100 base::JSONWriter::Write(&dict, &result); |
| 101 return result; |
| 102 } |
| 103 |
| 104 } // namespace |
| 105 |
| 106 class FamilyInfoFetcherTest : public testing::Test, |
| 107 public FamilyInfoFetcher::Consumer { |
| 108 public: |
| 109 FamilyInfoFetcherTest() |
| 110 : request_context_(new net::TestURLRequestContextGetter( |
| 111 base::MessageLoopProxy::current())), |
| 112 fetcher_(this, kAccountId, &token_service_, request_context_.get()) { |
| 113 } |
| 114 |
| 115 MOCK_METHOD1(OnGetFamilyProfileSuccess, |
| 116 void(const FamilyInfoFetcher::FamilyProfile& family)); |
| 117 MOCK_METHOD1(OnGetFamilyMembersSuccess, |
| 118 void(const std::vector<FamilyInfoFetcher::FamilyMember>& |
| 119 members)); |
| 120 MOCK_METHOD1(OnFailure, void(FamilyInfoFetcher::ErrorCode error)); |
| 121 |
| 122 protected: |
| 123 void IssueRefreshToken() { |
| 124 token_service_.UpdateCredentials(kAccountId, "refresh_token"); |
| 125 } |
| 126 |
| 127 void IssueRefreshTokenForDifferentAccount() { |
| 128 token_service_.UpdateCredentials(kDifferentAccountId, "refresh_token"); |
| 129 } |
| 130 |
| 131 void IssueAccessToken() { |
| 132 token_service_.IssueAllTokensForAccount( |
| 133 kAccountId, |
| 134 "access_token", |
| 135 base::Time::Now() + base::TimeDelta::FromHours(1)); |
| 136 } |
| 137 |
| 138 net::TestURLFetcher* GetURLFetcher() { |
| 139 net::TestURLFetcher* url_fetcher = |
| 140 url_fetcher_factory_.GetFetcherByID( |
| 141 kFamilyInfoFetcherURLFetcherID); |
| 142 EXPECT_TRUE(url_fetcher); |
| 143 return url_fetcher; |
| 144 } |
| 145 |
| 146 void SendResponse(net::URLRequestStatus::Status status, |
| 147 const std::string& response) { |
| 148 net::TestURLFetcher* url_fetcher = GetURLFetcher(); |
| 149 url_fetcher->set_status(net::URLRequestStatus(status, 0)); |
| 150 url_fetcher->set_response_code(net::HTTP_OK); |
| 151 url_fetcher->SetResponseString(response); |
| 152 url_fetcher->delegate()->OnURLFetchComplete(url_fetcher); |
| 153 } |
| 154 |
| 155 void SendValidGetFamilyProfileResponse( |
| 156 const FamilyInfoFetcher::FamilyProfile& family) { |
| 157 SendResponse(net::URLRequestStatus::SUCCESS, |
| 158 BuildGetFamilyProfileResponse(family)); |
| 159 } |
| 160 |
| 161 void SendValidGetFamilyMembersResponse( |
| 162 const std::vector<FamilyInfoFetcher::FamilyMember>& members) { |
| 163 SendResponse(net::URLRequestStatus::SUCCESS, |
| 164 BuildGetFamilyMembersResponse(members)); |
| 165 } |
| 166 |
| 167 void SendInvalidGetFamilyProfileResponse() { |
| 168 SendResponse(net::URLRequestStatus::SUCCESS, |
| 169 BuildEmptyGetFamilyProfileResponse()); |
| 170 } |
| 171 |
| 172 void SendFailedResponse() { |
| 173 SendResponse(net::URLRequestStatus::CANCELED, std::string()); |
| 174 } |
| 175 |
| 176 base::MessageLoop message_loop_; |
| 177 FakeProfileOAuth2TokenService token_service_; |
| 178 scoped_refptr<net::TestURLRequestContextGetter> request_context_; |
| 179 net::TestURLFetcherFactory url_fetcher_factory_; |
| 180 FamilyInfoFetcher fetcher_; |
| 181 }; |
| 182 |
| 183 |
| 184 TEST_F(FamilyInfoFetcherTest, GetFamilyProfileSuccess) { |
| 185 IssueRefreshToken(); |
| 186 |
| 187 fetcher_.StartGetFamilyProfile(); |
| 188 |
| 189 // Since a refresh token is already available, we should immediately get a |
| 190 // request for an access token. |
| 191 EXPECT_EQ(1U, token_service_.GetPendingRequests().size()); |
| 192 |
| 193 IssueAccessToken(); |
| 194 |
| 195 FamilyInfoFetcher::FamilyProfile family("test", "My Test Family"); |
| 196 EXPECT_CALL(*this, OnGetFamilyProfileSuccess(family)); |
| 197 SendValidGetFamilyProfileResponse(family); |
| 198 } |
| 199 |
| 200 TEST_F(FamilyInfoFetcherTest, GetFamilyMembersSuccess) { |
| 201 IssueRefreshToken(); |
| 202 |
| 203 fetcher_.StartGetFamilyMembers(); |
| 204 |
| 205 // Since a refresh token is already available, we should immediately get a |
| 206 // request for an access token. |
| 207 EXPECT_EQ(1U, token_service_.GetPendingRequests().size()); |
| 208 |
| 209 IssueAccessToken(); |
| 210 |
| 211 std::vector<FamilyInfoFetcher::FamilyMember> members; |
| 212 members.push_back( |
| 213 FamilyInfoFetcher::FamilyMember("someObfuscatedGaiaId", |
| 214 FamilyInfoFetcher::HEAD_OF_HOUSEHOLD, |
| 215 "Homer Simpson", |
| 216 "homer@simpson.com", |
| 217 "http://profile.url/homer", |
| 218 "http://profile.url/homer/image")); |
| 219 members.push_back( |
| 220 FamilyInfoFetcher::FamilyMember("anotherObfuscatedGaiaId", |
| 221 FamilyInfoFetcher::PARENT, |
| 222 "Marge Simpson", |
| 223 std::string(), |
| 224 "http://profile.url/marge", |
| 225 std::string())); |
| 226 members.push_back( |
| 227 FamilyInfoFetcher::FamilyMember("obfuscatedGaiaId3", |
| 228 FamilyInfoFetcher::CHILD, |
| 229 "Lisa Simpson", |
| 230 "lisa@gmail.com", |
| 231 std::string(), |
| 232 "http://profile.url/lisa/image")); |
| 233 members.push_back( |
| 234 FamilyInfoFetcher::FamilyMember("obfuscatedGaiaId4", |
| 235 FamilyInfoFetcher::CHILD, |
| 236 "Bart Simpson", |
| 237 "bart@bart.bart", |
| 238 std::string(), |
| 239 std::string())); |
| 240 members.push_back( |
| 241 FamilyInfoFetcher::FamilyMember("obfuscatedGaiaId5", |
| 242 FamilyInfoFetcher::MEMBER, |
| 243 std::string(), |
| 244 std::string(), |
| 245 std::string(), |
| 246 std::string())); |
| 247 |
| 248 EXPECT_CALL(*this, OnGetFamilyMembersSuccess(members)); |
| 249 SendValidGetFamilyMembersResponse(members); |
| 250 } |
| 251 |
| 252 |
| 253 TEST_F(FamilyInfoFetcherTest, SuccessAfterWaitingForRefreshToken) { |
| 254 fetcher_.StartGetFamilyProfile(); |
| 255 |
| 256 // Since there is no refresh token yet, we should not get a request for an |
| 257 // access token at this point. |
| 258 EXPECT_EQ(0U, token_service_.GetPendingRequests().size()); |
| 259 |
| 260 IssueRefreshToken(); |
| 261 |
| 262 // Now there is a refresh token and we should have got a request for an |
| 263 // access token. |
| 264 EXPECT_EQ(1U, token_service_.GetPendingRequests().size()); |
| 265 |
| 266 IssueAccessToken(); |
| 267 |
| 268 FamilyInfoFetcher::FamilyProfile family("test", "My Test Family"); |
| 269 EXPECT_CALL(*this, OnGetFamilyProfileSuccess(family)); |
| 270 SendValidGetFamilyProfileResponse(family); |
| 271 } |
| 272 |
| 273 TEST_F(FamilyInfoFetcherTest, NoRefreshToken) { |
| 274 fetcher_.StartGetFamilyProfile(); |
| 275 |
| 276 IssueRefreshTokenForDifferentAccount(); |
| 277 |
| 278 // Credentials for a different user should be ignored, i.e. not result in a |
| 279 // request for an access token. |
| 280 EXPECT_EQ(0U, token_service_.GetPendingRequests().size()); |
| 281 |
| 282 // After all refresh tokens have been loaded, there is still no token for our |
| 283 // user, so we expect a token error. |
| 284 EXPECT_CALL(*this, OnFailure(FamilyInfoFetcher::TOKEN_ERROR)); |
| 285 token_service_.IssueAllRefreshTokensLoaded(); |
| 286 } |
| 287 |
| 288 TEST_F(FamilyInfoFetcherTest, GetTokenFailure) { |
| 289 IssueRefreshToken(); |
| 290 |
| 291 fetcher_.StartGetFamilyProfile(); |
| 292 |
| 293 // On failure to get an access token we expect a token error. |
| 294 EXPECT_CALL(*this, OnFailure(FamilyInfoFetcher::TOKEN_ERROR)); |
| 295 token_service_.IssueErrorForAllPendingRequestsForAccount( |
| 296 kAccountId, |
| 297 GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); |
| 298 } |
| 299 |
| 300 TEST_F(FamilyInfoFetcherTest, InvalidResponse) { |
| 301 IssueRefreshToken(); |
| 302 |
| 303 fetcher_.StartGetFamilyProfile(); |
| 304 |
| 305 IssueAccessToken(); |
| 306 |
| 307 // Invalid response data should result in a service error. |
| 308 EXPECT_CALL(*this, OnFailure(FamilyInfoFetcher::SERVICE_ERROR)); |
| 309 SendInvalidGetFamilyProfileResponse(); |
| 310 } |
| 311 |
| 312 TEST_F(FamilyInfoFetcherTest, FailedResponse) { |
| 313 IssueRefreshToken(); |
| 314 |
| 315 fetcher_.StartGetFamilyProfile(); |
| 316 |
| 317 IssueAccessToken(); |
| 318 |
| 319 // Failed API call should result in a network error. |
| 320 EXPECT_CALL(*this, OnFailure(FamilyInfoFetcher::NETWORK_ERROR)); |
| 321 SendFailedResponse(); |
| 322 } |
| OLD | NEW |