feat(persist): persist BankBook under banks key, retire legacy bank_index, route capture to active bank (B2)
This commit is contained in:
@@ -341,6 +341,70 @@ static void testMalformedJson() {
|
||||
CHECK(!BankBook::deserialize(dup).has_value());
|
||||
}
|
||||
|
||||
// --- B2: persist-load source-precedence decision (pure) --------------------
|
||||
// loadFromPersisted picks the load source the persist shell will feed it from the
|
||||
// two ext-state values a project may carry: the authoritative `banks` blob and the
|
||||
// retired legacy `bank_index` blob. Precedence: banks > legacy > empty; a malformed
|
||||
// banks blob degrades to empty WITHOUT falling back to the stale legacy key.
|
||||
|
||||
static void testLoadPrefersBanksBlob() {
|
||||
// A full book + a stale legacy index both present: `banks` wins, legacy ignored.
|
||||
BankBook src;
|
||||
CHECK(src.createBank("drums", "Drums"));
|
||||
CHECK(src.setActiveBank("drums"));
|
||||
CHECK(src.bank("drums")->index.add(sampleWith("new1")) == AddResult::Added);
|
||||
const std::string banksJson = src.serialize();
|
||||
|
||||
BankIndex stale;
|
||||
CHECK(stale.add(sampleWith("stale-old")) == AddResult::Added);
|
||||
const std::string legacyJson = stale.serialize();
|
||||
|
||||
BankBook loaded = BankBook::loadFromPersisted(banksJson, legacyJson);
|
||||
// The book equals the source book — the legacy key had NO effect.
|
||||
CHECK(loaded == src);
|
||||
CHECK(loaded.activeBankId() == "drums");
|
||||
CHECK(loaded.bank("drums") != nullptr);
|
||||
CHECK(loaded.bank("drums")->index.query("id-new1") != nullptr);
|
||||
// The stale legacy sample must NOT have leaked into the pool.
|
||||
CHECK(loaded.pool().index.query("id-stale-old") == nullptr);
|
||||
}
|
||||
|
||||
static void testLoadMigratesLegacyWhenNoBanks() {
|
||||
// No `banks` key, a legacy `bank_index` present: migrate into the pool, zero named.
|
||||
BankIndex legacy;
|
||||
CHECK(legacy.add(sampleWith("l1")) == AddResult::Added);
|
||||
CHECK(legacy.add(sampleWith("l2")) == AddResult::Added);
|
||||
const std::string legacyJson = legacy.serialize();
|
||||
|
||||
BankBook loaded = BankBook::loadFromPersisted(std::string{}, legacyJson);
|
||||
CHECK(loaded.size() == 1); // pool only
|
||||
CHECK(loaded.pool().id == kPoolBankId);
|
||||
CHECK(loaded.activeBankId() == std::string(kPoolBankId));
|
||||
CHECK(loaded.pool().index.size() == 2);
|
||||
CHECK(loaded.pool().index == legacy); // lossless
|
||||
}
|
||||
|
||||
static void testLoadEmptyWhenNeither() {
|
||||
// Both absent: a fresh empty book (pool only, empty index, active = pool).
|
||||
BankBook loaded = BankBook::loadFromPersisted(std::string{}, std::string{});
|
||||
CHECK(loaded == BankBook{});
|
||||
CHECK(loaded.size() == 1);
|
||||
CHECK(loaded.pool().index.empty());
|
||||
CHECK(loaded.activeBankId() == std::string(kPoolBankId));
|
||||
}
|
||||
|
||||
static void testLoadMalformedBanksDegradesWithoutLegacyFallback() {
|
||||
// A present-but-malformed `banks` blob must degrade to an empty book and must NOT
|
||||
// resurrect the stale legacy key (that would revive superseded single-bank state).
|
||||
BankIndex stale;
|
||||
CHECK(stale.add(sampleWith("stale")) == AddResult::Added);
|
||||
const std::string legacyJson = stale.serialize();
|
||||
|
||||
BankBook loaded = BankBook::loadFromPersisted("{\"banks\":[", legacyJson);
|
||||
CHECK(loaded == BankBook{}); // empty, NOT the legacy
|
||||
CHECK(loaded.pool().index.query("id-stale") == nullptr); // legacy did not leak
|
||||
}
|
||||
|
||||
static void testActiveBankResolveAfterCorruptPersistedId() {
|
||||
// A book blob whose activeBank names no bank resolves to the pool (defensive).
|
||||
const char* json =
|
||||
@@ -366,6 +430,10 @@ int main() {
|
||||
testJsonEmptyBookRoundTrip();
|
||||
testLegacyMigration();
|
||||
testMalformedJson();
|
||||
testLoadPrefersBanksBlob();
|
||||
testLoadMigratesLegacyWhenNoBanks();
|
||||
testLoadEmptyWhenNeither();
|
||||
testLoadMalformedBanksDegradesWithoutLegacyFallback();
|
||||
testActiveBankResolveAfterCorruptPersistedId();
|
||||
|
||||
if (g_fail == 0) std::printf("All tests passed.\n");
|
||||
|
||||
Reference in New Issue
Block a user