#include "IdentityStore.h" #include #include "ContactFileTransaction.h" #include "PersistentStoreFormat.h" #include #include #include #include class RegionMap; namespace mesh { struct DataRouteState; } class CommonCLI { public: mesh::DataRouteState* _data_route = nullptr; bool adoptLegacyDataTxPath(const uint8_t* path, uint8_t path_len); }; // Generated from the production source by the host test runner. Includes the // real route state, save helper, and adoption method rather than copies of their // behavior. The filesystem and packet boundary use the existing host fixtures. #include "production_route.inc" struct Fixture { MemoryFS fs; mesh::DataRouteState route; CommonCLI cli; Fixture() { route.fs = &fs; cli._data_route = &route; } }; static void verifySavedRoute(const Fixture& f) { assert(f.route.persisted); const auto& data = f.fs.files.at("/data_tx"); assert(data.size() == 108); assert(!memcmp(data.data(), "DTX1", 4)); assert(data[4] == f.route.path_len && data[5] == f.route.region_mode); assert(data[6] == 0 && data[7] == 0 && data[103] == 0); assert(!memcmp(data.data() + 8, f.route.path, MAX_PATH_SIZE)); assert(!memcmp(data.data() + 72, f.route.region, sizeof(f.route.region))); assert(mesh::storage::readLE32(data.data() + 104) == mesh::storage::updateCRC32(0xffffffff, data.data(), 104)); assert(!f.fs.exists("/data_tx.tmp") && !f.fs.exists("/data_tx.bak")); } static void checkPersistedRouteWins() { const uint8_t legacy[] = {0x12, 0x34, 0x56, 0x78}; for (const uint8_t explicit_len : {uint8_t(0), uint8_t(0x42), uint8_t(OUT_PATH_UNKNOWN)}) { Fixture f; f.route.path_len = explicit_len; if (explicit_len == 0x42) memcpy(f.route.path, "\xab\xcd\xef\x01", 4); f.route.region_mode = mesh::DATA_REGION_NAMED; strcpy(f.route.region, "shared-scope"); assert(mesh::dataRouteSave(f.route)); verifySavedRoute(f); const auto saved_files = f.fs.files; const mesh::DataRouteState previous = f.route; // A configured shared path, including an explicit none, takes precedence // over a legacy path. Legacy none must also leave that shared route intact. assert(f.cli.adoptLegacyDataTxPath(legacy, 0x42)); assert(f.cli.adoptLegacyDataTxPath(nullptr, OUT_PATH_UNKNOWN)); assert(f.cli.adoptLegacyDataTxPath(nullptr, 0)); assert(f.route.path_len == previous.path_len); assert(!memcmp(f.route.path, previous.path, sizeof(f.route.path))); assert(f.route.region_mode == previous.region_mode); assert(!memcmp(f.route.region, previous.region, sizeof(f.route.region))); assert(f.route.healthy && f.route.persisted); assert(f.fs.files == saved_files); } } static void checkSuccessfulMigration() { const uint8_t legacy[] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc}; for (const uint8_t path_len : {uint8_t(0), uint8_t(2), uint8_t(0x42), uint8_t(0x82)}) { Fixture f; memset(f.route.path, 0xa5, sizeof(f.route.path)); assert(f.cli.adoptLegacyDataTxPath(path_len ? legacy : nullptr, path_len)); assert(f.route.path_len == path_len && f.route.healthy); const size_t bytes = (path_len & 63) * ((path_len >> 6) + 1); assert(!memcmp(f.route.path, legacy, bytes)); for (size_t i = bytes; i < sizeof(f.route.path); ++i) assert(f.route.path[i] == 0); assert(f.route.region_mode == mesh::DATA_REGION_AUTO && !f.route.region[0]); verifySavedRoute(f); } } static void checkIsolatedPublishFailureAndRetry() { Fixture f; f.route.path_len = 1; memset(f.route.path, 0xa5, sizeof(f.route.path)); f.route.region_mode = mesh::DATA_REGION_NAMED; strcpy(f.route.region, "shared-scope"); f.fs.files["/management"] = std::vector(120, 0); memcpy(f.fs.files["/management"].data(), "MGC1", 4); const auto saved_files = f.fs.files; const mesh::DataRouteState previous = f.route; const uint8_t legacy[] = {0x12, 0x34, 0x56, 0x78}; f.fs.fail_rename_from = {"/data_tx.tmp"}; assert(!f.cli.adoptLegacyDataTxPath(legacy, 0x42)); assert(f.route.path_len == previous.path_len && !f.route.persisted); assert(!memcmp(f.route.path, previous.path, sizeof(f.route.path))); assert(f.route.region_mode == previous.region_mode); assert(!memcmp(f.route.region, previous.region, sizeof(f.route.region))); assert(f.route.healthy); assert(f.fs.files == saved_files); // The failed /data_tx publish is isolated: a later /management transaction // can succeed. The production loader must therefore act on adoption failure // before allowing a management checkpoint to discard the legacy route. uint8_t management[120] = {}; memcpy(management, "MGC2", 4); mesh::ContactFileTransaction writer(&f.fs, "/management"); assert(writer && writer.write(management, sizeof(management)) == sizeof(management)); assert(writer.commit()); assert(f.fs.files.at("/management") == std::vector(management, management + sizeof(management))); assert(!f.fs.exists("/data_tx")); assert(!f.fs.exists("/management.tmp") && !f.fs.exists("/management.bak")); f.fs.fail_rename_from.clear(); assert(f.cli.adoptLegacyDataTxPath(legacy, 0x42)); verifySavedRoute(f); assert(f.route.path_len == 0x42 && !memcmp(f.route.path, legacy, sizeof(legacy))); } static void checkUnavailableUnhealthyAndInvalidRoutes() { const uint8_t legacy[MAX_PATH_SIZE] = {}; CommonCLI unavailable; assert(!unavailable.adoptLegacyDataTxPath(legacy, 1)); for (const bool persisted : {false, true}) { Fixture f; f.route.healthy = false; f.route.persisted = persisted; assert(!f.cli.adoptLegacyDataTxPath(legacy, 1)); assert(f.fs.files.empty() && f.route.path_len == 0); } for (const uint8_t invalid : {uint8_t(0xc0), uint8_t(0x61), uint8_t(0x96)}) { Fixture f; assert(!f.cli.adoptLegacyDataTxPath(legacy, invalid)); assert(f.fs.files.empty() && f.route.path_len == 0 && !f.route.persisted); } Fixture missing_path; assert(!missing_path.cli.adoptLegacyDataTxPath(nullptr, 1)); assert(missing_path.fs.files.empty() && !missing_path.route.persisted); } static void checkPathParsing() { struct Case { const char* text; uint8_t encoded; const char* bytes; }; for (const auto& valid : {Case{"direct", 0, ""}, Case{" none ", OUT_PATH_UNKNOWN, ""}, Case{"clear", OUT_PATH_UNKNOWN, ""}, Case{"-", OUT_PATH_UNKNOWN, ""}, Case{"1:12ab", 2, "\x12\xab"}, Case{" 12, AB ", 2, "\x12\xab"}, Case{"2:12abcd34", 0x42, "\x12\xab\xcd\x34"}, Case{"12ab, cd34", 0x42, "\x12\xab\xcd\x34"}, Case{"3:123456abcdef", 0x82, "\x12\x34\x56\xab\xcd\xef"}}) { std::vector text(valid.text, valid.text + strlen(valid.text) + 1); uint8_t path[MAX_PATH_SIZE], encoded = OUT_PATH_UNKNOWN; const char* error = nullptr; assert(mesh::parseDataPath(text.data(), path, encoded, error)); assert(encoded == valid.encoded && !error); const size_t bytes = encoded == OUT_PATH_UNKNOWN ? 0 : (encoded & 63) * ((encoded >> 6) + 1); assert(!memcmp(path, valid.bytes, bytes)); for (size_t i = bytes; i < sizeof(path); ++i) assert(path[i] == 0); } for (const char* malformed : {"", " ", ",12", "12,,ab", "12,", "12, ", "12ab,cd34,", "12ab,cd34, ", "1:", "1:123", "2:12ab34", "3:123456ab", "4:12345678", "12,abcd", "12;ab", "12,xx"}) { std::vector text(malformed, malformed + strlen(malformed) + 1); uint8_t path[MAX_PATH_SIZE], encoded = OUT_PATH_UNKNOWN; const char* error = nullptr; if (mesh::parseDataPath(text.data(), path, encoded, error)) { fprintf(stderr, "malformed data.tx path accepted: '%s'\n", malformed); assert(false); } assert(error); } } int main() { checkPersistedRouteWins(); checkSuccessfulMigration(); checkIsolatedPublishFailureAndRetry(); checkUnavailableUnhealthyAndInvalidRoutes(); checkPathParsing(); puts("management route adoption checks passed"); }