blob: 2b13614f3eef12769646d2a42b586a2d3b86cdab [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/attribution_reporting/attribution_storage_sql.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/guid.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/time/time.h"
#include "content/browser/attribution_reporting/attribution_storage.h"
#include "content/browser/attribution_reporting/attribution_test_utils.h"
#include "sql/database.h"
#include "sql/statement.h"
#include "sql/test/test_helpers.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace {
std::string RemoveQuotes(std::string input) {
std::string output;
base::RemoveChars(input, "\"", &output);
return output;
}
const int kCurrentVersionNumber = 15;
} // namespace
class AttributionStorageSqlMigrationsTest : public testing::Test {
public:
AttributionStorageSqlMigrationsTest() = default;
void SetUp() override { ASSERT_TRUE(temp_directory_.CreateUniqueTempDir()); }
void MigrateDatabase() {
AttributionStorageSql storage(
temp_directory_.GetPath(),
std::make_unique<ConfigurableStorageDelegate>());
// We need to run an operation on storage to force the lazy initialization.
std::ignore =
static_cast<AttributionStorage*>(&storage)->GetAttributionsToReport(
base::Time::Min());
}
base::FilePath DbPath() {
return temp_directory_.GetPath().Append(FILE_PATH_LITERAL("Conversions"));
}
std::string GetCurrentSchema() {
base::FilePath current_version_path = temp_directory_.GetPath().Append(
FILE_PATH_LITERAL("TestCurrentVersion.db"));
LoadDatabase(FILE_PATH_LITERAL("version_15.sql"), current_version_path);
sql::Database db;
EXPECT_TRUE(db.Open(current_version_path));
return db.GetSchema();
}
protected:
// The textual contents of |file| are read from
// "content/test/data/attribution_reporting/databases/" and returned in the
// string |contents|. Returns true if the file exists and is read
// successfully, false otherwise.
bool GetDatabaseData(const base::FilePath& file, std::string* contents) {
base::FilePath source_path;
base::PathService::Get(base::DIR_SOURCE_ROOT, &source_path);
source_path = source_path.AppendASCII(
"content/test/data/attribution_reporting/databases");
source_path = source_path.Append(file);
return base::PathExists(source_path) &&
base::ReadFileToString(source_path, contents);
}
static int VersionFromDatabase(sql::Database* db) {
// Get version.
sql::Statement s(
db->GetUniqueStatement("SELECT value FROM meta WHERE key='version'"));
if (!s.Step())
return 0;
return s.ColumnInt(0);
}
void LoadDatabase(const base::FilePath::StringType& file,
const base::FilePath& db_path) {
std::string contents;
ASSERT_TRUE(GetDatabaseData(base::FilePath(file), &contents));
sql::Database db;
ASSERT_TRUE(db.Open(db_path));
ASSERT_TRUE(db.Execute(contents.data()));
}
base::ScopedTempDir temp_directory_;
};
TEST_F(AttributionStorageSqlMigrationsTest, MigrateEmptyToCurrent) {
base::HistogramTester histograms;
{
AttributionStorageSql storage(
temp_directory_.GetPath(),
std::make_unique<ConfigurableStorageDelegate>());
// We need to perform an operation that is non-trivial on an empty database
// to force initialization.
static_cast<AttributionStorage*>(&storage)->StoreSource(
SourceBuilder(base::Time::Min()).Build());
}
// Verify schema is current.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
// Check version.
EXPECT_EQ(kCurrentVersionNumber, VersionFromDatabase(&db));
// Check that expected tables are present.
EXPECT_TRUE(db.DoesTableExist("conversions"));
EXPECT_TRUE(db.DoesTableExist("impressions"));
EXPECT_TRUE(db.DoesTableExist("meta"));
EXPECT_EQ(GetCurrentSchema(), db.GetSchema());
}
// DB creation histograms should be recorded.
histograms.ExpectTotalCount("Conversions.Storage.CreationTime", 1);
histograms.ExpectTotalCount("Conversions.Storage.MigrationTime", 0);
}
TEST_F(AttributionStorageSqlMigrationsTest, MigrateVersion1ToCurrent) {
base::HistogramTester histograms;
LoadDatabase(FILE_PATH_LITERAL("version_1.sql"), DbPath());
// Verify pre-conditions.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
ASSERT_FALSE(db.DoesTableExist("meta"));
ASSERT_FALSE(db.DoesColumnExist("impressions", "conversion_destination"));
sql::Statement s(
db.GetUniqueStatement("SELECT conversion_origin FROM impressions"));
ASSERT_TRUE(s.Step());
ASSERT_EQ("https://sub.conversion.test", s.ColumnString(0));
ASSERT_FALSE(s.Step());
}
MigrateDatabase();
// Verify schema is current.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
// Check version.
EXPECT_EQ(kCurrentVersionNumber, VersionFromDatabase(&db));
// Check that expected tables are present.
EXPECT_TRUE(db.DoesTableExist("conversions"));
EXPECT_TRUE(db.DoesTableExist("impressions"));
EXPECT_TRUE(db.DoesTableExist("meta"));
// Compare without quotes as sometimes migrations cause table names to be
// string literals.
EXPECT_EQ(RemoveQuotes(GetCurrentSchema()), RemoveQuotes(db.GetSchema()));
// Check that the relevant schema changes are made.
EXPECT_TRUE(db.DoesColumnExist("impressions", "conversion_destination"));
// Verify that data is preserved across the migration.
size_t rows = 0;
sql::test::CountTableRows(&db, "impressions", &rows);
EXPECT_EQ(1u, rows);
sql::Statement s(db.GetUniqueStatement(
"SELECT conversion_origin, conversion_destination FROM impressions"));
ASSERT_TRUE(s.Step());
ASSERT_EQ("https://sub.conversion.test", s.ColumnString(0));
ASSERT_EQ("https://conversion.test", s.ColumnString(1));
ASSERT_FALSE(s.Step());
}
// DB migration histograms should be recorded.
histograms.ExpectTotalCount("Conversions.Storage.CreationTime", 0);
histograms.ExpectTotalCount("Conversions.Storage.MigrationTime", 1);
}
TEST_F(AttributionStorageSqlMigrationsTest, MigrateVersion2ToCurrent) {
base::HistogramTester histograms;
LoadDatabase(FILE_PATH_LITERAL("version_2.sql"), DbPath());
// Verify pre-conditions.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
ASSERT_FALSE(db.DoesColumnExist("impressions", "source_type"));
ASSERT_FALSE(db.DoesColumnExist("impressions", "attributed_truthfully"));
}
MigrateDatabase();
// Verify schema is current.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
// Check version.
EXPECT_EQ(kCurrentVersionNumber, VersionFromDatabase(&db));
// Check that expected tables are present.
EXPECT_TRUE(db.DoesTableExist("conversions"));
EXPECT_TRUE(db.DoesTableExist("impressions"));
EXPECT_TRUE(db.DoesTableExist("meta"));
// Compare without quotes as sometimes migrations cause table names to be
// string literals.
EXPECT_EQ(RemoveQuotes(GetCurrentSchema()), RemoveQuotes(db.GetSchema()));
// Check that the relevant schema changes are made.
EXPECT_TRUE(db.DoesColumnExist("impressions", "source_type"));
EXPECT_TRUE(db.DoesColumnExist("impressions", "attributed_truthfully"));
// Verify that data is preserved across the migration.
size_t rows = 0;
sql::test::CountTableRows(&db, "impressions", &rows);
EXPECT_EQ(1u, rows);
sql::Statement s(db.GetUniqueStatement(
"SELECT source_type, attributed_truthfully FROM impressions"));
ASSERT_TRUE(s.Step());
ASSERT_EQ(0, s.ColumnInt(0));
ASSERT_EQ(true, s.ColumnBool(1));
ASSERT_FALSE(s.Step());
}
// DB migration histograms should be recorded.
histograms.ExpectTotalCount("Conversions.Storage.CreationTime", 0);
histograms.ExpectTotalCount("Conversions.Storage.MigrationTime", 1);
}
TEST_F(AttributionStorageSqlMigrationsTest, MigrateVersion3ToCurrent) {
base::HistogramTester histograms;
LoadDatabase(FILE_PATH_LITERAL("version_3.sql"), DbPath());
// Verify pre-conditions.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
ASSERT_FALSE(db.DoesTableExist("rate_limits"));
}
MigrateDatabase();
// Verify schema is current.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
// Check version.
EXPECT_EQ(kCurrentVersionNumber, VersionFromDatabase(&db));
// Check that expected tables are present.
EXPECT_TRUE(db.DoesTableExist("conversions"));
EXPECT_TRUE(db.DoesTableExist("impressions"));
EXPECT_TRUE(db.DoesTableExist("meta"));
EXPECT_TRUE(db.DoesTableExist("rate_limits"));
// Compare without quotes as sometimes migrations cause table names to be
// string literals.
EXPECT_EQ(RemoveQuotes(GetCurrentSchema()), RemoveQuotes(db.GetSchema()));
}
// DB migration histograms should be recorded.
histograms.ExpectTotalCount("Conversions.Storage.CreationTime", 0);
histograms.ExpectTotalCount("Conversions.Storage.MigrationTime", 1);
}
TEST_F(AttributionStorageSqlMigrationsTest, MigrateVersion4ToCurrent) {
base::HistogramTester histograms;
LoadDatabase(FILE_PATH_LITERAL("version_4.sql"), DbPath());
// Verify pre-conditions.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
ASSERT_TRUE(db.DoesColumnExist("conversions", "attribution_credit"));
sql::Statement s(db.GetUniqueStatement(
"SELECT conversion_id FROM conversions ORDER BY conversion_id"));
ASSERT_TRUE(s.Step());
ASSERT_EQ(1, s.ColumnInt(0));
ASSERT_TRUE(s.Step());
ASSERT_EQ(2, s.ColumnInt(0));
ASSERT_TRUE(s.Step());
ASSERT_EQ(3, s.ColumnInt(0));
ASSERT_FALSE(s.Step());
}
MigrateDatabase();
// Verify schema is current.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
// Check version.
EXPECT_EQ(kCurrentVersionNumber, VersionFromDatabase(&db));
// Check that expected tables are present.
EXPECT_TRUE(db.DoesTableExist("conversions"));
EXPECT_TRUE(db.DoesTableExist("impressions"));
EXPECT_TRUE(db.DoesTableExist("meta"));
EXPECT_TRUE(db.DoesTableExist("rate_limits"));
// Check that the expected column is dropped.
EXPECT_FALSE(db.DoesColumnExist("conversions", "attribution_credit"));
// Check that only the 0-credit conversions are deleted.
sql::Statement s(db.GetUniqueStatement(
"SELECT conversion_id FROM conversions ORDER BY conversion_id"));
ASSERT_TRUE(s.Step());
ASSERT_EQ(1, s.ColumnInt(0));
ASSERT_TRUE(s.Step());
ASSERT_EQ(3, s.ColumnInt(0));
ASSERT_FALSE(s.Step());
// Compare without quotes as sometimes migrations cause table names to be
// string literals.
EXPECT_EQ(RemoveQuotes(GetCurrentSchema()), RemoveQuotes(db.GetSchema()));
}
// DB migration histograms should be recorded.
histograms.ExpectTotalCount("Conversions.Storage.CreationTime", 0);
histograms.ExpectTotalCount("Conversions.Storage.MigrationTime", 1);
}
TEST_F(AttributionStorageSqlMigrationsTest, MigrateVersion5ToCurrent) {
base::HistogramTester histograms;
LoadDatabase(FILE_PATH_LITERAL("version_5.sql"), DbPath());
// Verify pre-conditions.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
ASSERT_FALSE(db.DoesColumnExist("impressions", "priority"));
}
MigrateDatabase();
// Verify schema is current.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
// Check version.
EXPECT_EQ(kCurrentVersionNumber, VersionFromDatabase(&db));
// Check that expected tables are present.
EXPECT_TRUE(db.DoesTableExist("conversions"));
EXPECT_TRUE(db.DoesTableExist("impressions"));
EXPECT_TRUE(db.DoesTableExist("meta"));
EXPECT_TRUE(db.DoesTableExist("rate_limits"));
// Compare without quotes as sometimes migrations cause table names to be
// string literals.
EXPECT_EQ(RemoveQuotes(GetCurrentSchema()), RemoveQuotes(db.GetSchema()));
// Check that the relevant schema changes are made.
EXPECT_TRUE(db.DoesColumnExist("impressions", "priority"));
// Verify that data is preserved across the migration.
size_t rows = 0;
sql::test::CountTableRows(&db, "impressions", &rows);
EXPECT_EQ(1u, rows);
sql::Statement s(db.GetUniqueStatement(
"SELECT conversion_origin, priority FROM impressions"));
ASSERT_TRUE(s.Step());
ASSERT_EQ("https://conversion.test", s.ColumnString(0));
ASSERT_EQ(0, s.ColumnInt64(1));
ASSERT_FALSE(s.Step());
}
// DB migration histograms should be recorded.
histograms.ExpectTotalCount("Conversions.Storage.CreationTime", 0);
histograms.ExpectTotalCount("Conversions.Storage.MigrationTime", 1);
}
TEST_F(AttributionStorageSqlMigrationsTest, MigrateVersion6ToCurrent) {
base::HistogramTester histograms;
LoadDatabase(FILE_PATH_LITERAL("version_6.sql"), DbPath());
// Verify pre-conditions.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
ASSERT_FALSE(db.DoesColumnExist("impressions", "impression_site"));
}
MigrateDatabase();
// Verify schema is current.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
// Check version.
EXPECT_EQ(kCurrentVersionNumber, VersionFromDatabase(&db));
// Compare without quotes as sometimes migrations cause table names to be
// string literals.
EXPECT_EQ(RemoveQuotes(GetCurrentSchema()), RemoveQuotes(db.GetSchema()));
// Check that the relevant schema changes are made.
EXPECT_TRUE(db.DoesColumnExist("impressions", "impression_site"));
// Verify that data is preserved across the migration.
size_t rows = 0;
sql::test::CountTableRows(&db, "impressions", &rows);
EXPECT_EQ(2u, rows);
sql::Statement s(db.GetUniqueStatement(
"SELECT impression_origin, impression_site FROM impressions"));
ASSERT_TRUE(s.Step());
ASSERT_EQ("https://a.impression.test", s.ColumnString(0));
ASSERT_EQ("https://impression.test", s.ColumnString(1));
ASSERT_TRUE(s.Step());
ASSERT_EQ("https://b.impression.test", s.ColumnString(0));
ASSERT_EQ("https://impression.test", s.ColumnString(1));
ASSERT_FALSE(s.Step());
}
// DB migration histograms should be recorded.
histograms.ExpectTotalCount("Conversions.Storage.CreationTime", 0);
histograms.ExpectTotalCount("Conversions.Storage.MigrationTime", 1);
}
TEST_F(AttributionStorageSqlMigrationsTest, MigrateVersion7ToCurrent) {
base::HistogramTester histograms;
LoadDatabase(FILE_PATH_LITERAL("version_7.sql"), DbPath());
// Verify pre-conditions.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
sql::Statement impression_statement(db.GetUniqueStatement(
"SELECT impression_data FROM impressions ORDER BY impression_id"));
ASSERT_TRUE(impression_statement.Step());
ASSERT_EQ("18446744073709551615", impression_statement.ColumnString(0));
ASSERT_TRUE(impression_statement.Step());
ASSERT_EQ("invalid", impression_statement.ColumnString(0));
ASSERT_FALSE(impression_statement.Step());
sql::Statement conversion_statement(
db.GetUniqueStatement("SELECT conversion_data, impression_id FROM "
"conversions ORDER BY conversion_id"));
ASSERT_TRUE(conversion_statement.Step());
ASSERT_EQ("234", conversion_statement.ColumnString(0));
ASSERT_EQ(29L, conversion_statement.ColumnInt64(1));
ASSERT_TRUE(conversion_statement.Step());
ASSERT_EQ("invalid", conversion_statement.ColumnString(0));
ASSERT_EQ(sql::ColumnType::kNull, conversion_statement.GetColumnType(1));
ASSERT_FALSE(conversion_statement.Step());
}
MigrateDatabase();
// Verify schema is current.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
// Check version.
EXPECT_EQ(kCurrentVersionNumber, VersionFromDatabase(&db));
// Check that expected tables are present.
EXPECT_TRUE(db.DoesTableExist("conversions"));
EXPECT_TRUE(db.DoesTableExist("impressions"));
EXPECT_TRUE(db.DoesTableExist("meta"));
EXPECT_TRUE(db.DoesTableExist("rate_limits"));
// Compare without quotes as sometimes migrations cause table names to be
// string literals.
EXPECT_EQ(RemoveQuotes(GetCurrentSchema()), RemoveQuotes(db.GetSchema()));
sql::Statement impression_statement(db.GetUniqueStatement(
"SELECT impression_data FROM impressions ORDER BY impression_id"));
ASSERT_TRUE(impression_statement.Step());
ASSERT_EQ(18446744073709551615UL,
static_cast<uint64_t>(impression_statement.ColumnInt64(0)));
ASSERT_TRUE(impression_statement.Step());
ASSERT_EQ(0L, impression_statement.ColumnInt64(0));
ASSERT_FALSE(impression_statement.Step());
sql::Statement conversion_statement(
db.GetUniqueStatement("SELECT conversion_data, impression_id FROM "
"conversions ORDER BY conversion_id"));
ASSERT_TRUE(conversion_statement.Step());
ASSERT_EQ(234L, conversion_statement.ColumnInt64(0));
ASSERT_EQ(29L, conversion_statement.ColumnInt64(1));
ASSERT_TRUE(conversion_statement.Step());
ASSERT_EQ(0L, conversion_statement.ColumnInt64(0));
ASSERT_EQ(0L, conversion_statement.ColumnInt64(1));
ASSERT_FALSE(conversion_statement.Step());
}
// DB migration histograms should be recorded.
histograms.ExpectTotalCount("Conversions.Storage.CreationTime", 0);
histograms.ExpectTotalCount("Conversions.Storage.MigrationTime", 1);
}
TEST_F(AttributionStorageSqlMigrationsTest, MigrateVersion8ToCurrent) {
base::HistogramTester histograms;
LoadDatabase(FILE_PATH_LITERAL("version_8.sql"), DbPath());
// Verify pre-conditions.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
ASSERT_FALSE(db.DoesColumnExist("conversions", "priority"));
}
MigrateDatabase();
// Verify schema is current.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
// Check version.
EXPECT_EQ(kCurrentVersionNumber, VersionFromDatabase(&db));
// Compare without quotes as sometimes migrations cause table names to be
// string literals.
EXPECT_EQ(RemoveQuotes(GetCurrentSchema()), RemoveQuotes(db.GetSchema()));
// Check that the relevant schema changes are made.
EXPECT_TRUE(db.DoesColumnExist("conversions", "priority"));
}
// DB migration histograms should be recorded.
histograms.ExpectTotalCount("Conversions.Storage.CreationTime", 0);
histograms.ExpectTotalCount("Conversions.Storage.MigrationTime", 1);
}
TEST_F(AttributionStorageSqlMigrationsTest, MigrateVersion9ToCurrent) {
base::HistogramTester histograms;
LoadDatabase(FILE_PATH_LITERAL("version_9.sql"), DbPath());
// Verify pre-conditions.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
ASSERT_FALSE(db.DoesTableExist("dedup_keys"));
}
MigrateDatabase();
// Verify schema is current.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
// Check version.
EXPECT_EQ(kCurrentVersionNumber, VersionFromDatabase(&db));
// Compare without quotes as sometimes migrations cause table names to be
// string literals.
EXPECT_EQ(RemoveQuotes(GetCurrentSchema()), RemoveQuotes(db.GetSchema()));
// Check that the relevant schema changes are made.
EXPECT_TRUE(db.DoesTableExist("dedup_keys"));
}
// DB migration histograms should be recorded.
histograms.ExpectTotalCount("Conversions.Storage.CreationTime", 0);
histograms.ExpectTotalCount("Conversions.Storage.MigrationTime", 1);
}
TEST_F(AttributionStorageSqlMigrationsTest, MigrateVersion10ToCurrent) {
base::HistogramTester histograms;
LoadDatabase(FILE_PATH_LITERAL("version_10.sql"), DbPath());
// Verify pre-conditions.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
ASSERT_TRUE(db.DoesIndexExist("impression_site_idx"));
ASSERT_FALSE(db.DoesIndexExist("event_source_impression_site_idx"));
}
MigrateDatabase();
// Verify schema is current.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
// Check version.
EXPECT_EQ(kCurrentVersionNumber, VersionFromDatabase(&db));
// Compare without quotes as sometimes migrations cause table names to be
// string literals.
EXPECT_EQ(RemoveQuotes(GetCurrentSchema()), RemoveQuotes(db.GetSchema()));
// Check that the relevant schema changes are made.
ASSERT_FALSE(db.DoesIndexExist("impression_site_idx"));
ASSERT_TRUE(db.DoesIndexExist("event_source_impression_site_idx"));
}
// DB migration histograms should be recorded.
histograms.ExpectTotalCount("Conversions.Storage.CreationTime", 0);
histograms.ExpectTotalCount("Conversions.Storage.MigrationTime", 1);
}
TEST_F(AttributionStorageSqlMigrationsTest, MigrateVersion11ToCurrent) {
base::HistogramTester histograms;
LoadDatabase(FILE_PATH_LITERAL("version_11.sql"), DbPath());
// Verify pre-conditions.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
ASSERT_FALSE(db.DoesColumnExist("rate_limits", "bucket"));
ASSERT_FALSE(db.DoesColumnExist("rate_limits", "value"));
ASSERT_FALSE(
db.DoesIndexExist("rate_limit_attribution_type_conversion_time_idx"));
ASSERT_TRUE(db.DoesIndexExist("rate_limit_conversion_time_idx"));
sql::Statement s(db.GetUniqueStatement("SELECT * FROM rate_limits"));
ASSERT_TRUE(s.Step());
ASSERT_EQ(1, s.ColumnInt(0));
ASSERT_EQ(0, s.ColumnInt(1));
ASSERT_EQ(11, s.ColumnInt64(2));
ASSERT_EQ("https://a.example", s.ColumnString(3));
ASSERT_EQ("https://a.a.example", s.ColumnString(4));
ASSERT_EQ("https://b.example", s.ColumnString(5));
ASSERT_EQ("https://b.b.example", s.ColumnString(6));
ASSERT_EQ(7, s.ColumnInt64(7));
ASSERT_FALSE(s.Step());
}
MigrateDatabase();
// Verify schema is current.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
// Check version.
EXPECT_EQ(kCurrentVersionNumber, VersionFromDatabase(&db));
// Compare without quotes as sometimes migrations cause table names to be
// string literals.
EXPECT_EQ(RemoveQuotes(GetCurrentSchema()), RemoveQuotes(db.GetSchema()));
// Check that the relevant schema changes are made.
ASSERT_TRUE(db.DoesColumnExist("rate_limits", "bucket"));
ASSERT_TRUE(db.DoesColumnExist("rate_limits", "value"));
ASSERT_TRUE(
db.DoesIndexExist("rate_limit_attribution_type_conversion_time_idx"));
ASSERT_FALSE(db.DoesIndexExist("rate_limit_conversion_time_idx"));
// Verify that data is preserved across the migration.
sql::Statement s(db.GetUniqueStatement("SELECT * FROM rate_limits"));
ASSERT_TRUE(s.Step());
ASSERT_EQ(1, s.ColumnInt(0));
ASSERT_EQ(0, s.ColumnInt(1));
ASSERT_EQ(11, s.ColumnInt64(2));
ASSERT_EQ("https://a.example", s.ColumnString(3));
ASSERT_EQ("https://a.a.example", s.ColumnString(4));
ASSERT_EQ("https://b.example", s.ColumnString(5));
ASSERT_EQ("https://b.b.example", s.ColumnString(6));
ASSERT_EQ(7, s.ColumnInt64(7));
ASSERT_EQ(0, s.ColumnInt64(8));
ASSERT_EQ(1, s.ColumnInt64(9));
ASSERT_FALSE(s.Step());
}
// DB migration histograms should be recorded.
histograms.ExpectTotalCount("Conversions.Storage.CreationTime", 0);
histograms.ExpectTotalCount("Conversions.Storage.MigrationTime", 1);
}
TEST_F(AttributionStorageSqlMigrationsTest, MigrateVersion12ToCurrent) {
base::HistogramTester histograms;
LoadDatabase(FILE_PATH_LITERAL("version_12.sql"), DbPath());
auto check_data = [](sql::Database& db) {
sql::Statement s(db.GetUniqueStatement("SELECT * FROM impressions"));
ASSERT_TRUE(s.Step());
ASSERT_EQ(1, s.ColumnInt(0));
ASSERT_EQ(2, s.ColumnInt(1));
ASSERT_EQ("a", s.ColumnString(2));
ASSERT_EQ("b", s.ColumnString(3));
ASSERT_EQ("c", s.ColumnString(4));
ASSERT_EQ(3, s.ColumnInt(5));
ASSERT_EQ(4, s.ColumnInt(6));
ASSERT_EQ(5, s.ColumnInt(7));
ASSERT_EQ(6, s.ColumnInt(8));
ASSERT_EQ("d", s.ColumnString(9));
ASSERT_EQ(7, s.ColumnInt(10));
ASSERT_EQ(8, s.ColumnInt(11));
ASSERT_EQ(9, s.ColumnInt(12));
ASSERT_EQ("e", s.ColumnString(13));
ASSERT_FALSE(s.Step());
sql::Statement t(db.GetUniqueStatement("SELECT * FROM conversions"));
ASSERT_TRUE(t.Step());
ASSERT_EQ(10, t.ColumnInt(0));
ASSERT_EQ(11, t.ColumnInt(1));
ASSERT_EQ(12, t.ColumnInt(2));
ASSERT_EQ(13, t.ColumnInt(3));
ASSERT_EQ(14, t.ColumnInt(4));
ASSERT_EQ(15, t.ColumnInt(5));
ASSERT_FALSE(t.Step());
};
// Verify pre-conditions.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
check_data(db);
}
MigrateDatabase();
// Verify schema is current.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
// Check version.
EXPECT_EQ(kCurrentVersionNumber, VersionFromDatabase(&db));
// Compare without quotes as sometimes migrations cause table names to be
// string literals.
EXPECT_EQ(RemoveQuotes(GetCurrentSchema()), RemoveQuotes(db.GetSchema()));
check_data(db);
}
// DB migration histograms should be recorded.
histograms.ExpectTotalCount("Conversions.Storage.CreationTime", 0);
histograms.ExpectTotalCount("Conversions.Storage.MigrationTime", 1);
}
TEST_F(AttributionStorageSqlMigrationsTest, MigrateVersion13ToCurrent) {
base::HistogramTester histograms;
LoadDatabase(FILE_PATH_LITERAL("version_13.sql"), DbPath());
// Verify pre-conditions.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
ASSERT_FALSE(db.DoesColumnExist("conversions", "failed_send_attempts"));
sql::Statement s(db.GetUniqueStatement("SELECT * FROM conversions"));
ASSERT_TRUE(s.Step());
ASSERT_EQ(1, s.ColumnInt(0));
ASSERT_EQ(2, s.ColumnInt(1));
ASSERT_EQ(3, s.ColumnInt(2));
ASSERT_EQ(4, s.ColumnInt(3));
ASSERT_EQ(5, s.ColumnInt(4));
ASSERT_EQ(6, s.ColumnInt(5));
ASSERT_FALSE(s.Step());
}
MigrateDatabase();
// Verify schema is current.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
// Check version.
EXPECT_EQ(kCurrentVersionNumber, VersionFromDatabase(&db));
// Compare without quotes as sometimes migrations cause table names to be
// string literals.
EXPECT_EQ(RemoveQuotes(GetCurrentSchema()), RemoveQuotes(db.GetSchema()));
// Check that the relevant schema changes are made.
ASSERT_TRUE(db.DoesColumnExist("conversions", "failed_send_attempts"));
// Verify that data is preserved across the migration.
sql::Statement s(db.GetUniqueStatement("SELECT * FROM conversions"));
ASSERT_TRUE(s.Step());
ASSERT_EQ(1, s.ColumnInt(0));
ASSERT_EQ(2, s.ColumnInt(1));
ASSERT_EQ(3, s.ColumnInt(2));
ASSERT_EQ(4, s.ColumnInt(3));
ASSERT_EQ(5, s.ColumnInt(4));
ASSERT_EQ(6, s.ColumnInt(5));
ASSERT_EQ(0, s.ColumnInt(6));
ASSERT_FALSE(s.Step());
}
// DB migration histograms should be recorded.
histograms.ExpectTotalCount("Conversions.Storage.CreationTime", 0);
histograms.ExpectTotalCount("Conversions.Storage.MigrationTime", 1);
}
TEST_F(AttributionStorageSqlMigrationsTest, MigrateVersion14ToCurrent) {
base::HistogramTester histograms;
LoadDatabase(FILE_PATH_LITERAL("version_14.sql"), DbPath());
// Verify pre-conditions.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
ASSERT_FALSE(db.DoesColumnExist("conversions", "external_report_id"));
sql::Statement s(db.GetUniqueStatement("SELECT * FROM conversions"));
ASSERT_TRUE(s.Step());
ASSERT_EQ(1, s.ColumnInt(0));
ASSERT_EQ(2, s.ColumnInt(1));
ASSERT_EQ(3, s.ColumnInt(2));
ASSERT_EQ(4, s.ColumnInt(3));
ASSERT_EQ(5, s.ColumnInt(4));
ASSERT_EQ(6, s.ColumnInt(5));
ASSERT_EQ(7, s.ColumnInt(6));
ASSERT_FALSE(s.Step());
}
MigrateDatabase();
// Verify schema is current.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));
// Check version.
EXPECT_EQ(kCurrentVersionNumber, VersionFromDatabase(&db));
// Compare without quotes as sometimes migrations cause table names to be
// string literals.
EXPECT_EQ(RemoveQuotes(GetCurrentSchema()), RemoveQuotes(db.GetSchema()));
// Check that the relevant schema changes are made.
ASSERT_TRUE(db.DoesColumnExist("conversions", "external_report_id"));
// Verify that data is preserved across the migration.
sql::Statement s(db.GetUniqueStatement("SELECT * FROM conversions"));
ASSERT_TRUE(s.Step());
ASSERT_EQ(1, s.ColumnInt(0));
ASSERT_EQ(2, s.ColumnInt(1));
ASSERT_EQ(3, s.ColumnInt(2));
ASSERT_EQ(4, s.ColumnInt(3));
ASSERT_EQ(5, s.ColumnInt(4));
ASSERT_EQ(6, s.ColumnInt(5));
ASSERT_EQ(7, s.ColumnInt(6));
ASSERT_TRUE(base::GUID::ParseLowercase(s.ColumnString(7)).is_valid());
ASSERT_FALSE(s.Step());
}
// DB migration histograms should be recorded.
histograms.ExpectTotalCount("Conversions.Storage.CreationTime", 0);
histograms.ExpectTotalCount("Conversions.Storage.MigrationTime", 1);
}
} // namespace content