BareGit
#include <cstdint>
#include <limits>
#include <string>
#include <variant>

#include <gtest/gtest.h>

#include "game_field.h"

namespace
{

GameDefinitionSnapshot definition()
{
    return {
        {"test", "Test", "", GameVisibility::PUBLIC, 1},
        {
            {1, "test", "score", "Score", GameFieldType::INTEGER, 0, {}},
            {2, "test", "note", "Note", GameFieldType::STRING, 1, {}},
            {3, "test", "route", "Route", GameFieldType::CHOICE, 2,
             {{"Home", 0}, {"Forest", 1}, {"A&B+ C", 2}}},
        },
    };
}

TEST(GameFieldTest, DecodesAllSupportedTypesAndUnsetValues)
{
    const auto decoded = decodeGameFields(definition(), {
        {"score", "-7"}, {"note", "Near\nthe station"},
        {"route", "Forest"},
    });
    ASSERT_TRUE(decoded) << decoded.error().msg();
    ASSERT_EQ(decoded->size(), 3);
    EXPECT_EQ(std::get<std::int64_t>((*decoded)[0].value), -7);
    EXPECT_EQ(
        std::get<std::string>((*decoded)[1].value), "Near\nthe station");
    EXPECT_EQ(std::get<std::string>((*decoded)[2].value), "Forest");

    const auto empty = decodeGameFields(definition(), {
        {"score", ""}, {"note", ""}, {"route", ""},
    });
    ASSERT_TRUE(empty);
    EXPECT_TRUE(empty->empty());
}

TEST(GameFieldTest, AcceptsFullSignedIntegerRange)
{
    auto smallest = decodeGameFields(definition(), {{
        "score", std::to_string(std::numeric_limits<std::int64_t>::min())}});
    auto largest = decodeGameFields(definition(), {{
        "score", std::to_string(std::numeric_limits<std::int64_t>::max())}});
    ASSERT_TRUE(smallest);
    ASSERT_TRUE(largest);
    EXPECT_EQ(
        std::get<std::int64_t>(smallest->front().value),
        std::numeric_limits<std::int64_t>::min());
    EXPECT_EQ(
        std::get<std::int64_t>(largest->front().value),
        std::numeric_limits<std::int64_t>::max());

    auto zero = decodeGameFields(definition(), {{"score", "0"}});
    auto leading_zero = decodeGameFields(
        definition(), {{"score", "007"}});
    ASSERT_TRUE(zero);
    ASSERT_TRUE(leading_zero);
    EXPECT_EQ(std::get<std::int64_t>(zero->front().value), 0);
    EXPECT_EQ(std::get<std::int64_t>(leading_zero->front().value), 7);
}

TEST(GameFieldTest, RejectsMalformedIntegersAndUnknownChoices)
{
    for(const std::string& value : {
            "+1", " 1", "1 ", "1.0", "1e2", "1x",
            "9223372036854775808", "-9223372036854775809"})
    {
        EXPECT_FALSE(decodeGameFields(definition(), {{"score", value}}))
            << value;
    }
    EXPECT_FALSE(decodeGameFields(definition(), {{"route", "home"}}));
    EXPECT_FALSE(decodeGameFields(definition(), {{"route", "Home "}}));
    EXPECT_FALSE(decodeGameFields(definition(), {{"route", "Unknown"}}));
    auto punctuation = decodeGameFields(
        definition(), {{"route", "A&B+ C"}});
    ASSERT_TRUE(punctuation);
    EXPECT_EQ(
        std::get<std::string>(punctuation->front().value), "A&B+ C");
}

TEST(GameFieldTest, RejectsUnknownKeysAndInvalidText)
{
    EXPECT_FALSE(decodeGameFields(definition(), {{"other", "value"}}));
    EXPECT_FALSE(decodeGameFields(
        definition(), {{"note", std::string("bad\0text", 8)}}));
    EXPECT_FALSE(decodeGameFields(
        definition(), {{"note", std::string("\xc3", 1)}}));
    const auto whitespace = decodeGameFields(
        definition(), {{"note", "  \n"}});
    ASSERT_TRUE(whitespace);
    EXPECT_EQ(std::get<std::string>(whitespace->front().value), "  \n");

    const std::string large_text(2 * 1024 * 1024, 'x');
    auto large = decodeGameFields(
        definition(), {{"note", large_text}});
    ASSERT_TRUE(large);
    EXPECT_EQ(std::get<std::string>(large->front().value), large_text);
}

} // namespace