chromium/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubFetchServiceTest.java

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.safety_hub;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

import org.chromium.base.Callback;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.Features;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.password_manager.FakePasswordCheckupClientHelper;
import org.chromium.chrome.browser.preferences.Pref;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.components.background_task_scheduler.BackgroundTaskScheduler;
import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerFactory;
import org.chromium.components.background_task_scheduler.TaskIds;
import org.chromium.components.background_task_scheduler.TaskInfo;
import org.chromium.components.prefs.PrefService;

import java.util.concurrent.TimeUnit;

/** Unit tests for SafetyHubFetchService. */
@RunWith(BaseRobolectricTestRunner.class)
public class SafetyHubFetchServiceTest {
    private static final int ONE_DAY_IN_MILLISECONDS = (int) TimeUnit.DAYS.toMillis(1);

    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
    @Rule public SafetyHubTestRule mSafetyHubTestRule = new SafetyHubTestRule();

    @Mock private BackgroundTaskScheduler mTaskScheduler;
    @Mock private Callback<Boolean> mTaskFinishedCallback;
    @Captor private ArgumentCaptor<TaskInfo> mTaskInfoCaptor;

    private Profile mProfile;
    private PrefService mPrefService;
    private FakePasswordCheckupClientHelper mPasswordCheckupClientHelper;

    @Before
    public void setUp() {
        BackgroundTaskSchedulerFactory.setSchedulerForTesting(mTaskScheduler);
        mProfile = mSafetyHubTestRule.getProfile();
        mPrefService = mSafetyHubTestRule.getPrefService();
        mPasswordCheckupClientHelper = mSafetyHubTestRule.getPasswordCheckupClientHelper();
    }

    @Test
    @Features.EnableFeatures(ChromeFeatureList.SAFETY_HUB)
    public void testTaskScheduledImmediately_WhenConditionsMet() {
        new SafetyHubFetchService(mProfile).onForegroundSessionStart();

        verify(mTaskScheduler, times(1)).schedule(any(), mTaskInfoCaptor.capture());
        TaskInfo taskInfo = mTaskInfoCaptor.getValue();
        assertEquals(TaskIds.SAFETY_HUB_JOB_ID, taskInfo.getTaskId());
        assertTrue(taskInfo.isPersisted());
        assertFalse(taskInfo.shouldUpdateCurrent());
        assertEquals(0, ((TaskInfo.OneOffInfo) taskInfo.getTimingInfo()).getWindowStartTimeMs());
        assertEquals(0, ((TaskInfo.OneOffInfo) taskInfo.getTimingInfo()).getWindowEndTimeMs());
    }

    @Test
    @Features.DisableFeatures(ChromeFeatureList.SAFETY_HUB)
    public void testTaskCancelled_WhenConditionsNotMet() {
        new SafetyHubFetchService(mProfile).onForegroundSessionStart();

        // Verify prefs are cleaned up when task is cancelled.
        verify(mPrefService, times(1)).clearPref(Pref.BREACHED_CREDENTIALS_COUNT);
        verify(mTaskScheduler, times(1)).cancel(any(), eq(TaskIds.SAFETY_HUB_JOB_ID));
        verify(mTaskScheduler, never()).schedule(any(), mTaskInfoCaptor.capture());
    }

    @Test
    @Features.EnableFeatures(ChromeFeatureList.SAFETY_HUB)
    public void testTaskCancelled_WhenSigninStatusChanged_SignOut() {
        mSafetyHubTestRule.setSignedInState(false);

        new SafetyHubFetchService(mProfile).onSignedOut();

        // Verify prefs are cleaned up when task is cancelled.
        verify(mPrefService, times(1)).clearPref(Pref.BREACHED_CREDENTIALS_COUNT);
        verify(mTaskScheduler, times(1)).cancel(any(), eq(TaskIds.SAFETY_HUB_JOB_ID));
        verify(mTaskScheduler, never()).schedule(any(), any());
    }

    @Test
    @Features.EnableFeatures(ChromeFeatureList.SAFETY_HUB)
    public void testTaskScheduled_WhenSigninStatusChanged_SignIn() {
        mSafetyHubTestRule.setSignedInState(true);

        new SafetyHubFetchService(mProfile).onSignedIn();

        verify(mTaskScheduler, times(1)).schedule(any(), mTaskInfoCaptor.capture());
        TaskInfo taskInfo = mTaskInfoCaptor.getValue();
        assertEquals(TaskIds.SAFETY_HUB_JOB_ID, taskInfo.getTaskId());
        assertEquals(0, ((TaskInfo.OneOffInfo) taskInfo.getTimingInfo()).getWindowStartTimeMs());
        assertEquals(0, ((TaskInfo.OneOffInfo) taskInfo.getTimingInfo()).getWindowEndTimeMs());
    }

    @Test
    @Features.EnableFeatures(ChromeFeatureList.SAFETY_HUB)
    public void testTaskCancelled_WhenUPMDisabled() {
        mSafetyHubTestRule.setUPMStatus(false);
        new SafetyHubFetchService(mProfile).onForegroundSessionStart();

        // Verify prefs are cleaned up when task is cancelled.
        verify(mPrefService, times(1)).clearPref(Pref.BREACHED_CREDENTIALS_COUNT);
        verify(mTaskScheduler, times(1)).cancel(any(), eq(TaskIds.SAFETY_HUB_JOB_ID));
        verify(mTaskScheduler, never()).schedule(any(), mTaskInfoCaptor.capture());
    }

    @Test
    @Features.EnableFeatures(ChromeFeatureList.SAFETY_HUB)
    public void testTaskRescheduled_whenFetchFails() {
        mPasswordCheckupClientHelper.setError(new Exception());

        new SafetyHubFetchService(mProfile).fetchBreachedCredentialsCount(mTaskFinishedCallback);

        verify(mPrefService, never()).setInteger(eq(Pref.BREACHED_CREDENTIALS_COUNT), anyInt());
        verify(mTaskFinishedCallback, times(1)).onResult(eq(/* needsReschedule= */ true));
    }

    @Test
    @Features.EnableFeatures(ChromeFeatureList.SAFETY_HUB)
    public void testNextTaskScheduled_WhenFetchSucceeds() {
        int breachedCredentialsCount = 5;
        mPasswordCheckupClientHelper.setBreachedCredentialsCount(breachedCredentialsCount);

        new SafetyHubFetchService(mProfile).fetchBreachedCredentialsCount(mTaskFinishedCallback);

        verify(mPrefService, times(1))
                .setInteger(Pref.BREACHED_CREDENTIALS_COUNT, breachedCredentialsCount);
        verify(mTaskFinishedCallback, times(1)).onResult(eq(/* needsReschedule= */ false));

        // Check previous job is cleaned up.
        verify(mTaskScheduler, times(1)).cancel(any(), eq(TaskIds.SAFETY_HUB_JOB_ID));

        // Check next job is scheduled after the specified period.
        verify(mTaskScheduler, times(1)).schedule(any(), mTaskInfoCaptor.capture());
        TaskInfo taskInfo = mTaskInfoCaptor.getValue();
        assertEquals(TaskIds.SAFETY_HUB_JOB_ID, taskInfo.getTaskId());
        assertTrue(taskInfo.isPersisted());
        assertFalse(taskInfo.shouldUpdateCurrent());
        assertEquals(
                ONE_DAY_IN_MILLISECONDS,
                ((TaskInfo.OneOffInfo) taskInfo.getTimingInfo()).getWindowStartTimeMs());
        assertEquals(
                ONE_DAY_IN_MILLISECONDS,
                ((TaskInfo.OneOffInfo) taskInfo.getTimingInfo()).getWindowEndTimeMs());
    }
}