//////////////////////////////////////////////////////////////////////////////// // (c) Copyright, Ubisoft Entertainment Inc. || Created by Harbour // //////////////////////////////////////////////////////////////////////////////// #include "tests/config.h" #include "tests/common/helpers.h" #include "tests/fixture/authenticationFixture.h" #include "tests/fixture/loginMockFixture.h" #include "tests/mock/mockLogDevice.h" #include "tests/mock/mockResultHelper.h" #include "tests/mock/parametersMockHelper.h" #include <../private/ubiservices/core/extensions/enumeration.h> namespace US_NS {; US_DEFINE_TEST(TestExtendSession); typedef AuthenticationFixture<1> EXTEND_SESSION; typedef LoginMockFixture EXTEND_SESSION_MOCK_NOLOGIN; namespace TestSuspendedMode_BF { #if defined(US_SDK_S2S) const char* const EXTEND_SESSION_ENDPOINT = "/applications/sessions"; #else const char* const EXTEND_SESSION_ENDPOINT = "/profiles/sessions"; #endif #if !defined(US_SDK_S2S) AuthenticationNotificationType waitForExtendSessionNotification(Facade& facade, DurationNano waitTime); #endif AuthenticationNotificationType waitForExtendSessionNotification(ListenerHandler& listener, DurationNano waitTime); } namespace TestExtendSession_BF { AuthenticationNotificationType waitForNotification( ListenerHandler& listener, DurationMicro waitNano = DurationMicro(DurationMilli(NS_COMMON::DEFAULT_WAIT_TIME))) { return TestSuspendedMode_BF::waitForExtendSessionNotification(listener, DurationNano(waitNano)); } } #if !defined(US_DLL) #if !defined(DECLARE_TEST_EXTEND_SESSION_HELPER) #define DECLARE_TEST_EXTEND_SESSION_HELPER struct TestExtendSessionHelper { US_FRIEND_TEST(EXTEND_SESSION, US_ExtendSession); US_FRIEND_TEST(USTK_NOLOGIN_NOSCHEDULER, USNOT_ExerciseRenewTicket); static void reduceSessionDuration(Facade& facade, uint64 expirationTimeMs); // For test purposes ONLY }; #endif // !defined(DECLARE_TEST_EXTEND_SESSION_HELPER) TEST_F(EXTEND_SESSION, US_ExtendSession) { //#define ENABLING_VERBOSE_LOGGING #if defined(ENABLING_VERBOSE_LOGGING) LoggingHelper::enableVerbose(); LoggingHelper::enableMultiLines(); #endif //setupDebugLogging(); waitForPostLoginCompletion(); // Create listener before triggering state change, so we can catch notification ListenerHandler notificationListener = getFacade().getAuthenticationClient().createNotificationListener(); TestExtendSessionHelper::reduceSessionDuration(getFacade(), ClockSteady::getTimeMilli() + 500); // Setup expiration ASSERT_EQ(AuthenticationNotificationType::SessionInfoChanged, TestExtendSession_BF::waitForNotification(notificationListener, DurationMicro(Duration<>(5)))); const SessionInfo sessionInfo = getFacade().getAuthenticationClient().getSessionInfo(); ASSERT_TRUE(sessionInfo.hasValidSession()); #if defined(US_SDK_S2S) EXPECT_FALSE(sessionInfo.getTicket().isEmpty()); #else EXPECT_FALSE(sessionInfo.getNameOnPlatform().isEmpty()); EXPECT_TRUE(sessionInfo.getProfileId().isValid()); // Running next line triggers an assert if the PlayerCredential is not set. sessionInfo.getPlayerCredentials(); #endif } #endif /// @test Checks that the extend session is retried appropriately TEST_F(EXTEND_SESSION_MOCK_NOLOGIN, USNOT_LONG_ExtendSessionRetry) { { const uint8 SESSION_DURATION_SECONDS = 20; // TODO: Review this value to shorten the test duration const MockAsyncResult sessionResult = getMockedLoginShortSession(SESSION_DURATION_SECONDS, false); const MockAsyncResult failedExtendSession = MockResultHelper::createRestErrorResult(RestStatusCode::Error404, 1000); mockFullLogin(&sessionResult); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, TestSuspendedMode_BF::EXTEND_SESSION_ENDPOINT) .Times(3) .WillOnce(testing::Return(failedExtendSession)) .WillOnce(testing::Return(failedExtendSession)) .WillOnce(testing::Return(getMockedLoginResponseNoUbiAccount())); //works on last attempt. } // Performs a login to trigger the extend session // Create listener before triggering state change, so we can catch notification ListenerHandler notificationListener = getFacade().getAuthenticationClient().createNotificationListener(); ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().createSession(GET_CREDENTIALS(), {}, {}))); const AuthenticationNotificationType notifCreateSession = TestExtendSession_BF::waitForNotification(notificationListener, DurationMicro(Duration<>(6))); EXPECT_EQ(AuthenticationNotificationType::SessionInfoChanged, notifCreateSession) << "We didn't get the expected notification for createSession."; const AuthenticationNotificationType notif = TestExtendSession_BF::waitForNotification(notificationListener); EXPECT_EQ(AuthenticationNotificationType::SessionInfoChanged, notif) << "We didn't get the expected client notification."; } /// @test Checks that the extend session is retried and that it sends the according notification when it fails. TEST_F(EXTEND_SESSION_MOCK_NOLOGIN, USNOT_LONG_ExtendSessionFailure) { PrepareLoggingMock([](US_TEST_NS::MockLogDevice& device) { // Note : Since we return an error, all mandatory fields in SessionInfo are missing and will all raise a log // This will happen for each extend session retry // so there should be (number of session retries) * (number of mandatory sessionInfo fields) logs const uint32 NUMBER_OF_EXTENDSESSION_RETRIES = 3; const uint32 NUMBER_OF_MANDATORY_FIELDS_IN_SESSIONINFO = 9; US_EXPECT_LOG("Missing mandatory field:", LogLevel::Error, Times(NUMBER_OF_EXTENDSESSION_RETRIES * NUMBER_OF_MANDATORY_FIELDS_IN_SESSIONINFO)); US_EXPECT_LOG("Failed to extend the current session because of invalid JSON:", LogLevel::Error, Times(3)); }); { const uint8 SESSION_DURATION_SECONDS = 20; // TODO: Review this value to shorten the test duration const MockAsyncResult sessionResult = getMockedLoginShortSession(SESSION_DURATION_SECONDS, false); mockFullLogin(&sessionResult); const MockAsyncResult failedExtendSession = MockResultHelper::createRestErrorResult(RestStatusCode::Error404, 1000); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, TestSuspendedMode_BF::EXTEND_SESSION_ENDPOINT) .Times(3) .WillRepeatedly(testing::Return(failedExtendSession)); // fails on all attempts } // Performs a login to trigger the extend session // Create listener before triggering state change, so we can catch notification ListenerHandler notificationListener = getFacade().getAuthenticationClient().createNotificationListener(); ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().createSession(GET_CREDENTIALS(), {}, {}))); const AuthenticationNotificationType notifCreateSession = TestExtendSession_BF::waitForNotification(notificationListener, DurationMicro(Duration<>(6))); EXPECT_EQ(AuthenticationNotificationType::SessionInfoChanged, notifCreateSession) << "We didn't get the expected notification for createSession."; const AuthenticationNotificationType notifExtendSessionFailed = TestExtendSession_BF::waitForNotification(notificationListener); EXPECT_EQ(AuthenticationNotificationType::SessionExtensionFailed, notifExtendSessionFailed); } /// @test Checks that the extend session is periodic and is achieved several times. TEST_F(EXTEND_SESSION_MOCK_NOLOGIN, USNOT_SeveralExtendSession) { const int ITERATION_COUNT = 3; { const Duration SESSION_DURATION_SECONDS = 5; const MockAsyncResult sessionResult = getMockedLoginShortSession(SESSION_DURATION_SECONDS, false); mockFullLogin(&sessionResult); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, TestSuspendedMode_BF::EXTEND_SESSION_ENDPOINT) .Times(ITERATION_COUNT) .WillRepeatedly(testing::Return(sessionResult)); } // Performs a login to trigger the extend session // Create listener before triggering state change, so we can catch notification ListenerHandler notificationListener = getFacade().getAuthenticationClient().createNotificationListener(); ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().createSession(GET_CREDENTIALS(), {}, {}))); const AuthenticationNotificationType notifCreateSession = TestExtendSession_BF::waitForNotification(notificationListener, DurationMicro(Duration<>(6))); EXPECT_EQ(AuthenticationNotificationType::SessionInfoChanged, notifCreateSession) << "We didn't get the expected notification for createSession."; for (int i = 0; i < ITERATION_COUNT; i++) { PRINT_PROGRESS(i + 1, ITERATION_COUNT, "Waiting notification %d", i + 1); const AuthenticationNotificationType notifExtendSessionFailed = TestExtendSession_BF::waitForNotification(notificationListener, DurationMicro(Duration<>(6))); EXPECT_EQ(AuthenticationNotificationType::SessionInfoChanged, notifExtendSessionFailed) << "We didn't get the expected notification for session extension."; } } /// @test Checks that there is no session extension when the ExtendSession switch is off. /// @note This test is not in the testKillSwitches file mainly because everything /// is set in this file to ease making this test. Furthermore, the extendSession /// switch does not deactivate communication like other switches, it disables a feature. /// Having it in a separate file is therefore not completely absurd... TEST_F(EXTEND_SESSION_MOCK_NOLOGIN, USNOT_SwitchedExtendSessionOff) { const Duration SESSION_DURATION_SECONDS = 5; { const MockAsyncResult sessionResult = getMockedLoginShortSession(SESSION_DURATION_SECONDS, false); auto parametersApplicationResult = getMockedParametersApplication(); auto parametersSpaceResult = MockResultHelper::createSuccessResult(ParametersSpaceMockHelper().changeFeatureSwitch(FeatureSwitchId::ExtendSession, FeatureSwitchState::Disabled).getParameters().renderContent()); mockFullLogin(&sessionResult, ¶metersApplicationResult, ¶metersSpaceResult); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, TestSuspendedMode_BF::EXTEND_SESSION_ENDPOINT).Times(0); } // Performs a login to trigger the extend session // Create listener before triggering state change, so we can catch notification ListenerHandler notificationListener = getFacade().getAuthenticationClient().createNotificationListener(); ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().createSession(GET_CREDENTIALS(), {}, {}))); const AuthenticationNotificationType notifCreateSession = TestExtendSession_BF::waitForNotification(notificationListener, DurationMicro(Duration<>(6))); EXPECT_EQ(AuthenticationNotificationType::SessionInfoChanged, notifCreateSession) << "We didn't get the expected notification for createSession."; const AuthenticationNotificationType notifNone = TestExtendSession_BF::waitForNotification(notificationListener, DurationMicro(2*SESSION_DURATION_SECONDS)); EXPECT_EQ(AuthenticationNotificationType::None, notifNone) << "There should not be an extend session notification."; } };