blob: 5622064f0736318f1a59489394d937ff69852252 [file] [log] [blame]
// Copyright 2014 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 "chrome/browser/supervised_user/child_accounts/family_info_fetcher.h"
#include <stddef.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/json/json_writer.h"
#include "base/message_loop/message_loop.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
#include "net/base/net_errors.h"
#include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
const char kAccountId[] = "user@gmail.com";
const char kDifferentAccountId[] = "some_other_user@gmail.com";
const int kFamilyInfoFetcherURLFetcherID = 0;
bool operator==(const FamilyInfoFetcher::FamilyProfile& family1,
const FamilyInfoFetcher::FamilyProfile& family2) {
return family1.id == family2.id &&
family1.name == family2.name;
}
bool operator==(const FamilyInfoFetcher::FamilyMember& account1,
const FamilyInfoFetcher::FamilyMember& account2) {
return account1.obfuscated_gaia_id == account2.obfuscated_gaia_id &&
account1.role == account2.role &&
account1.display_name == account2.display_name &&
account1.email == account2.email &&
account1.profile_url == account2.profile_url &&
account1.profile_image_url == account2.profile_image_url;
}
namespace {
std::string BuildGetFamilyProfileResponse(
const FamilyInfoFetcher::FamilyProfile& family) {
base::DictionaryValue dict;
base::DictionaryValue* family_dict = new base::DictionaryValue;
family_dict->SetStringWithoutPathExpansion("familyId", family.id);
base::DictionaryValue* profile_dict = new base::DictionaryValue;
profile_dict->SetStringWithoutPathExpansion("name", family.name);
family_dict->SetWithoutPathExpansion("profile", profile_dict);
dict.SetWithoutPathExpansion("family", family_dict);
std::string result;
base::JSONWriter::Write(dict, &result);
return result;
}
std::string BuildEmptyGetFamilyProfileResponse() {
base::DictionaryValue dict;
base::DictionaryValue* family_dict = new base::DictionaryValue;
dict.SetWithoutPathExpansion("family", family_dict);
std::string result;
base::JSONWriter::Write(dict, &result);
return result;
}
std::string BuildGetFamilyMembersResponse(
const std::vector<FamilyInfoFetcher::FamilyMember>& members) {
base::DictionaryValue dict;
base::ListValue* list = new base::ListValue;
for (size_t i = 0; i < members.size(); i++) {
const FamilyInfoFetcher::FamilyMember& member = members[i];
std::unique_ptr<base::DictionaryValue> member_dict(
new base::DictionaryValue);
member_dict->SetStringWithoutPathExpansion("userId",
member.obfuscated_gaia_id);
member_dict->SetStringWithoutPathExpansion(
"role", FamilyInfoFetcher::RoleToString(member.role));
if (!member.display_name.empty() ||
!member.email.empty() ||
!member.profile_url.empty() ||
!member.profile_image_url.empty()) {
base::DictionaryValue* profile_dict = new base::DictionaryValue;
if (!member.display_name.empty())
profile_dict->SetStringWithoutPathExpansion("displayName",
member.display_name);
if (!member.email.empty())
profile_dict->SetStringWithoutPathExpansion("email",
member.email);
if (!member.profile_url.empty())
profile_dict->SetStringWithoutPathExpansion("profileUrl",
member.profile_url);
if (!member.profile_image_url.empty())
profile_dict->SetStringWithoutPathExpansion("profileImageUrl",
member.profile_image_url);
member_dict->SetWithoutPathExpansion("profile", profile_dict);
}
list->Append(std::move(member_dict));
}
dict.SetWithoutPathExpansion("members", list);
std::string result;
base::JSONWriter::Write(dict, &result);
return result;
}
} // namespace
class FamilyInfoFetcherTest : public testing::Test,
public FamilyInfoFetcher::Consumer {
public:
FamilyInfoFetcherTest()
: request_context_(new net::TestURLRequestContextGetter(
base::ThreadTaskRunnerHandle::Get())),
fetcher_(this, kAccountId, &token_service_, request_context_.get()) {}
MOCK_METHOD1(OnGetFamilyProfileSuccess,
void(const FamilyInfoFetcher::FamilyProfile& family));
MOCK_METHOD1(OnGetFamilyMembersSuccess,
void(const std::vector<FamilyInfoFetcher::FamilyMember>&
members));
MOCK_METHOD1(OnFailure, void(FamilyInfoFetcher::ErrorCode error));
protected:
void IssueRefreshToken() {
token_service_.UpdateCredentials(kAccountId, "refresh_token");
}
void IssueRefreshTokenForDifferentAccount() {
token_service_.UpdateCredentials(kDifferentAccountId, "refresh_token");
}
void IssueAccessToken() {
token_service_.IssueAllTokensForAccount(
kAccountId,
"access_token",
base::Time::Now() + base::TimeDelta::FromHours(1));
}
net::TestURLFetcher* GetURLFetcher() {
net::TestURLFetcher* url_fetcher =
url_fetcher_factory_.GetFetcherByID(
kFamilyInfoFetcherURLFetcherID);
EXPECT_TRUE(url_fetcher);
return url_fetcher;
}
void SendResponse(net::Error error, const std::string& response) {
net::TestURLFetcher* url_fetcher = GetURLFetcher();
url_fetcher->set_status(net::URLRequestStatus::FromError(error));
url_fetcher->set_response_code(net::HTTP_OK);
url_fetcher->SetResponseString(response);
url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
}
void SendValidGetFamilyProfileResponse(
const FamilyInfoFetcher::FamilyProfile& family) {
SendResponse(net::OK, BuildGetFamilyProfileResponse(family));
}
void SendValidGetFamilyMembersResponse(
const std::vector<FamilyInfoFetcher::FamilyMember>& members) {
SendResponse(net::OK, BuildGetFamilyMembersResponse(members));
}
void SendInvalidGetFamilyProfileResponse() {
SendResponse(net::OK, BuildEmptyGetFamilyProfileResponse());
}
void SendFailedResponse() {
SendResponse(net::ERR_ABORTED, std::string());
}
base::MessageLoop message_loop_;
FakeProfileOAuth2TokenService token_service_;
scoped_refptr<net::TestURLRequestContextGetter> request_context_;
net::TestURLFetcherFactory url_fetcher_factory_;
FamilyInfoFetcher fetcher_;
};
TEST_F(FamilyInfoFetcherTest, GetFamilyProfileSuccess) {
IssueRefreshToken();
fetcher_.StartGetFamilyProfile();
// Since a refresh token is already available, we should immediately get a
// request for an access token.
EXPECT_EQ(1U, token_service_.GetPendingRequests().size());
IssueAccessToken();
FamilyInfoFetcher::FamilyProfile family("test", "My Test Family");
EXPECT_CALL(*this, OnGetFamilyProfileSuccess(family));
SendValidGetFamilyProfileResponse(family);
}
TEST_F(FamilyInfoFetcherTest, GetFamilyMembersSuccess) {
IssueRefreshToken();
fetcher_.StartGetFamilyMembers();
// Since a refresh token is already available, we should immediately get a
// request for an access token.
EXPECT_EQ(1U, token_service_.GetPendingRequests().size());
IssueAccessToken();
std::vector<FamilyInfoFetcher::FamilyMember> members;
members.push_back(
FamilyInfoFetcher::FamilyMember("someObfuscatedGaiaId",
FamilyInfoFetcher::HEAD_OF_HOUSEHOLD,
"Homer Simpson",
"homer@simpson.com",
"http://profile.url/homer",
"http://profile.url/homer/image"));
members.push_back(
FamilyInfoFetcher::FamilyMember("anotherObfuscatedGaiaId",
FamilyInfoFetcher::PARENT,
"Marge Simpson",
std::string(),
"http://profile.url/marge",
std::string()));
members.push_back(
FamilyInfoFetcher::FamilyMember("obfuscatedGaiaId3",
FamilyInfoFetcher::CHILD,
"Lisa Simpson",
"lisa@gmail.com",
std::string(),
"http://profile.url/lisa/image"));
members.push_back(
FamilyInfoFetcher::FamilyMember("obfuscatedGaiaId4",
FamilyInfoFetcher::CHILD,
"Bart Simpson",
"bart@bart.bart",
std::string(),
std::string()));
members.push_back(
FamilyInfoFetcher::FamilyMember("obfuscatedGaiaId5",
FamilyInfoFetcher::MEMBER,
std::string(),
std::string(),
std::string(),
std::string()));
EXPECT_CALL(*this, OnGetFamilyMembersSuccess(members));
SendValidGetFamilyMembersResponse(members);
}
TEST_F(FamilyInfoFetcherTest, SuccessAfterWaitingForRefreshToken) {
fetcher_.StartGetFamilyProfile();
// Since there is no refresh token yet, we should not get a request for an
// access token at this point.
EXPECT_EQ(0U, token_service_.GetPendingRequests().size());
IssueRefreshToken();
// Now there is a refresh token and we should have got a request for an
// access token.
EXPECT_EQ(1U, token_service_.GetPendingRequests().size());
IssueAccessToken();
FamilyInfoFetcher::FamilyProfile family("test", "My Test Family");
EXPECT_CALL(*this, OnGetFamilyProfileSuccess(family));
SendValidGetFamilyProfileResponse(family);
}
TEST_F(FamilyInfoFetcherTest, NoRefreshToken) {
fetcher_.StartGetFamilyProfile();
IssueRefreshTokenForDifferentAccount();
// Credentials for a different user should be ignored, i.e. not result in a
// request for an access token.
EXPECT_EQ(0U, token_service_.GetPendingRequests().size());
// After all refresh tokens have been loaded, there is still no token for our
// user, so we expect a token error.
EXPECT_CALL(*this, OnFailure(FamilyInfoFetcher::TOKEN_ERROR));
token_service_.LoadCredentials("");
}
TEST_F(FamilyInfoFetcherTest, GetTokenFailure) {
IssueRefreshToken();
fetcher_.StartGetFamilyProfile();
// On failure to get an access token we expect a token error.
EXPECT_CALL(*this, OnFailure(FamilyInfoFetcher::TOKEN_ERROR));
token_service_.IssueErrorForAllPendingRequestsForAccount(
kAccountId,
GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
}
TEST_F(FamilyInfoFetcherTest, InvalidResponse) {
IssueRefreshToken();
fetcher_.StartGetFamilyProfile();
IssueAccessToken();
// Invalid response data should result in a service error.
EXPECT_CALL(*this, OnFailure(FamilyInfoFetcher::SERVICE_ERROR));
SendInvalidGetFamilyProfileResponse();
}
TEST_F(FamilyInfoFetcherTest, FailedResponse) {
IssueRefreshToken();
fetcher_.StartGetFamilyProfile();
IssueAccessToken();
// Failed API call should result in a network error.
EXPECT_CALL(*this, OnFailure(FamilyInfoFetcher::NETWORK_ERROR));
SendFailedResponse();
}