Files
Card-Collection-Manager-3-s…/tests/set_service_tests.cpp
T
Sebastian Dine 55ace147bc major: initial release
* initial development

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* pipeline

* ci/cd

* ci/cd

* ci/cd

* ci/cd

* ci/cd

* ci/cd

* ci/cd

* pokemon

* pokemon

* pokemon

* pokemon

* pokemon

* pokemon

* improvements

* improvements

* ci/cd

* ci/cd

* improvements

* improvements

* improvements

* improvements

* improvements

* improvements

* improvements

* improvements

* improvements

---------

Co-authored-by: sdine <sdine@sdine.com>
2026-05-09 11:05:47 +02:00

120 lines
3.7 KiB
C++

#include <doctest/doctest.h>
#include "ccm/games/IGameModule.hpp"
#include "ccm/ports/ISetRepository.hpp"
#include "ccm/services/SetService.hpp"
#include <vector>
using namespace ccm;
namespace {
class FakeSetSource final : public ISetSource {
public:
Result<std::vector<Set>> result = Result<std::vector<Set>>::err("not set");
int calls = 0;
Result<std::vector<Set>> fetchAll() override {
++calls;
return result;
}
};
class FakeGameModule final : public IGameModule {
public:
FakeSetSource source;
Game gameId;
explicit FakeGameModule(Game id) : gameId(id) {}
Game id() const noexcept override { return gameId; }
std::string dirName() const override { return gameId == Game::Magic ? "magic" : "pokemon"; }
std::string displayName() const override { return dirName(); }
ISetSource& setSource() override { return source; }
};
class InMemSetRepo final : public ISetRepository {
public:
std::vector<Set> stored;
bool hasStored = false;
Result<std::vector<Set>> load(Game) override {
if (!hasStored) return Result<std::vector<Set>>::err("no cache");
return Result<std::vector<Set>>::ok(stored);
}
Result<void> save(Game, const std::vector<Set>& s) override {
stored = s;
hasStored = true;
return Result<void>::ok();
}
};
} // namespace
TEST_SUITE("SetService") {
TEST_CASE("updateSets fetches via the registered module and persists the result") {
InMemSetRepo repo;
SetService svc{repo};
FakeGameModule magic{Game::Magic};
magic.source.result = Result<std::vector<Set>>::ok({
{"lea", "Alpha", "1993/08/05"},
});
svc.registerModule(&magic);
const auto out = svc.updateSets(Game::Magic);
REQUIRE(out.isOk());
CHECK(out.value().size() == 1);
CHECK(magic.source.calls == 1);
// Cache is now warm.
const auto cached = svc.getSets(Game::Magic);
REQUIRE(cached.isOk());
CHECK(cached.value() == out.value());
}
TEST_CASE("updateSets fails cleanly for unregistered games") {
InMemSetRepo repo;
SetService svc{repo};
const auto out = svc.updateSets(Game::Pokemon);
CHECK(out.isErr());
}
TEST_CASE("propagates upstream fetch errors without persisting") {
InMemSetRepo repo;
SetService svc{repo};
FakeGameModule pokemon{Game::Pokemon};
pokemon.source.result = Result<std::vector<Set>>::err("upstream is down");
svc.registerModule(&pokemon);
const auto out = svc.updateSets(Game::Pokemon);
CHECK(out.isErr());
CHECK_FALSE(repo.hasStored);
}
TEST_CASE("Pokemon module is routed independently of Magic") {
// Both modules registered; updating Pokemon must hit the Pokemon
// source and persist under the Pokemon Game key without disturbing
// a previously cached Magic list.
InMemSetRepo repo;
SetService svc{repo};
FakeGameModule magic{Game::Magic};
magic.source.result = Result<std::vector<Set>>::ok({
{"lea", "Alpha", "1993/08/05"},
});
FakeGameModule pokemon{Game::Pokemon};
pokemon.source.result = Result<std::vector<Set>>::ok({
{"base1", "Base", "1999/01/09"},
});
svc.registerModule(&magic);
svc.registerModule(&pokemon);
REQUIRE(svc.updateSets(Game::Magic).isOk());
const auto poke = svc.updateSets(Game::Pokemon);
REQUIRE(poke.isOk());
REQUIRE(poke.value().size() == 1);
CHECK(poke.value().front().id == "base1");
CHECK(magic.source.calls == 1);
CHECK(pokemon.source.calls == 1);
}
}