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.
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/thread_task_runner_handle.h"
12 #include "base/values.h"
13 #include "chrome/browser/supervised_user/child_accounts/family_info_fetcher.h"
14 #include "components/signin/core/browser/fake_profile_oauth2_token_service.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"
20 const char kAccountId
[] = "user@gmail.com";
21 const char kDifferentAccountId
[] = "some_other_user@gmail.com";
22 const int kFamilyInfoFetcherURLFetcherID
= 0;
24 bool operator==(const FamilyInfoFetcher::FamilyProfile
& family1
,
25 const FamilyInfoFetcher::FamilyProfile
& family2
) {
26 return family1
.id
== family2
.id
&&
27 family1
.name
== family2
.name
;
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
;
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
);
52 base::JSONWriter::Write(dict
, &result
);
56 std::string
BuildEmptyGetFamilyProfileResponse() {
57 base::DictionaryValue dict
;
58 base::DictionaryValue
* family_dict
= new base::DictionaryValue
;
59 dict
.SetWithoutPathExpansion("family", family_dict
);
61 base::JSONWriter::Write(dict
, &result
);
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",
84 if (!member
.email
.empty())
85 profile_dict
->SetStringWithoutPathExpansion("email",
87 if (!member
.profile_url
.empty())
88 profile_dict
->SetStringWithoutPathExpansion("profileUrl",
90 if (!member
.profile_image_url
.empty())
91 profile_dict
->SetStringWithoutPathExpansion("profileImageUrl",
92 member
.profile_image_url
);
94 member_dict
->SetWithoutPathExpansion("profile", profile_dict
);
96 list
->Append(member_dict
);
98 dict
.SetWithoutPathExpansion("members", list
);
100 base::JSONWriter::Write(dict
, &result
);
106 class FamilyInfoFetcherTest
: public testing::Test
,
107 public FamilyInfoFetcher::Consumer
{
109 FamilyInfoFetcherTest()
110 : request_context_(new net::TestURLRequestContextGetter(
111 base::ThreadTaskRunnerHandle::Get())),
112 fetcher_(this, kAccountId
, &token_service_
, request_context_
.get()) {}
114 MOCK_METHOD1(OnGetFamilyProfileSuccess
,
115 void(const FamilyInfoFetcher::FamilyProfile
& family
));
116 MOCK_METHOD1(OnGetFamilyMembersSuccess
,
117 void(const std::vector
<FamilyInfoFetcher::FamilyMember
>&
119 MOCK_METHOD1(OnFailure
, void(FamilyInfoFetcher::ErrorCode error
));
122 void IssueRefreshToken() {
123 token_service_
.UpdateCredentials(kAccountId
, "refresh_token");
126 void IssueRefreshTokenForDifferentAccount() {
127 token_service_
.UpdateCredentials(kDifferentAccountId
, "refresh_token");
130 void IssueAccessToken() {
131 token_service_
.IssueAllTokensForAccount(
134 base::Time::Now() + base::TimeDelta::FromHours(1));
137 net::TestURLFetcher
* GetURLFetcher() {
138 net::TestURLFetcher
* url_fetcher
=
139 url_fetcher_factory_
.GetFetcherByID(
140 kFamilyInfoFetcherURLFetcherID
);
141 EXPECT_TRUE(url_fetcher
);
145 void SendResponse(net::URLRequestStatus::Status status
,
146 const std::string
& response
) {
147 net::TestURLFetcher
* url_fetcher
= GetURLFetcher();
148 url_fetcher
->set_status(net::URLRequestStatus(status
, 0));
149 url_fetcher
->set_response_code(net::HTTP_OK
);
150 url_fetcher
->SetResponseString(response
);
151 url_fetcher
->delegate()->OnURLFetchComplete(url_fetcher
);
154 void SendValidGetFamilyProfileResponse(
155 const FamilyInfoFetcher::FamilyProfile
& family
) {
156 SendResponse(net::URLRequestStatus::SUCCESS
,
157 BuildGetFamilyProfileResponse(family
));
160 void SendValidGetFamilyMembersResponse(
161 const std::vector
<FamilyInfoFetcher::FamilyMember
>& members
) {
162 SendResponse(net::URLRequestStatus::SUCCESS
,
163 BuildGetFamilyMembersResponse(members
));
166 void SendInvalidGetFamilyProfileResponse() {
167 SendResponse(net::URLRequestStatus::SUCCESS
,
168 BuildEmptyGetFamilyProfileResponse());
171 void SendFailedResponse() {
172 SendResponse(net::URLRequestStatus::CANCELED
, std::string());
175 base::MessageLoop message_loop_
;
176 FakeProfileOAuth2TokenService token_service_
;
177 scoped_refptr
<net::TestURLRequestContextGetter
> request_context_
;
178 net::TestURLFetcherFactory url_fetcher_factory_
;
179 FamilyInfoFetcher fetcher_
;
183 TEST_F(FamilyInfoFetcherTest
, GetFamilyProfileSuccess
) {
186 fetcher_
.StartGetFamilyProfile();
188 // Since a refresh token is already available, we should immediately get a
189 // request for an access token.
190 EXPECT_EQ(1U, token_service_
.GetPendingRequests().size());
194 FamilyInfoFetcher::FamilyProfile
family("test", "My Test Family");
195 EXPECT_CALL(*this, OnGetFamilyProfileSuccess(family
));
196 SendValidGetFamilyProfileResponse(family
);
199 TEST_F(FamilyInfoFetcherTest
, GetFamilyMembersSuccess
) {
202 fetcher_
.StartGetFamilyMembers();
204 // Since a refresh token is already available, we should immediately get a
205 // request for an access token.
206 EXPECT_EQ(1U, token_service_
.GetPendingRequests().size());
210 std::vector
<FamilyInfoFetcher::FamilyMember
> members
;
212 FamilyInfoFetcher::FamilyMember("someObfuscatedGaiaId",
213 FamilyInfoFetcher::HEAD_OF_HOUSEHOLD
,
216 "http://profile.url/homer",
217 "http://profile.url/homer/image"));
219 FamilyInfoFetcher::FamilyMember("anotherObfuscatedGaiaId",
220 FamilyInfoFetcher::PARENT
,
223 "http://profile.url/marge",
226 FamilyInfoFetcher::FamilyMember("obfuscatedGaiaId3",
227 FamilyInfoFetcher::CHILD
,
231 "http://profile.url/lisa/image"));
233 FamilyInfoFetcher::FamilyMember("obfuscatedGaiaId4",
234 FamilyInfoFetcher::CHILD
,
240 FamilyInfoFetcher::FamilyMember("obfuscatedGaiaId5",
241 FamilyInfoFetcher::MEMBER
,
247 EXPECT_CALL(*this, OnGetFamilyMembersSuccess(members
));
248 SendValidGetFamilyMembersResponse(members
);
252 TEST_F(FamilyInfoFetcherTest
, SuccessAfterWaitingForRefreshToken
) {
253 fetcher_
.StartGetFamilyProfile();
255 // Since there is no refresh token yet, we should not get a request for an
256 // access token at this point.
257 EXPECT_EQ(0U, token_service_
.GetPendingRequests().size());
261 // Now there is a refresh token and we should have got a request for an
263 EXPECT_EQ(1U, token_service_
.GetPendingRequests().size());
267 FamilyInfoFetcher::FamilyProfile
family("test", "My Test Family");
268 EXPECT_CALL(*this, OnGetFamilyProfileSuccess(family
));
269 SendValidGetFamilyProfileResponse(family
);
272 TEST_F(FamilyInfoFetcherTest
, NoRefreshToken
) {
273 fetcher_
.StartGetFamilyProfile();
275 IssueRefreshTokenForDifferentAccount();
277 // Credentials for a different user should be ignored, i.e. not result in a
278 // request for an access token.
279 EXPECT_EQ(0U, token_service_
.GetPendingRequests().size());
281 // After all refresh tokens have been loaded, there is still no token for our
282 // user, so we expect a token error.
283 EXPECT_CALL(*this, OnFailure(FamilyInfoFetcher::TOKEN_ERROR
));
284 token_service_
.LoadCredentials("");
287 TEST_F(FamilyInfoFetcherTest
, GetTokenFailure
) {
290 fetcher_
.StartGetFamilyProfile();
292 // On failure to get an access token we expect a token error.
293 EXPECT_CALL(*this, OnFailure(FamilyInfoFetcher::TOKEN_ERROR
));
294 token_service_
.IssueErrorForAllPendingRequestsForAccount(
296 GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS
));
299 TEST_F(FamilyInfoFetcherTest
, InvalidResponse
) {
302 fetcher_
.StartGetFamilyProfile();
306 // Invalid response data should result in a service error.
307 EXPECT_CALL(*this, OnFailure(FamilyInfoFetcher::SERVICE_ERROR
));
308 SendInvalidGetFamilyProfileResponse();
311 TEST_F(FamilyInfoFetcherTest
, FailedResponse
) {
314 fetcher_
.StartGetFamilyProfile();
318 // Failed API call should result in a network error.
319 EXPECT_CALL(*this, OnFailure(FamilyInfoFetcher::NETWORK_ERROR
));
320 SendFailedResponse();