chromium/ios/chrome/browser/push_notification/model/push_notification_account_context_manager_unittest.mm

// Copyright 2022 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/push_notification/model/push_notification_account_context_manager.h"

#import "base/files/file_path.h"
#import "base/strings/sys_string_conversions.h"
#import "ios/chrome/browser/push_notification/model/push_notification_account_context_manager+testing.h"
#import "ios/chrome/browser/push_notification/model/push_notification_client_id.h"
#import "ios/chrome/browser/shared/model/application_context/application_context.h"
#import "ios/chrome/browser/shared/model/profile/profile_attributes_ios.h"
#import "ios/chrome/browser/shared/model/profile/profile_attributes_storage_ios.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
#import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
#import "ios/chrome/browser/shared/model/profile/test/test_profile_manager_ios.h"
#import "ios/chrome/test/ios_chrome_scoped_testing_local_state.h"
#import "ios/web/public/test/web_task_environment.h"
#import "testing/platform_test.h"

namespace {

struct TestCase {
  std::string gaia;
};

// Updates information about a Profile into `storage`.
void UpdateProfileAuthInfo(ProfileAttributesStorageIOS* storage,
                           const std::string& browser_state_name,
                           const std::string& gaia) {
  storage->UpdateAttributesForProfileWithName(
      browser_state_name,
      base::BindOnce(
          [](const std::string& gaia, ProfileAttributesIOS attr) {
            attr.SetAuthenticationInfo(gaia, std::string());
            return attr;
          },
          gaia));
}

// Iterates through the testcases and creates a new BrowserState for each
// testcase's gaia ID in storage and adds it to the AccountContextManager. In
// addition, this function validates that the testcases were added to the
// AccountContextManager.
template <unsigned long N>
void AddTestCasesToManagerAndValidate(
    PushNotificationAccountContextManager* manager,
    const TestCase (&test_cases)[N],
    ProfileAttributesStorageIOS* storage,
    const std::string& browser_state_name) {
  // Construct the BrowserStates with the given gaia id and add the gaia id into
  // the AccountContextManager.
  for (const TestCase& test_case : test_cases) {
    UpdateProfileAuthInfo(storage, browser_state_name, test_case.gaia);
    [manager addAccount:test_case.gaia];
  }

  ASSERT_EQ([manager accountIDs].count, N);

  // Validate that the given testcases exist inside the AccountContextManager.
  bool entries_are_valid = true;
  for (const TestCase& test_case : test_cases) {
    if (![manager preferenceMapForAccount:test_case.gaia]) {
      entries_are_valid = false;
    }
  }

  ASSERT_EQ(entries_are_valid, true);
}

}  // namespace

class PushNotificationAccountContextManagerTest : public PlatformTest {
 public:
  PushNotificationAccountContextManagerTest() {
    browser_state_ = profile_manager_.AddProfileWithBuilder(
        TestChromeBrowserState::Builder());

    manager_ = [[PushNotificationAccountContextManager alloc]
        initWithProfileManager:&profile_manager_];
  }

  ProfileAttributesStorageIOS* profile_attributes_storage() const {
    return GetApplicationContext()
        ->GetProfileManager()
        ->GetProfileAttributesStorage();
  }

  const std::string& browser_state_name() const {
    return browser_state_->GetBrowserStateName();
  }

 protected:
  web::WebTaskEnvironment task_environment_;
  IOSChromeScopedTestingLocalState scoped_testing_local_state_;
  TestProfileManagerIOS profile_manager_;
  raw_ptr<ChromeBrowserState> browser_state_;
  PushNotificationAccountContextManager* manager_;
};

// This test ensures that the AccountContextManager can store a new account ID.
TEST_F(PushNotificationAccountContextManagerTest, AddAccount) {
  static const TestCase kTestCase[] = {{"0"}};

  AddTestCasesToManagerAndValidate(
      manager_, kTestCase, profile_attributes_storage(), browser_state_name());
}

// This test ensures that the AccountContextManager can store multiple new
// account IDs.
TEST_F(PushNotificationAccountContextManagerTest, AddMultipleAccounts) {
  static const TestCase kTestCase[] = {{"0"}, {"1"}, {"2"}, {"3"}, {"4"}};

  AddTestCasesToManagerAndValidate(
      manager_, kTestCase, profile_attributes_storage(), browser_state_name());
}

// This test ensures that new entries in the context map are not added for
// duplicates and that the occurence counter is properly incremented.
TEST_F(PushNotificationAccountContextManagerTest, AddDuplicates) {
  static const TestCase kTestCase[] = {{"0"}, {"1"}, {"2"}, {"3"}, {"4"}};

  static const TestCase kNoDuplicatesTestCase[] = {{"1"}, {"2"}, {"2"}};

  AddTestCasesToManagerAndValidate(
      manager_, kTestCase, profile_attributes_storage(), browser_state_name());

  for (const TestCase& test_case : kNoDuplicatesTestCase) {
    UpdateProfileAuthInfo(profile_attributes_storage(), browser_state_name(),
                          test_case.gaia);
    [manager_ addAccount:test_case.gaia];
  }

  // Validate again that the original testcases are inside the
  // AccountContextManager.
  bool entries_are_valid = true;
  for (const TestCase& test_case : kTestCase) {
    if (![manager_ preferenceMapForAccount:test_case.gaia]) {
      entries_are_valid = false;
      break;
    }
  }
  ASSERT_EQ(entries_are_valid, true);

  // Validate the occurence counter has increased.
  ASSERT_EQ(
      [manager_ registrationCountForAccount:kNoDuplicatesTestCase[0].gaia], 2u);
  ASSERT_EQ(
      [manager_ registrationCountForAccount:kNoDuplicatesTestCase[1].gaia], 3u);
}

// This test ensures that the AccountContextManager can remove an account ID.
TEST_F(PushNotificationAccountContextManagerTest, RemoveAccount) {
  static const TestCase kTestCase[] = {{"0"}, {"1"}, {"2"}, {"3"}, {"4"}};

  const TestCase kRemovalTestCase = {"5"};

  AddTestCasesToManagerAndValidate(
      manager_, kTestCase, profile_attributes_storage(), browser_state_name());

  // Add the testcase we would like to check for its removal into the manager.
  UpdateProfileAuthInfo(profile_attributes_storage(), browser_state_name(),
                        kRemovalTestCase.gaia);
  [manager_ addAccount:kRemovalTestCase.gaia];

  // Remove the testcase
  ASSERT_EQ([manager_ removeAccount:kRemovalTestCase.gaia], true);

  // Validate again that the original testcases are inside the
  // AccountContextManager.
  bool entries_are_valid = true;
  for (const TestCase& test_case : kTestCase) {
    if (![manager_ preferenceMapForAccount:test_case.gaia]) {
      entries_are_valid = false;
    }
  }

  ASSERT_EQ(entries_are_valid, true);
}

// This test ensures that the AccountContextManager can remove multiple account
// IDs.
TEST_F(PushNotificationAccountContextManagerTest, RemoveMultipleAccounts) {
  static const TestCase kTestCase[] = {{"0"}, {"1"}, {"2"}, {"3"}, {"4"}};

  static const TestCase kRemovalTestCase[] = {{"5"}, {"6"}, {"7"}};

  AddTestCasesToManagerAndValidate(
      manager_, kTestCase, profile_attributes_storage(), browser_state_name());

  for (const TestCase& test_case : kRemovalTestCase) {
    UpdateProfileAuthInfo(profile_attributes_storage(), browser_state_name(),
                          test_case.gaia);
    [manager_ addAccount:test_case.gaia];
  }
  for (const TestCase& test_case : kRemovalTestCase) {
    // Remove the testcase
    ASSERT_EQ([manager_ removeAccount:test_case.gaia], true);
  }

  // Validate again that the original testcases are inside the
  // AccountContextManager.
  bool entries_are_valid = true;
  for (const TestCase& test_case : kTestCase) {
    if (![manager_ preferenceMapForAccount:test_case.gaia]) {
      entries_are_valid = false;
    }
  }

  ASSERT_EQ(entries_are_valid, true);
}

// This test ensures AccountContextManager can remove duplicate values.
TEST_F(PushNotificationAccountContextManagerTest, AddDuplicateThenRemove) {
  static const TestCase kTestCase[] = {{"0"}};
  const TestCase kRemovalTestCase = {"5"};
  static const TestCase kNoDuplicatesTestCase[] = {kRemovalTestCase};

  AddTestCasesToManagerAndValidate(
      manager_, kTestCase, profile_attributes_storage(), browser_state_name());

  UpdateProfileAuthInfo(profile_attributes_storage(), browser_state_name(),
                        kRemovalTestCase.gaia);
  [manager_ addAccount:kRemovalTestCase.gaia];

  for (const TestCase& test_case : kNoDuplicatesTestCase) {
    UpdateProfileAuthInfo(profile_attributes_storage(), browser_state_name(),
                          test_case.gaia);
    [manager_ addAccount:test_case.gaia];
  }

  // Validate the occurence counter has increased.
  ASSERT_EQ(
      [manager_ registrationCountForAccount:kNoDuplicatesTestCase[0].gaia], 2u);
  // Remove the duplicate testcase twice.
  [manager_ removeAccount:kRemovalTestCase.gaia];
  ASSERT_EQ([manager_ removeAccount:kRemovalTestCase.gaia], true);

  // Validate again that the original testcases are inside the
  // AccountContextManager.
  bool entries_are_valid = true;
  for (const TestCase& test_case : kTestCase) {
    if (![manager_ preferenceMapForAccount:test_case.gaia]) {
      entries_are_valid = false;
      break;
    }
  }
  ASSERT_EQ(entries_are_valid, true);
}

// This test ensures the AccountContextManager can update its values. This test
// depends on the existence of the chosen push notification enabled feature to
// toggle.
TEST_F(PushNotificationAccountContextManagerTest, UpdatePreferences) {
  static const TestCase kTestCase[] = {{"0"}, {"1"}, {"2"}, {"3"}, {"4"}};

  AddTestCasesToManagerAndValidate(
      manager_, kTestCase, profile_attributes_storage(), browser_state_name());

  static const TestCase kUpdateTestCase[] = {{"0"}, {"2"}, {"4"}};

  PushNotificationClientId clientID = PushNotificationClientId::kCommerce;

  for (const TestCase& test_case : kTestCase) {
    UpdateProfileAuthInfo(profile_attributes_storage(), browser_state_name(),
                          test_case.gaia);
    [manager_ enablePushNotification:clientID forAccount:test_case.gaia];
    ASSERT_EQ([manager_ isPushNotificationEnabledForClient:clientID
                                                forAccount:test_case.gaia],
              YES);
  }

  for (const TestCase& test_case : kUpdateTestCase) {
    UpdateProfileAuthInfo(profile_attributes_storage(), browser_state_name(),
                          test_case.gaia);
    [manager_ disablePushNotification:clientID forAccount:test_case.gaia];
    ASSERT_EQ([manager_ isPushNotificationEnabledForClient:clientID
                                                forAccount:test_case.gaia],
              NO);
  }
}