Introduce MockableDatabase for wallet unit tests

MockableDatabase is a WalletDatabase that allows us to interact with the
records to change them independently from the wallet, as well as
changing the return values from within the tests. This will give us
greater flexibility in testing the wallet.
This commit is contained in:
Andrew Chow
2022-12-12 14:54:01 -05:00
parent 539452242e
commit 33c6245ac1
4 changed files with 167 additions and 51 deletions

View File

@@ -7,6 +7,7 @@
#include <chain.h>
#include <key.h>
#include <key_io.h>
#include <streams.h>
#include <test/util/setup_common.h>
#include <wallet/wallet.h>
#include <wallet/walletdb.h>
@@ -79,4 +80,93 @@ CTxDestination getNewDestination(CWallet& w, OutputType output_type)
return *Assert(w.GetNewDestination(output_type, ""));
}
DatabaseCursor::Status MockableCursor::Next(DataStream& key, DataStream& value)
{
if (!m_pass) {
return Status::FAIL;
}
if (m_cursor == m_cursor_end) {
return Status::DONE;
}
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.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.count(key_data) > 0;
}
bool MockableBatch::ErasePrefix(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;
}
std::unique_ptr<WalletDatabase> CreateMockableWalletDatabase(std::map<SerializeData, SerializeData> records)
{
return std::make_unique<MockableDatabase>(records);
}
MockableDatabase& GetMockableDatabase(CWallet& wallet)
{
return dynamic_cast<MockableDatabase&>(wallet.GetDatabase());
}
} // namespace wallet