Add WebConfig CLI and standalone WiFi controls
This commit is contained in:
1 parent
7bbde76380
commit
3f5e497ca6
24 files changed
+2698
-1224
No files matched your search
+1
-1
@@ -25,7 +25,7 @@ does not reflect the GoogleTest count -- run the built binary directly
|
||||
|-------|-------------------|--------|
|
||||
| `test_mqtt_presets` | `src/helpers/MQTTPresets.h` | preset lookup; table integrity (unique names, non-empty URLs, JWT-audience invariant, names fit the slot buffer); `mqttPresetNeedsSlotCredentials`; slot-count constants |
|
||||
| `test_observer_validation` | `src/helpers/MQTTObserverValidation.h` | IATA (exactly 3 alphanumerics), owner key (64 hex), NTP hostname, and the buffer-fit check behind the #17 length validation -- including boundaries and nulls |
|
||||
| `test_webconfig_keys` | `src/helpers/WebConfigKeys.h` | POST-key allowlist, secret detection, admin-password classification/validation, slot-index bounds, and the short-key out-of-bounds guard (attacker-supplied keys) |
|
||||
| `test_webconfig_keys` | `src/helpers/WebConfigKeys.h` | POST-key allowlist, secret detection, admin-password and browser-terminal command validation, slot-index bounds, and short-input guards |
|
||||
| `test_topic_template` | `src/helpers/MQTTTopicTemplate.h` | `{iata}/{device}/{token}/{type}` expansion, overflow/NUL-termination, and a buffer-size fuzz |
|
||||
| `test_mqtt_topic_router` | `src/helpers/MQTTTopicRouter.h` | complete preset/custom topic-routing contract; MeshRank packets-only behavior; required identifiers; invalid inputs/slots; exact buffer boundaries |
|
||||
| `test_mqtt_connection_policy` | `src/helpers/MQTTConnectionPolicy.h` | reconnect guard/backoff/stagger and breaker transitions; stable reset; JWT lifetime/renewal policy; exact timing boundaries and 32-bit `millis()` rollover |
|
||||
|
||||
@@ -65,6 +65,101 @@ TEST(CLICommandUtils, RejectsNonDecimalOrOverflowValues) {
|
||||
EXPECT_FLOAT_EQ(123.0f, value);
|
||||
}
|
||||
|
||||
TEST(CLICommandUtils, ClassifiesStandaloneWiFiGetKeysExactly) {
|
||||
using mesh::cli::StandaloneWiFiKey;
|
||||
|
||||
EXPECT_EQ(StandaloneWiFiKey::SSID,
|
||||
mesh::cli::classifyStandaloneWiFiGet("wifi.ssid"));
|
||||
EXPECT_EQ(StandaloneWiFiKey::Status,
|
||||
mesh::cli::classifyStandaloneWiFiGet("wifi.status"));
|
||||
EXPECT_EQ(StandaloneWiFiKey::PowerSave,
|
||||
mesh::cli::classifyStandaloneWiFiGet("wifi.powersave"));
|
||||
EXPECT_EQ(StandaloneWiFiKey::CLI,
|
||||
mesh::cli::classifyStandaloneWiFiGet("wifi.cli"));
|
||||
EXPECT_EQ(StandaloneWiFiKey::None,
|
||||
mesh::cli::classifyStandaloneWiFiGet("wifi.pwd"));
|
||||
EXPECT_EQ(StandaloneWiFiKey::None,
|
||||
mesh::cli::classifyStandaloneWiFiGet("wifi.status.extra"));
|
||||
}
|
||||
|
||||
TEST(CLICommandUtils, ClassifiesStandaloneWiFiSetKeysAndPreservesValues) {
|
||||
using mesh::cli::StandaloneWiFiKey;
|
||||
const char* value = nullptr;
|
||||
|
||||
EXPECT_EQ(StandaloneWiFiKey::SSID,
|
||||
mesh::cli::classifyStandaloneWiFiSet(
|
||||
"wifi.ssid Slow Fi", &value));
|
||||
EXPECT_STREQ("Slow Fi", value);
|
||||
|
||||
EXPECT_EQ(StandaloneWiFiKey::Password,
|
||||
mesh::cli::classifyStandaloneWiFiSet("wifi.pwd", &value));
|
||||
EXPECT_STREQ("", value);
|
||||
|
||||
EXPECT_EQ(StandaloneWiFiKey::PowerSave,
|
||||
mesh::cli::classifyStandaloneWiFiSet(
|
||||
"wifi.powersave max", &value));
|
||||
EXPECT_STREQ("max", value);
|
||||
|
||||
EXPECT_EQ(StandaloneWiFiKey::CLI,
|
||||
mesh::cli::classifyStandaloneWiFiSet("wifi.cli on", &value));
|
||||
EXPECT_STREQ("on", value);
|
||||
|
||||
EXPECT_EQ(StandaloneWiFiKey::None,
|
||||
mesh::cli::classifyStandaloneWiFiSet(
|
||||
"wifi.powersaver max", &value));
|
||||
EXPECT_EQ(nullptr, value);
|
||||
}
|
||||
|
||||
TEST(CLICommandUtils, ValidatesStandaloneWiFiValues) {
|
||||
EXPECT_TRUE(mesh::cli::standaloneWiFiSSIDValid("SlowFi"));
|
||||
EXPECT_TRUE(mesh::cli::standaloneWiFiSSIDValid(
|
||||
"1234567890123456789012345678901"));
|
||||
EXPECT_FALSE(mesh::cli::standaloneWiFiSSIDValid(""));
|
||||
EXPECT_FALSE(mesh::cli::standaloneWiFiSSIDValid(
|
||||
"12345678901234567890123456789012"));
|
||||
|
||||
EXPECT_TRUE(mesh::cli::standaloneWiFiPasswordValid(""));
|
||||
EXPECT_TRUE(mesh::cli::standaloneWiFiPasswordValid(
|
||||
"123456789012345678901234567890123456789012345678901234567890123"));
|
||||
EXPECT_FALSE(mesh::cli::standaloneWiFiPasswordValid(
|
||||
"1234567890123456789012345678901234567890123456789012345678901234"));
|
||||
|
||||
uint8_t power_save = 99;
|
||||
EXPECT_TRUE(mesh::cli::parseStandaloneWiFiPowerSave("min", power_save));
|
||||
EXPECT_EQ(0, power_save);
|
||||
EXPECT_TRUE(mesh::cli::parseStandaloneWiFiPowerSave("none", power_save));
|
||||
EXPECT_EQ(1, power_save);
|
||||
EXPECT_TRUE(mesh::cli::parseStandaloneWiFiPowerSave("max", power_save));
|
||||
EXPECT_EQ(2, power_save);
|
||||
EXPECT_FALSE(mesh::cli::parseStandaloneWiFiPowerSave("off", power_save));
|
||||
}
|
||||
|
||||
TEST(CLICommandUtils, MatchesDiscoverNeighborsWithoutPrefixCollisions) {
|
||||
using mesh::cli::NoArgCommandMatch;
|
||||
|
||||
EXPECT_EQ(NoArgCommandMatch::Exact,
|
||||
mesh::cli::matchNoArgCommand(
|
||||
"discover.neighbors", "discover.neighbors"));
|
||||
EXPECT_EQ(NoArgCommandMatch::Exact,
|
||||
mesh::cli::matchNoArgCommand(
|
||||
"discover.neighbors ", "discover.neighbors"));
|
||||
EXPECT_EQ(NoArgCommandMatch::HasArguments,
|
||||
mesh::cli::matchNoArgCommand(
|
||||
"discover.neighbors now", "discover.neighbors"));
|
||||
EXPECT_EQ(NoArgCommandMatch::NoMatch,
|
||||
mesh::cli::matchNoArgCommand(
|
||||
"discover.neighbors.extra", "discover.neighbors"));
|
||||
}
|
||||
|
||||
TEST(CLICommandUtils, FormatsUsefulUnknownSettingErrors) {
|
||||
char reply[64] = "";
|
||||
|
||||
mesh::cli::formatUnknownSetting(reply, sizeof(reply), "wifi.typo");
|
||||
|
||||
EXPECT_STREQ("Error: unknown setting: wifi.typo", reply);
|
||||
EXPECT_EQ(nullptr, strstr(reply, "??:"));
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
|
||||
@@ -134,6 +134,32 @@ TEST(WebConfigKeys, RejectsMissingMalformedOrWrongLengthRequestIds) {
|
||||
EXPECT_FALSE(wcIsValidReqId("01234567 9abcdef"));
|
||||
}
|
||||
|
||||
// ---- browser terminal commands -------------------------------------------
|
||||
|
||||
TEST(WebConfigKeys, AcceptsSingleLineCliCommands) {
|
||||
EXPECT_TRUE(wcIsValidCliCommand("get wifi.status"));
|
||||
EXPECT_TRUE(wcIsValidCliCommand("set flood.max 12"));
|
||||
EXPECT_TRUE(wcIsValidCliCommand(" neighbors "));
|
||||
}
|
||||
|
||||
TEST(WebConfigKeys, RejectsBlankMultilineOrOversizedCliCommands) {
|
||||
EXPECT_FALSE(wcIsValidCliCommand(NULL));
|
||||
EXPECT_FALSE(wcIsValidCliCommand(""));
|
||||
EXPECT_FALSE(wcIsValidCliCommand(" \t"));
|
||||
EXPECT_FALSE(wcIsValidCliCommand("get role\nreboot"));
|
||||
EXPECT_FALSE(wcIsValidCliCommand("get role\rreboot"));
|
||||
|
||||
char max_command[160];
|
||||
memset(max_command, 'x', sizeof(max_command) - 1);
|
||||
max_command[sizeof(max_command) - 1] = 0;
|
||||
EXPECT_TRUE(wcIsValidCliCommand(max_command));
|
||||
|
||||
char oversized[161];
|
||||
memset(oversized, 'x', sizeof(oversized) - 1);
|
||||
oversized[sizeof(oversized) - 1] = 0;
|
||||
EXPECT_FALSE(wcIsValidCliCommand(oversized));
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <helpers/WiFiOtaSeederPolicy.h>
|
||||
#include <helpers/WiFiOtaSeederStatus.h>
|
||||
|
||||
namespace Policy = WiFiOtaSeederPolicy;
|
||||
|
||||
@@ -24,6 +25,34 @@ TEST(WiFiOtaSeederPolicy, DetectsExternalDetachOfTcpFolder) {
|
||||
EXPECT_TRUE(Policy::tcpFolderWasDetached(true, false));
|
||||
}
|
||||
|
||||
TEST(WiFiOtaSeederStatus, OmitsInactiveListener) {
|
||||
char reply[64] = "> connected";
|
||||
EXPECT_FALSE(WiFiOtaSeederStatus::append(
|
||||
reply, sizeof(reply), false, false, 5001));
|
||||
EXPECT_STREQ("> connected", reply);
|
||||
}
|
||||
|
||||
TEST(WiFiOtaSeederStatus, ReportsListeningPort) {
|
||||
char reply[64] = "> connected";
|
||||
EXPECT_TRUE(WiFiOtaSeederStatus::append(
|
||||
reply, sizeof(reply), true, false, 5001));
|
||||
EXPECT_STREQ("> connected, OTA TCP 5001: listening", reply);
|
||||
}
|
||||
|
||||
TEST(WiFiOtaSeederStatus, ReportsAttachedClient) {
|
||||
char reply[64] = "> connected";
|
||||
EXPECT_TRUE(WiFiOtaSeederStatus::append(
|
||||
reply, sizeof(reply), true, true, 5001));
|
||||
EXPECT_STREQ("> connected, OTA TCP 5001: client connected", reply);
|
||||
}
|
||||
|
||||
TEST(WiFiOtaSeederStatus, TruncatesSafelyWhenReplyIsFull) {
|
||||
char reply[20] = "> connected";
|
||||
EXPECT_FALSE(WiFiOtaSeederStatus::append(
|
||||
reply, sizeof(reply), true, false, 5001));
|
||||
EXPECT_EQ('\0', reply[sizeof(reply) - 1]);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
|
||||
Reference in new issue
Block a user