Tau.Acuvim/firmware/test/test_json/test_json.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

153 lines
4.4 KiB
C++

#include <unity.h>
#include <ArduinoJson.h>
void test_telemetry_serialization() {
JsonDocument doc;
JsonObject root = doc.to<JsonObject>();
root["ts"] = 1716000000;
root["dev"] = "ACV-TEST01";
JsonObject v = root["v"].to<JsonObject>();
v["a"] = 230.1;
v["b"] = 231.4;
v["c"] = 229.8;
JsonObject i = root["i"].to<JsonObject>();
i["a"] = 15.2;
i["b"] = 14.8;
i["c"] = 15.5;
JsonObject p = root["p"].to<JsonObject>();
p["act"] = 10.5;
p["react"] = 2.1;
p["app"] = 10.7;
root["f"] = 50.01;
String output;
serializeJson(doc, output);
TEST_ASSERT_TRUE(output.length() > 0);
TEST_ASSERT_TRUE(output.indexOf("ACV-TEST01") >= 0);
TEST_ASSERT_TRUE(output.indexOf("230.1") >= 0);
}
void test_telemetry_deserialization() {
const char* json = R"({"ts":1716000000,"dev":"ACV-TEST01","v":{"a":230.1,"b":231.4,"c":229.8},"f":50.01})";
JsonDocument doc;
DeserializationError err = deserializeJson(doc, json);
TEST_ASSERT_EQUAL(DeserializationError::Ok, err);
TEST_ASSERT_EQUAL(1716000000, doc["ts"].as<long>());
TEST_ASSERT_EQUAL_STRING("ACV-TEST01", doc["dev"].as<const char*>());
TEST_ASSERT_FLOAT_WITHIN(0.01, 230.1, doc["v"]["a"].as<float>());
TEST_ASSERT_FLOAT_WITHIN(0.01, 50.01, doc["f"].as<float>());
}
void test_command_parsing() {
const char* json = R"({"cmd":"wifi_set","request_id":"cmd-abc123","params":{"ssid":"TestNet","password":"secret"}})";
JsonDocument doc;
DeserializationError err = deserializeJson(doc, json);
TEST_ASSERT_EQUAL(DeserializationError::Ok, err);
TEST_ASSERT_EQUAL_STRING("wifi_set", doc["cmd"].as<const char*>());
TEST_ASSERT_EQUAL_STRING("cmd-abc123", doc["request_id"].as<const char*>());
TEST_ASSERT_EQUAL_STRING("TestNet", doc["params"]["ssid"].as<const char*>());
}
void test_heartbeat_serialization() {
JsonDocument doc;
JsonObject root = doc.to<JsonObject>();
root["ts"] = 1716000000;
root["dev"] = "ACV-TEST01";
root["fw"] = "1.0.0";
root["up"] = 86400;
root["boot"] = 5;
root["hb"] = 100;
JsonObject health = root["health"].to<JsonObject>();
health["heap_free"] = 120000;
health["heap_min"] = 80000;
JsonObject modbus = root["modbus"].to<JsonObject>();
modbus["connected"] = true;
modbus["success"] = 1000;
modbus["errors"] = 2;
String output;
serializeJson(doc, output);
TEST_ASSERT_TRUE(output.indexOf("\"boot\":5") >= 0);
TEST_ASSERT_TRUE(output.indexOf("\"heap_free\":120000") >= 0);
}
void test_alert_serialization() {
JsonDocument doc;
JsonObject root = doc.to<JsonObject>();
root["alert"] = "overvoltage";
root["severity"] = "warning";
root["message"] = "Phase A voltage 265.3V exceeds threshold 260.0V";
root["phase"] = "a";
root["value"] = 265.3;
root["threshold"] = 260.0;
String output;
serializeJson(doc, output);
TEST_ASSERT_TRUE(output.indexOf("overvoltage") >= 0);
TEST_ASSERT_TRUE(output.indexOf("265.3") >= 0);
}
void test_registration_payload() {
JsonDocument doc;
JsonObject root = doc.to<JsonObject>();
root["device_id"] = "ACV-AABBCCDDEEFF";
root["mac"] = "AA:BB:CC:DD:EE:FF";
root["firmware"] = "1.0.0";
root["hardware"] = "TTGO T-Call v1.4";
JsonArray caps = root["capabilities"].to<JsonArray>();
caps.add("modbus");
caps.add("wifi");
caps.add("gsm");
caps.add("sd");
caps.add("ota");
String output;
serializeJson(doc, output);
TEST_ASSERT_TRUE(output.indexOf("ACV-AABBCCDDEEFF") >= 0);
TEST_ASSERT_TRUE(output.indexOf("\"modbus\"") >= 0);
}
void test_empty_json_handling() {
JsonDocument doc;
DeserializationError err = deserializeJson(doc, "{}");
TEST_ASSERT_EQUAL(DeserializationError::Ok, err);
TEST_ASSERT_TRUE(doc["missing"].isNull());
}
void test_invalid_json_handling() {
JsonDocument doc;
DeserializationError err = deserializeJson(doc, "not json");
TEST_ASSERT_NOT_EQUAL(DeserializationError::Ok, err);
}
void setup() {
delay(2000);
UNITY_BEGIN();
RUN_TEST(test_telemetry_serialization);
RUN_TEST(test_telemetry_deserialization);
RUN_TEST(test_command_parsing);
RUN_TEST(test_heartbeat_serialization);
RUN_TEST(test_alert_serialization);
RUN_TEST(test_registration_payload);
RUN_TEST(test_empty_json_handling);
RUN_TEST(test_invalid_json_handling);
UNITY_END();
}
void loop() {}