wallet: Make Mockable{Database,Batch} subclasses of SQLite classes

The mocking functionality of MockableDatabase, MockableBatch, and
MockableCursor was not really being used. These are changed to be
subclasses of their respective SQLite* classes and will use in-memory
SQLite databases so that the tests are more representative of actual
database behavior.

MockableCursor is removed as there are no overrides needed in
SQLiteCursor for the tests.
This commit is contained in:
Ava Chow
2025-07-24 11:31:47 -07:00
parent b69f989dc5
commit 59484e2fdb
4 changed files with 19 additions and 152 deletions

View File

@@ -108,9 +108,9 @@ std::unique_ptr<WalletDatabase> DuplicateMockDatabase(WalletDatabase& database)
std::unique_ptr<DatabaseBatch> batch_orig = database.MakeBatch();
std::unique_ptr<DatabaseCursor> cursor_orig = batch_orig->GetNewCursor();
std::unique_ptr<WalletDatabase> new_db = std::make_unique<MockableDatabase>();
std::unique_ptr<WalletDatabase> new_db = CreateMockableWalletDatabase();
std::unique_ptr<DatabaseBatch> new_db_batch = new_db->MakeBatch();
MockableBatch* batch_new = dynamic_cast<MockableBatch*>(new_db_batch.get());
MockableSQLiteBatch* batch_new = dynamic_cast<MockableSQLiteBatch*>(new_db_batch.get());
Assert(batch_new);
while (true) {
@@ -135,103 +135,13 @@ CTxDestination getNewDestination(CWallet& w, OutputType output_type)
return *Assert(w.GetNewDestination(output_type, ""));
}
MockableCursor::MockableCursor(const MockableData& records, bool pass, std::span<const std::byte> prefix)
{
m_pass = pass;
std::tie(m_cursor, m_cursor_end) = records.equal_range(BytePrefix{prefix});
}
DatabaseCursor::Status MockableCursor::Next(DataStream& key, DataStream& value)
{
if (!m_pass) {
return Status::FAIL;
}
if (m_cursor == m_cursor_end) {
return Status::DONE;
}
key.clear();
value.clear();
const auto& [key_data, value_data] = *m_cursor;
key.write(key_data);
value.write(value_data);
m_cursor++;
return Status::MORE;
}
bool MockableBatch::ReadKey(DataStream&& key, DataStream& value)
{
if (!m_pass) {
return false;
}
SerializeData key_data{key.begin(), key.end()};
const auto& it = m_records.find(key_data);
if (it == m_records.end()) {
return false;
}
value.clear();
value.write(it->second);
return true;
}
bool MockableBatch::WriteKey(DataStream&& key, DataStream&& value, bool overwrite)
{
if (!m_pass) {
return false;
}
SerializeData key_data{key.begin(), key.end()};
SerializeData value_data{value.begin(), value.end()};
auto [it, inserted] = m_records.emplace(key_data, value_data);
if (!inserted && overwrite) { // Overwrite if requested
it->second = value_data;
inserted = true;
}
return inserted;
}
bool MockableBatch::EraseKey(DataStream&& key)
{
if (!m_pass) {
return false;
}
SerializeData key_data{key.begin(), key.end()};
m_records.erase(key_data);
return true;
}
bool MockableBatch::HasKey(DataStream&& key)
{
if (!m_pass) {
return false;
}
SerializeData key_data{key.begin(), key.end()};
return m_records.contains(key_data);
}
bool MockableBatch::ErasePrefix(std::span<const std::byte> prefix)
{
if (!m_pass) {
return false;
}
auto it = m_records.begin();
while (it != m_records.end()) {
auto& key = it->first;
if (key.size() < prefix.size() || std::search(key.begin(), key.end(), prefix.begin(), prefix.end()) != key.begin()) {
it++;
continue;
}
it = m_records.erase(it);
}
return true;
}
MockableSQLiteDatabase::MockableSQLiteDatabase()
: SQLiteDatabase(fs::PathFromString("mock/"), fs::PathFromString("mock/wallet.dat"), DatabaseOptions(), /*mock=*/true)
{}
std::unique_ptr<WalletDatabase> CreateMockableWalletDatabase()
{
return std::make_unique<MockableDatabase>();
}
MockableDatabase& GetMockableDatabase(CWallet& wallet)
{
return dynamic_cast<MockableDatabase&>(wallet.GetDatabase());
return std::make_unique<MockableSQLiteDatabase>();
}
wallet::DescriptorScriptPubKeyMan* CreateDescriptor(CWallet& keystore, const std::string& desc_str, const bool success)