fwt_software/tests/test_channelhistory.cpp

86 lines
3.0 KiB
C++

#include <doctest/doctest.h>
#include "fgc/ChannelHistory.h"
#include "fgc/ChannelTopics.h"
#include <string>
using namespace fgc;
TEST_CASE("ChannelHistory counts each delivery outcome separately") {
ChannelHistory h;
h.record(true, Delivery::Ok, "t/a", "1");
h.record(true, Delivery::Ok, "t/a", "2");
h.record(true, Delivery::Dropped, "t/a", "3");
h.record(true, Delivery::Failed, "t/a", "4");
h.record(false, Delivery::Ok, "t/b", "in");
ChannelStats s = h.stats();
CHECK(s.published_ok == 2);
CHECK(s.published_dropped == 1); // the silent-drop path: the number that matters
CHECK(s.published_failed == 1);
CHECK(s.received == 1); // inbound never touches the publish counters
}
TEST_CASE("ChannelHistory returns messages oldest first and keeps their fields") {
ChannelHistory h;
h.record(true, Delivery::Dropped, "t/first", "a");
h.record(false, Delivery::Ok, "t/second", "b");
auto r = h.recent();
REQUIRE(r.size() == 2);
CHECK(r[0].topic == "t/first");
CHECK(r[0].outbound);
CHECK(r[0].delivery == Delivery::Dropped);
CHECK(r[0].payload == "a");
CHECK(r[1].topic == "t/second");
CHECK_FALSE(r[1].outbound);
CHECK(r[1].timestamp_ms > 0);
}
TEST_CASE("ChannelHistory evicts the oldest beyond its capacity") {
ChannelHistory h;
const int n = static_cast<int>(ChannelHistory::kCapacity) + 10;
for (int i = 0; i < n; ++i) h.record(true, Delivery::Ok, "t", std::to_string(i));
auto r = h.recent();
REQUIRE(r.size() == ChannelHistory::kCapacity);
CHECK(r.front().payload == std::to_string(n - static_cast<int>(ChannelHistory::kCapacity)));
CHECK(r.back().payload == std::to_string(n - 1));
// Counters are cumulative; they must not be bounded by the ring.
CHECK(h.stats().published_ok == n);
}
TEST_CASE("ChannelHistory tracks connects, losses and the last status sent") {
ChannelHistory h;
CHECK(h.stats().connects == 0);
CHECK(h.stats().last_connect_ms == 0);
h.noteConnect();
h.noteLoss();
h.noteConnect();
h.noteStatus("1");
ChannelStats s = h.stats();
CHECK(s.connects == 2);
CHECK(s.last_connect_ms > 0);
CHECK(s.last_loss_ms > 0);
CHECK(s.last_status_payload == "1");
}
TEST_CASE("topic and payload builders match the documented wire format") {
CHECK(towerTopic("Tower", kTopicCamEvent) == "GGS/FWT/Tower/CamEvent");
CamEvent c;
c.tower = "Tower"; c.camera = "RGB";
c.heading_decideg = 1373; c.pitch_decideg = 300; c.timestamp_ms = 1719312345678LL;
const std::string j = camEventJson(c);
CHECK(j.find("\"fwt\":\"Tower\"") != std::string::npos);
CHECK(j.find("\"cam\":\"RGB\"") != std::string::npos);
CHECK(j.find("\"hdg\":1373") != std::string::npos);
CHECK(j.find("\"pit\":300") != std::string::npos);
CHECK(j.find("\"time\":1719312345678") != std::string::npos);
// The quality gate is gone; nothing should still be publishing a verdict.
CHECK(j.find("degraded") == std::string::npos);
}