BareGit
#include <gtest/gtest.h>

#include <mw/database.hpp>

#include "db/migrations.hpp"

namespace
{

bool tableExists(mw::SQLite& db, const std::string& name)
{
    auto stmt = db.statementFromStr(
        "SELECT 1 FROM sqlite_master WHERE type='table' AND name = ?;");
    EXPECT_TRUE(stmt.has_value());
    EXPECT_TRUE(stmt->bind(name).has_value());
    auto rows = db.eval<int64_t>(std::move(*stmt));
    EXPECT_TRUE(rows.has_value());
    return !rows->empty();
}

int64_t userVersion(mw::SQLite& db)
{
    auto v = db.evalToValue<int64_t>("PRAGMA user_version;");
    EXPECT_TRUE(v.has_value());
    return *v;
}

} // namespace

TEST(Migrations, V0ToV1AppliesSchema)
{
    auto db_r = mw::SQLite::connectMemory();
    ASSERT_TRUE(db_r.has_value());
    auto& db = **db_r;
    ASSERT_TRUE(overseer::db::migrateDB0To1(db).has_value());

    // The init migration creates a known set of tables.
    EXPECT_TRUE(tableExists(db, "storage"));
    EXPECT_TRUE(tableExists(db, "stuff"));
    EXPECT_TRUE(tableExists(db, "stuff_move"));
    EXPECT_TRUE(tableExists(db, "attachment"));
}

TEST(Migrations, MigrateIfNeededSetsUserVersion)
{
    auto db_r = mw::SQLite::connectMemory();
    ASSERT_TRUE(db_r.has_value());
    auto& db = **db_r;
    // Empty path -> skip backup, which is what we want in-memory.
    ASSERT_TRUE(overseer::db::migrateIfNeeded(db, "").has_value());
    EXPECT_EQ(userVersion(db), overseer::db::HIGHEST_KNOWN_VERSION);
}

TEST(Migrations, MigrateIfNeededIsIdempotent)
{
    auto db_r = mw::SQLite::connectMemory();
    ASSERT_TRUE(db_r.has_value());
    auto& db = **db_r;
    ASSERT_TRUE(overseer::db::migrateIfNeeded(db, "").has_value());
    const auto v1 = userVersion(db);
    // Running it again is a no-op: should succeed and the version
    // should not move.
    ASSERT_TRUE(overseer::db::migrateIfNeeded(db, "").has_value());
    EXPECT_EQ(userVersion(db), v1);
}

TEST(Migrations, RefusesNewerThanBinary)
{
    auto db_r = mw::SQLite::connectMemory();
    ASSERT_TRUE(db_r.has_value());
    auto& db = **db_r;
    ASSERT_TRUE(db.execute("PRAGMA user_version = 9999;").has_value());
    auto r = overseer::db::migrateIfNeeded(db, "");
    ASSERT_FALSE(r.has_value());
}