From 06edf9aa8a9772702527d9a53630fcf8af861a89 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Sat, 24 Oct 2026 16:51:42 -0700 Subject: [PATCH] Pin historical G3 catalog fixtures for sensor routing tests --- .../station_g3_mesh_america_1_17_1_9.json | 151 ++++++++++++++++++ test/test_station_g3_sensor_recipes.py | 48 +++++- 2 files changed, 194 insertions(+), 5 deletions(-) create mode 100644 test/fixtures/station_g3_mesh_america_1_17_1_9.json diff --git a/test/fixtures/station_g3_mesh_america_1_17_1_9.json b/test/fixtures/station_g3_mesh_america_1_17_1_9.json new file mode 100644 index 000000000..a2ed887e6 --- /dev/null +++ b/test/fixtures/station_g3_mesh_america_1_17_1_9.json @@ -0,0 +1,151 @@ +{ + "format": "meshcore-station-g3-provider-fixture-v1", + "provenance": { + "repository": "mikecarper/MeshCore", + "source_commit": "03214a16df2bd09d3272dfb001d2d322c6740f60", + "selection": "Exact pre-consolidation G3 and G3 R2 card metadata, with one unchanged real app-update file from a Repeater profile per card. Other cards, firmware profiles and release notes are omitted.", + "source_catalogs": [ + { + "path": "mesh-america/keymind-cascade-v1.16.0-provider.json", + "sha256": "f6a9d7f05a7dd271a0acd1fd7ac8a50dd8478922db7c6140cbf5aff5b7e090e5" + }, + { + "path": "mesh-america/keymind-cascade-logging-v1.16.0-provider.json", + "sha256": "f6a9d7f05a7dd271a0acd1fd7ac8a50dd8478922db7c6140cbf5aff5b7e090e5" + } + ] + }, + "canonical_device_names": [ + "UnitEng/BQ Voyage Station G3" + ], + "catalogs": { + "keymind-cascade-v1.16.0-provider.json": { + "description": "Pinned Station G3 hardware-card routing fixture.", + "maker": { + "uniteng": { + "name": "UnitEng" + } + }, + "role": { + "sensor": { + "name": "Sensor", + "description": "Telemetry node that reads and publishes only the sensors compiled for and detected on the selected board; this custom role is not a companion or repeater." + } + }, + "device": [ + { + "maker": "uniteng", + "name": "UnitEng/BQ Voyage Station G3", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "subTitle": "Station_G3_ESP32_repeater-full-usb-wifi-ota - Full profile", + "version": { + "v1.17.1.9-halo-keymind-cascade-dev-88c85108": { + "files": [ + { + "type": "flash-update", + "name": "Station_G3_ESP32_repeater-full-usb-wifi-ota-v1.17.1.9-halo-keymind-cascade-dev-88c85108.bin", + "title": "App update (matching partition layout only)", + "url": "https://github.com/mikecarper/MeshCore/releases/download/full-profiles-v1.17.1.9-halo-keymind-cascade-dev-88c85108/Station_G3_ESP32_repeater-full-usb-wifi-ota-v1.17.1.9-halo-keymind-cascade-dev-88c85108.bin" + } + ] + } + } + } + ] + }, + { + "maker": "uniteng", + "name": "UnitEng/BQ Voyage Station G3 ESP32 R2", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "subTitle": "Station_G3_ESP32_r2_repeater_observer_mqtt-full-usb-wifi-ota - Full profile", + "version": { + "v1.17.1.9-halo-keymind-cascade-dev-88c85108": { + "files": [ + { + "type": "flash-update", + "name": "Station_G3_ESP32_r2_repeater_observer_mqtt-full-usb-wifi-ota-v1.17.1.9-halo-keymind-cascade-dev-88c85108.bin", + "title": "App update (matching partition layout only)", + "url": "https://github.com/mikecarper/MeshCore/releases/download/full-profiles-v1.17.1.9-halo-keymind-cascade-dev-88c85108/Station_G3_ESP32_r2_repeater_observer_mqtt-full-usb-wifi-ota-v1.17.1.9-halo-keymind-cascade-dev-88c85108.bin" + } + ] + } + } + } + ] + } + ] + }, + "keymind-cascade-logging-v1.16.0-provider.json": { + "description": "Pinned Station G3 hardware-card routing fixture.", + "maker": { + "uniteng": { + "name": "UnitEng" + } + }, + "role": { + "sensor": { + "name": "Sensor", + "description": "Telemetry node that reads and publishes only the sensors compiled for and detected on the selected board; this custom role is not a companion or repeater." + } + }, + "device": [ + { + "maker": "uniteng", + "name": "UnitEng/BQ Voyage Station G3", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "subTitle": "Station_G3_ESP32_repeater-full-usb-wifi-ota - Full profile", + "version": { + "v1.17.1.9-halo-keymind-cascade-dev-88c85108": { + "files": [ + { + "type": "flash-update", + "name": "Station_G3_ESP32_repeater-full-usb-wifi-ota-v1.17.1.9-halo-keymind-cascade-dev-88c85108.bin", + "title": "App update (matching partition layout only)", + "url": "https://github.com/mikecarper/MeshCore/releases/download/full-profiles-v1.17.1.9-halo-keymind-cascade-dev-88c85108/Station_G3_ESP32_repeater-full-usb-wifi-ota-v1.17.1.9-halo-keymind-cascade-dev-88c85108.bin" + } + ] + } + } + } + ] + }, + { + "maker": "uniteng", + "name": "UnitEng/BQ Voyage Station G3 ESP32 R2", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "subTitle": "Station_G3_ESP32_r2_repeater_observer_mqtt-full-usb-wifi-ota - Full profile", + "version": { + "v1.17.1.9-halo-keymind-cascade-dev-88c85108": { + "files": [ + { + "type": "flash-update", + "name": "Station_G3_ESP32_r2_repeater_observer_mqtt-full-usb-wifi-ota-v1.17.1.9-halo-keymind-cascade-dev-88c85108.bin", + "title": "App update (matching partition layout only)", + "url": "https://github.com/mikecarper/MeshCore/releases/download/full-profiles-v1.17.1.9-halo-keymind-cascade-dev-88c85108/Station_G3_ESP32_r2_repeater_observer_mqtt-full-usb-wifi-ota-v1.17.1.9-halo-keymind-cascade-dev-88c85108.bin" + } + ] + } + } + } + ] + } + ] + } + } +} diff --git a/test/test_station_g3_sensor_recipes.py b/test/test_station_g3_sensor_recipes.py index 32ac94b69..fc26733be 100644 --- a/test/test_station_g3_sensor_recipes.py +++ b/test/test_station_g3_sensor_recipes.py @@ -35,6 +35,9 @@ RADIO_PINS = ( SENSORS = tuple(slot + "_sensor" for slot in SLOTS) SOURCE = "a" * 40 FAMILY = "v1.17.1.9-halo-keymind-cascade-dev-" + SOURCE[:8] +CATALOG_FILENAMES = ("keymind-cascade-v1.16.0-provider.json", + "keymind-cascade-logging-v1.16.0-provider.json") +CATALOG_FIXTURE = ROOT / "test/fixtures/station_g3_mesh_america_1_17_1_9.json" def definitions(flags): @@ -45,9 +48,9 @@ def definitions(flags): return result -def manifest(target): +def manifest(target, *, family=FAMILY): profile = target + "-full-logging-ota" - stem = profile + "-" + FAMILY + stem = profile + "-" + family capabilities = ["profile.full", "ota.update.lora", "ota.update.wifi"] return dict(target=target, artifact_target=profile, platformio_env=target, platform="ESP32_PLATFORM", build_profile="full", verified=True, @@ -249,9 +252,11 @@ run_logging_matrix_build_targets "${SUPPORTED_PIO_ENVS[@]}" >/dev/null plan = dict(version="1.17.1.9", source=SOURCE, groups=[dict(key="full-profiles")]) names = {"Station_G3_ESP32": "UnitEng/BQ Voyage Station G3", "Station_G3_ESP32_r2": "UnitEng/BQ Voyage Station G3 ESP32 R2"} - for filename in ("keymind-cascade-v1.16.0-provider.json", - "keymind-cascade-logging-v1.16.0-provider.json"): - catalog = json.loads((ROOT / "mesh-america" / filename).read_text()) + # These fixed-slot recipes predate the automatic-slot G3 release. + # Keep their exact historical cards independent of active catalogs. + fixture = json.loads(CATALOG_FIXTURE.read_text(encoding="ascii")) + for filename in CATALOG_FILENAMES: + catalog = fixture["catalogs"][filename] targets = {row["artifact_target"] for row in rows} targets.update(catalogs.identity(file["name"]) for device in catalog["device"] for firmware in device["firmware"] @@ -269,6 +274,39 @@ run_logging_matrix_build_targets "${SUPPORTED_PIO_ENVS[@]}" >/dev/null self.assertEqual({file["type"] for file in files}, {"flash-wipe", "flash-update"}) self.assertTrue(all(file["name"].startswith(slot + "_sensor-") for file in files)) + def test_canonical_sensor_routes_without_guessing_a_legacy_r2_card(self): + family = "v1.18.0.1-halo-keymind-cascade-dev-" + SOURCE[:8] + plan = dict(version="1.18.0.1", source=SOURCE, groups=[dict(key="full-profiles")]) + canonical = manifest("Station_G3_ESP32_sensor", family=family) + legacy = manifest("Station_G3_ESP32_r2_sensor", family=family) + fixture = json.loads(CATALOG_FIXTURE.read_text(encoding="ascii")) + for filename in CATALOG_FILENAMES: + original = fixture["catalogs"][filename] + # A pinned base-card-only input models the consolidated catalog; + # future published releases must not silently change this contract. + catalog = {**original, "device": [device for device in original["device"] + if device["name"] in fixture["canonical_device_names"]]} + seed_targets = {catalogs.identity(file["name"]) for device in catalog["device"] + for firmware in device["firmware"] + for version in firmware["version"].values() for file in version["files"]} + profiles = catalogs.target_profiles(seed_targets | {canonical["artifact_target"], legacy["artifact_target"]}) + def record(row): + return (row, "full-profiles-" + family, + [Path(name) for name in row["files"] if name.endswith(".bin")]) + with self.subTest(catalog=filename, profile="canonical"): + result = catalogs.generate(catalog, plan, family, [record(canonical)], profiles, "mikecarper/MeshCore") + card, = result["device"] + self.assertEqual(card["name"], "UnitEng/BQ Voyage Station G3") + firmware, = card["firmware"] + self.assertEqual(firmware["role"], "sensor") + self.assertEqual(firmware["subTitle"], "Station_G3_ESP32_sensor-full-logging-ota - Full profile") + self.assertEqual({file["name"] for file in firmware["version"][family]["files"]}, + {name for name in canonical["files"] if name.endswith(".bin")}) + with self.subTest(catalog=filename, profile="unqualified legacy R2"): + with self.assertRaisesRegex(ValueError, "exact hardware card mapping: Station_G3_ESP32_r2_sensor"): + catalogs.generate(catalog, plan, family, [record(legacy)], profiles, "mikecarper/MeshCore") + self.assertEqual([device["name"] for device in catalog["device"]], fixture["canonical_device_names"]) + def test_controls_expose_qualified_ota_without_inventing_a_sensor_web_portal(self): rows = [manifest(target) for target in SENSORS] config = [("env:" + target, [("build_flags", self.recipes.value(