blob: 2f527bef3bfb80b52203250c7efbf0a7d7e4a6c4 [file] [log] [blame]
// 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.
#import "ios/chrome/browser/safety_check_notifications/utils/utils.h"
#import <Foundation/Foundation.h>
#import <UserNotifications/UserNotifications.h>
#import <optional>
#import "base/strings/sys_string_conversions.h"
#import "base/time/time.h"
#import "ios/chrome/browser/passwords/model/password_checkup_utils.h"
#import "ios/chrome/browser/push_notification/model/push_notification_client.h"
#import "ios/chrome/browser/safety_check/model/ios_chrome_safety_check_manager_constants.h"
#import "ios/chrome/browser/safety_check_notifications/utils/constants.h"
#import "ios/chrome/grit/ios_branded_strings.h"
#import "ios/chrome/grit/ios_strings.h"
#import "testing/gtest/include/gtest/gtest.h"
#import "testing/gtest_mac.h"
#import "testing/platform_test.h"
#import "ui/base/l10n/l10n_util_mac.h"
class SafetyCheckNotificationUtilsTest : public PlatformTest {};
#pragma mark - Test cases
// Tests if notification content is correctly generated for the compromised
// password state.
TEST_F(SafetyCheckNotificationUtilsTest,
ReturnsCompromisedPasswordNotificationContentForCompromisedState) {
// Define insecure password counts for the test case.
password_manager::InsecurePasswordCounts counts = {/* compromised */ 3,
/* dismissed */ 1,
/* reused */ 2,
/* weak */ 3};
PasswordSafetyCheckState state =
PasswordSafetyCheckState::kUnmutedCompromisedPasswords;
// Call the function under test.
UNNotificationContent* compromised_password_notification =
NotificationForPasswordCheckState(state, counts);
// Verify the notification content is not nil.
EXPECT_NE(compromised_password_notification, nil);
// Verify the title matches the expected string.
NSString* expected_title = l10n_util::GetNSString(
IDS_IOS_SAFETY_CHECK_NOTIFICATIONS_CHANGE_PASSWORDS);
EXPECT_NSEQ(expected_title, compromised_password_notification.title);
// Verify the body matches the expected plural string.
NSString* expected_body = l10n_util::GetPluralNSStringF(
IDS_IOS_SAFETY_CHECK_NOTIFICATIONS_COMPROMISED_PASSWORD,
counts.compromised_count);
EXPECT_NSEQ(expected_body, compromised_password_notification.body);
// Verify user info contains the correct keys and values.
EXPECT_TRUE([compromised_password_notification
.userInfo[kSafetyCheckPasswordNotificationID] boolValue]);
// Verify the check result string matches the output of
// NameForSafetyCheckState.
EXPECT_NSEQ(compromised_password_notification
.userInfo[kSafetyCheckNotificationCheckResultKey],
base::SysUTF8ToNSString(NameForSafetyCheckState(state)));
EXPECT_EQ([compromised_password_notification
.userInfo[kSafetyCheckNotificationInsecurePasswordCountKey]
intValue],
counts.compromised_count);
}
// Tests if notification content is correctly generated for the weak password
// state.
TEST_F(SafetyCheckNotificationUtilsTest,
ReturnsWeakPasswordNotificationContentForWeakState) {
// Define insecure password counts for the test case.
password_manager::InsecurePasswordCounts counts = {/* compromised */ 1,
/* dismissed */ 2,
/* reused */ 3,
/* weak */ 4};
PasswordSafetyCheckState state = PasswordSafetyCheckState::kWeakPasswords;
// Call the function under test.
UNNotificationContent* weak_password_notification =
NotificationForPasswordCheckState(state, counts);
// Verify the notification content is not nil.
EXPECT_NE(weak_password_notification, nil);
// Verify the title matches the expected string.
NSString* expected_title = l10n_util::GetNSString(
IDS_IOS_SAFETY_CHECK_NOTIFICATIONS_CHANGE_PASSWORDS);
EXPECT_NSEQ(expected_title, weak_password_notification.title);
// Verify the body matches the expected plural string.
NSString* expected_body = l10n_util::GetPluralNSStringF(
IDS_IOS_SAFETY_CHECK_NOTIFICATIONS_WEAK_PASSWORD, counts.weak_count);
EXPECT_NSEQ(expected_body, weak_password_notification.body);
// Verify user info contains the correct keys and values.
EXPECT_TRUE(
[weak_password_notification.userInfo[kSafetyCheckPasswordNotificationID]
boolValue]);
// Verify the check result string matches the output of
// NameForSafetyCheckState.
EXPECT_NSEQ(weak_password_notification
.userInfo[kSafetyCheckNotificationCheckResultKey],
base::SysUTF8ToNSString(NameForSafetyCheckState(state)));
EXPECT_EQ([weak_password_notification
.userInfo[kSafetyCheckNotificationInsecurePasswordCountKey]
intValue],
counts.weak_count);
}
// Tests if notification content is correctly generated for the reused password
// state.
TEST_F(SafetyCheckNotificationUtilsTest,
ReturnsReusedPasswordNotificationContentForReusedState) {
// Define insecure password counts for the test case.
password_manager::InsecurePasswordCounts counts = {/* compromised */ 2,
/* dismissed */ 3,
/* reused */ 5,
/* weak */ 4};
PasswordSafetyCheckState state = PasswordSafetyCheckState::kReusedPasswords;
// Call the function under test.
UNNotificationContent* reused_password_notification =
NotificationForPasswordCheckState(state, counts);
// Verify the notification content is not nil.
EXPECT_NE(reused_password_notification, nil);
// Verify the title matches the expected string.
NSString* expected_title = l10n_util::GetNSString(
IDS_IOS_SAFETY_CHECK_NOTIFICATIONS_CHANGE_PASSWORDS);
EXPECT_NSEQ(expected_title, reused_password_notification.title);
// Verify the body matches the expected plural string.
NSString* expected_body = l10n_util::GetPluralNSStringF(
IDS_IOS_SAFETY_CHECK_NOTIFICATIONS_REUSED_PASSWORD, counts.reused_count);
EXPECT_NSEQ(expected_body, reused_password_notification.body);
// Verify user info contains the correct keys and values.
EXPECT_TRUE(
[reused_password_notification.userInfo[kSafetyCheckPasswordNotificationID]
boolValue]);
// Verify the check result string matches the output of
// NameForSafetyCheckState.
EXPECT_NSEQ(reused_password_notification
.userInfo[kSafetyCheckNotificationCheckResultKey],
base::SysUTF8ToNSString(NameForSafetyCheckState(state)));
EXPECT_EQ([reused_password_notification
.userInfo[kSafetyCheckNotificationInsecurePasswordCountKey]
intValue],
counts.reused_count);
}
// Tests that no notification content is generated for the safe password state.
TEST_F(SafetyCheckNotificationUtilsTest,
ReturnsNoPasswordNotificationContentForSafeState) {
// Define insecure password counts for the test case (all zero).
password_manager::InsecurePasswordCounts counts = {/* compromised */ 0,
/* dismissed */ 0,
/* reused */ 0,
/* weak */ 0};
PasswordSafetyCheckState state = PasswordSafetyCheckState::kSafe;
// Call the function under test.
UNNotificationContent* password_notification =
NotificationForPasswordCheckState(state, counts);
// Verify the notification content is nil.
EXPECT_EQ(password_notification, nil);
}
// Tests if notification content is correctly generated for the out-of-date
// Chrome state.
TEST_F(SafetyCheckNotificationUtilsTest,
ReturnsUpdateChromeNotificationContentForOutOfDateState) {
UpdateChromeSafetyCheckState state = UpdateChromeSafetyCheckState::kOutOfDate;
// Call the function under test.
UNNotificationContent* update_chrome_notification =
NotificationForUpdateChromeCheckState(state);
// Verify the notification content is not nil.
EXPECT_NE(update_chrome_notification, nil);
// Verify the title matches the expected string.
NSString* expected_title =
l10n_util::GetNSString(IDS_IOS_SAFETY_CHECK_NOTIFICATIONS_UPDATE_BROWSER);
EXPECT_NSEQ(expected_title, update_chrome_notification.title);
// Verify the body matches the expected string.
NSString* expected_body =
l10n_util::GetNSString(IDS_IOS_SAFETY_CHECK_DESCRIPTION_UPDATE_CHROME);
EXPECT_NSEQ(expected_body, update_chrome_notification.body);
// Verify user info contains the correct keys and values.
EXPECT_TRUE(
[update_chrome_notification
.userInfo[kSafetyCheckUpdateChromeNotificationID] boolValue]);
// Verify the check result string matches the output of
// NameForSafetyCheckState.
EXPECT_NSEQ(update_chrome_notification
.userInfo[kSafetyCheckNotificationCheckResultKey],
base::SysUTF8ToNSString(NameForSafetyCheckState(state)));
}
// Tests that no notification content is generated for the up-to-date Chrome
// state.
TEST_F(SafetyCheckNotificationUtilsTest,
ReturnsNoUpdateChromeNotificationContentForUpToDateState) {
UpdateChromeSafetyCheckState state = UpdateChromeSafetyCheckState::kUpToDate;
// Call the function under test.
UNNotificationContent* update_chrome_notification =
NotificationForUpdateChromeCheckState(state);
// Verify the notification content is nil.
EXPECT_EQ(update_chrome_notification, nil);
}
// Tests if notification content is correctly generated for the unsafe Safe
// Browsing state.
TEST_F(SafetyCheckNotificationUtilsTest,
ReturnsSafeBrowsingNotificationContentForUnsafeState) {
SafeBrowsingSafetyCheckState state = SafeBrowsingSafetyCheckState::kUnsafe;
// Call the function under test.
UNNotificationContent* safe_browsing_notification =
NotificationForSafeBrowsingCheckState(state);
// Verify the notification content is not nil.
EXPECT_NE(safe_browsing_notification, nil);
// Verify the title matches the expected string.
NSString* expected_title = l10n_util::GetNSString(
IDS_IOS_SAFETY_CHECK_NOTIFICATIONS_ADD_BROWSING_PROTECTION);
EXPECT_NSEQ(expected_title, safe_browsing_notification.title);
// Verify the body matches the expected string.
NSString* expected_body = l10n_util::GetNSString(
IDS_IOS_SAFETY_CHECK_NOTIFICATIONS_BROWSING_PROTECTION_OFF);
EXPECT_NSEQ(expected_body, safe_browsing_notification.body);
// Verify user info contains the correct keys and values.
EXPECT_TRUE(
[safe_browsing_notification
.userInfo[kSafetyCheckSafeBrowsingNotificationID] boolValue]);
// Verify the check result string matches the output of
// NameForSafetyCheckState.
EXPECT_NSEQ(safe_browsing_notification
.userInfo[kSafetyCheckNotificationCheckResultKey],
base::SysUTF8ToNSString(NameForSafetyCheckState(state)));
}
// Tests that no notification content is generated for the safe Safe Browsing
// state.
TEST_F(SafetyCheckNotificationUtilsTest,
ReturnsNoSafeBrowsingNotificationContentForSafeState) {
SafeBrowsingSafetyCheckState state = SafeBrowsingSafetyCheckState::kSafe;
// Call the function under test.
UNNotificationContent* safe_browsing_notification =
NotificationForSafeBrowsingCheckState(state);
// Verify the notification content is nil.
EXPECT_EQ(safe_browsing_notification, nil);
}
// Tests that GetPasswordNotificationRequest returns a valid request for a
// compromised state.
TEST_F(SafetyCheckNotificationUtilsTest,
ReturnsPasswordRequestForCompromisedState) {
// Define insecure password counts.
password_manager::InsecurePasswordCounts counts = {/* compromised */ 3,
/* dismissed */ 1,
/* reused */ 2,
/* weak */ 3};
PasswordSafetyCheckState state =
PasswordSafetyCheckState::kUnmutedCompromisedPasswords;
// Call the function under test.
std::optional<ScheduledNotificationRequest> request =
GetPasswordNotificationRequest(state, counts);
// Verify a request is returned.
ASSERT_TRUE(request.has_value());
// Verify the identifier is correct.
EXPECT_NSEQ(kSafetyCheckPasswordNotificationID, request.value().identifier);
// Verify the content is not nil.
EXPECT_NE(nil, request.value().content);
// Verify the time interval is positive.
EXPECT_GT(request.value().time_interval.InSeconds(), 0);
// Verify content details (spot check title).
NSString* expected_title = l10n_util::GetNSString(
IDS_IOS_SAFETY_CHECK_NOTIFICATIONS_CHANGE_PASSWORDS);
EXPECT_NSEQ(expected_title, request.value().content.title);
// Verify userInfo check result (spot check).
EXPECT_NSEQ(
request.value().content.userInfo[kSafetyCheckNotificationCheckResultKey],
base::SysUTF8ToNSString(NameForSafetyCheckState(state)));
}
// Tests that GetPasswordNotificationRequest returns nullopt for a safe state.
TEST_F(SafetyCheckNotificationUtilsTest, ReturnsNoPasswordRequestForSafeState) {
// Define insecure password counts (all zero).
password_manager::InsecurePasswordCounts counts = {/* compromised */ 0,
/* dismissed */ 0,
/* reused */ 0,
/* weak */ 0};
PasswordSafetyCheckState state = PasswordSafetyCheckState::kSafe;
// Call the function under test.
std::optional<ScheduledNotificationRequest> request =
GetPasswordNotificationRequest(state, counts);
// Verify no request is returned.
EXPECT_FALSE(request.has_value());
}
// Tests that GetUpdateChromeNotificationRequest returns a valid request for an
// out-of-date state.
TEST_F(SafetyCheckNotificationUtilsTest,
ReturnsUpdateChromeRequestForOutOfDateState) {
UpdateChromeSafetyCheckState state = UpdateChromeSafetyCheckState::kOutOfDate;
// Call the function under test.
std::optional<ScheduledNotificationRequest> request =
GetUpdateChromeNotificationRequest(state);
// Verify a request is returned.
ASSERT_TRUE(request.has_value());
// Verify the identifier is correct.
EXPECT_NSEQ(kSafetyCheckUpdateChromeNotificationID,
request.value().identifier);
// Verify the content is not nil.
EXPECT_NE(nil, request.value().content);
// Verify the time interval is positive.
EXPECT_GT(request.value().time_interval.InSeconds(), 0);
// Verify content details (spot check title).
NSString* expected_title =
l10n_util::GetNSString(IDS_IOS_SAFETY_CHECK_NOTIFICATIONS_UPDATE_BROWSER);
EXPECT_NSEQ(expected_title, request.value().content.title);
// Verify userInfo check result (spot check).
EXPECT_NSEQ(
request.value().content.userInfo[kSafetyCheckNotificationCheckResultKey],
base::SysUTF8ToNSString(NameForSafetyCheckState(state)));
}
// Tests that GetUpdateChromeNotificationRequest returns nullopt for an
// up-to-date state.
TEST_F(SafetyCheckNotificationUtilsTest,
ReturnsNoUpdateChromeRequestForUpToDateState) {
UpdateChromeSafetyCheckState state = UpdateChromeSafetyCheckState::kUpToDate;
// Call the function under test.
std::optional<ScheduledNotificationRequest> request =
GetUpdateChromeNotificationRequest(state);
// Verify no request is returned.
EXPECT_FALSE(request.has_value());
}
// Tests that GetSafeBrowsingNotificationRequest returns a valid request for an
// unsafe state.
TEST_F(SafetyCheckNotificationUtilsTest,
ReturnsSafeBrowsingRequestForUnsafeState) {
SafeBrowsingSafetyCheckState state = SafeBrowsingSafetyCheckState::kUnsafe;
// Call the function under test.
std::optional<ScheduledNotificationRequest> request =
GetSafeBrowsingNotificationRequest(state);
// Verify a request is returned.
ASSERT_TRUE(request.has_value());
// Verify the identifier is correct.
EXPECT_NSEQ(kSafetyCheckSafeBrowsingNotificationID,
request.value().identifier);
// Verify the content is not nil.
EXPECT_NE(nil, request.value().content);
// Verify the time interval is positive.
EXPECT_GT(request.value().time_interval.InSeconds(), 0);
// Verify content details (spot check title).
NSString* expected_title = l10n_util::GetNSString(
IDS_IOS_SAFETY_CHECK_NOTIFICATIONS_ADD_BROWSING_PROTECTION);
EXPECT_NSEQ(expected_title, request.value().content.title);
// Verify userInfo check result (spot check).
EXPECT_NSEQ(
request.value().content.userInfo[kSafetyCheckNotificationCheckResultKey],
base::SysUTF8ToNSString(NameForSafetyCheckState(state)));
}
// Tests that GetSafeBrowsingNotificationRequest returns nullopt for a safe
// state.
TEST_F(SafetyCheckNotificationUtilsTest,
ReturnsNoSafeBrowsingRequestForSafeState) {
SafeBrowsingSafetyCheckState state = SafeBrowsingSafetyCheckState::kSafe;
// Call the function under test.
std::optional<ScheduledNotificationRequest> request =
GetSafeBrowsingNotificationRequest(state);
// Verify no request is returned.
EXPECT_FALSE(request.has_value());
}
#pragma mark - ParseSafetyCheckNotificationType Tests
// Tests that a request without a Safety Check-related notification identifier
// is correctly identified as NOT a Safety Check notification.
TEST_F(SafetyCheckNotificationUtilsTest, IdentifiesNonSafetyCheckRequest) {
// Create a mock notification content.
UNMutableNotificationContent* content =
[[UNMutableNotificationContent alloc] init];
content.title = @"Not Safety Check";
// Create a mock notification request with a non-safety check identifier.
UNNotificationRequest* request =
[UNNotificationRequest requestWithIdentifier:@"NOT_SAFETY_CHECK"
content:content
trigger:nil];
// Verify the parsing function returns nullopt.
EXPECT_FALSE(ParseSafetyCheckNotificationType(request).has_value());
}
// Tests that an Update Chrome notification request is correctly identified and
// returns the `kUpdateChrome` type.
TEST_F(SafetyCheckNotificationUtilsTest, IdentifiesUpdateChromeRequest) {
// Create a mock notification content.
UNMutableNotificationContent* content =
[[UNMutableNotificationContent alloc] init];
content.title = @"Safety Check";
// Create a mock notification request with the Update Chrome identifier.
UNNotificationRequest* request = [UNNotificationRequest
requestWithIdentifier:kSafetyCheckUpdateChromeNotificationID
content:content
trigger:nil];
// Verify the parsing function returns the correct type.
std::optional<SafetyCheckNotificationType> type =
ParseSafetyCheckNotificationType(request);
ASSERT_TRUE(type.has_value());
EXPECT_EQ(type.value(), SafetyCheckNotificationType::kUpdateChrome);
}
// Tests that a Passwords notification request is correctly identified and
// returns the `kPasswords` type.
TEST_F(SafetyCheckNotificationUtilsTest, IdentifiesPasswordRequest) {
// Create a mock notification content.
UNMutableNotificationContent* content =
[[UNMutableNotificationContent alloc] init];
content.title = @"Safety Check";
// Create a mock notification request with the Password identifier.
UNNotificationRequest* request = [UNNotificationRequest
requestWithIdentifier:kSafetyCheckPasswordNotificationID
content:content
trigger:nil];
// Verify the parsing function returns the correct type.
std::optional<SafetyCheckNotificationType> type =
ParseSafetyCheckNotificationType(request);
ASSERT_TRUE(type.has_value());
EXPECT_EQ(type.value(), SafetyCheckNotificationType::kPasswords);
}
// Tests that a Safe Browsing notification request is correctly identified and
// returns the `kSafeBrowsing` type.
TEST_F(SafetyCheckNotificationUtilsTest, IdentifiesSafeBrowsingRequest) {
// Create a mock notification content.
UNMutableNotificationContent* content =
[[UNMutableNotificationContent alloc] init];
content.title = @"Safety Check";
// Create a mock notification request with the Safe Browsing identifier.
UNNotificationRequest* request = [UNNotificationRequest
requestWithIdentifier:kSafetyCheckSafeBrowsingNotificationID
content:content
trigger:nil];
// Verify the parsing function returns the correct type.
std::optional<SafetyCheckNotificationType> type =
ParseSafetyCheckNotificationType(request);
ASSERT_TRUE(type.has_value());
EXPECT_EQ(type.value(), SafetyCheckNotificationType::kSafeBrowsing);
}