chromium/chrome/browser/password_manager/android/unified_password_manager_proto_utils_unittest.cc

// 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.

#include "chrome/browser/password_manager/android/unified_password_manager_proto_utils.h"

#include "chrome/browser/password_manager/android/protos/list_passwords_result.pb.h"
#include "chrome/browser/password_manager/android/protos/password_info.pb.h"
#include "chrome/browser/password_manager/android/protos/password_with_local_data.pb.h"
#include "components/password_manager/core/browser/password_form.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace password_manager {

namespace {

using testing::ElementsAre;
using testing::Eq;

constexpr time_t kIssuesCreationTime = 1337;
constexpr char kTestOrigin[] = "https://www.origin.com/";
constexpr char kTestAction[] = "https://www.action.com/";
constexpr char kTestFormName[] = "login_form";
const std::u16string kTestFormName16(u"login_form");
constexpr char kTestUsernameElementName[] = "username_element";
const std::u16string kTestUsernameElementName16(u"username_element");
constexpr autofill::FormControlType kTestUsernameElementType =
    autofill::FormControlType::kInputText;
constexpr char kTestPasswordElementName[] = "password_element";
const std::u16string kTestPasswordElementName16(u"password_element");
constexpr autofill::FormControlType kTestPasswordElementType =
    autofill::FormControlType::kInputPassword;

sync_pb::PasswordSpecificsData CreateSpecificsData(
    const std::string& origin,
    const std::string& username_element,
    const std::string& username_value,
    const std::string& password_element,
    const std::string& signon_realm) {
  sync_pb::PasswordSpecificsData password_specifics;
  password_specifics.set_origin(origin);
  password_specifics.set_username_element(username_element);
  password_specifics.set_username_value(username_value);
  password_specifics.set_password_element(password_element);
  password_specifics.set_signon_realm(signon_realm);
  password_specifics.set_scheme(static_cast<int>(PasswordForm::Scheme::kHtml));
  password_specifics.set_action(GURL(origin).spec());
  password_specifics.set_password_value("D3f4ultP4$$w0rd");
  password_specifics.set_date_last_used(kIssuesCreationTime);
  password_specifics.set_date_created(kIssuesCreationTime);
  password_specifics.set_date_password_modified_windows_epoch_micros(
      kIssuesCreationTime);
  password_specifics.set_blacklisted(false);
  password_specifics.set_type(
      static_cast<int>(PasswordForm::Type::kFormSubmission));
  password_specifics.set_times_used(1);
  password_specifics.set_display_name("display_name");
  password_specifics.set_avatar_url(GURL(origin).spec());
  password_specifics.set_federation_url(std::string());
  // The current code always populates password issues for outgoing protos even
  // when none exist.
  *password_specifics.mutable_password_issues() = sync_pb::PasswordIssues();
  // The current code always populates notes for outgoing protos even when none
  // exist.
  password_specifics.mutable_notes();
  // The current code always populates shared password metadata for outgoing
  // protos even when none exist.
  password_specifics.set_sender_email("");
  password_specifics.set_sender_name("");
  password_specifics.set_date_received_windows_epoch_micros(0);
  password_specifics.set_sharing_notification_displayed(false);
  password_specifics.set_sender_profile_image_url("");
  return password_specifics;
}

}  // namespace

class UnifiedPasswordManagerProtoUtilsTest
    : public testing::Test,
      public testing::WithParamInterface<
          std::pair<IsAccountStore, PasswordForm::Store>> {};

TEST_P(UnifiedPasswordManagerProtoUtilsTest,
       ConvertPasswordWithLocalDataToFullPasswordFormAndBack) {
  PasswordWithLocalData password_data;
  *password_data.mutable_password_specifics_data() = CreateSpecificsData(
      kTestOrigin, kTestUsernameElementName, "username_value",
      kTestPasswordElementName, "signon_realm");
  (*password_data.mutable_local_data())
      .set_previously_associated_sync_account_email("[email protected]");
  const std::string kTestUsernameElementTypeStr(
      autofill::FormControlTypeToString(kTestUsernameElementType));
  const std::string kTestPasswordElementTypeStr(
      autofill::FormControlTypeToString(kTestPasswordElementType));
  std::string opaque_metadata =
      "{\"form_data\":{\"action\":\"" + std::string(kTestAction) +
      "\",\"fields\":[{\"form_control_type\":\"" + kTestUsernameElementTypeStr +
      "\",\"name\":\"" + kTestUsernameElementName +
      "\"},{\"form_control_type\":\"" + kTestPasswordElementTypeStr +
      "\",\"name\":\"" + kTestPasswordElementName + "\"}],\"name\":\"" +
      kTestFormName + "\",\"url\":\"" + kTestOrigin +
      "\"},\"skip_zero_click\":false}";
  (*password_data.mutable_local_data()).set_opaque_metadata(opaque_metadata);

  PasswordForm form = PasswordFromProtoWithLocalData(password_data);
  EXPECT_THAT(form.url, Eq(GURL(kTestOrigin)));
  EXPECT_THAT(form.username_element, Eq(kTestUsernameElementName16));
  EXPECT_THAT(form.username_value, Eq(u"username_value"));
  EXPECT_THAT(form.password_element, Eq(kTestPasswordElementName16));
  EXPECT_THAT(form.signon_realm, Eq("signon_realm"));
  EXPECT_FALSE(form.skip_zero_click);
  EXPECT_EQ(form.form_data.url(), GURL(kTestOrigin));
  EXPECT_EQ(form.form_data.action(), GURL(kTestAction));
  EXPECT_EQ(form.form_data.name(), kTestFormName16);
  ASSERT_EQ(form.form_data.fields().size(), 2u);
  EXPECT_EQ(form.form_data.fields()[0].name(), kTestUsernameElementName16);
  EXPECT_EQ(form.form_data.fields()[0].form_control_type(),
            kTestUsernameElementType);
  EXPECT_EQ(form.form_data.fields()[1].name(), kTestPasswordElementName16);
  EXPECT_EQ(form.form_data.fields()[1].form_control_type(),
            kTestPasswordElementType);

  PasswordWithLocalData password_data_converted_back =
      PasswordWithLocalDataFromPassword(form);
  EXPECT_EQ(password_data.SerializeAsString(),
            password_data_converted_back.SerializeAsString());
}

TEST_P(UnifiedPasswordManagerProtoUtilsTest, ConvertListResultToFormVector) {
  ListPasswordsResult list_result;
  PasswordWithLocalData password1;
  *password1.mutable_password_specifics_data() =
      CreateSpecificsData("http://1.origin.com/", "username_1", "username_1",
                          "password_1", "signon_1");
  PasswordWithLocalData password2;
  *password2.mutable_password_specifics_data() =
      CreateSpecificsData("http://2.origin.com/", "username_2", "username_2",
                          "password_2", "signon_2");
  *list_result.add_password_data() = password1;
  *list_result.add_password_data() = password2;

  std::vector<PasswordForm> forms =
      PasswordVectorFromListResult(list_result, GetParam().first);

  std::vector<PasswordForm> expected_forms = {
      PasswordFromProtoWithLocalData(password1),
      PasswordFromProtoWithLocalData(password2)};
  expected_forms[0].in_store = GetParam().second;
  expected_forms[1].in_store = GetParam().second;

  EXPECT_THAT(forms, testing::ElementsAreArray(expected_forms));
}

TEST_P(UnifiedPasswordManagerProtoUtilsTest,
       ConvertListPasswordsWithUiInfoResultToFormVector) {
  ListPasswordsWithUiInfoResult list_result;
  ListPasswordsWithUiInfoResult::PasswordWithUiInfo password1;
  *password1.mutable_password_data()->mutable_password_specifics_data() =
      CreateSpecificsData("http://1.origin.com/", "username_1", "username_1",
                          "password_1", "signon_1");
  PasswordInfo ui_info;
  ui_info.set_display_name("Example app");
  ui_info.set_icon_url("http://example.com/favicon.ico");
  *password1.mutable_ui_info() = ui_info;
  ListPasswordsWithUiInfoResult::PasswordWithUiInfo password2;
  *password2.mutable_password_data()->mutable_password_specifics_data() =
      CreateSpecificsData("http://2.origin.com/", "username_2", "username_2",
                          "password_2", "signon_2");
  *list_result.add_passwords_with_ui_info() = password1;
  *list_result.add_passwords_with_ui_info() = password2;

  std::vector<PasswordForm> forms =
      PasswordVectorFromListResult(list_result, GetParam().first);
  std::vector<PasswordForm> expected_forms = {
      PasswordFromProtoWithLocalData(password1.password_data()),
      PasswordFromProtoWithLocalData(password2.password_data())};
  expected_forms[0].app_display_name = ui_info.display_name();
  expected_forms[0].app_icon_url = GURL(ui_info.icon_url());
  expected_forms[0].in_store = GetParam().second;
  expected_forms[1].in_store = GetParam().second;

  EXPECT_THAT(forms, testing::ElementsAreArray(expected_forms));
}

INSTANTIATE_TEST_SUITE_P(
    ,
    UnifiedPasswordManagerProtoUtilsTest,
    testing::ValuesIn({std::make_pair(IsAccountStore(true),
                                      PasswordForm::Store::kAccountStore),
                       std::make_pair(IsAccountStore(false),
                                      PasswordForm::Store::kProfileStore)}),
    [](const ::testing::TestParamInfo<
        std::pair<IsAccountStore, PasswordForm::Store>>& info) {
      return info.param.first ? "Account" : "Profile";
    });

}  // namespace password_manager