Files
HaloKeymind/test/test_topic_template/test_topic_template.cpp
T
agessaman cbbd01b620 feat(docs): add local testing instructions for MQTT functionality
Include detailed instructions for local testing of observer and WiFi
functionality without hardware. Document the use of a mock backend and
Wokwi ESP32-S3 simulation for easier development and testing.
Enhance the MQTT implementation documentation to improve developer
experience and facilitate testing workflows.
2026-07-18 13:19:27 -07:00

102 lines
3.6 KiB
C++

// Host tests for the MQTT custom-topic placeholder expansion
// (src/helpers/MQTTTopicTemplate.h), the pure core of
// MQTTBridge::substituteTopicTemplate.
#include <gtest/gtest.h>
#include <cstring>
#include "helpers/MQTTTopicTemplate.h"
static const char* IATA = "DEN";
static const char* DEV = "abcdef0123456789";
static const char* TOK = "tok123";
TEST(TopicTemplate, SubstitutesAllPlaceholders) {
char buf[128];
ASSERT_TRUE(mqttSubstituteTopic("meshcore/{iata}/{device}/{type}", IATA, DEV, TOK, "status",
buf, sizeof(buf)));
EXPECT_STREQ("meshcore/DEN/abcdef0123456789/status", buf);
}
TEST(TopicTemplate, TokenPlaceholder) {
char buf[128];
ASSERT_TRUE(mqttSubstituteTopic("meshrank/uplink/{token}/{device}/packets",
IATA, DEV, TOK, "packets", buf, sizeof(buf)));
EXPECT_STREQ("meshrank/uplink/tok123/abcdef0123456789/packets", buf);
}
TEST(TopicTemplate, RepeatedPlaceholder) {
char buf[64];
ASSERT_TRUE(mqttSubstituteTopic("{iata}-{iata}", IATA, DEV, TOK, "raw", buf, sizeof(buf)));
EXPECT_STREQ("DEN-DEN", buf);
}
TEST(TopicTemplate, LiteralWithNoPlaceholders) {
char buf[64];
ASSERT_TRUE(mqttSubstituteTopic("plain/topic/path", IATA, DEV, TOK, "status", buf, sizeof(buf)));
EXPECT_STREQ("plain/topic/path", buf);
}
TEST(TopicTemplate, UnknownBracesCopiedVerbatim) {
char buf[64];
ASSERT_TRUE(mqttSubstituteTopic("a/{bogus}/{iata}", IATA, DEV, TOK, "status", buf, sizeof(buf)));
EXPECT_STREQ("a/{bogus}/DEN", buf);
}
TEST(TopicTemplate, TypeStringVaries) {
char buf[64];
mqttSubstituteTopic("{type}", IATA, DEV, TOK, "status", buf, sizeof(buf));
EXPECT_STREQ("status", buf);
mqttSubstituteTopic("{type}", IATA, DEV, TOK, "packets", buf, sizeof(buf));
EXPECT_STREQ("packets", buf);
mqttSubstituteTopic("{type}", IATA, DEV, TOK, "raw", buf, sizeof(buf));
EXPECT_STREQ("raw", buf);
}
TEST(TopicTemplate, NullValuesSubstituteEmpty) {
char buf[64];
ASSERT_TRUE(mqttSubstituteTopic("x/{token}/y", IATA, DEV, nullptr, "status", buf, sizeof(buf)));
EXPECT_STREQ("x//y", buf);
}
TEST(TopicTemplate, OverflowReturnsFalseNoWrite) {
// Substituting {device} (16 chars) into a template won't fit an 8-byte buffer.
char buf[8];
EXPECT_FALSE(mqttSubstituteTopic("{device}", IATA, DEV, TOK, "status", buf, sizeof(buf)));
}
TEST(TopicTemplate, LiteralOverflowTruncatesAndNulTerminates) {
char buf[5];
// Literal longer than the buffer: fills up to buf_size-1 and NUL-terminates.
mqttSubstituteTopic("abcdefghij", IATA, DEV, TOK, "status", buf, sizeof(buf));
EXPECT_EQ('\0', buf[4]);
EXPECT_EQ((size_t)4, strlen(buf));
}
TEST(TopicTemplate, AlwaysNulTerminatedAndBounded) {
// Fuzz-ish: many buffer sizes never overrun and always NUL-terminate.
const char* tmpl = "meshcore/{iata}/{device}/{token}/{type}/tail";
for (size_t sz = 1; sz <= 80; sz++) {
char buf[96];
memset(buf, 0x7f, sizeof(buf));
mqttSubstituteTopic(tmpl, IATA, DEV, TOK, "packets", buf, sz);
EXPECT_LT(strlen(buf), sz) << "size " << sz; // fits with room for NUL
EXPECT_EQ('\0', buf[strlen(buf)]);
}
}
TEST(TopicTemplate, ZeroBufferOrNullFails) {
char buf[8];
EXPECT_FALSE(mqttSubstituteTopic("x", IATA, DEV, TOK, "status", buf, 0));
EXPECT_FALSE(mqttSubstituteTopic("x", IATA, DEV, TOK, "status", nullptr, 8));
}
TEST(TopicTemplate, EmptyTemplateReturnsFalse) {
char buf[8];
EXPECT_FALSE(mqttSubstituteTopic("", IATA, DEV, TOK, "status", buf, sizeof(buf)));
EXPECT_STREQ("", buf);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}