BareGit
#include "data_fake.h"

#include <cstdint>
#include <optional>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

#include <gtest/gtest.h>

namespace
{

Card makeCard(
    std::int64_t id,
    std::optional<std::string> game_short_name,
    std::uint64_t card_number,
    std::string name)
{
    return {
        id,
        {std::move(game_short_name), card_number},
        std::move(name),
        std::nullopt,
        std::nullopt,
        0,
        "avif",
        std::nullopt,
        "avif",
        1,
    };
}

} // namespace

/// Verify configured card records can be read by identity.
TEST(DataSourceFakeTest, ReturnsCards)
{
    DataSourceFake data_source({
        makeCard(1, std::nullopt, 10, "Loose card"),
        makeCard(2, "test", 3, "Game card"),
    });

    const auto cards = data_source.getCards(
        GameContentScope::INCLUDE_INTERNAL);
    ASSERT_TRUE(cards);
    ASSERT_EQ(cards->size(), 2U);

    const auto card = data_source.getCard(
        {"test", 3}, GameContentScope::INCLUDE_INTERNAL);
    ASSERT_TRUE(card);
    ASSERT_TRUE(*card);
    EXPECT_EQ((*card)->name, "Game card");

    const auto missing = data_source.getCard(
        {"test", 4}, GameContentScope::INCLUDE_INTERNAL);
    ASSERT_TRUE(missing);
    EXPECT_FALSE(*missing);
}

/// Verify series data, memberships, and game definitions are deterministic.
TEST(DataSourceFakeTest, ReturnsRelatedData)
{
    DataSourceFake data_source(
        {makeCard(2, "test", 3, "Game card")},
        {{7, "test", "First series", "Description"}},
        {{2, {7}}},
        {{{"test", "Test Game", "", GameVisibility::PUBLIC, 1}, {}}});

    const auto memberships = data_source.getCardSeries(2);
    ASSERT_TRUE(memberships);
    EXPECT_EQ(*memberships, std::vector<std::int64_t>({7}));

    const auto games = data_source.getGames(
        GameContentScope::INCLUDE_INTERNAL);
    ASSERT_TRUE(games);
    ASSERT_EQ(games->size(), 1);
    EXPECT_EQ(games->front().short_name, "test");
}

/// Verify equal display names use the permanent short name as a tie-breaker.
TEST(DataSourceFakeTest, SortsGamesDeterministically)
{
    DataSourceFake data_source(
        {},
        {},
        {},
        {
            {{"z", "Same", "", GameVisibility::PUBLIC, 1}, {}},
            {{"a", "Same", "", GameVisibility::PUBLIC, 1}, {}},
        });

    const auto games = data_source.getGames(
        GameContentScope::INCLUDE_INTERNAL);
    ASSERT_TRUE(games);
    ASSERT_EQ(games->size(), 2);
    EXPECT_EQ((*games)[0].short_name, "a");
    EXPECT_EQ((*games)[1].short_name, "z");
}

TEST(DataSourceFakeTest, FiltersInternalGameContent)
{
    DataSourceFake data_source(
        {
            makeCard(1, std::nullopt, 1, "Loose"),
            makeCard(2, "public", 1, "Public"),
            makeCard(3, "internal", 1, "Internal"),
        },
        {
            {1, "public", "Public series", ""},
            {2, "internal", "Internal series", ""},
        },
        {},
        {
            {{"public", "Public", "", GameVisibility::PUBLIC, 1}, {}},
            {{"internal", "Internal", "", GameVisibility::INTERNAL, 1},
             {}},
        });

    auto cards = data_source.getCards(GameContentScope::PUBLIC_ONLY);
    ASSERT_TRUE(cards);
    ASSERT_EQ(cards->size(), 2);
    EXPECT_EQ((*cards)[0].name, "Loose");
    EXPECT_EQ((*cards)[1].name, "Public");
    auto hidden = data_source.getCard(
        {"internal", 1}, GameContentScope::PUBLIC_ONLY);
    ASSERT_TRUE(hidden);
    EXPECT_FALSE(*hidden);
    auto games = data_source.getGames(GameContentScope::PUBLIC_ONLY);
    ASSERT_TRUE(games);
    ASSERT_EQ(games->size(), 1);
    EXPECT_EQ(games->front().short_name, "public");
    auto series = data_source.getSeries(GameContentScope::PUBLIC_ONLY);
    ASSERT_TRUE(series);
    ASSERT_EQ(series->size(), 1);
    EXPECT_EQ(series->front().game_short_name, "public");
}

/// Verify development data cannot accidentally be mutated.
TEST(DataSourceFakeTest, RejectsMutations)
{
    DataSourceFake data_source;

    const auto transaction = data_source.beginTransaction();
    ASSERT_FALSE(transaction);
    EXPECT_EQ(
        transaction.error().msg(),
        "DataSourceFake is read-only and cannot begin a transaction.");
}