diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 9fa887bb..25b22ca3 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -297,3 +297,10 @@ target_compile_definitions(test_visual PRIVATE "FN_TEST_REPO_ROOT=\"${CMAKE_SOURCE_DIR}/..\"") # Asegura que primitives_gallery existe antes de correr el test. add_dependencies(test_visual primitives_gallery) + +# --- Issue 0117 — dod_evidence_panel helpers: logica pura para panel DoD ---- +# Solo helpers (count_status, find_evidence, status_icon_id, status_color_token). +# El render con ImGui (dod_evidence_panel.cpp) NO se compila aqui: requiere +# imgui + tokens + icons_tabler — cubierto en builds de apps consumidoras. +add_fn_test(test_dod_evidence_panel test_dod_evidence_panel.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../functions/viz/dod_evidence_panel_helpers.cpp) diff --git a/cpp/tests/test_dod_evidence_panel.cpp b/cpp/tests/test_dod_evidence_panel.cpp new file mode 100644 index 00000000..900f58f1 --- /dev/null +++ b/cpp/tests/test_dod_evidence_panel.cpp @@ -0,0 +1,119 @@ +// Tests para la logica pura de dod_evidence_panel (issue 0117). +// +// El render real con ImGui no se testea aqui (requiere context). Cubrimos: +// - count_status: tally por status + missing_required. +// - find_evidence: lookup por item_id. +// - status_icon_id: mapeo a icon key. +// - status_color_token: mapeo a token semantico. + +#define CATCH_CONFIG_MAIN +#include "catch_amalgamated.hpp" + +#include "viz/dod_evidence_panel_helpers.h" + +using namespace fn_viz; +using namespace fn_viz::dod_panel; + +namespace { + +DodItem mk_item(std::string id, std::string kind, std::string status, bool required = true) { + DodItem it; + it.id = std::move(id); + it.kind = std::move(kind); + it.status = std::move(status); + it.required = required; + it.expected = "expected_" + it.id; + return it; +} + +DodEvidence mk_evidence(std::string item_id, std::string kind = "screenshot") { + DodEvidence ev; + ev.item_id = std::move(item_id); + ev.kind = std::move(kind); + ev.payload_path = "/tmp/" + ev.item_id + ".png"; + return ev; +} + +} // namespace + +TEST_CASE("count_status: total = items.size", "[dod_panel]") { + DodPanelState s; + s.items.push_back(mk_item("a", "screenshot", "pending")); + s.items.push_back(mk_item("b", "log", "done")); + s.items.push_back(mk_item("c", "url", "validated")); + s.items.push_back(mk_item("d", "cmd", "failed")); + + auto c = count_status(s); + REQUIRE(c.total == 4); + REQUIRE(c.pending == 1); + REQUIRE(c.done == 1); + REQUIRE(c.validated == 1); + REQUIRE(c.failed == 1); +} + +TEST_CASE("count_status: unknown status counts as pending", "[dod_panel]") { + DodPanelState s; + s.items.push_back(mk_item("a", "log", "weird")); + s.items.push_back(mk_item("b", "log", "pending")); + + auto c = count_status(s); + REQUIRE(c.total == 2); + REQUIRE(c.pending == 2); +} + +TEST_CASE("count_status: missing_required only when required+no evidence+unresolved", "[dod_panel]") { + DodPanelState s; + // required + no evidence + pending -> missing + s.items.push_back(mk_item("missing_one", "screenshot", "pending", /*required*/ true)); + // required + no evidence + done -> resolved, no missing + s.items.push_back(mk_item("done_no_ev", "log", "done", true)); + // required + evidence + pending -> no missing + s.items.push_back(mk_item("has_ev", "url", "pending", true)); + s.evidences.push_back(mk_evidence("has_ev", "url")); + // optional + no evidence + pending -> no missing + s.items.push_back(mk_item("optional", "log", "pending", /*required*/ false)); + + auto c = count_status(s); + REQUIRE(c.missing_required == 1); +} + +TEST_CASE("find_evidence: returns matching evidence", "[dod_panel]") { + DodPanelState s; + s.items.push_back(mk_item("a", "log", "done")); + s.evidences.push_back(mk_evidence("a", "log")); + s.evidences.push_back(mk_evidence("b", "screenshot")); + + const DodEvidence* a = find_evidence(s, "a"); + REQUIRE(a != nullptr); + REQUIRE(a->item_id == "a"); + REQUIRE(a->kind == "log"); + + const DodEvidence* b = find_evidence(s, "b"); + REQUIRE(b != nullptr); + REQUIRE(b->kind == "screenshot"); + + REQUIRE(find_evidence(s, "missing") == nullptr); +} + +TEST_CASE("find_evidence: empty state returns nullptr", "[dod_panel]") { + DodPanelState s; + REQUIRE(find_evidence(s, "anything") == nullptr); +} + +TEST_CASE("status_icon_id: mapping per status", "[dod_panel]") { + REQUIRE(status_icon_id("pending") == "circle-dashed"); + REQUIRE(status_icon_id("done") == "circle-dot"); + REQUIRE(status_icon_id("validated") == "circle-check"); + REQUIRE(status_icon_id("failed") == "circle-x"); + REQUIRE(status_icon_id("") == "circle-dashed"); + REQUIRE(status_icon_id("garbage") == "circle-dashed"); +} + +TEST_CASE("status_color_token: mapping per status", "[dod_panel]") { + REQUIRE(status_color_token("pending") == 0); + REQUIRE(status_color_token("done") == 1); + REQUIRE(status_color_token("validated") == 2); + REQUIRE(status_color_token("failed") == 3); + REQUIRE(status_color_token("") == 0); + REQUIRE(status_color_token("garbage") == 0); +}