#include "zeroerr/assert.h" #include "zeroerr/unittest.h" #include "common/EventSequence.h" #include "common/Generation.h" #include "common/Identity.h" #include "common/Revision.h" #include "common/SchemaVersion.h" #include "common/Time.h" #include #include #include #include #include #include #include static_assert(!std::is_convertible_v); static_assert(!std::is_convertible_v); static_assert(!std::is_convertible_v); static_assert(!std::is_convertible_v); static_assert(!std::is_convertible_v); static_assert(!std::is_convertible_v); TEST_CASE("common.identity.persistentIdRoundTripAndNil") { const auto nil = eve::PersistentId::nil(); CHECK(nil.isNil()); CHECK_EQ(nil.format(), "00000000-0000-0000-0000-000000000000"); const auto parsed = eve::PersistentId::parse("01020304-0506-0708-090a-0b0c0d0e0f10"); REQUIRE(parsed.has_value()); CHECK(!parsed->isNil()); CHECK_EQ(parsed->format(), "01020304-0506-0708-090a-0b0c0d0e0f10"); CHECK(parsed == eve::PersistentId::parse(parsed->format())); CHECK(parsed == eve::PersistentId::parse("01020304-0506-0708-090A-0B0C0D0E0F10")); CHECK(parsed->hash() != nil.hash()); CHECK(!eve::PersistentId::parse("01020304-0506-0708-090a-0b0c0d0e0f1").has_value()); CHECK(!eve::PersistentId::parse("{01020304-0506-0708-090a-0b0c0d0e0f10}").has_value()); CHECK(!eve::PersistentId::parse("01020304-0506-0708-090a-0b0c0d0e0fzz").has_value()); } TEST_CASE("common.identity.uuidV7UsesInjectedEntropyAndClock") { using namespace std::chrono_literals; const auto timestamp = std::chrono::system_clock::time_point{std::chrono::milliseconds{0x010203040506}}; eve::UuidV7Generator generator( [](std::span bytes) { for (std::size_t i = 0; i < bytes.size(); ++i) bytes[i] = static_cast(0xa0 + i); return true; }, [timestamp] { return timestamp; }); const auto id = generator.generate(); REQUIRE(id.has_value()); CHECK_EQ(id->format(), "01020304-0506-70a1-a2a3-a4a5a6a7a8a9"); const auto bytes = id->bytes(); CHECK_EQ(bytes[6] & 0xf0u, 0x70u); CHECK_EQ(bytes[8] & 0xc0u, 0x80u); eve::UuidV7Generator unavailable([](std::span) { return false; }, [timestamp] { return timestamp; }); CHECK(!unavailable.generate().has_value()); const auto negative = generator.generate(std::chrono::system_clock::time_point{} - 1ms); CHECK(!negative.has_value()); } TEST_CASE("common.identity.logicalAndContentIdsHaveSeparateBoundaries") { const auto logical = eve::LogicalId::parse("rpg:skill.fire"); REQUIRE(logical.has_value()); CHECK(logical->isValid()); CHECK_EQ(logical->namespaceName(), "rpg"); CHECK_EQ(logical->name(), "skill.fire"); CHECK_EQ(logical->format(), "rpg:skill.fire"); CHECK(eve::LogicalId::fromParts("rpg", "skill.fire") == logical); CHECK(!eve::LogicalId::parse("rpg").has_value()); CHECK(!eve::LogicalId::parse("rpg:").has_value()); CHECK(!eve::LogicalId::parse("rpg:skill:fire").has_value()); CHECK(!eve::LogicalId::parse("rpg/skill:fire").has_value()); const std::array digest{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; const auto content = eve::ContentId::fromBytes(digest); REQUIRE(content.has_value()); CHECK(!content->isNil()); CHECK_EQ(content->format(), "00010203-0405-0607-0809-0a0b0c0d0e0f"); CHECK(content == eve::ContentId::parse(content->format())); CHECK(!eve::ContentId::fromBytes(std::span(digest).first(15)).has_value()); std::unordered_map values; values.emplace(*content, 42); CHECK_EQ(values.at(*content), 42); } TEST_CASE("common.versionTypesDoNotWrapOrMixDomains") { const eve::SchemaVersion schemaVersion(7); const eve::Generation generation(7); CHECK_EQ(schemaVersion.value(), 7u); CHECK_EQ(generation.value(), 7u); CHECK(schemaVersion != eve::SchemaVersion(8)); CHECK(generation < eve::Generation(8)); const auto next = schemaVersion.incremented(); REQUIRE(next.has_value()); CHECK_EQ(next->value(), 8u); const eve::Revision maximum(std::numeric_limits::max()); CHECK(!maximum.incremented().has_value()); const eve::EventSequence sequence(3); const eve::FrameIndex frame(4); CHECK_EQ(sequence.value(), 3u); CHECK_EQ(frame.value(), 4u); std::unordered_map frames; frames.emplace(eve::SimulationTick(12), eve::FrameIndex(24)); CHECK_EQ(frames.at(eve::SimulationTick(12)).value(), 24u); }