Tau.Acuvim/firmware/test/test_config/test_config.cpp
Renier Forster 84a0668c54 Initial commit: Tau Acuvim IoT monitoring system
Complete IoT monitoring platform for Acuvim II power meters via ESP32.

Firmware (Phases 1-7):
- ESP32-WROVER-B (TTGO T-Call v1.4) with RS485 Modbus RTU
- WiFi STA+AP concurrent mode with GSM/GPRS failover
- Transport abstraction layer with 4 priority modes
- MQTT protocol with 20 commands, LWT, QoS, exponential backoff
- SD card offline buffering with JSONL rotation and non-blocking drain
- OTA firmware updates with dual partition rollback protection
- Watchdog timer, crash loop detection, Acuvim health monitoring
- Captive portal provisioning with AP mode

Console backend (Phase 8):
- .NET 10 minimal API with PostgreSQL + EF Core
- JWT authentication, SignalR real-time updates
- MQTTnet 5.x bridge service with health monitoring
- Device, telemetry, firmware, alert, group management
- Rate limiting, security headers, Swagger/OpenAPI

Frontend (Phase 9):
- React 18 + TypeScript + Vite with Ant Design 5
- ECharts telemetry visualization, TanStack Query
- SignalR live updates, device management UI
- Dashboard, fleet management, firmware deployment

Testing & Production (Phase 10):
- 28 firmware unit tests (Modbus, JSON, config, version)
- 23 xUnit backend tests (device, telemetry, command, alert)
- Docker Compose with nginx, TLS MQTT, PostgreSQL
- Production deployment, commissioning, and troubleshooting docs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-16 19:05:32 +02:00

163 lines
5.0 KiB
C++

#include <unity.h>
#include <ArduinoJson.h>
struct TestConfig {
char wifi_ssid[33];
char wifi_password[65];
char mqtt_broker[65];
uint16_t mqtt_port;
uint8_t modbus_address;
uint32_t modbus_baud;
uint16_t poll_interval_sec;
uint8_t transport_priority;
};
static bool validateConfig(TestConfig& cfg) {
if (strlen(cfg.wifi_ssid) == 0) return false;
if (cfg.mqtt_port == 0 || cfg.mqtt_port > 65535) return false;
if (cfg.modbus_address == 0 || cfg.modbus_address > 247) return false;
if (cfg.modbus_baud != 9600 && cfg.modbus_baud != 19200 &&
cfg.modbus_baud != 38400 && cfg.modbus_baud != 57600 &&
cfg.modbus_baud != 115200) return false;
if (cfg.poll_interval_sec < 1 || cfg.poll_interval_sec > 3600) return false;
if (cfg.transport_priority > 3) return false;
return true;
}
void test_valid_config() {
TestConfig cfg;
strncpy(cfg.wifi_ssid, "TestNetwork", sizeof(cfg.wifi_ssid));
strncpy(cfg.wifi_password, "password123", sizeof(cfg.wifi_password));
strncpy(cfg.mqtt_broker, "broker.example.com", sizeof(cfg.mqtt_broker));
cfg.mqtt_port = 1883;
cfg.modbus_address = 1;
cfg.modbus_baud = 9600;
cfg.poll_interval_sec = 5;
cfg.transport_priority = 0;
TEST_ASSERT_TRUE(validateConfig(cfg));
}
void test_empty_ssid_invalid() {
TestConfig cfg = {};
cfg.mqtt_port = 1883;
cfg.modbus_address = 1;
cfg.modbus_baud = 9600;
cfg.poll_interval_sec = 5;
TEST_ASSERT_FALSE(validateConfig(cfg));
}
void test_invalid_modbus_address() {
TestConfig cfg;
strncpy(cfg.wifi_ssid, "TestNetwork", sizeof(cfg.wifi_ssid));
cfg.mqtt_port = 1883;
cfg.modbus_address = 0;
cfg.modbus_baud = 9600;
cfg.poll_interval_sec = 5;
cfg.transport_priority = 0;
TEST_ASSERT_FALSE(validateConfig(cfg));
cfg.modbus_address = 248;
TEST_ASSERT_FALSE(validateConfig(cfg));
}
void test_invalid_baud_rate() {
TestConfig cfg;
strncpy(cfg.wifi_ssid, "TestNetwork", sizeof(cfg.wifi_ssid));
cfg.mqtt_port = 1883;
cfg.modbus_address = 1;
cfg.modbus_baud = 12345;
cfg.poll_interval_sec = 5;
cfg.transport_priority = 0;
TEST_ASSERT_FALSE(validateConfig(cfg));
}
void test_poll_interval_bounds() {
TestConfig cfg;
strncpy(cfg.wifi_ssid, "TestNetwork", sizeof(cfg.wifi_ssid));
cfg.mqtt_port = 1883;
cfg.modbus_address = 1;
cfg.modbus_baud = 9600;
cfg.transport_priority = 0;
cfg.poll_interval_sec = 0;
TEST_ASSERT_FALSE(validateConfig(cfg));
cfg.poll_interval_sec = 3601;
TEST_ASSERT_FALSE(validateConfig(cfg));
cfg.poll_interval_sec = 1;
TEST_ASSERT_TRUE(validateConfig(cfg));
cfg.poll_interval_sec = 3600;
TEST_ASSERT_TRUE(validateConfig(cfg));
}
void test_config_from_json() {
const char* json = R"({"wifi":{"ssid":"TestNet","password":"pass123"},"mqtt":{"broker":"mqtt.test","port":8883},"modbus":{"address":5,"baud":19200,"poll_interval":10}})";
JsonDocument doc;
DeserializationError err = deserializeJson(doc, json);
TEST_ASSERT_EQUAL(DeserializationError::Ok, err);
TestConfig cfg = {};
strncpy(cfg.wifi_ssid, doc["wifi"]["ssid"] | "", sizeof(cfg.wifi_ssid));
strncpy(cfg.wifi_password, doc["wifi"]["password"] | "", sizeof(cfg.wifi_password));
strncpy(cfg.mqtt_broker, doc["mqtt"]["broker"] | "", sizeof(cfg.mqtt_broker));
cfg.mqtt_port = doc["mqtt"]["port"] | 1883;
cfg.modbus_address = doc["modbus"]["address"] | 1;
cfg.modbus_baud = doc["modbus"]["baud"] | 9600;
cfg.poll_interval_sec = doc["modbus"]["poll_interval"] | 5;
cfg.transport_priority = 0;
TEST_ASSERT_EQUAL_STRING("TestNet", cfg.wifi_ssid);
TEST_ASSERT_EQUAL(8883, cfg.mqtt_port);
TEST_ASSERT_EQUAL(5, cfg.modbus_address);
TEST_ASSERT_EQUAL(19200, cfg.modbus_baud);
TEST_ASSERT_EQUAL(10, cfg.poll_interval_sec);
TEST_ASSERT_TRUE(validateConfig(cfg));
}
void test_config_to_json() {
TestConfig cfg;
strncpy(cfg.wifi_ssid, "MyNetwork", sizeof(cfg.wifi_ssid));
strncpy(cfg.mqtt_broker, "broker.local", sizeof(cfg.mqtt_broker));
cfg.mqtt_port = 1883;
cfg.modbus_address = 1;
cfg.modbus_baud = 9600;
cfg.poll_interval_sec = 5;
JsonDocument doc;
JsonObject root = doc.to<JsonObject>();
root["wifi"]["ssid"] = cfg.wifi_ssid;
root["mqtt"]["broker"] = cfg.mqtt_broker;
root["mqtt"]["port"] = cfg.mqtt_port;
root["modbus"]["address"] = cfg.modbus_address;
root["modbus"]["baud"] = cfg.modbus_baud;
root["modbus"]["poll_interval"] = cfg.poll_interval_sec;
String output;
serializeJson(doc, output);
TEST_ASSERT_TRUE(output.indexOf("MyNetwork") >= 0);
TEST_ASSERT_TRUE(output.indexOf("1883") >= 0);
}
void setup() {
delay(2000);
UNITY_BEGIN();
RUN_TEST(test_valid_config);
RUN_TEST(test_empty_ssid_invalid);
RUN_TEST(test_invalid_modbus_address);
RUN_TEST(test_invalid_baud_rate);
RUN_TEST(test_poll_interval_bounds);
RUN_TEST(test_config_from_json);
RUN_TEST(test_config_to_json);
UNITY_END();
}
void loop() {}