mirror of
https://github.com/agessaman/MeshCore.git
synced 2026-08-29 05:28:58 +00:00
Rework the /mqtt_prefs load/save path so preference migrations are
crash-safe and, for the first time, unit-testable on the host.
Most of this is extraction. The multi-format migration that previously
lived inline in CommonCLI.cpp (and could only run on-device) is moved
into three dependency-free headers so it can be exercised without
Arduino, a filesystem, or the radio stack:
- MQTTPrefsStorage.h frozen layout structs for every /mqtt_prefs
format ever shipped, with static_asserts that
fail the build if any on-flash offset changes.
- MQTTPrefsCodec.h pure format classification, field-copy
migration, and plausibility validation.
- MQTTPrefsAtomicStore.h transactional writer plus the power-cut
upgrade gate, both host-testable.
New behavior, beyond the refactor:
- Atomic writes: /mqtt_prefs is written to /mqtt_prefs.tmp, verified,
then published with an atomic rename; the writer never removes the
existing file. A failed or interrupted save leaves the current
config intact.
- Power-cut ordering: LegacyUpgradeGate guarantees /mqtt_prefs is
durably committed before the legacy /com_prefs (or /node_prefs)
carrying the observer tail is compacted or removed, so an
interrupted two-file upgrade retries on the next boot without
losing settings.
- Corrupt, unsupported-version, and newer-than-known files are
preserved and the device boots on in-RAM defaults, rather than
overwriting a file this firmware cannot fully decode.
- Headerless legacy formats are validated for plausibility before
they are trusted and rewritten (raw prefs carry no checksum).
The full historical format matrix is migrated forward to the versioned
v1 layout: pre-slot (including pre-wifi-power), 3-slot (base and
token/topic tails), and headerless 6-slot (base, audience, rx, ntp).
Scope note: only /mqtt_prefs and the one-time /node_prefs -> /com_prefs
name migration use the atomic path. Ordinary /com_prefs saves remain a
direct rewrite, unchanged by this commit.
Tests: adds two host GoogleTest suites (pio test -e native).
- test_mqtt_prefs_codec: format classification, migration fixtures,
v1 header integrity, downgrade preservation.
- test_mqtt_prefs_atomic_store: transactional writes, short-write
detection, begin/finish/rename failure cleanup, original-file
preservation.
1.4 KiB
1.4 KiB
MQTT preferences fixture provenance
The codec tests construct synthetic, non-secret byte vectors at hard-coded offsets. They intentionally do not serialize the production structs: a layout change must disagree with the frozen fixture bytes and fail the field checks.
| Bytes | Historical layout | Source history |
|---|---|---|
| 472 | Pre-slot, before and after wifi_power_save |
Initial /mqtt_prefs layout; 34c8bea7 inserted WiFi power without changing the padded total size |
| 1032 | Initial three-slot layout | b43e9618 |
| 1464 | Three slots with token/topic tails | 95874f0c |
| 2452 | Six slots with token/topic tails | 1b5884bd |
| 2836 | Six slots with audience tail | 1263e71d |
| 2840 | Six slots with RX flag | 47b632aa |
| 2904 | Six slots with NTP server | 7416d632 |
| 2736 / 2860 | Version-1 payload before observer tail / complete payload | 58b9cb66 introduced the eight-byte versioned header; the shorter form exercises its append-compatible prefix contract |
The 3024-byte raw observer-tail form is not accepted as deployed fleet data: repository history indicates it existed briefly before versioning but was not shipped. Tests require it to be preserved rather than guessed and rewritten.
Headerless formats have no checksum. Plausibility checks reject obvious random or malformed content, but cannot authenticate a byte sequence that happens to look like a valid historical struct.