mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-09-11 21:20:39 +02:00
test: add missing coverage for CDBWrapper::Read() errors
CDBWrapper::Read() errors have no test coverage or documentation. Currently, the function can return false when deserialization fails (indistinguishable from a missing key) and throw dbwrapper_error on an internal database error. This commit introduces tests that pin both behaviors so we can work on improvements in the next commits without worrying about introducing a behavior change.
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
#include <uint256.h>
|
||||
#include <util/string.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <ranges>
|
||||
|
||||
@@ -186,6 +187,60 @@ BOOST_AUTO_TEST_CASE(dbwrapper_batch)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that Read() returns false (without throwing) when the stored value
|
||||
// fails to be deserialized
|
||||
BOOST_AUTO_TEST_CASE(dbwrapper_read_returns_false_on_deserialization_error)
|
||||
{
|
||||
for (const bool obfuscate : {false, true}) {
|
||||
const fs::path path{m_args.GetDataDirBase() / (obfuscate ? "dbwrapper_deser_obf" : "dbwrapper_deser_noobf")};
|
||||
CDBWrapper dbw({.path = path, .cache_bytes = 1 << 20, .wipe_data = true, .obfuscate = obfuscate});
|
||||
|
||||
constexpr uint8_t key{'X'};
|
||||
|
||||
// Write a single byte. uint256 requires 32 bytes, so reading this key
|
||||
// as uint256 must trigger a deserialization error inside Read()
|
||||
dbw.Write(key, uint8_t{0xFF});
|
||||
BOOST_CHECK(dbw.Exists(key));
|
||||
|
||||
// Read() must catch the deserialization exception and return false,
|
||||
// the same as if the key were absent
|
||||
uint256 result;
|
||||
BOOST_CHECK(!dbw.Read(key, result));
|
||||
}
|
||||
}
|
||||
|
||||
// Verify Read() throws dbwrapper_error due to an internal db error
|
||||
BOOST_AUTO_TEST_CASE(dbwrapper_read_throws_on_db_error)
|
||||
{
|
||||
const fs::path path{m_args.GetDataDirBase() / "dbwrapper_db_error"};
|
||||
constexpr uint8_t key{'Y'};
|
||||
|
||||
const auto make_db = [] (const fs::path& path, const bool force_compact) {
|
||||
return CDBWrapper({.path = path, .cache_bytes = 1 << 20, .obfuscate = false,
|
||||
.options = {.force_compact = force_compact}});
|
||||
};
|
||||
|
||||
// Write a value and close the database
|
||||
make_db(path, /*force_compact=*/false).Write(key, m_rng.rand256());
|
||||
|
||||
// Force compaction to ensure the data is written into the .ldb files
|
||||
// rather than left in the WAL.
|
||||
(void)make_db(path, /*force_compact=*/true);
|
||||
|
||||
// Corrupt every table so any subsequent Read() fails
|
||||
for (const auto& entry : fs::directory_iterator(path)) {
|
||||
if (entry.path().extension() == ".ldb") {
|
||||
std::ofstream{entry.path(), std::ios::binary | std::ios::trunc}
|
||||
.write("\xff", 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Read() should detect the issue now and throw
|
||||
const auto db{make_db(path, /*force_compact=*/false)};
|
||||
uint256 result;
|
||||
BOOST_CHECK_EXCEPTION(db.Read(key, result), dbwrapper_error, HasReason("Fatal LevelDB error"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(dbwrapper_iterator)
|
||||
{
|
||||
// Perform tests both obfuscated and non-obfuscated.
|
||||
|
||||
Reference in New Issue
Block a user