fix(lite): parse real syncstatus shapes (idle vs in-progress)

The real backend returns syncstatus as idle {"syncing":"false"} (string) or in-progress
{"syncing":"true","synced_blocks":N,"total_blocks":M} (commands.rs:83-87), but
parseLiteSyncStatusResponse hard-required the block fields and failed whenever the wallet
wasn't actively syncing — so sync/progress never updated in the real app.

- Read "syncing" as a string; require synced_blocks/total_blocks only when syncing=true;
  idle => complete, synced/total 0.
- fake_lite_backend syncstatus now uses the real "syncing":"true" shape.
- testLiteSyncStatusParserRealShapes covers idle, in-progress, and missing-counts-while-syncing.
- Verified against the live backend via lite_smoke --refresh (syncstatus parse_ok=1).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-04 22:52:42 -05:00
parent a8b5d2f6a3
commit 59c55e33f8
4 changed files with 55 additions and 8 deletions

View File

@@ -65,8 +65,8 @@ inline char* liteFakeExecute(const char* command, const char*)
if (command) {
const char* c = command;
if (std::strcmp(c, "sync") == 0) return liteFakeDup("{\"result\":\"success\"}");
if (std::strcmp(c, "syncstatus") == 0)
return liteFakeDup("{\"synced_blocks\":1000,\"total_blocks\":1000}");
if (std::strcmp(c, "syncstatus") == 0) // real backend shape: "syncing" is a string
return liteFakeDup("{\"syncing\":\"true\",\"synced_blocks\":1000,\"total_blocks\":1000}");
if (std::strcmp(c, "balance") == 0)
return liteFakeDup("{\"tbalance\":100000000,\"zbalance\":200000000,\"unconfirmed\":50000000,"
"\"verified_zbalance\":180000000,\"spendable_zbalance\":170000000}");

View File

@@ -4651,6 +4651,36 @@ void testLiteWalletControllerRefreshPopulatesState()
EXPECT_FALSE(fresh.refreshWalletState(empty));
}
// syncstatus parser must accept both real backend shapes: idle {"syncing":"false"} and
// in-progress {"syncing":"true","synced_blocks":N,"total_blocks":M} ("syncing" is a string).
void testLiteSyncStatusParserRealShapes()
{
using namespace dragonx::wallet;
// Idle: no block counts, syncing is the string "false".
{
const auto p = parseLiteSyncStatusResponse(std::string("{\"syncing\":\"false\"}"));
EXPECT_TRUE(p.ok);
EXPECT_EQ(static_cast<long long>(p.syncStatus.syncedBlocks), 0LL);
EXPECT_TRUE(p.syncStatus.complete);
}
// In-progress: block counts present.
{
const auto p = parseLiteSyncStatusResponse(
std::string("{\"syncing\":\"true\",\"synced_blocks\":900,\"total_blocks\":1000}"));
EXPECT_TRUE(p.ok);
EXPECT_EQ(static_cast<long long>(p.syncStatus.syncedBlocks), 900LL);
EXPECT_EQ(static_cast<long long>(p.syncStatus.totalBlocks), 1000LL);
EXPECT_FALSE(p.syncStatus.complete);
EXPECT_NEAR(p.syncStatus.progress, 0.9, 1e-9);
}
// While syncing, missing block counts is still an error.
{
const auto p = parseLiteSyncStatusResponse(std::string("{\"syncing\":\"true\"}"));
EXPECT_FALSE(p.ok);
}
}
// M2b-3: opening a wallet auto-starts the background worker, which produces a refresh model
// the main thread can pick up via takeRefreshedModel() and apply to WalletState.
void testLiteWalletControllerWorkerProducesModel()
@@ -4719,6 +4749,7 @@ int main()
testLiteWalletControllerLifecycle();
testLiteChainNameMigration();
testLiteRefreshModelAppliesToWalletState();
testLiteSyncStatusParserRealShapes();
testLiteWalletControllerRefreshPopulatesState();
testLiteWalletControllerWorkerProducesModel();
testLiteBridgeRuntimeShutdownIsIdempotent();