JD2022-TU1/main/extern/UbiServices/custom/testsIntegration/testProfileClient.cpp

1411 lines
63 KiB
C++

////////////////////////////////////////////////////////////////////////////////
// (c) Copyright, Ubisoft Entertainment Inc. || Created by Harbour //
////////////////////////////////////////////////////////////////////////////////
#include "tests/config.h"
#if defined(US_SDK_LIGHT_MOBILE)
#error This file shall not be part of the solution
#endif
#include <tests/common/cancelTest.h>
#include <tests/common/configs.h>
#include <tests/fixture/authenticationFixture.h>
#include <tests/fixture/loginMockFixture.h>
#include <tests/fixture/paramFixture.h>
#include "tests/mock/mockLogDevice.h"
#include "tests/mock/mockProfileHelper.h"
#include "tests/mock/mockResultHelper.h"
#include "tests/mock/parametersMockHelper.h"
#include "testsIntegration/event/eventsConfigMockHelper.h"
#include <../private/ubiservices/core/configs/platformName.h>
#include <../private/ubiservices/core/encoding/base64.h>
#include <ubiservices/core/errors/errorCodes.h>
#include <../private/ubiservices/core/localization/localizationBase.h>
#include <ubiservices/core/types/dateTimeHelper.h>
#include <ubiservices/services/authentication/sessionInfo.h>
#include <ubiservices/services/authentication/authenticationClient.h>
#include <ubiservices/services/authentication/credentialsExternalToken.h>
#include <ubiservices/services/authentication/credentialsType.h>
#include <ubiservices/services/http/request/httpGet.h>
#include <ubiservices/services/http/request/httpPost.h>
#include <ubiservices/services/profile/profileClient.h>
#include <ubiservices/services/profile/profileInfoExternal.h>
#include <../private/ubiservices/services/profile/profileTypeHelper.h>
#include <ubiservices/services/user/userClient.h>
namespace US_NS {;
US_DEFINE_TEST(TestProfileClient);
typedef AuthenticationFixture<1> PROFILE;
typedef AuthenticationFixture<1, false> PROFILE_NOLOGIN;
typedef LoginMockFixture<true> PROFILE_MOCK;
typedef LoginMockFixture<false> PROFILE_MOCK_NOLOGIN;
typedef AuthenticationFixtureWithParam<TestConfigCancel>::type PROFILE_CANCEL;
#if defined (US_TARGET_XONE)
// Microsoft is applying fine grained limiting rate on S2S calls (25 calls in 30 seconds currently)
// So the test must be less aggressive on XBoxOne (empirical value, some failures happened on the build farm with 40)
US_DEFINE_LONG_TEST_P(PROFILE_CANCEL, TestConfigCancel(200), TestConfigCancel(50));
#else
US_DEFINE_LONG_TEST_P(PROFILE_CANCEL, TestConfigCancel(200), TestConfigCancel(20));
#endif
namespace TestUserClient_BF
{
String getBase64Encoding(String str);
}
namespace TestProfileClient_BF
{
String getSessionPlatformOnlineId(const SessionInfo& session)
{
#if defined(US_ENABLE_UPLAY_LOGIN) || defined(US_TARGET_MANJARO)
return session.getNameOnPlatform();
#elif defined(US_TARGET_XONE)
return session.getPlayerCredentials().getXuid();
#elif defined(US_TARGET_PS4) || defined(US_TARGET_KILI)
SceNpAccountId accountId;
sceNpGetAccountIdA(session.getPlayerCredentials().getUserId(), &accountId);
return String::formatText("%lld", accountId);
#elif defined(US_TARGET_WIIU)
return session.getPlayerCredentials().getPrincipalIdWiiu();
#elif defined(US_TARGET_SWITCH)
const uint64 id = session.getPlayerCredentials().getNsaId().id;
return String::formatText("%08X%08X",
static_cast<uint32>((id >> 32) & 0xFFFFFFFF),
static_cast<uint32>(id & 0xFFFFFFFF));
#elif defined(US_TARGET_IOS)
return session.getPlayerCredentials().getPlayerID();
#elif defined(US_TARGET_GGP)
return String::formatText("%llu", session.getPlayerCredentials().getPlayerId());
#elif defined(US_TARGET_KILI)
SceNpAccountId accountId;
sceNpGetAccountIdA(session.getPlayerCredentials().getUserId(), &accountId);
return String::formatText("%lld", accountId);
#else
#error Oups, not yet supported!!!
#endif
}
const String mockProfileInfoExternal()
{
JsonWriter writer;
writer["country"] = "CA";
writer["dateOfBirth"] = "1980-01-01T00:00:00Z";
writer["email"] = "email@dummy.com";
return writer.getJson().renderContent();
}
}
#if !defined(US_DLL)
/// @test Cancels request profiles requests with various delays.
TEST_P(PROFILE_CANCEL, US_RequestProfileIdCancel)
{
TestCanceler<> canceler(this->getParam());
ASSERT_TRUE(getFacade().getAuthenticationClient().getSessionInfo().hasValidSession());
const String onlineId = TestProfileClient_BF::getSessionPlatformOnlineId(getFacade().getAuthenticationClient().getSessionInfo());
while (!canceler.isComplete())
{
#if defined(US_ENABLE_UPLAY_LOGIN)
EXPECT_TRUE(canceler.cancel(getFacade().getProfileClient().requestProfiles({ onlineId }, ProfileTypeHelper::getProfileTypeDefault())));
#else
EXPECT_TRUE(canceler.cancel(getFacade().getProfileClient().requestProfiles({ onlineId })));
#endif
}
}
#endif
TEST_F(PROFILE, US_SearchProfileByUserName)
{
#if defined(US_SDK_TEST_CHINA)
const String username("ussdk001");
#else
const String username("ussdk002");
#endif
const AsyncResult<Map<String, ProfileInfo> > result = getFacade().getProfileClient().requestUplayProfiles({ username });
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
const Map<String, ProfileInfo>& profiles = result.getResult();
EXPECT_EQ(1u, profiles.size());
EXPECT_TRUE(profiles.find(username) != profiles.end());
}
TEST_F(PROFILE, US_SearchProfileByUserId)
{
const Vector<UserId> ids = { getFacade().getAuthenticationClient().getSessionInfo().getUserId() };
const AsyncResult<Map<UserId, ProfileInfo> > result = getFacade().getProfileClient().requestUplayProfiles(ids);
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
const Map<UserId, ProfileInfo>& profiles = result.getResult();
EXPECT_EQ(1u, profiles.size());
EXPECT_TRUE(profiles.find(getFacade().getAuthenticationClient().getSessionInfo().getUserId()) != profiles.end());
}
#if !defined(US_DLL)
TEST_F(PROFILE, US_MINRUN_RequestProfileDefault)
{
ASSERT_TRUE(getFacade().getAuthenticationClient().getSessionInfo().hasValidSession());
const SessionInfo sessionInfo = getFacade().getAuthenticationClient().getSessionInfo();
#if defined(US_TARGET_PS4) || defined(US_TARGET_XONE) || defined(US_TARGET_SWITCH) || defined(US_TARGET_GGP) || defined(US_TARGET_KILI)
const String platformOnlineId = TestProfileClient_BF::getSessionPlatformOnlineId(sessionInfo);
#elif defined(US_TARGET_IOS) && !defined(US_UPLAY_LOGIN_FOR_IOS)
const String platformOnlineId = sessionInfo.getPlayerCredentials().getPlayerID();
#else
const String platformOnlineId = sessionInfo.getUserId();
#endif
const Vector<String> identifier = {platformOnlineId};
ASSERT_EQ(1ull, identifier.size());
#if defined(US_ENABLE_UPLAY_LOGIN)
const AsyncResult<Map<String, ProfileInfo> > result =
getFacade().getProfileClient().requestProfiles(identifier, ProfileTypeHelper::getProfileTypeDefault());
#else
const AsyncResult<Map<String, ProfileInfo> > result = getFacade().getProfileClient().requestProfiles(identifier);
#endif
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
const Map<String, ProfileInfo>& results = result.getResult();
ASSERT_EQ(1u, results.size());
const ProfileInfo& info = results.begin()->second;
ASSERT_STRCASEEQ(sessionInfo.getNameOnPlatform().getUtf8(), info.m_nameOnPlatform.getUtf8());
}
#endif
#if !defined(US_DLL)
TEST_F(PROFILE, US_RequestProfileWithPlatform)
{
ASSERT_TRUE(getFacade().getAuthenticationClient().getSessionInfo().hasValidSession());
const SessionInfo sessionInfo = getFacade().getAuthenticationClient().getSessionInfo();
#if defined(US_TARGET_PS4) || defined(US_TARGET_XONE) || defined(US_TARGET_SWITCH) || defined(US_TARGET_GGP) || defined(US_TARGET_KILI)
const String platformOnlineId = TestProfileClient_BF::getSessionPlatformOnlineId(sessionInfo);
#elif defined(US_TARGET_IOS) && !defined(US_UPLAY_LOGIN_FOR_IOS)
const String platformOnlineId = sessionInfo.getPlayerCredentials().getPlayerID();
#else
const String platformOnlineId = sessionInfo.getUserId();
#endif
const Vector<String> identifier = {platformOnlineId};
ASSERT_EQ(1ull, identifier.size());
#if defined(US_ENABLE_UPLAY_LOGIN)
const AsyncResult<Map<String, ProfileInfo> > result =
getFacade().getProfileClient().requestProfiles(identifier, ProfileTypeHelper::getProfileTypeDefault());
#else
const AsyncResult<Map<String, ProfileInfo> > result = getFacade().getProfileClient().requestProfiles(identifier);
#endif
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
const Map<String, ProfileInfo>& results = result.getResult();
ASSERT_EQ(1u, results.size());
const ProfileInfo& info = results.begin()->second;
ASSERT_STRCASEEQ(sessionInfo.getNameOnPlatform().getUtf8(), info.m_nameOnPlatform.getUtf8());
}
#endif
#if defined(US_ENABLE_UPLAY_LOGIN) && defined(US_TARGET_GGP)
/// @test Checks that ggp only allows request with profile type uplay and/or googlestream
TEST_F(PROFILE, US_RequestProfileWithPlatformError)
{
ASSERT_TRUE(getFacade().getAuthenticationClient().getSessionInfo().hasValidSession());
const Vector<String> identifier = { String("DummyId") };
for (uint32 i = 0; i < static_cast<uint32>(ProfileType::ENUM_END); i++)
{
const auto profileType = static_cast<ProfileType>(i);
if (profileType == ProfileType::GoogleStream || profileType == ProfileType::Uplay)
{
ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().getProfileClient().requestProfiles(identifier, profileType)));
}
else
{
ASSERT_TRUE(AsyncHelper::waitFailure(
getFacade().getProfileClient().requestProfiles(identifier, profileType), Errors::PROFILES_CANNOT_ACCESS_OTHER_PROFILETYPES));
}
}
}
#endif
#if defined(US_ENABLE_UPLAY_LOGIN) && !defined(US_DLL)
/// @test Checks that platforms with uplay log in enabled allow other platform type requests.
TEST_F(PROFILE, US_RequestProfileWithExternalPlatformTypes)
{
struct Local
{
static testing::AssertionResult checkRequestProfile(Facade& facade, const String& id, ProfileType profileType, const String& expectedPlatformName)
{
const AsyncResult<Map<String, ProfileInfo> > result = facade.getProfileClient().requestProfiles({ id }, profileType);
result.wait(NS_COMMON::DEFAULT_WAIT_TIME);
if (!result.hasSucceeded())
{
return testing::AssertionFailure() << "Call to requestProfiles() on '" << ProfileTypeHelper::getEnumStringValue(profileType) << "' failed";
}
Map<String, ProfileInfo>::const_iterator it = result.getResult().find(id);
if (it == result.getResult().end())
{
return testing::AssertionFailure() << "id '" << id << "' was not found in the result of requestProfiles";
}
if (it->second.m_nameOnPlatform != expectedPlatformName)
{
return testing::AssertionFailure() << "Platform name is '" << it->second.m_nameOnPlatform << "' instead of expected '" << expectedPlatformName << "'";
}
return testing::AssertionSuccess();
}
};
ASSERT_TRUE(getFacade().getAuthenticationClient().getSessionInfo().hasValidSession());
#if defined(US_SDK_TEST_CHINA)
EXPECT_TRUE(Local::checkRequestProfile(getFacade(), "64a89d15-8983-471d-a41f-b10a08114e9e", ProfileType::Uplay, "ussdk01"));
#else
EXPECT_TRUE(Local::checkRequestProfile(getFacade(), "2535285332402925", ProfileType::XboxLive, "ussdk001"));
EXPECT_TRUE(Local::checkRequestProfile(getFacade(), "6800812194353234709", ProfileType::PlaystationNetwork, "ussdk001"));
EXPECT_TRUE(Local::checkRequestProfile(getFacade(), "4d9061de-7c20-4cc9-9f55-b8d6228da6ae", ProfileType::Uplay, "ussdk001"));
EXPECT_TRUE(Local::checkRequestProfile(getFacade(), "1799895225", ProfileType::Wiiu, "ussdk001"));
EXPECT_TRUE(Local::checkRequestProfile(getFacade(), "106977145948960724807", ProfileType::GoogleGames, "Samuel Verreault"));
EXPECT_TRUE(Local::checkRequestProfile(getFacade(), "G:1918575422", ProfileType::GameCenter, "G:1918575422"));
#endif
// TODO : Add tests for following platforms : Switch, Steam, Ubimobile, Oculus and Owlient (cf. JIRA US-12787)
}
#endif
TEST_F(PROFILE, US_MINRUN_RequestUsers)
{
#if defined(US_SDK_TEST_CHINA)
const Vector<String> usernames = { "ussdk001" }; // Lookup user Ids
#else
const Vector<String> usernames = { "ussdk002", "ussdk003" }; // Lookup user Ids
#endif
AsyncResult<Map<String, ProfileInfo> > profileResult = getFacade().getProfileClient().requestUplayProfiles(usernames);
ASSERT_TRUE(AsyncHelper::waitSuccess(profileResult));
Map<String, ProfileInfo>& profileMap = const_cast<Map<String, ProfileInfo>&>(profileResult.getResult());
ASSERT_TRUE(profileMap.count(usernames[0]) > 0);
#if !defined(US_SDK_TEST_CHINA)
ASSERT_TRUE(profileMap.count(usernames[1]) > 0);
#endif
const Optional<UserId>& userId1Ptr = profileMap[usernames[0]].m_userId;
EXPECT_TRUE(userId1Ptr.isSpecified());
const UserId user1 = userId1Ptr.isSpecified() ? userId1Ptr.value() : UserId();
#if !defined(US_SDK_TEST_CHINA)
const Optional<UserId>& userId2Ptr = profileMap[usernames[1]].m_userId;
EXPECT_TRUE(userId2Ptr.isSpecified());
const UserId user2 = userId2Ptr.isSpecified() ? userId2Ptr.value() : UserId();
#endif
#if defined(US_SDK_TEST_CHINA)
const AsyncResult<Map<UserId, UserInfo> > result = getFacade().getUserClient().requestUserInfo({ user1 });
#else
const AsyncResult<Map<UserId, UserInfo> > result = getFacade().getUserClient().requestUserInfo({ user1, user2 });
#endif
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
const Map<UserId, UserInfo>& userInfo = result.getResult();
#if defined(US_SDK_TEST_CHINA)
EXPECT_EQ(1u, userInfo.size());
#else
EXPECT_EQ(2u, userInfo.size());
#endif
const Map<UserId, UserInfo>::const_iterator it = userInfo.find(user1);
ASSERT_TRUE(it != userInfo.end());
#if defined(US_SDK_TEST_CHINA)
EXPECT_EQ(1u, it->second.m_linkedProfiles.size());
#else
EXPECT_EQ(5u, it->second.m_linkedProfiles.size());
#endif
}
TEST_F(PROFILE_MOCK, USNOT_RequestUsersWithModifiedResponseForForwardCompatibility)
{
const char* const JSON_PROFILE_INFO =
"{"
" \"before\" : true,"
" \"profiles\": ["
" {"
" \"profileId\": \"b2cfc7a1-a08e-4bd8-adf3-73cb1c19faf1\","
" \"userId\" : \"b2cfc7a1-a08e-4bd8-adf3-73cb1c19faf1\","
" \"platformType\" : \"uplay\","
" \"idOnPlatform\" : \"B2CFC7A1-A08E-4BD8-ADF3-73CB1C19FAF1\","
" \"nameOnPlatform\" : \"ussdk002\""
" },"
" {"
" \"profileId\": \"a701a60d-1fd9-4c4f-98a2-b33fa8beb00f\","
" \"userId\" : \"a701a60d-1fd9-4c4f-98a2-b33fa8beb00f\","
" \"platformType\" : \"uplay\","
" \"idOnPlatform\" : \"A701A60D-1FD9-4C4F-98A2-B33FA8BEB00F\","
" \"nameOnPlatform\" : \"ussdk003\","
" \"uselessArray\" : [ 1, 2, 3, 4, 5 ]"
" },"
" {"
" \"profileId\": \"aaaaaaaa-1111-bbbb-2222-0123456789ab\","
" \"userId\" : \"aaaaaaaa-1111-bbbb-2222-0123456789ab\","
" \"platformType\" : \"uplay\","
" \"idOnPlatform\" : \"AAAAAAAA-1111-BBBB-2222-0123456789AB\","
" \"nameOnPlatform\" : \"otherName\""
" }"
" ],"
" \"other\" : 5"
"}";
const char* const JSON_USER_INFO =
"{"
" \"profiles\": ["
" {"
" \"profileId\": \"a701a60d-1fd9-4c4f-98a2-b33fa8beb00f\","
" \"userId\" : \"a701a60d-1fd9-4c4f-98a2-b33fa8beb00f\","
" \"platformType\" : \"uplay\","
" \"idOnPlatform\" : \"A701A60D-1FD9-4C4F-98A2-B33FA8BEB00F\","
" \"nameOnPlatform\" : \"ussdk003\""
" },"
" {"
" \"profileId\": \"469a8893-af19-4b18-9c36-2bc2365c076b\","
" \"userId\" : \"a701a60d-1fd9-4c4f-98a2-b33fa8beb00f\","
" \"platformType\" : \"xbl\","
" \"idOnPlatform\" : \"2814625590076873\","
" \"nameOnPlatform\" : \"2 Dev 582379320\""
" },"
" {"
" \"profileId\": \"b2cfc7a1-a08e-4bd8-adf3-73cb1c19faf1\","
" \"userId\" : \"b2cfc7a1-a08e-4bd8-adf3-73cb1c19faf1\","
" \"platformType\" : \"uplay\","
" \"idOnPlatform\" : \"B2CFC7A1-A08E-4BD8-ADF3-73CB1C19FAF1\","
" \"nameOnPlatform\" : \"ussdk002\","
" \"uselessArray\" : [ 1, 2, 3, 4, 5 ]"
" },"
" {"
" \"profileId\": \"8ff7d01d-6917-4149-8c5c-9ab6eaf8b70c\","
" \"platformType\" : \"xbl\","
" \"idOnPlatform\" : \"2814675809618166\","
" \"userId\" : \"b2cfc7a1-a08e-4bd8-adf3-73cb1c19faf1\","
" \"nameOnPlatform\" : \"2 Dev 820734878\""
" },"
" {"
" \"profileId\": \"aaaaaaaa-1111-bbbb-2222-0123456789ab\","
" \"userId\" : \"aaaaaaaa-1111-bbbb-2222-0123456789ab\","
" \"platformType\" : \"uplay\","
" \"idOnPlatform\" : \"AAAAAAAA-1111-BBBB-2222-0123456789AB\","
" \"nameOnPlatform\" : \"otherName\""
" }"
" ],"
" \"other\" : 5"
"}";
HttpGet request(StringUrl(""));
{
MockAsyncResult mockResult = MockResultHelper::createSuccessResult(JSON_PROFILE_INFO);
StringStream expectedUrl;
expectedUrl << "/mock-profiles";
US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, expectedUrl.getContent())
.WillRepeatedly(testing::DoAll(testing::SaveArg<0>(static_cast<HttpRequest*>(&request)), testing::Return(mockResult)));
}
// Lookup user Ids
const String username1("ussdk002");
const String username2("ussdk003");
const AsyncResult<Map<String, ProfileInfo> > profileResult = getFacade().getProfileClient().requestUplayProfiles(Vector<String>({ username1, username2 }));
ASSERT_TRUE(AsyncHelper::waitSuccess(profileResult));
const Map<String, ProfileInfo>& profileMap = profileResult.getResult();
ASSERT_TRUE(profileMap.count(username1) > 0);
ASSERT_TRUE(profileMap.count(username2) > 0);
const Optional<UserId>& userId1Ptr = profileMap.at(username1).m_userId;
ASSERT_TRUE(userId1Ptr.isSpecified());
const Optional<UserId>& userId2Ptr = profileMap.at(username2).m_userId;
ASSERT_TRUE(userId2Ptr.isSpecified());
{
MockAsyncResult mockResult = MockResultHelper::createSuccessResult(JSON_USER_INFO);
StringStream expectedUrl;
expectedUrl << "/mock-profiles";
US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, expectedUrl.getContent())
.WillRepeatedly(testing::DoAll(testing::SaveArg<0>(static_cast<HttpRequest*>(&request)), testing::Return(mockResult)));
}
const AsyncResult<Map<UserId, UserInfo> > result = getFacade().getUserClient().requestUserInfo({ userId1Ptr.value(), userId2Ptr.value() });
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
const Map<UserId, UserInfo>& userInfo = result.getResult();
EXPECT_EQ(3u, userInfo.size());
const Map<UserId, UserInfo>::const_iterator it = userInfo.find(userId1Ptr.value());
ASSERT_TRUE(it != userInfo.end());
EXPECT_EQ(2u, it->second.m_linkedProfiles.size());
}
TEST_F(PROFILE_MOCK, USNOT_RequestUserInfoPluralEmptyList)
{
ASSERT_TRUE(AsyncHelper::waitFailure(getFacade().getUserClient().requestUserInfo(Vector<UserId>()), Errors::USERS_INVALID_PARAM));
}
TEST_F(PROFILE_MOCK, USNOT_RequestUserInfoPluralFailure)
{
PrepareLoggingMock([](US_TEST_NS::MockLogDevice& device)
{
US_EXPECT_LOG("Status code: 400", LogLevel::Error, Times(1));
});
US_EXPECT_CALL_HTTP_SIMPLE(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "mock-profiles?userId",
MockResultHelper::createHttpErrorResult(RestStatusCode::Error400, "{\"error\":\"myError\"}"));
ASSERT_TRUE(AsyncHelper::waitFailure(getFacade().getUserClient().requestUserInfo(
{ UserId("11111111-2222-3333-4444-555566667777") }), Errors::USERS_REQUEST_USERS_BATCH_FAILURE));
}
TEST_F(PROFILE_MOCK, USNOT_RequestUserInfoPluralInvalidJson)
{
US_EXPECT_CALL_HTTP_SIMPLE(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "mock-profiles?userId",
MockResultHelper::createSuccessResult("{\"invalidJson\":\"invalidData\"}"));
ASSERT_TRUE(AsyncHelper::waitFailure(getFacade().getUserClient().requestUserInfo(
{ UserId("11111111-2222-3333-4444-555566667777") }), Errors::CORE_INVALID_JSON_FORMAT));
}
/// @test Requests information for an empty list of user
TEST_F(PROFILE_MOCK, USNOT_SearchProfileFailingWithEmptyList)
{
ASSERT_TRUE(AsyncHelper::waitFailure(getFacade().getProfileClient().requestUplayProfiles(Vector<String>()), Errors::PROFILES_INVALID_PARAM));
}
/// @note Crashes on Xbox One. The XUID is not known in the MS Sandbox. XDK crashes
#if !defined(US_TARGET_XONE)
TEST_F(PROFILE_MOCK, USNOT_SearchProfileByUserNameSuccess)
{
US_EXPECT_CALL_HTTP_SIMPLE(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "mock-profiles", MockProfileHelper::buildProfileIdResponse(1));
ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().getProfileClient().requestUplayProfiles({ "ol_uplay" })));
}
/// @test Requests information for more user than what may be processed in a single HTTP call.
TEST_F(PROFILE_MOCK, USNOT_SearchProfileByUserNameSuccess25)
{
const uint32 BATCH_SIZE = 50;
Vector<ProfileId> profiles1;
Vector<ProfileId> profiles2;
for (uint32 index = 1; index <= BATCH_SIZE; index++)
{
profiles1.push_back(ProfileId(String::formatText("20ee52cb-36a5-47cb-891e-433812169%03d", 50 + index)));
}
for (uint32 index = BATCH_SIZE + 1; index <= BATCH_SIZE + 5; index++)
{
profiles2.push_back(ProfileId(String::formatText("20ee52cb-36a5-47cb-891e-433812169%03d", 50 + index)));
}
{
const MockAsyncResult mockResult1 = MockProfileHelper::buildProfileIdResponse(profiles1);
const MockAsyncResult mockResult2 = MockProfileHelper::buildProfileIdResponse(profiles2);
::testing::InSequence s;
US_EXPECT_CALL_HTTP_SIMPLE(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "mock-profiles", mockResult1);
US_EXPECT_CALL_HTTP_SIMPLE(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "mock-profiles", mockResult2);
}
Vector<String> usernames;
uint32 index = 1;
for(const auto& it : profiles1)
{
US_UNUSED(it);
usernames.push_back(String::formatText("ol_uplay%02d", index++));
}
for(const auto& it : profiles2)
{
US_UNUSED(it);
usernames.push_back(String::formatText("ol_uplay%02d", index++));
}
ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().getProfileClient().requestUplayProfiles(usernames)));
}
#endif
/// @test Searches for player profiles by user name, validating error path.
/// The test must request more profiles than the job maximum batch size (\c BATCH_SIZE).
/// This test validates the error processing when the second (or at least not
/// the first one) batch HTTP call fails.
TEST_F(PROFILE_MOCK, USNOT_SearchProfileByUserNameSuccess25Failure)
{
PrepareLoggingMock([](US_TEST_NS::MockLogDevice& device)
{
US_EXPECT_LOG("Status code: 400", LogLevel::Error, Times(1));
});
const uint32 BATCH_SIZE = 50;
Vector<ProfileId> profiles1;
for (uint32 index = 1; index <= BATCH_SIZE; index++)
{
profiles1.push_back(ProfileId(String::formatText("20ee52cb-36a5-47cb-891e-433812169%003d", 50 + index)));
}
{
// Prepare the mock for the first batch call (success)
const MockAsyncResult mockResult1 = MockProfileHelper::buildProfileIdResponse(profiles1);
const MockAsyncResult mockResult2 = MockResultHelper::createRestErrorResult(RestStatusCode::Error400, 1u, "No search criteria");
::testing::InSequence s;
US_EXPECT_CALL_HTTP_SIMPLE(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "mock-profiles", mockResult1);
US_EXPECT_CALL_HTTP_SIMPLE(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "mock-profiles", mockResult2);
}
// Prepare the user names list (containing more than the maximum batch size).
Vector<String> usernames;
for (uint32 index = 1; index <= BATCH_SIZE + 5; index++)
{
usernames.push_back(String::formatText("ol_uplay%02d", index));
}
ASSERT_TRUE(AsyncHelper::waitFailure(getFacade().getProfileClient().requestUplayProfiles(usernames), Errors::SERVICE_INVALID_PARAMETER));
}
/// @test Searches for player profiles by user name, validating error path.
/// The test must request more profiles than the amount fitting in two batch call
/// (that is more than two times the (\c BATCH_SIZE) value).
/// This test validates the error processing when the second batch HTTP call fails
/// while having another batch to call remaining. This ends up in a slightly
/// different code path than the previous test (failing in the last batch request).
TEST_F(PROFILE_MOCK, USNOT_SearchProfileByUserNameSuccess55Failure)
{
PrepareLoggingMock([](US_TEST_NS::MockLogDevice& device)
{
US_EXPECT_LOG("Status code: 400", LogLevel::Error, Times(1));
US_EXPECT_LOG("Received an error response from the server with an unexpected format, body: 'Dummy'", LogLevel::Error, Times(2));
});
const uint32 BATCH_SIZE = 50;
Vector<ProfileId> profiles1;
for (uint32 index = 1; index <= BATCH_SIZE; index++)
{
profiles1.push_back(ProfileId(String::formatText("20ee52cb-36a5-47cb-891e-433812169%003d", 50 + index)));
}
{
const MockAsyncResult mockResult1 = MockProfileHelper::buildProfileIdResponse(profiles1);
const MockAsyncResult mockResult2 = MockResultHelper::createHttpErrorResult(RestStatusCode::Error400, "Dummy");
::testing::InSequence s;
US_EXPECT_CALL_HTTP_SIMPLE(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "mock-profiles", mockResult1);
US_EXPECT_CALL_HTTP_SIMPLE(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "mock-profiles", mockResult2);
}
Vector<String> usernames;
for (uint32 index = 1; index <= BATCH_SIZE*2 + 5; index++)
{
usernames.push_back(String::formatText("ol_uplay%02d", index));
}
ASSERT_TRUE(AsyncHelper::waitFailure(getFacade().getProfileClient().requestUplayProfiles(usernames), Errors::PROFILES_REQUEST_ID_FROM_UPLAY_GUID_FAILURE));
}
TEST_F(PROFILE_MOCK, USNOT_SearchProfileByUserNameFailure)
{
PrepareLoggingMock([](US_TEST_NS::MockLogDevice& device)
{
US_EXPECT_LOG("Status code: 400", LogLevel::Error, Times(1));
});
US_EXPECT_CALL_HTTP_SIMPLE(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "mock-profiles",
MockResultHelper::createRestErrorResult(RestStatusCode::Error400, 1u, "No search criteria"));
ASSERT_TRUE(AsyncHelper::waitFailure(getFacade().getProfileClient().requestUplayProfiles({ "ol_uplay" }), Errors::SERVICE_INVALID_PARAMETER));
}
TEST_F(PROFILE_MOCK, USNOT_SearchProfileByUserNameInvalidJson)
{
PrepareLoggingMock([](US_TEST_NS::MockLogDevice& device)
{
US_EXPECT_LOG("Failed to parse the following JSON body: Dummy", LogLevel::Error, Times(2));
});
US_EXPECT_CALL_HTTP_SIMPLE(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "mock-profiles",
MockResultHelper::createSuccessResult("Dummy")); // "{\"profiles\":[]}" // Same as dummy JSON
ASSERT_TRUE(AsyncHelper::waitFailure(getFacade().getProfileClient().requestUplayProfiles({ "ol_uplay" }), Errors::CORE_INVALID_JSON_FORMAT));
}
TEST_F(PROFILE_MOCK, USNOT_NotLinkedProfile)
{
{
const char* const PROFILES =
"{\"profiles\":[{\"profileId\":\"0048b875-581f-428e-afe2-5cf5f46d5670\","
"\"userId\":\"49e430d9-0316-4c01-9b58-b1b5b9f77bcd\",\"platformType\":\"psn\",\"idOnPlatform\""
":\"1309567837933790580\",\"nameOnPlatform\":\"q-cc919f6d-US-en\"},{\"profileId\":"
"\"46c04506-9f24-431a-8a92-ba6730257bc1\",\"userId\":null,\"platformType\":\"psn\","
"\"idOnPlatform\":\"5076140339895768489\",\"nameOnPlatform\":\"q-deddf75d-FR-fr\"}]}";
US_EXPECT_CALL_HTTP_SIMPLE(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "mock-profiles", MockResultHelper::createSuccessResult(PROFILES));
}
const AsyncResult<Map<String, ProfileInfo> > result = getFacade().getProfileClient().requestUplayProfiles({ "TestName" });
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
const Map<String, ProfileInfo>& map = result.getResult();
ASSERT_EQ(2u, map.size());
Map<String, ProfileInfo>::const_iterator it = map.begin();
ASSERT_TRUE(it->second.m_userId.isSpecified());
ASSERT_TRUE(it->second.m_userId.value().isValid());
it++;
ASSERT_TRUE(it != map.end());
EXPECT_TRUE(!it->second.m_userId.isSpecified());
}
/// @test Checks that request is not sent, and an error is raised when only invalid profileId are given
TEST_F(PROFILE_MOCK, USNOT_SearchProfileByProfileIdOnlyInvalidProfileId)
{
PrepareLoggingMock([](US_TEST_NS::MockLogDevice& device)
{
US_EXPECT_LOG("No profile is retrieved for this ProfileId", LogLevel::Warning, Times(5));
US_EXPECT_LOG("Invalid parameter: only invalid ProfileId", LogLevel::Error, Times(2));
});
Vector<ProfileId> profiles;
for (uint32 index = 0; index < 5; index++)
{
profiles.push_back(ProfileId(String::formatText("Invalid ProfileId %003d", index)));
}
US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "mock-profiles").Times(0);
ASSERT_TRUE(AsyncHelper::waitFailure(getFacade().getProfileClient().requestProfiles(profiles), Errors::PROFILES_INVALID_PARAM));
}
/// @test Checks that when the list of profileIds contains both valid and invalid profileId mixed,
/// the job is still successful, but invalid profileIds are not used
TEST_F(PROFILE_MOCK, USNOT_SearchProfileByProfileIdSuccessWithInvalidProfileId)
{
PrepareLoggingMock([](US_TEST_NS::MockLogDevice& device)
{
US_EXPECT_LOG("is not a valid GUID. No profile is retrieved for this ProfileId", LogLevel::Warning, Times(10));
});
Vector<ProfileId> profileIdsSearch;
StringStream queryParamsStreams;
Vector<ProfileId> profileIdsResult;
for (uint32 index = 1; index <= 10; index++)
{
// Fill profileIdsSearch with valid and invalid values
const ProfileId validProfileId(String::formatText("20ee52cb-36a5-47cb-891e-433812169%003d", index));
profileIdsSearch.push_back(validProfileId);
profileIdsSearch.push_back(ProfileId(String::formatText("Invalid_ProfileId_%003d", index)));
// Fill expected query params
if (index > 1)
{
queryParamsStreams << ",";
}
queryParamsStreams << validProfileId;
// Fill result
profileIdsResult.push_back(validProfileId);
}
HttpGet request(StringUrl(""));
{
const MockAsyncResult mockResult = MockProfileHelper::buildProfileIdResponse(profileIdsResult);
US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "mock-profiles")
.WillOnce(testing::DoAll(testing::SaveArg<0>(static_cast<HttpRequest*>(&request)), testing::Return(mockResult)));
}
ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().getProfileClient().requestProfiles(profileIdsSearch)));
const String query = URLInfo(request.getUrl()).getQuery();
EXPECT_NE(String::npos, query.findSubstringCase(queryParamsStreams.getContent()));
EXPECT_EQ(String::npos, query.findSubstringCase("Invalid_ProfileId_"));
}
/// @test Requests Profile parsing a JSON structure containing extra Root node.
TEST_F(PROFILE_MOCK, USNOT_RequestProfileWithExtraRootNode)
{
Vector<ProfileId> profileIdsSearch;
StringStream queryParamsStreams;
Vector<ProfileId> profileIdsResult;
for (uint32 index = 1; index <= 10; index++)
{
// Fill profileIdsSearch with valid and invalid values
const ProfileId validProfileId(String::formatText("20ee52cb-36a5-47cb-891e-433812169%003d", index));
profileIdsSearch.push_back(validProfileId);
// Fill expected query params
if (index > 1)
{
queryParamsStreams << ",";
}
queryParamsStreams << validProfileId;
// Fill result
profileIdsResult.push_back(validProfileId);
}
HttpGet request(StringUrl(""));
{
const MockAsyncResult mockResult = MockProfileHelper::buildProfileIdResponseWithExtraNode(profileIdsResult);
US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "mock-profiles")
.WillOnce(testing::Return(mockResult));
}
const AsyncResult<Map<ProfileId, ProfileInfo> > result = getFacade().getProfileClient().requestProfiles(profileIdsSearch);
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
const Map<ProfileId, ProfileInfo> results = result.getResult();
EXPECT_EQ(10u, results.size());
}
/// @test Checks that when the first batch is successful, and the following batch would only contain
/// invalid ids (and is thus not sent), the job is still successful
TEST_F(PROFILE_MOCK, USNOT_SearchProfileByProfileIdSuccessWithInvalidProfileId55)
{
PrepareLoggingMock([](US_TEST_NS::MockLogDevice& device)
{
US_EXPECT_LOG("is not a valid GUID. No profile is retrieved for this ProfileId", LogLevel::Warning, Times(5));
});
const uint32 BATCH_SIZE = 50;
Vector<ProfileId> profileIds;
StringStream queryParamsStreams;
for (uint32 index = 1; index <= BATCH_SIZE; index++)
{
const ProfileId validProfileId(String::formatText("20ee52cb-36a5-47cb-891e-433812169%003d", index));
profileIds.push_back(validProfileId);
if (index > 1)
{
queryParamsStreams << ",";
}
queryParamsStreams << validProfileId;
}
HttpGet request(StringUrl(""));
{
const MockAsyncResult mockResult = MockProfileHelper::buildProfileIdResponse(profileIds);
US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "mock-profiles")
.WillOnce(testing::DoAll(testing::SaveArg<0>(static_cast<HttpRequest*>(&request)), testing::Return(mockResult)));
}
for (uint32 index = 1; index <= 5; index++)
{
profileIds.push_back(ProfileId(String::formatText("Invalid_ProfileId_%003d", index)));
}
ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().getProfileClient().requestProfiles(profileIds)));
const String query = URLInfo(request.getUrl()).getQuery();
EXPECT_NE(String::npos, query.findSubstringCase(queryParamsStreams.getContent()));
EXPECT_EQ(String::npos, query.findSubstringCase("Invalid_ProfileId_"));
}
/// @test Checks that request is not sent, and an error is raised when only invalid userId are given
TEST_F(PROFILE_MOCK, USNOT_SearchUplayProfileByUserIdOnlyInvalidUserId)
{
PrepareLoggingMock([](US_TEST_NS::MockLogDevice& device)
{
US_EXPECT_LOG("Invalid parameter: only invalid UserId", LogLevel::Error, Times(2));
US_EXPECT_LOG("No profile is retrieved for this UserId", LogLevel::Warning, Times(5));
});
Vector<UserId> userIds;
for (uint32 index = 0; index < 5; index++)
{
userIds.push_back(UserId(String::formatText("Invalid UserId %003d", index)));
}
{
US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "mock-profiles").Times(0);
}
ASSERT_TRUE(AsyncHelper::waitFailure(getFacade().getProfileClient().requestUplayProfiles(userIds), Errors::PROFILES_INVALID_PARAM));
}
/// @test Checks that when the list of userIds contains both valid and invalid userId mixed,
/// the job is still successful, but invalid userIds are not used
TEST_F(PROFILE_MOCK, USNOT_SearchUplayProfileByUserIdSuccessWithInvalidUserId)
{
PrepareLoggingMock([](US_TEST_NS::MockLogDevice& device)
{
US_EXPECT_LOG("is not a valid GUID. No profile is retrieved for this UserId", LogLevel::Warning, Times(10));
});
Vector<UserId> userIdsSearch;
StringStream queryParamsStreams;
Vector<ProfileId> profileIdsResult;
for (uint32 index = 1; index <= 10; index++)
{
// Fill userIdsSearch with valid and invalid values
const UserId validUserId(String::formatText("43381216-36a5-47cb-891e-20ee52cb9%003d", index));
userIdsSearch.push_back(validUserId);
userIdsSearch.push_back(UserId(String::formatText("Invalid_UserId_%003d", index)));
// Fill expected query params
if (index > 1)
{
queryParamsStreams << ",";
}
queryParamsStreams << validUserId;
// Fill result
ProfileId validProfileId(String::formatText("20ee52cb-36a5-47cb-891e-433812169%003d", index));
profileIdsResult.push_back(validProfileId);
}
HttpGet request(StringUrl(""));
{
const MockAsyncResult mockResult = MockProfileHelper::buildProfileIdResponse(profileIdsResult);
US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "mock-profiles")
.WillOnce(testing::DoAll(testing::SaveArg<0>(static_cast<HttpRequest*>(&request)), testing::Return(mockResult)));
}
ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().getProfileClient().requestUplayProfiles(userIdsSearch)));
const String query = URLInfo(request.getUrl()).getQuery();
EXPECT_NE(String::npos, query.findSubstringCase(queryParamsStreams.getContent()));
EXPECT_EQ(String::npos, query.findSubstringCase("Invalid_UserId_"));
}
/// @test Check that when the first batch is successful, and the following batch would only contain
/// invalid ids (and is thus not sent), the job is still successful
TEST_F(PROFILE_MOCK, USNOT_SearchUplayProfileByUserIdSuccessWithInvalidUserId55)
{
PrepareLoggingMock([](US_TEST_NS::MockLogDevice& device)
{
US_EXPECT_LOG("is not a valid GUID. No profile is retrieved for this UserId", LogLevel::Warning, Times(5));
});
const uint32 BATCH_SIZE = 50;
Vector<UserId> userIdsSearch;
StringStream queryParamsStreams;
Vector<ProfileId> profileIdsResult;
for (uint32 index = 1; index <= BATCH_SIZE; index++)
{
// Fill userIdsSearch with valid values for all the first batch
const UserId validUserId(String::formatText("43381216-36a5-47cb-891e-20ee52cb9%003d", index));
userIdsSearch.push_back(validUserId);
if (index > 1)
{
queryParamsStreams << ",";
}
queryParamsStreams << validUserId;
ProfileId validProfileId(String::formatText("20ee52cb-36a5-47cb-891e-433812169%003d", index));
profileIdsResult.push_back(validProfileId);
}
// Add only invalid userId for the second batch
for (uint32 index = 1; index <= 5; index++)
{
userIdsSearch.push_back(UserId(String::formatText("Invalid_UserId_%003d", index)));
}
HttpGet request(StringUrl(""));
{
const MockAsyncResult mockResult = MockProfileHelper::buildProfileIdResponse(profileIdsResult);
US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "mock-profiles")
.WillOnce(testing::DoAll(testing::SaveArg<0>(static_cast<HttpRequest*>(&request)), testing::Return(mockResult)));
}
ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().getProfileClient().requestUplayProfiles(userIdsSearch)));
const String query = URLInfo(request.getUrl()).getQuery();
EXPECT_NE(String::npos, query.findSubstringCase(queryParamsStreams.getContent()));
EXPECT_EQ(String::npos, query.findSubstringCase("Invalid_UserId_"));
}
TEST_F(PROFILE, US_RequestProfileInfoExternal)
{
struct Local
{
static bool isValidLanguage(const String& language)
{
// This is only a subset to minimize error during automated tests
const Vector<const char*> values =
{
US_LG_XX,
US_LG_DE,
US_LG_EN,
US_LG_ES,
US_LG_FR,
US_LG_JP,
US_LG_KO,
US_LG_IT,
US_LG_PO,
US_LG_PT,
US_LG_RU,
US_LG_NL,
US_LG_FI,
US_LG_SV,
US_LG_DA,
US_LG_NO
};
for(const auto& it : values)
{
if(language.isEqualCaseInsensitive(it))
{
return true;
}
}
return false;
}
static bool isValidCountry(const String& country)
{
// This is only a subset to minimize error during automated tests
const Vector<const char*> values =
{
US_COUNTRY_AUSTRALIA,
US_COUNTRY_AUSTRIA,
US_COUNTRY_BELGIUM,
US_COUNTRY_BRAZIL,
US_COUNTRY_CANADA,
US_COUNTRY_CHILE,
US_COUNTRY_CHINA,
US_COUNTRY_COLOMBIA,
US_COUNTRY_CZECH_REPUBLIC,
US_COUNTRY_DENMARK,
US_COUNTRY_FINLAND,
US_COUNTRY_FRANCE,
US_COUNTRY_GERMANY,
US_COUNTRY_GREECE,
US_COUNTRY_HONG_KONG,
US_COUNTRY_HUNGARY,
US_COUNTRY_INDIA,
US_COUNTRY_IRELAND,
US_COUNTRY_ITALY,
US_COUNTRY_JAPAN,
US_COUNTRY_KOREA,
US_COUNTRY_MEXICO,
US_COUNTRY_NETHERLANDS,
US_COUNTRY_NEW_ZEALAND,
US_COUNTRY_NORWAY,
US_COUNTRY_POLAND,
US_COUNTRY_PORTUGAL,
US_COUNTRY_SINGAPORE,
US_COUNTRY_SLOVAK_REPUBLIC,
US_COUNTRY_SOUTH_AFRICA,
US_COUNTRY_SPAIN,
US_COUNTRY_SWEDEN,
US_COUNTRY_SWITZERLAND,
US_COUNTRY_TAIWAN,
US_COUNTRY_GREAT_BRITAIN,
US_COUNTRY_UNITED_STATES,
US_COUNTRY_RUSSIAN_FEDERATION,
US_COUNTRY_TURKEY,
US_COUNTRY_ARGENTINA,
US_COUNTRY_SAUDI_ARABIA,
US_COUNTRY_ISRAEL,
US_COUNTRY_UNITED_ARAB_EMIRATES
};
for (const auto& it : values)
{
if(country.isEqualCaseInsensitive(it))
{
return true;
}
}
return false;
}
};
const AsyncResult<ProfileInfoExternal> result = getFacade().getProfileClient().requestProfileInfoExternal();
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
const ProfileInfoExternal& info = result.getResult();
EXPECT_EQ(2u, info.m_country.getLength());
EXPECT_EQ(2u, info.m_language.getLength());
EXPECT_TRUE(Local::isValidCountry(info.m_country)) << "Country not found (\"" << info.m_country.getUtf8() << "\")" << endl;
EXPECT_TRUE(Local::isValidLanguage(info.m_language)) << "Language not found (\"" << info.m_language.getUtf8() << "\")" << endl;
PRINT_STREAM(" ****Personal Infos **** ");
PRINT_STREAM("Date of Birth : " << info.m_dateOfBirth);
PRINT_STREAM("Age : " << info.m_age);
PRINT_STREAM("Age Group : " << static_cast<uint32>(info.m_ageGroup));
PRINT_STREAM("Country : " << info.m_country);
PRINT_STREAM("Email : " << info.m_email);
PRINT_STREAM("Language : " << info.m_language);
#if defined(US_TARGET_XONE)
// XB1 fills the AgeGroup attribute OR the date of birth and the email if available.
if(info.m_dateOfBirth.isValid())
{
EXPECT_EQ(ProfileInfoAgeGroup::Unknown, info.m_ageGroup);
}
else
{
EXPECT_NE(ProfileInfoAgeGroup::Unknown, info.m_ageGroup);
}
EXPECT_EQ(0u, info.m_age);
#elif defined(US_TARGET_PS4) || defined(US_TARGET_KILI)
// PS4 fills the date of birth attribute
EXPECT_EQ(ProfileInfoAgeGroup::Unknown, info.m_ageGroup);
EXPECT_EQ(0, info.m_age);
EXPECT_TRUE(info.m_dateOfBirth.isValid());
#elif defined(US_TARGET_SWITCH)
EXPECT_TRUE(info.m_dateOfBirth.isValid());
EXPECT_GT(info.m_age, 0);
EXPECT_EQ(2, info.m_country.getLength());
EXPECT_TRUE(info.m_email.findSubstringCase("ussdk") != String::npos);
EXPECT_TRUE(info.m_email.findSubstringCase("@ubisoft.com") != String::npos);
#else
// Other platform do not get age related data
EXPECT_EQ(ProfileInfoAgeGroup::Unknown, info.m_ageGroup);
EXPECT_EQ(0u, info.m_age);
EXPECT_FALSE(info.m_dateOfBirth.isValid());
#endif
}
#if defined(US_TARGET_XONE) || defined(US_TARGET_SWITCH)
/// Tests the API result mocking the HTTP GET request to the Identity service
/// for extra user profile information (from XBL).
TEST_F(PROFILE_MOCK, USNOT_RequestProfileInfoExternalMockingData)
{
{
#if defined(US_TARGET_SWITCH)
const MockAsyncResult mockResult = MockResultHelper::createHttpErrorResult(RestStatusCode::OK, TestProfileClient_BF::mockProfileInfoExternal());
#else
const MockAsyncResult mockResult = MockResultHelper::createHttpErrorResult(
RestStatusCode::OK, _T("{\"dateOfBirth\":\"1980-01-01T00:00:00.0000000\",\"email\":\"email@dummy.com\"}"));
#endif
US_EXPECT_CALL_HTTP_SIMPLE(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "/external", mockResult);
}
const AsyncResult<ProfileInfoExternal> result = getFacade().getProfileClient().requestProfileInfoExternal();
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
const ProfileInfoExternal& info = result.getResult();
// XB1 fills the AgeGroup attribute AND the date of birth if available.
EXPECT_STREQ("email@dummy.com", info.m_email.getUtf8());
EXPECT_TRUE(info.m_dateOfBirth.isValid());
EXPECT_TRUE(Date(1980, 1, 1) == info.m_dateOfBirth);
EXPECT_EQ(ProfileInfoAgeGroup::Unknown, info.m_ageGroup);
#if defined(US_TARGET_SWITCH)
EXPECT_STREQ("CA", info.m_country.getUtf8());
#endif
}
TEST_F(PROFILE_MOCK, USNOT_RequestProfileInfoExternalMockingError)
{
{
const MockAsyncResult mockResult = MockResultHelper::createHttpErrorResult(
RestStatusCode::Error400, _T("{\"error\":\"myerror\"}"));
US_EXPECT_CALL_HTTP_SIMPLE(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "/external", mockResult);
}
ASSERT_TRUE(AsyncHelper::waitFailure(getFacade().getProfileClient().requestProfileInfoExternal(), Errors::USERS_GENERIC_FAILURE));
}
#endif
TEST_P(PROFILE_CANCEL, US_RequestProfileInfoExternalCancel)
{
// Current behavior when canceling an authentication request is that
// the request is only "canceled" once the request is finished
// (due to our critical sections and the fact that we are using blocking socket)
// So we are only doing a few "cancel" here to avoid the test being too long.
// Furthermore, since the login lasts more than 7s on PS4,
// letting the default parameters is not a good idea anyway...
TestCanceler<> canceler(this->getParam());
while (!canceler.isComplete())
{
EXPECT_TRUE(canceler.cancel(getFacade().getProfileClient().requestProfileInfoExternal()));
}
}
#if defined(US_TARGET_PS4) || defined(US_TARGET_KILI)
/// @test Checks that the profiles returned by the request corresponds to the associated PS4 gamertags/nameOnPlatform
TEST_F(PROFILE, US_RequestProfilesFromNamesOnPlatform)
{
const Vector<String> gamerTags =
{
// These two gamertags only have case sensitive differences.
// They should return one entry in the mapResult because the
// ProfileInfo lookup is case insensitive
"ussdk_ps_4_01", "USSDK_PS_4_01",
// These two identical gamertags should return one ProfileInfo in the mapResult
"ussdk_ps_4_02", "ussdk_ps_4_02",
// This gamertag should return a result with "ussdk_ps_4_03" as mapkey (service's response
// gamertag)
"USSDK_ps_4_03",
// This gamertag shouldn't return a result because it doesn't exist
"dqwdascqwewas",
// This gamertag shouldn't return a result because it is empty
String()
};
const AsyncResult<Map<String, ProfileInfo> > result = getFacade().getProfileClient().requestProfilesFromNamesOnPlatform(gamerTags);
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
const Map<String, ProfileInfo>& map = result.getResult();
ASSERT_EQ(3, map.size());
EXPECT_EQ(ProfileId("b1eb2d18-4fb4-4c14-8e9f-646369d12409"), map.at("ussdk_ps_4_01").m_profileId);
EXPECT_EQ(ProfileId("1af396c2-4f85-40a7-bec3-bd79f38769ec"), map.at("ussdk_ps_4_02").m_profileId);
EXPECT_EQ(ProfileId("8269e628-b852-4e95-9c22-badd418e96ed"), map.at("ussdk_ps_4_03").m_profileId);
}
#elif defined(US_TARGET_XONE)
/// @test Checks that the profiles returned by the request corresponds to the associated XBL gamertags/nameOnPlatform
TEST_F(PROFILE, US_RequestProfilesFromNamesOnPlatform)
{
const Vector<String> gamerTags =
{
// These two gamertags only have case sensitive differences. They should return one entry in the
// mapResult because the ProfileInfo lookup is case insensitive
"2 Dev 92425616",
"2 DEV 92425616",
// These two identical gamertags should return one ProfileInfo in the mapResult
"2 Dev 402090687",
"2 Dev 402090687",
// This gamertag should return a result with "2 Dev 784630699" as mapkey (service's response gamertag)
"2 dev 784630699",
// This gamertag shouldn't return a result because it doesn't exist
"dqwdascqwewas",
// This gamertag shouldn't return a result because it is empty
String()
};
const AsyncResult<Map<String, ProfileInfo> > result = getFacade().getProfileClient().requestProfilesFromNamesOnPlatform(gamerTags);
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
const Map<String, ProfileInfo>& map = result.getResult();
ASSERT_EQ(3, map.size());
EXPECT_EQ(ProfileId("38d8c9a7-d91e-4fa6-a480-67005a2aead7"), map.at("2 Dev 92425616").m_profileId);
EXPECT_EQ(ProfileId("8d717349-c525-4d6d-8b64-a2482e16f7f0"), map.at("2 Dev 402090687").m_profileId);
EXPECT_EQ(ProfileId("2c093017-0c3b-4282-9c3e-fe7113bb50f9"), map.at("2 Dev 784630699").m_profileId);
}
#elif defined(US_TARGET_SWITCH)
/// @test Checks that the profiles returned by the request corresponds to the associated gamertag/nameOnPlatform
TEST_F(PROFILE, US_RequestProfilesFromNamesOnPlatform)
{
const char* const TAG1 = "sdkswitch1";
const char* const TAG2 = "sdkswitch2";
const char* const TAG3 = "sdkswitch3";
const AsyncResult<Map<String, ProfileInfo> > result = getFacade().getProfileClient().requestProfilesFromNamesOnPlatform({ TAG1, TAG2, TAG3 });
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
const Map<String, ProfileInfo>& map = result.getResult();
ASSERT_EQ(3, map.size());
EXPECT_EQ(ProfileId("1f83bda1-67cf-41e1-89be-bc1371740250"), map.at(TAG1).m_profileId);
EXPECT_EQ(ProfileId("34d34930-6688-49c6-8a28-7a0f06c8b41e"), map.at(TAG2).m_profileId);
EXPECT_EQ(ProfileId("b3814923-0f3f-42b2-ac4c-d24e6520872b"), map.at(TAG3).m_profileId);
}
#endif
#if !defined(US_TARGET_SWITCH) && !defined(US_TARGET_PS4) && !defined(US_TARGET_XONE) && !defined(US_TARGET_MANJARO) && !defined(US_TARGET_GGP) && !defined(US_TARGET_KILI)
/// @test Checks that external profile info (i.e. info coming from the first party account) are correctly retrieved when passing an external token
TEST_F(PROFILE_NOLOGIN, DISABLED_MANUAL_US_RequestProfileInfoExternalXBoxOne)
{
// The following ticket is valid only for a short period of time.
// It probably must be updated to run the test successfully
const String externalToken = "XBL3.0 x=8900138882159667348;eyJlbmMiOiJBMTI4Q0JDK0hTMjU2IiwiYWxnIjoiUlNBLU9BRVAiLCJjdHkiOiJKV1QiLCJ6aXAiOiJERUYiLCJ4NXQiOiJIczVfRXFrQTdJdzJpU2lrRmRSanpGd2ZRUFkifQ.HUUCt-XAJU6ZmYSMzQVpTF-paaVnfQBSkBWUjYkyhRo9op0LV4i0HETKOp7f8qOXJ415TbXPlF9nYqxN5OTYHo1AO-d1RDfMMsszR9tbxWpkVMYlC3vEqC4zHNfMqRq2Dgs6Elhnz4UP9jQo2PFYsofvNccgdL5_l6ER-udQ1JvCoMsUhWFUgMuKVsPA0bZNHDV1QYFSXtR-fzv-nslnHKc_M7xd4bU9TIe2Aq1VoHDlUcNucRurVw5V9O8Kksr0vdRGK16seXUpf7-sVYaKhwXl6x3Iwqh7E6SpKakB_e3LSmwr_QL_65qSZG55Cl7rkPeZOYGEDyzW60Aq0QRQew.rW6JfJc9mcC8IDSVEOJA5g.M9yNoA8UgvJrRzscl0fh_ZgNpouNqXrbgD4Hbcb-i2GW_W2CMQZbKYhtRkRZO4aXO2p0BonKXrYdyQznMpRguEMsdFbLH92rtMdLoRkFWXlqjdyFCIzV5ON_eSYUZCddCmcNJZtLry9-oLBOeIekfC32YGhhXHIDVhWN_KApoLOHS24orez7OFuLKT_0kfp34_-WsHXIqTSVpbXiWErhe3uJzxQWUP0yh7Q1AkqRLIfnwofy3yxAuD78qGGh4Vd_8ANADHSb4z5f_ZfvilmfQ2DzvPlrKptSoldFZxIe2FLKG94SCMktUBYbsQJmpmW5Sf7hZwUSft3ofpBMFAdVxXr2NP_CZ0g9jqLgV9k_1ZbNrj3Vr8JV26DpdQh6NY077T3UEByAAHrtlpe2O7QkoO6Y2bS-kMDejtC52ySlq0p0GSTX0pSz_8vtLgg7x816UPp0vOF5ZNDYLUcaG0teIApyYU8AeUH5rO-JIsPmB_HDCQ12qJFzYQGkMFpDsa8UU9BfOSE2bIihm3TvDsn2SRA6fdmY8DIoVHph9y3UJh3HUGt8EOOsEd2hlendXJRyAv8S77VvpIQ3GBqh595cxVJ8PtiA4gPnGEeL2vP-oZCvtaltPYJNtQm0Wea9RpNexVO4APT0xKPOdVu_irKhHzyNt4k02mAeNBJV1Xst5_7AwCRMpDX5Y2bNVNwEuS6zihRJedkqqBYQziFmill3SIBF_sMSpBwH7-J4sbWQSK1IB34jWCLJdQBWqCXRprlarMiYkGSz786AN7TBvIAk5MmuUZFltkvC8BdcsWzyggX14mwXK9aB-YoQTgrBYHNCBES6RX-ccVZAvR0vjDjlf9BC2cPg8RXewRPIa8LmRFqmpuj4FslvgwfNp8EaVaJvqpOd51qYMIEybDdcdauEieKq0VX7GaUYqK7sUQ8Mm7etdgr21t6UQWUDhrhdiXoaYTDUYGordxRrHhnDrtWIJdLZYrs83_fg9h6YUsehIQBe_eacyqMiVFEHxq_PQi-On8qSS-hFO1Rz1mmmgW2EQBXgpO3Lsf1fT8SipkbdbCzxZlcgzjPvkx-ctqfbVpBVEnSoHMOVazrL_ootoRyk2TgzztTJptCjR-0-p1nOjbQ3PbDwxVXdCX7P9xMwkUoxWomVLuHPW3BcnLIbxixxWOBW93NFxtSYRGO61e441akop8xDQu0DBBclxg7Dbu0d5gPDz67xRsGm-Bc_SxYKOz2XwWE1BOgilpFSGOJllvSgcSPLoyiv60OmHd-CHvo423BgUo2goZFx-Bp3JEsLnBTcHSwPCETUcAdl5hA2S_YfOqyjDwK6Zwkw7q9zxMDGd7dYZEzV0Njl0SBY_eetzypyiCjYWtCnwCRI_XBtco0tAUvD264ZFYT_WrJAL9jysZE8XaypkZqpfJgq5Ha81MkD8wB17GIKdN9D7DObOWeT4fFwboN84EjyW8-DhY8YsHi_Wgm2Nz9oVfe_8pZcdEXVML_K2Fvgx3o50_gVehKA-jEKVLj0qfLXFZWT3b81p1mIvPCWtbMTlVuK47KCw1wO415_BdX6-B_wIasisX8j23U6mm22m7zgezLZgkrUfOL7uC9_7W_fgDnfDwRbpQ.YtAylxelu-hZiSlTZtKlbfwIfxpQnnroxgFigpBTwIs";
const CredentialsExternalToken externalCredentials(externalToken, CredentialsType::XblXtoken);
const AsyncResult<ProfileInfoExternal> result = getFacade().getProfileClient().requestProfileInfoExternal(externalCredentials);
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
const ProfileInfoExternal& profileInfo = result.getResult();
EXPECT_EQ(Date(1980, 12, 12), profileInfo.m_dateOfBirth);
EXPECT_EQ("UBMN-UbiServiceTest03@xboxtest.com", profileInfo.m_email);
EXPECT_EQ("US", profileInfo.m_country);
}
/// @test Checks that external profile info (i.e. info coming from the first party account) are correctly retrieved when passing an external token
TEST_F(PROFILE_NOLOGIN, DISABLED_MANUAL_US_RequestProfileInfoExternalPS4)
{
// The following ticket is valid only for a short period of time.
// It probably must be updated to run the test successfully
const String externalToken = "645e3c30-afbf-4f53-8c61-e281b8c74add";
const uint32 SERVER_ENV = 1;
const String usablePsnToken(CredentialsTypeHelper::getPsnToken(PsnAccessToken(externalToken), SERVER_ENV));
const CredentialsExternalToken externalCredentials(usablePsnToken, CredentialsType::PsnAccessToken);
const AsyncResult<ProfileInfoExternal> result = getFacade().getProfileClient().requestProfileInfoExternal(externalCredentials);
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
const ProfileInfoExternal& profileInfo = result.getResult();
EXPECT_EQ(Date(1982, 02, 20), profileInfo.m_dateOfBirth);
EXPECT_EQ("ussdkps4_04@ubisoft.com", profileInfo.m_email);
EXPECT_EQ("US", profileInfo.m_country);
}
/// @test Checks that external profile info (i.e. info coming from the first party account)
/// are correctly retrieved when passing an external token
TEST_F(PROFILE_MOCK_NOLOGIN, USNOT_RequestProfileInfoExternal)
{
// Expectations for create session
const String ubiTicket = "dummyUbiTicketFromCreateSession";
const String expectedEnvironment = "Cert2";
const String expectedUrlPrefix = "cert2-";
HttpPost requestCreateSession(StringUrl(""), "");
{
JsonWriter sessionInfowriter;
for (const auto& it : LoginMockFixture::getMinimalSessionInfo().getJson().getItems())
{
if (it.getKey() != "ticket" && it.getKey() != "environment")
{
sessionInfowriter[it.getKey()] = it;
}
}
sessionInfowriter["ticket"] = ubiTicket;
sessionInfowriter["environment"] = expectedEnvironment;
const MockAsyncResult mockResult = MockResultHelper::createSuccessResult(sessionInfowriter.renderContent());
US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_POST, "/profiles/sessions")
.WillOnce(testing::DoAll(testing::SaveArg<0>(static_cast<HttpRequest*>(&requestCreateSession)), testing::Return(mockResult)));
US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "parameters")
.WillOnce(testing::Return(getMockedParametersApplication()))
.WillOnce(testing::Return(getMockedParametersSpace()))
.RetiresOnSaturation();
}
// Expectations for profile/external
const Date externalDateOfBirth(1980, 12, 12);
const String externalEmail = "UBMN-UbiServiceTest03@xboxtest.com";
const String externalCountry = "US";
HttpGet requestProfilesExternal(StringUrl(""));
{
JsonWriter writer;
writer["dateOfBirth"] = DateTimeHelper::formatDateISO8601(DateTime(externalDateOfBirth, Time(0, 0, 0)));
writer["email"] = externalEmail;
writer["country"] = externalCountry;
const MockAsyncResult mockResult = MockResultHelper::createSuccessResult(writer.renderContent());
US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "/profiles/external")
.WillOnce(testing::DoAll(testing::SaveArg<0>(static_cast<HttpRequest*>(&requestProfilesExternal)), testing::Return(mockResult)));
}
const String externalToken = "DummyTokenFromTest";
const CredentialsExternalToken externalCredentials(externalToken, CredentialsType::PsnAccessToken);
const String encodedExternalToken = TestUserClient_BF::getBase64Encoding(externalToken);
const AsyncResult<ProfileInfoExternal> result = getFacade().getProfileClient().requestProfileInfoExternal(externalCredentials);
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
// Check the authorization header in requestCreateSession contains the token given to the function
EXPECT_TRUE(
requestCreateSession.getHeader("Authorization").findSubstringCase(externalToken) != String::npos ||
requestCreateSession.getHeader("Authorization").findSubstringCase(encodedExternalToken) != String::npos);
// Check the authorization header in requestProfilesExternal contains the token given to the function, not the ticket from the created session
EXPECT_TRUE(
requestProfilesExternal.getHeader("Authorization").findSubstringCase(externalToken) != String::npos ||
requestProfilesExternal.getHeader("Authorization").findSubstringCase(encodedExternalToken) != String::npos);
EXPECT_TRUE(
requestProfilesExternal.getHeader("Authorization").findSubstringCase(ubiTicket) == String::npos);
// Check the URL hits the expected environment. This is the only reason why a session is created.
EXPECT_TRUE(URLInfo(requestProfilesExternal.getUrl()).getHost().findSubstringCase(expectedUrlPrefix) != String::npos);
// Check result
const ProfileInfoExternal profileInfo = result.getResult();
EXPECT_EQ(externalDateOfBirth, profileInfo.m_dateOfBirth);
EXPECT_EQ(externalEmail, profileInfo.m_email);
EXPECT_EQ(externalCountry, profileInfo.m_country);
}
/// @test Checks that credentials supported are only XBL and PSN
TEST_F(PROFILE_NOLOGIN, USNOT_RequestProfileInfoExternalUnsupportedCredentials)
{
ASSERT_TRUE(AsyncHelper::waitFailure(getFacade().getProfileClient().requestProfileInfoExternal(
CredentialsExternalToken(String(), CredentialsType::Facebook)), Errors::PROFILES_INVALID_PARAM));
ASSERT_TRUE(AsyncHelper::waitFailure(getFacade().getProfileClient().requestProfileInfoExternal(
CredentialsExternalToken(String(), CredentialsType::UbiMobile)), Errors::PROFILES_INVALID_PARAM));
ASSERT_TRUE(AsyncHelper::waitFailure(getFacade().getProfileClient().requestProfileInfoExternal(
CredentialsExternalToken(String(), CredentialsType::UbiServices)), Errors::PROFILES_INVALID_PARAM));
ASSERT_TRUE(AsyncHelper::waitFailure(getFacade().getProfileClient().requestProfileInfoExternal(
CredentialsExternalToken(String(), CredentialsType::UplayPC)), Errors::PROFILES_INVALID_PARAM));
ASSERT_TRUE(AsyncHelper::waitFailure(getFacade().getProfileClient().requestProfileInfoExternal(
CredentialsExternalToken(String(), CredentialsType::GameCenter)), Errors::PROFILES_INVALID_PARAM));
ASSERT_TRUE(AsyncHelper::waitFailure(getFacade().getProfileClient().requestProfileInfoExternal(
CredentialsExternalToken(String(), CredentialsType::GoogleGames)), Errors::PROFILES_INVALID_PARAM));
}
/// @test Checks that external profile info (i.e. info coming from the first party account)
/// is not retrieved if profile feature switch is disabled
TEST_F(PROFILE_MOCK_NOLOGIN, USNOT_RequestProfilesSwitch)
{
// Expectations for create session
const String ubiTicket = "dummyUbiTicketFromCreateSession";
const String expectedEnvironment = "Demo";
const String expectedUrlPrefix = "demo-";
HttpPost requestCreateSession(StringUrl(""), "");
{
JsonWriter sessionInfowriter;
for (const auto& it : LoginMockFixture::getMinimalSessionInfo().getJson().getItems())
{
if (it.getKey() != "ticket" && it.getKey() != "environment")
{
sessionInfowriter[it.getKey()] = it;
}
}
sessionInfowriter["ticket"] = ubiTicket;
sessionInfowriter["environment"] = expectedEnvironment;
const MockAsyncResult mockResult = MockResultHelper::createSuccessResult(sessionInfowriter.renderContent());
US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_POST, "/profiles/sessions")
.WillOnce(testing::DoAll(testing::SaveArg<0>(static_cast<HttpRequest*>(&requestCreateSession)), testing::Return(mockResult)));
const auto spaceParametersMockResult = MockResultHelper::createSuccessResult(ParametersSpaceMockHelper()
.changeFeatureSwitch(FeatureSwitchId::Profiles, FeatureSwitchState::Disabled).getParameters().renderContent());
US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "parameters")
.WillOnce(testing::Return(getMockedParametersApplication()))
.WillOnce(testing::Return(spaceParametersMockResult))
.RetiresOnSaturation();
}
// Expectations for profile/external
{
US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "/profiles/external").Times(0);
}
const String externalToken = "DummyTokenFromTest";
const String encodedExternalToken = TestUserClient_BF::getBase64Encoding(externalToken);
ASSERT_TRUE(AsyncHelper::waitFailure(getFacade().getProfileClient().requestProfileInfoExternal(
CredentialsExternalToken(externalToken, CredentialsType::PsnAccessToken)), Errors::SKIPPED_FEATURESWITCH));
}
#endif // !defined(US_TARGET_SWITCH)
};