mirror of
https://github.com/sebastiandine/Card-Collection-Manager-3.git
synced 2026-08-28 22:01:12 +00:00
55ace147bc
* 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>
153 lines
4.8 KiB
C++
153 lines
4.8 KiB
C++
#include <doctest/doctest.h>
|
|
|
|
#include "ccm/games/IGameModule.hpp"
|
|
#include "ccm/ports/ICardPreviewSource.hpp"
|
|
#include "ccm/ports/IHttpClient.hpp"
|
|
#include "ccm/services/CardPreviewService.hpp"
|
|
|
|
#include <string>
|
|
|
|
using namespace ccm;
|
|
|
|
namespace {
|
|
|
|
class FakeSource final : public ICardPreviewSource {
|
|
public:
|
|
std::string url = "https://example.com/preview.jpg";
|
|
bool ok = true;
|
|
std::string err = "boom";
|
|
|
|
// Capture inputs so tests can assert routing.
|
|
std::string lastName;
|
|
std::string lastSetId;
|
|
std::string lastSetNo;
|
|
|
|
Result<std::string> fetchImageUrl(std::string_view name,
|
|
std::string_view setId,
|
|
std::string_view setNo) override {
|
|
lastName = std::string(name);
|
|
lastSetId = std::string(setId);
|
|
lastSetNo = std::string(setNo);
|
|
return ok ? Result<std::string>::ok(url)
|
|
: Result<std::string>::err(err);
|
|
}
|
|
};
|
|
|
|
class FixedHttpClient final : public IHttpClient {
|
|
public:
|
|
std::string lastUrl;
|
|
std::string body;
|
|
bool ok = true;
|
|
std::string err = "offline";
|
|
|
|
Result<std::string> get(std::string_view url) override {
|
|
lastUrl = std::string(url);
|
|
return ok ? Result<std::string>::ok(body)
|
|
: Result<std::string>::err(err);
|
|
}
|
|
};
|
|
|
|
// Minimal IGameModule fake that exposes a configurable preview source.
|
|
class FakeGameModule final : public IGameModule {
|
|
public:
|
|
Game gameId = Game::Magic;
|
|
ICardPreviewSource* preview = nullptr;
|
|
|
|
[[nodiscard]] Game id() const noexcept override { return gameId; }
|
|
[[nodiscard]] std::string dirName() const override { return "fake"; }
|
|
[[nodiscard]] std::string displayName() const override { return "Fake"; }
|
|
|
|
ISetSource& setSource() override {
|
|
// Tests in this file never call setSource(); a never-returned helper
|
|
// would clutter the fake. Throwing keeps misuse loud.
|
|
throw std::logic_error("FakeGameModule::setSource() not used in this test");
|
|
}
|
|
ICardPreviewSource* cardPreviewSource() noexcept override { return preview; }
|
|
};
|
|
|
|
} // namespace
|
|
|
|
TEST_SUITE("CardPreviewService::fetchPreviewBytes") {
|
|
TEST_CASE("happy path: routes through registered module then http GET") {
|
|
FakeSource source;
|
|
source.url = "https://example.com/img.png";
|
|
FakeGameModule module;
|
|
module.gameId = Game::Magic;
|
|
module.preview = &source;
|
|
|
|
FixedHttpClient http;
|
|
http.body = std::string("\x89PNG\r\n\x1a\n", 8); // arbitrary binary
|
|
|
|
CardPreviewService svc{http};
|
|
svc.registerModule(module);
|
|
|
|
const auto out = svc.fetchPreviewBytes(Game::Magic, "Lightning Bolt", "lea", "");
|
|
REQUIRE(out.isOk());
|
|
CHECK(out.value() == http.body);
|
|
CHECK(http.lastUrl == "https://example.com/img.png");
|
|
CHECK(source.lastName == "Lightning Bolt");
|
|
CHECK(source.lastSetId == "lea");
|
|
CHECK(source.lastSetNo.empty());
|
|
}
|
|
|
|
TEST_CASE("module returning nullptr preview source is skipped silently") {
|
|
FakeGameModule module;
|
|
module.gameId = Game::Pokemon;
|
|
module.preview = nullptr;
|
|
|
|
FixedHttpClient http;
|
|
CardPreviewService svc{http};
|
|
svc.registerModule(module); // no-op
|
|
|
|
const auto out = svc.fetchPreviewBytes(Game::Pokemon, "Pikachu", "sv3", "1");
|
|
CHECK(out.isErr());
|
|
CHECK(out.error().find("No preview source registered") != std::string::npos);
|
|
}
|
|
|
|
TEST_CASE("unregistered game returns an explicit error") {
|
|
FixedHttpClient http;
|
|
CardPreviewService svc{http};
|
|
const auto out = svc.fetchPreviewBytes(Game::Pokemon, "Pikachu", "sv3", "1");
|
|
CHECK(out.isErr());
|
|
CHECK(out.error().find("No preview source registered") != std::string::npos);
|
|
}
|
|
|
|
TEST_CASE("source error propagates and http is not called") {
|
|
FakeSource source;
|
|
source.ok = false;
|
|
source.err = "scryfall 404";
|
|
FakeGameModule module;
|
|
module.gameId = Game::Magic;
|
|
module.preview = &source;
|
|
|
|
FixedHttpClient http;
|
|
http.lastUrl = "<unset>";
|
|
|
|
CardPreviewService svc{http};
|
|
svc.registerModule(module);
|
|
|
|
const auto out = svc.fetchPreviewBytes(Game::Magic, "X", "abc", "");
|
|
CHECK(out.isErr());
|
|
CHECK(out.error() == "scryfall 404");
|
|
CHECK(http.lastUrl == "<unset>");
|
|
}
|
|
|
|
TEST_CASE("http GET error propagates") {
|
|
FakeSource source;
|
|
FakeGameModule module;
|
|
module.gameId = Game::Magic;
|
|
module.preview = &source;
|
|
|
|
FixedHttpClient http;
|
|
http.ok = false;
|
|
http.err = "net down";
|
|
|
|
CardPreviewService svc{http};
|
|
svc.registerModule(module);
|
|
|
|
const auto out = svc.fetchPreviewBytes(Game::Magic, "X", "abc", "");
|
|
CHECK(out.isErr());
|
|
CHECK(out.error() == "net down");
|
|
}
|
|
}
|