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>
123 lines
3.8 KiB
C#
123 lines
3.8 KiB
C#
using System.Text.Json;
|
|
using Tau.Acuvim.Console.Models;
|
|
using Tau.Acuvim.Console.Services;
|
|
|
|
namespace Tau.Acuvim.Console.Tests;
|
|
|
|
public class TelemetryServiceTests
|
|
{
|
|
private TelemetryService CreateService(Data.AppDbContext db)
|
|
=> new(db, TestHelpers.CreateLogger<TelemetryService>());
|
|
|
|
[Fact]
|
|
public async Task IngestAsync_CreatesTelemetryRecord()
|
|
{
|
|
using var db = TestHelpers.CreateDbContext();
|
|
var svc = CreateService(db);
|
|
|
|
db.Devices.Add(new Device { Id = Guid.NewGuid(), DeviceId = "DEV-T1" });
|
|
await db.SaveChangesAsync();
|
|
|
|
var payload = JsonSerializer.Serialize(new { ts = 1700000000L, conn = "wifi", voltage = 3.3 });
|
|
await svc.IngestAsync("DEV-T1", payload);
|
|
|
|
Assert.Single(db.TelemetryRecords);
|
|
var record = db.TelemetryRecords.First();
|
|
Assert.Equal("DEV-T1", record.DeviceId);
|
|
Assert.Equal("live", record.Source);
|
|
Assert.Equal("wifi", record.Connection);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task IngestAsync_UpdatesDeviceLastTelemetry()
|
|
{
|
|
using var db = TestHelpers.CreateDbContext();
|
|
var svc = CreateService(db);
|
|
|
|
var device = new Device { Id = Guid.NewGuid(), DeviceId = "DEV-T2", LastTelemetry = null };
|
|
db.Devices.Add(device);
|
|
await db.SaveChangesAsync();
|
|
|
|
var payload = JsonSerializer.Serialize(new { ts = 1700000000L });
|
|
await svc.IngestAsync("DEV-T2", payload);
|
|
|
|
var updated = db.Devices.First(d => d.DeviceId == "DEV-T2");
|
|
Assert.NotNull(updated.LastTelemetry);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task IngestAsync_ThrowsOnInvalidJson()
|
|
{
|
|
using var db = TestHelpers.CreateDbContext();
|
|
var svc = CreateService(db);
|
|
|
|
await Assert.ThrowsAnyAsync<JsonException>(() => svc.IngestAsync("DEV-X", "not-json!!!"));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetLatestAsync_ReturnsLatestRecord()
|
|
{
|
|
using var db = TestHelpers.CreateDbContext();
|
|
var svc = CreateService(db);
|
|
|
|
db.TelemetryRecords.AddRange(
|
|
new TelemetryRecord
|
|
{
|
|
DeviceId = "DEV-T3",
|
|
Timestamp = new DateTime(2025, 1, 1, 0, 0, 0, DateTimeKind.Utc),
|
|
Data = JsonDocument.Parse("{\"v\":1}"),
|
|
ReceivedAt = DateTime.UtcNow
|
|
},
|
|
new TelemetryRecord
|
|
{
|
|
DeviceId = "DEV-T3",
|
|
Timestamp = new DateTime(2025, 6, 1, 0, 0, 0, DateTimeKind.Utc),
|
|
Data = JsonDocument.Parse("{\"v\":2}"),
|
|
ReceivedAt = DateTime.UtcNow
|
|
}
|
|
);
|
|
await db.SaveChangesAsync();
|
|
|
|
var latest = await svc.GetLatestAsync("DEV-T3");
|
|
|
|
Assert.NotNull(latest);
|
|
Assert.Equal(new DateTime(2025, 6, 1, 0, 0, 0, DateTimeKind.Utc), latest.Timestamp);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetCountTodayAsync_CountsRecordsFromToday()
|
|
{
|
|
using var db = TestHelpers.CreateDbContext();
|
|
var svc = CreateService(db);
|
|
|
|
db.TelemetryRecords.AddRange(
|
|
new TelemetryRecord
|
|
{
|
|
DeviceId = "DEV-T4",
|
|
Timestamp = DateTime.UtcNow,
|
|
Data = JsonDocument.Parse("{}"),
|
|
ReceivedAt = DateTime.UtcNow
|
|
},
|
|
new TelemetryRecord
|
|
{
|
|
DeviceId = "DEV-T4",
|
|
Timestamp = DateTime.UtcNow,
|
|
Data = JsonDocument.Parse("{}"),
|
|
ReceivedAt = DateTime.UtcNow
|
|
},
|
|
new TelemetryRecord
|
|
{
|
|
DeviceId = "DEV-T4",
|
|
Timestamp = DateTime.UtcNow.AddDays(-2),
|
|
Data = JsonDocument.Parse("{}"),
|
|
ReceivedAt = DateTime.UtcNow.AddDays(-2)
|
|
}
|
|
);
|
|
await db.SaveChangesAsync();
|
|
|
|
var count = await svc.GetCountTodayAsync();
|
|
|
|
Assert.Equal(2, count);
|
|
}
|
|
}
|