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

#include <algorithm>
#include <string>

#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "base/values.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/data_model/addresses/autofill_profile.h"
#include "components/autofill/core/browser/data_model/payments/credit_card.h"
#include "components/autofill/core/browser/geo/autofill_country.h"
#include "components/autofill/core/browser/webdata/addresses/address_autofill_table.h"
#include "components/autofill/core/browser/webdata/autocomplete/autocomplete_table.h"
#include "components/autofill/core/browser/webdata/autofill_ai/entity_table.h"
#include "components/autofill/core/browser/webdata/autofill_change.h"
#include "components/autofill/core/browser/webdata/autofill_sync_metadata_table.h"
#include "components/autofill/core/browser/webdata/payments/payments_autofill_table.h"
#include "components/autofill/core/browser/webdata/valuables/valuables_table.h"
#include "components/autofill/core/common/autofill_constants.h"
#include "components/os_crypt/async/browser/test_utils.h"
#include "components/os_crypt/async/common/test_encryptor.h"
#include "components/search_engines/keyword_table.h"
#include "components/search_engines/template_url_data.h"
#include "components/signin/public/webdata/token_service_table.h"
#include "components/webdata/common/web_database.h"
#include "sql/statement.h"
#include "sql/test/test_helpers.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

// To make the comparison with golden files less whitespace sensitive:
// - Remove SQLite quotes: http://www.sqlite.org/lang_keywords.html.
// - Collapse multiple spaces into one.
// - Ensure that there is no space before or after ',', '(' or ')'.
std::string NormalizeSchemaForComparison(const std::string& schema) {
  std::string normalized;
  normalized.reserve(schema.size());
  bool skip_following_spaces = false;
  for (char c : schema) {
    if (std::ranges::contains("\"[]`", c)) {  // Quotes
      continue;
    }
    if (c == ' ' && skip_following_spaces) {
      continue;
    }
    bool is_separator = std::ranges::contains(",()", c);
    if (is_separator && !normalized.empty() && normalized.back() == ' ') {
      normalized.pop_back();
    }
    normalized.push_back(c);
    skip_following_spaces = c == ' ' || is_separator;
  }
  return normalized;
}

// The WebDatabaseMigrationTest encapsulates testing of database migrations.
// Specifically, these tests are intended to exercise any schema changes in
// the WebDatabase and data migrations that occur in
// `WebDatabase::MigrateOldVersionsAsNeeded()` (most likely through one of the
// `WebDatabaseTable::MigrateToVersion()` overrides).
//
// When bumping `WebDatabase::kCurrentVersionNumber`, add a new
// `MigrateVersionXXToCurrent` test below and generate a new version_XX.sql
// file, following the instructions from the `VersionXxSqlFilesAreGolden` test
// description.
class WebDatabaseMigrationTest : public testing::Test {
 public:
  WebDatabaseMigrationTest()
      : encryptor_(os_crypt_async::GetTestEncryptorForTesting()) {}

  WebDatabaseMigrationTest(const WebDatabaseMigrationTest&) = delete;
  WebDatabaseMigrationTest& operator=(const WebDatabaseMigrationTest&) = delete;

  ~WebDatabaseMigrationTest() override = default;

  void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }

  // Load the database via the WebDatabase class and migrate the database to
  // the current version.
  void DoMigration() {
    autofill::AddressAutofillTable address_autofill_table;
    autofill::AutocompleteTable autocomplete_table;
    autofill::EntityTable entity_table;
    autofill::AutofillSyncMetadataTable autofill_sync_metadata_table;
    autofill::PaymentsAutofillTable payments_autofill_table;
    autofill::ValuablesTable valuables_table;
    KeywordTable keyword_table;
    TokenServiceTable token_service_table;

    WebDatabase db;
    db.AddTable(&address_autofill_table);
    db.AddTable(&autocomplete_table);
    db.AddTable(&autofill_sync_metadata_table);
    db.AddTable(&entity_table);
    db.AddTable(&payments_autofill_table);
    db.AddTable(&keyword_table);
    db.AddTable(&token_service_table);
    db.AddTable(&valuables_table);

    // This causes the migration to occur.
    ASSERT_EQ(sql::INIT_OK, db.Init(GetDatabasePath(), encryptor_));
  }

 protected:
  base::FilePath GetDatabasePath() {
    const base::FilePath::CharType kWebDatabaseFilename[] =
        FILE_PATH_LITERAL("TestWebDatabase.sqlite3");
    return temp_dir_.GetPath().Append(base::FilePath(kWebDatabaseFilename));
  }

  // The textual contents of |file| are read from
  // "components/test/data/web_database" and returned in the string |contents|.
  // Returns true if the file exists and is read successfully, false otherwise.
  bool GetWebDatabaseData(const base::FilePath& file, std::string* contents) {
    base::FilePath source_path;
    base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &source_path);
    source_path = source_path.AppendASCII("components");
    source_path = source_path.AppendASCII("test");
    source_path = source_path.AppendASCII("data");
    source_path = source_path.AppendASCII("web_database");
    source_path = source_path.Append(file);
    return base::PathExists(source_path) &&
           base::ReadFileToString(source_path, contents);
  }

  static int VersionFromConnection(sql::Database* connection) {
    // Get version.
    sql::Statement s(connection->GetUniqueStatement(
        "SELECT value FROM meta WHERE key='version'"));
    if (!s.Step()) {
      return 0;
    }
    return s.ColumnInt(0);
  }

  // The sql files located in "components/test/data/web_database" were generated
  // by launching the Chromium application prior to schema change, then using
  // the sqlite3 command-line application to dump the contents of the "Web Data"
  // database.
  // Like this:
  //   > .output version_nn.sql
  //   > .dump
  void LoadDatabase(const base::FilePath::StringType& file);

  scoped_refptr<os_crypt_async::TestEncryptor> encryptor_;

 private:
  base::ScopedTempDir temp_dir_;
};

void WebDatabaseMigrationTest::LoadDatabase(
    const base::FilePath::StringType& file) {
  std::string contents;
  ASSERT_TRUE(GetWebDatabaseData(base::FilePath(file), &contents));

  sql::Database connection(sql::test::kTestTag);
  ASSERT_TRUE(connection.Open(GetDatabasePath()));
  ASSERT_TRUE(connection.ExecuteScriptForTesting(contents));
}

// Tests that migrating from the golden files version_XX.sql results in the same
// schema as migrating from an empty database.
//
// Whenever `WebDatabase::kCurrentVersionNumber` is updated to X, add a new
// version_X.sql file to components/test/data/web_database/.
//
// There are generally two ways of doing so:
// - Copy version_X-1.sql. Update the version to X and make any changes that
//   were made in version X (new tables, columns, etc).
// - Generate the file from scratch:
//   1. Launch Chrome with WebDatabase version X.
//      ./out/Default/chrome --user-data-dir=/tmp/sql
//      No need to complete the first run -- closing Chrome immediately is fine.
//   2. Run sqlite3 '/tmp/sql/Default/Web Data'
//        .output version_X.sql
//        .dump
//        .exit
//   3. Remove any INSERT statements to tables other than "meta" from
//      version_X.sql.
TEST_F(WebDatabaseMigrationTest, VersionXxSqlFilesAreGolden) {
  DoMigration();

  // Initialize the database and retrieve the initial schema. The database needs
  // to be closed.
  const base::FilePath db_path = GetDatabasePath();
  std::string expected_schema;
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(db_path));
    expected_schema = connection.GetSchema();
    ASSERT_TRUE(connection.Raze());
  }

  for (int i = WebDatabase::kDeprecatedVersionNumber + 1;
       i <= WebDatabase::kCurrentVersionNumber; ++i) {
    SCOPED_TRACE(testing::Message() << "DB Version: " << i);
    const base::FilePath file_name = base::FilePath::FromUTF8Unsafe(
        "version_" + base::NumberToString(i) + ".sql");
    ASSERT_NO_FATAL_FAILURE(LoadDatabase(file_name.value()))
        << "Failed to load " << file_name.MaybeAsASCII();
    {
      // Check that the database file contains the right version.
      sql::Database connection(sql::test::kTestTag);
      ASSERT_TRUE(connection.Open(GetDatabasePath()));
      EXPECT_EQ(i, VersionFromConnection(&connection)) << "For version " << i;
    }

    DoMigration();

    {
      sql::Database connection(sql::test::kTestTag);
      ASSERT_TRUE(connection.Open(db_path));
      EXPECT_EQ(NormalizeSchemaForComparison(expected_schema),
                NormalizeSchemaForComparison(connection.GetSchema()))
          << "For version " << i;
      ASSERT_TRUE(connection.Raze());
    }
  }
}

// Tests that the all migrations from an empty database succeed.
TEST_F(WebDatabaseMigrationTest, MigrateEmptyToCurrent) {
  DoMigration();

  // Verify post-conditions.  These are expectations for current version of the
  // database.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));

    // Check version.
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));

    // Check that expected tables are present.
    EXPECT_TRUE(connection.DoesTableExist("autofill"));
    EXPECT_TRUE(connection.DoesTableExist("addresses"));
    EXPECT_TRUE(connection.DoesTableExist("credit_cards"));
    EXPECT_TRUE(connection.DoesTableExist("local_ibans"));
    EXPECT_TRUE(connection.DoesTableExist("keywords"));
    EXPECT_TRUE(connection.DoesTableExist("meta"));
    EXPECT_TRUE(connection.DoesTableExist("token_service"));
    // The web_apps and web_apps_icons tables are obsolete as of version 58.
    EXPECT_FALSE(connection.DoesTableExist("web_apps"));
    EXPECT_FALSE(connection.DoesTableExist("web_app_icons"));
    // The web_intents and web_intents_defaults tables are obsolete as of
    // version 58.
    EXPECT_FALSE(connection.DoesTableExist("web_intents"));
    EXPECT_FALSE(connection.DoesTableExist("web_intents_defaults"));
    // The plus_addresses table is obsolete as of version 154.
    EXPECT_FALSE(connection.DoesTableExist("plus_addresses"));
    EXPECT_FALSE(
        connection.DoesTableExist("plus_address_sync_model_type_state"));
    EXPECT_FALSE(
        connection.DoesTableExist("plus_address_sync_entity_metadata"));
  }
}

// Versions below 83 are deprecated. This verifies that old databases are razed.
TEST_F(WebDatabaseMigrationTest, RazeDeprecatedVersionAndReinit) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_82.sql")));

  // Verify pre-conditions. These are expectations for version 82 of the
  // database.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    sql::MetaTable meta_table;
    ASSERT_TRUE(meta_table.Init(&connection, 82, 79));

    EXPECT_TRUE(connection.DoesColumnExist("masked_credit_cards", "type"));
  }

  DoMigration();

  // Check post-conditions of version 104. This ensures that the migration has
  // happened.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));

    // The product_description column and should exist.
    EXPECT_TRUE(connection.DoesColumnExist("masked_credit_cards",
                                           "product_description"));
  }
}

// Tests addition of nickname column in masked_credit_cards table.
TEST_F(WebDatabaseMigrationTest, MigrateVersion83ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_83.sql")));

  // Verify pre-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    sql::MetaTable meta_table;
    ASSERT_TRUE(meta_table.Init(&connection, 83, 79));

    EXPECT_FALSE(connection.DoesColumnExist("masked_credit_cards", "nickname"));
    ASSERT_TRUE(connection.ExecuteScriptForTesting(R"(
      INSERT INTO masked_credit_cards (id, status, name_on_card, network,
      last_four, exp_month, exp_year, bank_name)
      VALUES ('card_1', 'status', 'bob', 'VISA', '1234', 12, 2050, 'Chase');
    )"));
  }

  DoMigration();

  // Verify post-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));

    // The nickname column should exist.
    EXPECT_TRUE(connection.DoesColumnExist("masked_credit_cards", "nickname"));

    // Make sure that the default nickname value is empty.
    sql::Statement s_masked_cards(connection.GetUniqueStatement(
        "SELECT nickname FROM masked_credit_cards"));
    ASSERT_TRUE(s_masked_cards.Step());
    EXPECT_EQ("", s_masked_cards.ColumnString(0));
  }
}

// Tests addition of card_issuer column in masked_credit_cards table.
TEST_F(WebDatabaseMigrationTest, MigrateVersion84ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_84.sql")));

  // Verify pre-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    sql::MetaTable meta_table;
    ASSERT_TRUE(meta_table.Init(&connection, 84, 79));

    EXPECT_FALSE(
        connection.DoesColumnExist("masked_credit_cards", "card_issuer"));
  }

  DoMigration();

  // Verify post-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));

    // The card_issuer column should exist.
    EXPECT_TRUE(
        connection.DoesColumnExist("masked_credit_cards", "card_issuer"));
  }
}

// Tests addition of nickname column in credit_cards table.
TEST_F(WebDatabaseMigrationTest, MigrateVersion86ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_86.sql")));

  // Verify pre-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    sql::MetaTable meta_table;
    ASSERT_TRUE(meta_table.Init(&connection, 86, 83));

    EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "nickname"));
  }

  DoMigration();

  // Verify post-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));

    // The nickname column should exist.
    EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "nickname"));
  }
}

// Version 87 added new columns to the autofill_profile_names table. This table
// was since deprecated and replaced by local_profiles. The migration unit test
// to the current version thus no longer applies.

// Tests addition of instrument_id column in masked_credit_cards table.
TEST_F(WebDatabaseMigrationTest, MigrateVersion88ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_88.sql")));

  // Verify pre-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    sql::MetaTable meta_table;
    ASSERT_TRUE(meta_table.Init(&connection, 88, 83));

    EXPECT_FALSE(
        connection.DoesColumnExist("masked_credit_cards", "instrument_id"));
  }

  DoMigration();

  // Verify post-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));

    // The instrument_id column should exist.
    EXPECT_TRUE(
        connection.DoesColumnExist("masked_credit_cards", "instrument_id"));
  }
}

// Tests addition of promo code and display strings columns in offer_data table.
TEST_F(WebDatabaseMigrationTest, MigrateVersion93ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_93.sql")));

  // Verify pre-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    sql::MetaTable meta_table;
    ASSERT_TRUE(meta_table.Init(&connection, 93, 83));

    EXPECT_FALSE(connection.DoesColumnExist("offer_data", "promo_code"));
    EXPECT_FALSE(connection.DoesColumnExist("offer_data", "value_prop_text"));
    EXPECT_FALSE(connection.DoesColumnExist("offer_data", "see_details_text"));
    EXPECT_FALSE(
        connection.DoesColumnExist("offer_data", "usage_instructions_text"));
  }

  DoMigration();

  // Verify post-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));

    // The new offer_data columns should exist.
    EXPECT_TRUE(connection.DoesColumnExist("offer_data", "promo_code"));
    EXPECT_TRUE(connection.DoesColumnExist("offer_data", "value_prop_text"));
    EXPECT_TRUE(connection.DoesColumnExist("offer_data", "see_details_text"));
    EXPECT_TRUE(
        connection.DoesColumnExist("offer_data", "usage_instructions_text"));
  }
}

// Tests addition of virtual_card_enrollment_state and card_art_url columns in
// masked_credit_cards table.
TEST_F(WebDatabaseMigrationTest, MigrateVersion94ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_94.sql")));

  // Verify pre-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    sql::MetaTable meta_table;
    ASSERT_TRUE(meta_table.Init(&connection, 94, 83));

    EXPECT_FALSE(connection.DoesColumnExist("masked_credit_cards",
                                            "virtual_card_enrollment_state"));
    EXPECT_FALSE(
        connection.DoesColumnExist("masked_credit_cards", "card_art_url"));
  }

  DoMigration();

  // Verify post-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));

    // The virtual_card_enrollment_state column and the card_art_url column
    // should exist.
    EXPECT_TRUE(connection.DoesColumnExist("masked_credit_cards",
                                           "virtual_card_enrollment_state"));
    EXPECT_TRUE(
        connection.DoesColumnExist("masked_credit_cards", "card_art_url"));
  }
}

// Version 95 added a new column to the autofill_profile table. This table
// was since deprecated and replaced by local_profiles. The migration unit test
// to the current version thus no longer applies.

// Tests addition of is_active column in keywords table.
TEST_F(WebDatabaseMigrationTest, MigrateVersion96ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_96.sql")));

  // Verify pre-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    sql::MetaTable meta_table;
    ASSERT_TRUE(meta_table.Init(&connection, 96, 83));

    EXPECT_FALSE(connection.DoesColumnExist("keywords", "is_active"));
  }

  DoMigration();

  // Verify post-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));

    EXPECT_TRUE(connection.DoesColumnExist("keywords", "is_active"));
  }
}

TEST_F(WebDatabaseMigrationTest, MigrateVersion97ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_97.sql")));

  // Verify pre-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    sql::MetaTable meta_table;
    ASSERT_TRUE(meta_table.Init(&connection, 97, 83));

    // The status column should exist.
    EXPECT_TRUE(connection.DoesColumnExist("masked_credit_cards", "status"));
  }

  DoMigration();

  // Verify post-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));

    // The status column should not exist.
    EXPECT_FALSE(connection.DoesColumnExist("masked_credit_cards", "status"));
  }
}

TEST_F(WebDatabaseMigrationTest, MigrateVersion98ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_98.sql")));

  // Verify pre-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    sql::MetaTable meta_table;
    ASSERT_TRUE(meta_table.Init(&connection, 98, 98));

    // The autofill_profiles_trash table should exist.
    EXPECT_TRUE(connection.DoesTableExist("autofill_profiles_trash"));
  }

  DoMigration();

  // Verify post-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    // The autofill_profiles_trash table should not exist.
    EXPECT_FALSE(connection.DoesTableExist("autofill_profiles_trash"));
  }
}

// Version 99 removed columns from the autofill_profile_names table. This table
// was since deprecated and replaced by local_profiles. The migration unit test
// to the current version thus no longer applies.

TEST_F(WebDatabaseMigrationTest, MigrateVersion100ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_100.sql")));

  // Verify pre-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    sql::MetaTable meta_table;
    ASSERT_TRUE(meta_table.Init(&connection, 100, 99));

    // The validity-related columns should exist.
    EXPECT_TRUE(connection.DoesTableExist("credit_card_art_images"));
  }

  DoMigration();

  // Verify post-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));

    EXPECT_FALSE(connection.DoesTableExist("credit_card_art_images"));
  }
}

// Version 101 added a new table autofill_profiles_birthdates. This table was
// since deprecated and replaced by local_profiles. The migration unit test
// to the current version thus no longer applies.

// Tests addition of starter_pack_id column in keywords table.
TEST_F(WebDatabaseMigrationTest, MigrateVersion102ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_102.sql")));

  // Verify pre-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    sql::MetaTable meta_table;
    ASSERT_TRUE(meta_table.Init(&connection, 102, 99));

    EXPECT_FALSE(connection.DoesColumnExist("keywords", "starter_pack_id"));
  }

  DoMigration();

  // Verify post-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));

    EXPECT_TRUE(connection.DoesColumnExist("keywords", "starter_pack_id"));
  }
}

// Tests addition of product_description in masked_credit_cards table.
TEST_F(WebDatabaseMigrationTest, MigrateVersion103ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_103.sql")));

  // Verify pre-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    sql::MetaTable meta_table;
    ASSERT_TRUE(meta_table.Init(&connection, 103, 99));

    EXPECT_FALSE(connection.DoesColumnExist("masked_credit_cards",
                                            "product_description"));
  }

  DoMigration();

  // Verify post-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));

    // The product_description column and should exist.
    EXPECT_TRUE(connection.DoesColumnExist("masked_credit_cards",
                                           "product_description"));
  }
}

// Tests addition of new table 'local_ibans'.
TEST_F(WebDatabaseMigrationTest, MigrateVersion104ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_104.sql")));

  // Verify pre-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(104, VersionFromConnection(&connection));

    sql::MetaTable meta_table;
    ASSERT_TRUE(meta_table.Init(&connection, 104, 100));

    // The ibans table should not exist.
    EXPECT_FALSE(connection.DoesTableExist("ibans"));
  }

  DoMigration();

  // Verify post-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));

    // The local_ibans table should exist.
    EXPECT_TRUE(connection.DoesTableExist("local_ibans"));
  }
}

// Tests addition of new table 'ibans' with guid as PRIMARY KEY.
TEST_F(WebDatabaseMigrationTest, MigrateVersion105ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_105.sql")));

  // Verify pre-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(105, VersionFromConnection(&connection));

    sql::MetaTable meta_table;
    ASSERT_TRUE(meta_table.Init(&connection, 105, 100));

    // The ibans table should exist, but should not have been created with guid
    // as PRIMARY KEY.
    ASSERT_TRUE(connection.DoesTableExist("ibans"));
    ASSERT_EQ(connection.GetSchema().find(
                  "CREATE TABLE ibans (guid VARCHAR PRIMARY KEY"),
              std::string::npos);
  }

  DoMigration();

  // Verify post-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));

    // The local_ibans table should exist with guid as primary key.
    EXPECT_TRUE(connection.DoesTableExist("local_ibans"));
    ASSERT_NE(connection.GetSchema().find(
                  "CREATE TABLE \"local_ibans\" (guid VARCHAR PRIMARY KEY"),
              std::string::npos);
  }
}

// Version 106 added new contact_info and contact_info_types tables. These
// tables were since deprecated and replaced by addresses and
// address_type_tokens. The migration unit test to the current version thus no
// longer applies.

// Tests addition of card_isser_id in masked_credit_cards table.
TEST_F(WebDatabaseMigrationTest, MigrateVersion107ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_107.sql")));

  // Verify pre-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    sql::MetaTable meta_table;
    ASSERT_TRUE(meta_table.Init(&connection, 107, 106));

    EXPECT_FALSE(
        connection.DoesColumnExist("masked_credit_cards", "card_issuer_id"));
  }

  DoMigration();

  // Verify post-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));

    // The card_issuer_id column and should exist.
    EXPECT_TRUE(
        connection.DoesColumnExist("masked_credit_cards", "card_issuer_id"));
  }
}

// Tests verifying the Virtual Card Usage Data table is created.
TEST_F(WebDatabaseMigrationTest, MigrateVersion108ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_108.sql")));

  // Verify pre-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(108, VersionFromConnection(&connection));

    // The virtual_card_usage_data table should not exist.
    EXPECT_FALSE(connection.DoesTableExist("virtual_card_usage_data"));
  }

  DoMigration();

  // Verify post-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));

    // The virtual_card_usage_data tables should exist.
    EXPECT_TRUE(connection.DoesTableExist("virtual_card_usage_data"));
  }
}

// Version 109 added new columns to the contact_info table. This table was since
// deprecated and replaced by addresses. The migration unit test to the current
// version thus no longer applies.

// Tests that the virtual_card_enrollment_type column is added to the
// masked_credit_cards table.
TEST_F(WebDatabaseMigrationTest, MigrateVersion110ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_110.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    EXPECT_EQ(110, VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesColumnExist("masked_credit_cards",
                                            "virtual_card_enrollment_type"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_TRUE(connection.DoesColumnExist("masked_credit_cards",
                                           "virtual_card_enrollment_type"));
  }
}

// Tests that the enforced_by_policy column is added to the keywords table.
TEST_F(WebDatabaseMigrationTest, MigrateVersion111ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_111.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    EXPECT_EQ(111, VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesColumnExist("keywords", "enforced_by_policy"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_TRUE(connection.DoesColumnExist("keywords", "enforced_by_policy"));
  }
}

// Version 112 and 113 migrated autofill_profiles tables to local_address
// tables. Since the local_address tables have since been deprecated, the
// migration unit test to the current version no longer applies.

// Tests that the IBAN value column is encrypted in local_ibans table.
TEST_F(WebDatabaseMigrationTest, MigrateVersion114ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_114.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    EXPECT_EQ(114, VersionFromConnection(&connection));
    EXPECT_TRUE(connection.DoesColumnExist("ibans", "value"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_TRUE(connection.DoesColumnExist("local_ibans", "value_encrypted"));
    EXPECT_FALSE(connection.DoesColumnExist("local_ibans", "value"));
  }
}

// Tests verifying both stored_cvc tables are created.
TEST_F(WebDatabaseMigrationTest, MigrateVersion115ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_115.sql")));

  // Verify pre-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(115, VersionFromConnection(&connection));

    // The stored_cvc tables should not exist.
    EXPECT_FALSE(connection.DoesTableExist("local_stored_cvc"));
    EXPECT_FALSE(connection.DoesTableExist("server_stored_cvc"));
  }

  DoMigration();

  // Verify post-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));

    // The stored_cvc tables should exist.
    EXPECT_TRUE(connection.DoesTableExist("local_stored_cvc"));
    EXPECT_TRUE(connection.DoesTableExist("server_stored_cvc"));
  }
}

// Version 116 added new columns to the contact_info_type_tokens and
// local_addresses_type_tokens tables. These tables were since deprecated and
// replaced by address_type_tokens. The migration unit test to the current
// version thus no longer applies.

TEST_F(WebDatabaseMigrationTest, MigrateVersion117ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_117.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    EXPECT_EQ(117, VersionFromConnection(&connection));
    EXPECT_TRUE(connection.DoesTableExist("payments_upi_vpa"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesTableExist("payments_upi_vpa"));
  }
}

// Tests addition of new tables 'masked_ibans' and `masked_iban_metadata`, also
// test that `ibans` has been renamed to `local_ibans`.
TEST_F(WebDatabaseMigrationTest, MigrateVersion118ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_118.sql")));

  // Verify pre-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
    EXPECT_EQ(118, VersionFromConnection(&connection));

    EXPECT_FALSE(connection.DoesTableExist("masked_ibans"));
    EXPECT_FALSE(connection.DoesTableExist("masked_ibans_metadata"));
    EXPECT_TRUE(connection.DoesTableExist("ibans"));
    EXPECT_FALSE(connection.DoesTableExist("local_ibans"));
  }

  DoMigration();

  // Verify post-conditions.
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));

    // The `masked_ibans` and `masked_iban_metadata` tables should exist.
    EXPECT_TRUE(connection.DoesTableExist("masked_ibans"));
    EXPECT_TRUE(connection.DoesTableExist("masked_ibans_metadata"));
    // The `ibans` table should be renamed to `local_ibans`.
    EXPECT_TRUE(connection.DoesTableExist("local_ibans"));
    EXPECT_FALSE(connection.DoesTableExist("ibans"));
  }
}

// Tests that the server_address* tables are dropped.
TEST_F(WebDatabaseMigrationTest, MigrateVersion120ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_120.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    EXPECT_EQ(120, VersionFromConnection(&connection));
    EXPECT_TRUE(connection.DoesTableExist("server_addresses"));
    EXPECT_TRUE(connection.DoesTableExist("server_address_metadata"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesTableExist("server_addresses"));
    EXPECT_FALSE(connection.DoesTableExist("server_address_metadata"));
  }
}

// Tests that the `featured_by_policy` column is added to the keywords table.
TEST_F(WebDatabaseMigrationTest, MigrateVersion121ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_121.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    EXPECT_EQ(121, VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesColumnExist("keywords", "featured_by_policy"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_TRUE(connection.DoesColumnExist("keywords", "featured_by_policy"));
  }
}

// Tests that the `product_terms_url` column is added to the
// `masked_credit_card` table, and the `masked_credit_card_benefits` and the
// `benefit_merchant_domains` tables are added.
TEST_F(WebDatabaseMigrationTest, MigrateVersion122ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_122.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    EXPECT_EQ(122, VersionFromConnection(&connection));
    EXPECT_TRUE(connection.DoesTableExist("masked_credit_cards"));
    EXPECT_FALSE(
        connection.DoesColumnExist("masked_credit_cards", "product_terms_url"));
    EXPECT_FALSE(connection.DoesTableExist("masked_credit_card_benefits"));
    EXPECT_FALSE(connection.DoesTableExist("benefit_merchant_domains"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));

    EXPECT_TRUE(connection.DoesTableExist("masked_credit_cards"));
    EXPECT_TRUE(
        connection.DoesColumnExist("masked_credit_cards", "product_terms_url"));

    EXPECT_TRUE(connection.DoesTableExist("masked_credit_card_benefits"));
    EXPECT_TRUE(connection.DoesColumnExist("masked_credit_card_benefits",
                                           "benefit_id"));
    EXPECT_TRUE(connection.DoesColumnExist("masked_credit_card_benefits",
                                           "instrument_id"));
    EXPECT_TRUE(connection.DoesColumnExist("masked_credit_card_benefits",
                                           "benefit_type"));
    EXPECT_TRUE(connection.DoesColumnExist("masked_credit_card_benefits",
                                           "benefit_category"));
    EXPECT_TRUE(connection.DoesColumnExist("masked_credit_card_benefits",
                                           "benefit_description"));
    EXPECT_TRUE(connection.DoesColumnExist("masked_credit_card_benefits",
                                           "start_time"));
    EXPECT_TRUE(
        connection.DoesColumnExist("masked_credit_card_benefits", "end_time"));

    EXPECT_TRUE(connection.DoesTableExist("benefit_merchant_domains"));
    EXPECT_TRUE(
        connection.DoesColumnExist("benefit_merchant_domains", "benefit_id"));
    EXPECT_TRUE(connection.DoesColumnExist("benefit_merchant_domains",
                                           "merchant_domain"));
  }
}

TEST_F(WebDatabaseMigrationTest, MigrateVersion123ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_123.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(123, VersionFromConnection(&connection));

    EXPECT_TRUE(connection.DoesTableExist("payment_instruments"));
    EXPECT_TRUE(
        connection.DoesTableExist("payment_instrument_supported_rails"));
    EXPECT_TRUE(connection.DoesTableExist("payment_instruments_metadata"));
    EXPECT_TRUE(connection.DoesTableExist("bank_accounts"));
    EXPECT_FALSE(connection.DoesTableExist("masked_bank_accounts"));
    EXPECT_FALSE(connection.DoesTableExist("masked_bank_accounts_metadata"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));

    EXPECT_FALSE(connection.DoesTableExist("payment_instruments"));
    EXPECT_FALSE(
        connection.DoesTableExist("payment_instrument_supported_rails"));
    EXPECT_FALSE(connection.DoesTableExist("payment_instruments_metadata"));
    EXPECT_FALSE(connection.DoesTableExist("bank_accounts"));
    EXPECT_TRUE(connection.DoesTableExist("masked_bank_accounts"));
    EXPECT_TRUE(connection.DoesTableExist("masked_bank_accounts_metadata"));
  }
}

TEST_F(WebDatabaseMigrationTest, MigrateVersion124ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_124.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(124, VersionFromConnection(&connection));

    EXPECT_TRUE(connection.DoesTableExist("unmasked_credit_cards"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));

    // Check version.
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));

    EXPECT_FALSE(connection.DoesTableExist("unmasked_credit_cards"));
  }
}

TEST_F(WebDatabaseMigrationTest, MigrateVersion125ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_125.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(125, VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesTableExist("plus_addresses"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesTableExist("plus_addresses"));
  }
}

TEST_F(WebDatabaseMigrationTest, MigrateVersion126ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_126.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(126, VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesColumnExist("plus_addresses", "profile_id"));
    EXPECT_FALSE(
        connection.DoesTableExist("plus_address_sync_model_type_state"));
    EXPECT_FALSE(
        connection.DoesTableExist("plus_address_sync_entity_metadata"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesTableExist("plus_addresses"));
    EXPECT_FALSE(
        connection.DoesTableExist("plus_address_sync_model_type_state"));
    EXPECT_FALSE(
        connection.DoesTableExist("plus_address_sync_entity_metadata"));
  }
}

// Expect that version 128 altered the type plus_addresses' primary key column
// from INTEGER to VARCHAR, and version 154 dropped the table.
TEST_F(WebDatabaseMigrationTest, MigrateVersion127ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_127.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(127, VersionFromConnection(&connection));
    EXPECT_NE(
        connection.GetSchema().find(
            "CREATE TABLE plus_addresses (profile_id INTEGER PRIMARY KEY"),
        std::string::npos);
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesTableExist("plus_addresses"));
  }
}

TEST_F(WebDatabaseMigrationTest, MigrateVersion128ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_128.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(128, VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesTableExist("generic_payment_instruments"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_TRUE(connection.DoesTableExist("generic_payment_instruments"));
  }
}

TEST_F(WebDatabaseMigrationTest, MigrateVersion129ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_129.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(129, VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesColumnExist("token_service", "binding_key"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_TRUE(connection.DoesColumnExist("token_service", "binding_key"));
  }
}

TEST_F(WebDatabaseMigrationTest, MigrateVersion130ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_130.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(130, VersionFromConnection(&connection));
    EXPECT_TRUE(connection.DoesColumnExist("generic_payment_instruments",
                                           "payment_instrument_type"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesColumnExist("generic_payment_instruments",
                                            "payment_instrument_type"));
  }
}

// Version 131 added new columns to the contact_info and local_addresses tables.
// These tables were since deprecated and replaced by addresses. The migration
// unit test to the current version thus no longer applies.

TEST_F(WebDatabaseMigrationTest, MigrateVersion132ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_132.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(132, VersionFromConnection(&connection));
    EXPECT_TRUE(connection.DoesColumnExist("masked_ibans", "length"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesColumnExist("masked_ibans", "length"));
  }
}

// Tests that addresses stored in the legacy contact_info, local_addresses,
// contact_info_type_tokens and local_addresses_type_tokens tables are migrated
// to the addresses and address_type_tokens tables with the correct record type.
TEST_F(WebDatabaseMigrationTest, MigrateVersion133ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_133.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(133, VersionFromConnection(&connection));
    EXPECT_TRUE(connection.DoesTableExist("contact_info"));
    EXPECT_TRUE(connection.DoesTableExist("contact_info_type_tokens"));
    EXPECT_TRUE(connection.DoesTableExist("local_addresses"));
    EXPECT_TRUE(connection.DoesTableExist("local_addresses_type_tokens"));
    EXPECT_FALSE(connection.DoesTableExist("addresses"));
    EXPECT_FALSE(connection.DoesTableExist("address_type_tokens"));

    // Insert a dummy local and account address to test that they are migrated
    // correctly.
    ASSERT_TRUE(connection.ExecuteScriptForTesting(R"(
      INSERT INTO contact_info (guid)
      VALUES ('00000000-0000-0000-0000-000000000000');
      INSERT INTO contact_info_type_tokens (guid, type, value)
      VALUES ('00000000-0000-0000-0000-000000000000', 7, 'value1');
      INSERT INTO local_addresses (guid)
      VALUES ('00000000-0000-0000-0000-000000000001');
      INSERT INTO local_addresses_type_tokens (guid, type, value)
      VALUES ('00000000-0000-0000-0000-000000000001', 9, 'value2');
    )"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesTableExist("contact_info"));
    EXPECT_FALSE(connection.DoesTableExist("contact_info_type_tokens"));
    EXPECT_FALSE(connection.DoesTableExist("local_addresses"));
    EXPECT_FALSE(connection.DoesTableExist("local_addresses_type_tokens"));
    EXPECT_TRUE(connection.DoesTableExist("addresses"));
    EXPECT_TRUE(connection.DoesTableExist("address_type_tokens"));

    // Expect both addresses in the migrated table with the correct record type.
    sql::Statement s_addresses(connection.GetUniqueStatement(
        "SELECT guid, record_type from addresses ORDER BY guid"));
    ASSERT_TRUE(s_addresses.Step());
    EXPECT_EQ(s_addresses.ColumnString(0),
              "00000000-0000-0000-0000-000000000000");
    EXPECT_EQ(
        s_addresses.ColumnInt(1),
        static_cast<int>(autofill::AutofillProfile::RecordType::kAccount));
    ASSERT_TRUE(s_addresses.Step());
    EXPECT_EQ(s_addresses.ColumnString(0),
              "00000000-0000-0000-0000-000000000001");
    EXPECT_EQ(s_addresses.ColumnInt(1),
              static_cast<int>(
                  autofill::AutofillProfile::RecordType::kLocalOrSyncable));
    ASSERT_FALSE(s_addresses.Step());

    // Expect that the information from the type tokens tables was merged.
    sql::Statement s_type_tokens(connection.GetUniqueStatement(
        "SELECT guid, type, value from address_type_tokens ORDER BY guid"));
    ASSERT_TRUE(s_type_tokens.Step());
    EXPECT_EQ(s_type_tokens.ColumnString(0),
              "00000000-0000-0000-0000-000000000000");
    EXPECT_EQ(s_type_tokens.ColumnInt(1), 7);
    EXPECT_EQ(s_type_tokens.ColumnString(2), "value1");
    ASSERT_TRUE(s_type_tokens.Step());
    EXPECT_EQ(s_type_tokens.ColumnString(0),
              "00000000-0000-0000-0000-000000000001");
    EXPECT_EQ(s_type_tokens.ColumnInt(1), 9);
    EXPECT_EQ(s_type_tokens.ColumnString(2), "value2");
    ASSERT_FALSE(s_type_tokens.Step());
  }
}

// Tests addition of card_info_retrieval_enrollment_state columns in
// masked_credit_cards table.
TEST_F(WebDatabaseMigrationTest, MigrateVersion134ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_134.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(134, VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesColumnExist(
        "masked_credit_cards", "card_info_retrieval_enrollment_state"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_TRUE(connection.DoesColumnExist(
        "masked_credit_cards", "card_info_retrieval_enrollment_state"));
  }
}

// Tests addition of the new table `payment_instrument_creation_options` and
// its columns `id` and `serialized_value_encrypted`.
TEST_F(WebDatabaseMigrationTest, MigrateVersion135ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_135.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(135, VersionFromConnection(&connection));
    EXPECT_FALSE(
        connection.DoesTableExist("payment_instrument_creation_options"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_TRUE(
        connection.DoesTableExist("payment_instrument_creation_options"));
    EXPECT_TRUE(connection.DoesColumnExist(
        "payment_instrument_creation_options", "id"));
    EXPECT_TRUE(connection.DoesColumnExist(
        "payment_instrument_creation_options", "serialized_value_encrypted"));
  }
}

#if BUILDFLAG(IS_WIN)
class WebDatabaseMigrationTestEncryption
    : public WebDatabaseMigrationTest,
      public ::testing::WithParamInterface<bool> {
 protected:
  auto& IsEncryptionAvailable() { return GetParam(); }
};

// Tests addition of the url_hash column to the keywords table.
TEST_P(WebDatabaseMigrationTestEncryption, MigrateVersion136ToCurrent) {
  encryptor_->set_encryption_available_for_testing(IsEncryptionAvailable());
  encryptor_->set_decryption_available_for_testing(IsEncryptionAvailable());

  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_136.sql")));
  const char kTestUrl[] = "chrome://test/?q={searchTerms}";
  const std::string_view kTestKeyword = "@testing";
  const TemplateURLID kTestId = 1;
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(136, VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesColumnExist("keywords", "url_hash"));

    // Insert a keyword to test that it is migrated correctly.
    ASSERT_TRUE(connection.ExecuteScriptForTesting(base::StrCat(
        {"INSERT INTO keywords VALUES(", base::NumberToString(kTestId),
         ",'Test','", kTestKeyword, "','','", kTestUrl,
         "',1,'',0,0,'','',0,0,0,'','[]','','','','','',0,0,1,2,0,0);"})));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_TRUE(connection.DoesColumnExist("keywords", "url_hash"));
    sql::Statement stmt(
        connection.GetUniqueStatement("SELECT url_hash FROM keywords"));
    EXPECT_TRUE(stmt.Step());
    const auto type = stmt.GetColumnType(0);
    if (!IsEncryptionAvailable()) {
      EXPECT_EQ(type, sql::ColumnType::kNull);
      return;
    }

    EXPECT_EQ(type, sql::ColumnType::kBlob);
    const auto encrypted_hash = stmt.ColumnBlob(0);
    const auto hash = encryptor_->DecryptData(encrypted_hash);
    EXPECT_TRUE(hash.has_value());
    TemplateURLData data;
    data.id = kTestId;
    data.SetURL(kTestUrl);
    data.SetKeyword(base::UTF8ToUTF16(kTestKeyword));
    data.starter_pack_id = 2;
    auto expected_hash = data.GenerateHash();
    EXPECT_EQ(hash->size(), expected_hash.size());
    EXPECT_TRUE(std::ranges::equal(
        hash.value(), expected_hash,
        [](char c, uint8_t b) { return static_cast<uint8_t>(c) == b; }));
  }
}

INSTANTIATE_TEST_SUITE_P(/*empty*/,
                         WebDatabaseMigrationTestEncryption,
                         testing::Bool(),
                         [](const auto& info) {
                           return info.param ? "Encryption" : "NoEncryption";
                         });

// Tests migration of a keywords table with an empty url, which is invalid. The
// entry should not be migrated, and the test should not crash. The dropping of
// the invalid entry takes place upon the first GetKeywords call, and this is
// tested elsewhere in KeywordTableTest.KeywordBadUrl. This test is only valid
// on Windows because the bad url detection only happens if encrypted hashing is
// enabled.
TEST_F(WebDatabaseMigrationTest, MigrateVersion136ToCurrentBadUrl) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_136.sql")));
  const TemplateURLID kTestId = 99;
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(136, VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesColumnExist("keywords", "url_hash"));

    // Insert a keyword to test that it is migrated correctly.
    ASSERT_TRUE(connection.ExecuteScriptForTesting(base::StrCat(
        {"INSERT INTO keywords VALUES(", base::NumberToString(kTestId),
         ",'Test','@test','','", /*url=*/"",
         "',1,'',0,0,'','',0,0,0,'','[]','','','','','',0,0,1,2,0,0);"})));
  }
  DoMigration();
}
#else
// On non-Windows the 136 to 137 migration does nothing except update add the
// `url_hash` column and update the database version.
TEST_F(WebDatabaseMigrationTest, MigrateVersion136ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_136.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(136, VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesColumnExist("keywords", "url_hash"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_TRUE(connection.DoesColumnExist("keywords", "url_hash"));
  }
}
#endif  // BUILDFLAG(IS_WIN)

TEST_F(WebDatabaseMigrationTest, MigrateVersion137ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_137.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(137, VersionFromConnection(&connection));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesTableExist("attributes"));
    EXPECT_FALSE(connection.DoesTableExist("entities"));
    EXPECT_FALSE(connection.DoesTableExist("entities_version"));
    EXPECT_TRUE(connection.DoesTableExist("autofill_ai_attributes"));
    EXPECT_TRUE(connection.DoesColumnExist("autofill_ai_attributes",
                                           "value_encrypted"));
    EXPECT_TRUE(connection.DoesTableExist("autofill_ai_entities"));
  }
}

// Tests the renaming of the column in the loyalty_card table from
// `unmasked_loyalty_card_suffix` to 'loyalty_card_number`.
TEST_F(WebDatabaseMigrationTest, MigrateVersion138ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_138.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(138, VersionFromConnection(&connection));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_TRUE(connection.DoesTableExist("loyalty_cards"));

    EXPECT_TRUE(connection.DoesColumnExist("loyalty_cards", "loyalty_card_id"));
    EXPECT_FALSE(connection.DoesColumnExist("loyalty_cards", "guid"));

    EXPECT_TRUE(
        connection.DoesColumnExist("loyalty_cards", "loyalty_card_number"));
    EXPECT_FALSE(connection.DoesColumnExist("loyalty_cards",
                                            "unmasked_loyalty_card_suffix"));
  }
}

// Version 139 added new columns to the entities table. These columns are now
// deprecated and moved to the entities_metadata table. The migration unit test
// to the current version thus no longer applies.

TEST_F(WebDatabaseMigrationTest, MigrateVersion140ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_140.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(140, VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesColumnExist("masked_credit_cards",
                                            "card_benefit_source"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_TRUE(connection.DoesColumnExist("masked_credit_cards",
                                           "card_benefit_source"));
  }
}

TEST_F(WebDatabaseMigrationTest, MigrateVersion141ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_141.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(141, VersionFromConnection(&connection));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    ASSERT_TRUE(connection.DoesTableExist("autofill_ai_entities"));
    EXPECT_TRUE(
        connection.DoesColumnExist("autofill_ai_entities", "record_type"));
  }
}

TEST_F(WebDatabaseMigrationTest, MigrateVersion142ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_142.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(142, VersionFromConnection(&connection));
    ASSERT_TRUE(connection.ExecuteScriptForTesting(R"(
      INSERT INTO autofill_ai_entities
      (guid, entity_type, nickname, date_modified, use_count, use_date)
      VALUES
      ('00000000-0000-0000-0000-000000000000', 'Passport', 'My Passport', 123, 123, 123);
    )"));
  }

  DoMigration();

  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    ASSERT_TRUE(connection.DoesTableExist("autofill_ai_entities"));
    EXPECT_TRUE(connection.DoesColumnExist("autofill_ai_entities",
                                           "attributes_read_only"));

    sql::Statement s_entities(connection.GetUniqueStatement(
        "SELECT guid, attributes_read_only from autofill_ai_entities"));
    ASSERT_TRUE(s_entities.Step());
    EXPECT_EQ(s_entities.ColumnString(0),
              "00000000-0000-0000-0000-000000000000");
    EXPECT_FALSE(s_entities.ColumnBool(1));
    ASSERT_FALSE(s_entities.Step());
  }
}

// Tests addition of card_creation_source column in masked_credit_cards table.
TEST_F(WebDatabaseMigrationTest, MigrateVersion143ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_143.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(143, VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesColumnExist("masked_credit_cards",
                                            "card_creation_source"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_TRUE(connection.DoesColumnExist("masked_credit_cards",
                                           "card_creation_source"));
  }
}

// Tests dropping the use_date2 and use_date3 columns from the addresses table.
TEST_F(WebDatabaseMigrationTest, MigrateVersion144ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_144.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(144, VersionFromConnection(&connection));
    EXPECT_TRUE(connection.DoesColumnExist("addresses", "use_date2"));
    EXPECT_TRUE(connection.DoesColumnExist("addresses", "use_date3"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesColumnExist("addresses", "use_date2"));
    EXPECT_FALSE(connection.DoesColumnExist("addresses", "use_date3"));
  }
}

// Tests addition of frecency_override column in autofill_ai_entities table.
TEST_F(WebDatabaseMigrationTest, MigrateVersion145ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_145.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(145, VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesColumnExist("autofill_ai_entities",
                                            "frecency_override"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_TRUE(connection.DoesColumnExist("autofill_ai_entities",
                                           "frecency_override"));
  }
}

// Tests addition of autofill_ai_entities_metadata table.
TEST_F(WebDatabaseMigrationTest, MigrateVersion146ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_146.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(146, VersionFromConnection(&connection));
    EXPECT_TRUE(
        connection.DoesColumnExist("autofill_ai_entities", "use_count"));
    EXPECT_TRUE(connection.DoesColumnExist("autofill_ai_entities", "use_date"));
    EXPECT_TRUE(
        connection.DoesColumnExist("autofill_ai_entities", "date_modified"));

    // Insert a dummy entity to test that it is migrated correctly.
    ASSERT_TRUE(connection.ExecuteScriptForTesting(R"(
      INSERT INTO autofill_ai_entities
      (guid, entity_type, nickname, use_count, use_date, date_modified)
      VALUES
      ('00000000-0000-0000-0000-000000000001', 'TestEntity', 'TestNickname', 11, 22, 33);
    )"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_FALSE(
        connection.DoesColumnExist("autofill_ai_entities", "use_count"));
    EXPECT_FALSE(
        connection.DoesColumnExist("autofill_ai_entities", "use_date"));
    EXPECT_FALSE(
        connection.DoesColumnExist("autofill_ai_entities", "date_modified"));

    EXPECT_TRUE(connection.DoesColumnExist("autofill_ai_entities_metadata",
                                           "entity_guid"));
    EXPECT_TRUE(connection.DoesColumnExist("autofill_ai_entities_metadata",
                                           "use_count"));
    EXPECT_TRUE(connection.DoesColumnExist("autofill_ai_entities_metadata",
                                           "use_date"));
    EXPECT_TRUE(connection.DoesColumnExist("autofill_ai_entities_metadata",
                                           "date_modified"));

    // The entity should still exist.
    sql::Statement s_entities(connection.GetUniqueStatement(
        "SELECT guid, entity_type, nickname from autofill_ai_entities"));
    ASSERT_TRUE(s_entities.Step());
    EXPECT_EQ(s_entities.ColumnString(0),
              "00000000-0000-0000-0000-000000000001");
    EXPECT_EQ(s_entities.ColumnString(1), "TestEntity");
    EXPECT_EQ(s_entities.ColumnString(2), "TestNickname");
    ASSERT_FALSE(s_entities.Step());

    // Expect the entity's metadata in the migrated table.
    sql::Statement s_metadata(connection.GetUniqueStatement(
        "SELECT entity_guid, use_count, use_date, date_modified from "
        "autofill_ai_entities_metadata"));
    ASSERT_TRUE(s_metadata.Step());
    EXPECT_EQ(s_metadata.ColumnString(0),
              "00000000-0000-0000-0000-000000000001");
    EXPECT_EQ(s_metadata.ColumnInt(1), 11);
    EXPECT_EQ(s_metadata.ColumnInt(2), 22);
    EXPECT_EQ(s_metadata.ColumnInt(3), 33);
    ASSERT_FALSE(s_metadata.Step());
  }
}

TEST_F(WebDatabaseMigrationTest, MigrateVersion147ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_147.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
    EXPECT_EQ(147, VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesTableExist("valuables_metadata"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_TRUE(connection.DoesTableExist("valuables_metadata"));
  }
}

TEST_F(WebDatabaseMigrationTest, MigrateVersion148ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_148.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(148, VersionFromConnection(&connection));
    EXPECT_TRUE(connection.DoesColumnExist("addresses", "last_modifier_id"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesColumnExist("addresses", "last_modifier_id"));
  }
}

TEST_F(WebDatabaseMigrationTest, MigrateVersion149ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_149.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(149, VersionFromConnection(&connection));
    EXPECT_FALSE(
        connection.DoesColumnExist("token_service", "mtls_token_binding"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_TRUE(
        connection.DoesColumnExist("token_service", "mtls_token_binding"));
  }
}

TEST_F(WebDatabaseMigrationTest, MigrateVersion150ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_150.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(150, VersionFromConnection(&connection));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
  }
}

TEST_F(WebDatabaseMigrationTest, MigrateVersion151ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_151.sql")));
  const char kTestUrl[] = "chrome://test/?q={searchTerms}";
  const std::string_view kTestKeyword = "@testing";
  const TemplateURLID kTestId = 1;
  const int kTestStarterPackId = 1234;
  const int kTestEnforcedByPolicy = 1;
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(151, VersionFromConnection(&connection));

    // Insert a keyword to test that it is migrated correctly.
    ASSERT_TRUE(connection.ExecuteScriptForTesting(base::StrCat(
        {"INSERT INTO keywords (id, short_name, keyword, favicon_url, url, "
         "safe_for_autoreplace, url_hash, starter_pack_id, enforced_by_policy) "
         "VALUES (",
         base::NumberToString(kTestId), ",'Test','", kTestKeyword, "','','",
         kTestUrl, "',1, NULL,", base::NumberToString(kTestStarterPackId), ",",
         base::NumberToString(kTestEnforcedByPolicy), ");"})));
  }
  {
    base::HistogramTester histograms;
    DoMigration();
    histograms.ExpectUniqueSample("Search.KeywordTable.MigrationSuccess.V152",
                                  true, 1);
  }
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    sql::Statement stmt(connection.GetUniqueStatement(
        base::StrCat({"SELECT url, keyword, starter_pack_id, "
                      "enforced_by_policy FROM keywords WHERE id=",
                      base::NumberToString(kTestId)})));
    EXPECT_TRUE(stmt.Step());
    EXPECT_EQ(kTestUrl, stmt.ColumnString(0));
    EXPECT_EQ(kTestKeyword, stmt.ColumnString(1));
    EXPECT_EQ(kTestStarterPackId, stmt.ColumnInt(2));
    EXPECT_EQ(kTestEnforcedByPolicy, stmt.ColumnInt(3));
  }
}

// Tests replacing the origin column with is_user_confirmed in credit_cards
// table.
TEST_F(WebDatabaseMigrationTest, MigrateVersion152ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_152.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(152, VersionFromConnection(&connection));
    EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "origin"));
    EXPECT_FALSE(
        connection.DoesColumnExist("credit_cards", "is_user_confirmed"));

    // Insert dummy credit cards to test that values are migrated correctly.
    ASSERT_TRUE(connection.ExecuteScriptForTesting(R"(
      INSERT INTO credit_cards (guid, origin) VALUES ('guid1', 'Chrome settings');
      INSERT INTO credit_cards (guid, origin) VALUES ('guid2', 'http://www.example.com');
      INSERT INTO credit_cards (guid, origin) VALUES ('guid3', '');
    )"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "origin"));
    EXPECT_TRUE(
        connection.DoesColumnExist("credit_cards", "is_user_confirmed"));

    sql::Statement stmt(connection.GetUniqueStatement(
        "SELECT guid, is_user_confirmed FROM credit_cards ORDER BY guid"));
    ASSERT_TRUE(stmt.Step());
    EXPECT_EQ("guid1", stmt.ColumnString(0));
    EXPECT_TRUE(stmt.ColumnBool(1));

    ASSERT_TRUE(stmt.Step());
    EXPECT_EQ("guid2", stmt.ColumnString(0));
    EXPECT_FALSE(stmt.ColumnBool(1));

    ASSERT_TRUE(stmt.Step());
    EXPECT_EQ("guid3", stmt.ColumnString(0));
    EXPECT_FALSE(stmt.ColumnBool(1));

    EXPECT_FALSE(stmt.Step());
  }
}

// Tests that version 154 drops plus_addresses and its sync tables.
TEST_F(WebDatabaseMigrationTest, MigrateVersion153ToCurrent) {
  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_153.sql")));
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(153, VersionFromConnection(&connection));
    EXPECT_TRUE(connection.DoesTableExist("plus_addresses"));
    EXPECT_TRUE(
        connection.DoesTableExist("plus_address_sync_model_type_state"));
    EXPECT_TRUE(connection.DoesTableExist("plus_address_sync_entity_metadata"));
  }
  DoMigration();
  {
    sql::Database connection(sql::test::kTestTag);
    ASSERT_TRUE(connection.Open(GetDatabasePath()));
    EXPECT_EQ(WebDatabase::kCurrentVersionNumber,
              VersionFromConnection(&connection));
    EXPECT_FALSE(connection.DoesTableExist("plus_addresses"));
    EXPECT_FALSE(
        connection.DoesTableExist("plus_address_sync_model_type_state"));
    EXPECT_FALSE(
        connection.DoesTableExist("plus_address_sync_entity_metadata"));
  }
}

}  // anonymous namespace
