#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace yams; using namespace yams::integrity; namespace { std::vector makeBytes(std::string_view payload) { std::vector bytes(payload.size()); std::memcpy(bytes.data(), payload.data(), payload.size()); return bytes; } } // namespace TEST_CASE("IntegrationSmoke.IntegrityAutoRepair_FromBackupDir", "[smoke][integrationsmoke]") { namespace fs = std::filesystem; // Prepare a temp working area fs::path temp = fs::temp_directory_path() / "yams-int-repair-smoke"; std::error_code ec; fs::remove_all(temp, ec); fs::create_directories(temp / "objects", ec); fs::create_directories(temp / "backup", ec); // Create storage engine storage::StorageEngine storage({.basePath = temp / "objects"}); // Create ref counter storage::ReferenceCounter::Config rcfg; rcfg.databasePath = temp / "refs.db"; auto refCounter = storage::createReferenceCounter(rcfg); // Prepare a block payload and its hash const std::string payload = "auto-repair"; std::vector bytes = makeBytes(payload); auto hasher = crypto::createSHA256Hasher(); hasher->init(); hasher->update(std::span(bytes.data(), bytes.size())); auto hash = hasher->finalize(); // Simulate that primary storage is missing the block, but backup dir has it { std::ofstream out(temp / "backup" / hash, std::ios::binary | std::ios::trunc); out.write(payload.data(), static_cast(payload.size())); } // Build verifier with auto repair VerificationConfig vcfg; vcfg.enableAutoRepair = true; vcfg.maxRepairAttempts = 1; IntegrityVerifier verifier(storage, static_cast(*refCounter), vcfg); // Wire RepairManager with a backup fetcher to our backup dir RepairManagerConfig rmCfg; rmCfg.backupFetcher = [&](const std::string& requested) -> Result> { fs::path p = temp / "backup" / requested; if (!fs::exists(p)) return Error{ErrorCode::NotFound, "no backup"}; std::ifstream in(p, std::ios::binary); std::vector buf((std::istreambuf_iterator(in)), {}); std::vector out(buf.size()); std::memcpy(out.data(), buf.data(), buf.size()); return out; }; auto rm = makeRepairManager(storage, rmCfg); verifier.setRepairManager(rm); // Verify the block: should be repaired auto result = verifier.verifyBlock(hash); CHECK(result.status == VerificationStatus::Repaired); // Confirm block is now stored auto retrieved = storage.retrieve(hash); REQUIRE(retrieved.has_value()); std::string restored(reinterpret_cast(retrieved.value().data()), retrieved.value().size()); CHECK(restored == payload); fs::remove_all(temp, ec); } TEST_CASE("IntegrationSmoke.IntegrityAutoRepair_FromP2PDir", "[smoke][integrationsmoke]") { namespace fs = std::filesystem; fs::path temp = fs::temp_directory_path() / "yams-int-repair-p2p-smoke"; std::error_code ec; fs::remove_all(temp, ec); fs::create_directories(temp / "objects", ec); fs::create_directories(temp / "p2p", ec); storage::StorageEngine storage({.basePath = temp / "objects"}); storage::ReferenceCounter::Config rcfg; rcfg.databasePath = temp / "refs.db"; auto refCounter = storage::createReferenceCounter(rcfg); const std::string payload = "auto-repair-p2p"; std::vector bytes = makeBytes(payload); auto hasher = crypto::createSHA256Hasher(); hasher->init(); hasher->update(std::span(bytes.data(), bytes.size())); auto hash = hasher->finalize(); { std::ofstream out(temp / "p2p" / hash, std::ios::binary | std::ios::trunc); out.write(payload.data(), static_cast(payload.size())); } VerificationConfig vcfg; vcfg.enableAutoRepair = true; IntegrityVerifier verifier(storage, static_cast(*refCounter), vcfg); RepairManagerConfig rmCfg; rmCfg.p2pFetcher = [&](const std::string& requested) -> Result> { fs::path p = temp / "p2p" / requested; if (!fs::exists(p)) return Error{ErrorCode::NotFound, "no p2p"}; std::ifstream in(p, std::ios::binary); std::vector buf((std::istreambuf_iterator(in)), {}); std::vector out(buf.size()); std::memcpy(out.data(), buf.data(), buf.size()); return out; }; verifier.setRepairManager(makeRepairManager(storage, rmCfg)); auto result = verifier.verifyBlock(hash); CHECK(result.status == VerificationStatus::Repaired); auto retrieved = storage.retrieve(hash); REQUIRE(retrieved.has_value()); std::string restored(reinterpret_cast(retrieved.value().data()), retrieved.value().size()); CHECK(restored == payload); fs::remove_all(temp, ec); } TEST_CASE("IntegrationSmoke.VerifyCLIRepairsFromBackupFlag", "[smoke][integrationsmoke]") { namespace fs = std::filesystem; fs::path temp = fs::temp_directory_path() / "yams-verify-cli-repair-smoke"; std::error_code ec; fs::remove_all(temp, ec); fs::create_directories(temp / "backup", ec); fs::create_directories(temp / "blocks", ec); const std::string payload = "cli-repair-backup"; std::vector bytes = makeBytes(payload); auto hasher = crypto::createSHA256Hasher(); hasher->init(); hasher->update(std::span(bytes.data(), bytes.size())); const std::string hash = hasher->finalize(); { std::ofstream out(temp / "backup" / hash, std::ios::binary | std::ios::trunc); REQUIRE(out.is_open()); out.write(payload.data(), static_cast(payload.size())); REQUIRE(out.good()); } CLI::App verifyApp{"verify"}; bool attemptRepair = false; std::string hashArg; std::string storageArg; std::string backupArg; verifyApp.add_option("--hash", hashArg)->required(); verifyApp.add_flag("--repair", attemptRepair); verifyApp.add_option("--repair-backup-dir", backupArg)->required(); verifyApp.add_option("--storage", storageArg)->required(); std::vector args = { "verify", "--hash", hash, "--repair", "--repair-backup-dir", (temp / "backup").string(), "--storage", temp.string(), }; std::vector argv; argv.reserve(args.size()); for (auto& arg : args) { argv.push_back(arg.data()); } int argc = static_cast(argv.size()); REQUIRE_NOTHROW(verifyApp.parse(argc, argv.data())); REQUIRE(attemptRepair); REQUIRE(hashArg == hash); fs::path storageRoot = fs::path(storageArg); fs::path backupRoot = fs::path(backupArg); storage::StorageEngine storageEngine({.basePath = storageRoot / "blocks"}); storage::ReferenceCounter::Config rcfg; rcfg.databasePath = storageRoot / "refs.db"; auto refCounterPtr = storage::createReferenceCounter(rcfg); VerificationConfig vcfg; vcfg.enableAutoRepair = attemptRepair; vcfg.maxRepairAttempts = 1; IntegrityVerifier verifier(storageEngine, static_cast(*refCounterPtr), vcfg); RepairManagerConfig rmCfg; rmCfg.backupFetcher = [backupRoot](const std::string& requested) -> Result> { fs::path p = backupRoot / requested; if (!fs::exists(p)) return Error{ErrorCode::NotFound, "backup not found"}; std::ifstream in(p, std::ios::binary); if (!in) return Error{ErrorCode::IOError, "open failed"}; std::vector buf((std::istreambuf_iterator(in)), {}); std::vector out(buf.size()); std::memcpy(out.data(), buf.data(), buf.size()); return out; }; verifier.setRepairManager(makeRepairManager(storageEngine, rmCfg)); auto result = verifier.verifyBlock(hashArg); CHECK(result.status == VerificationStatus::Repaired); auto retrieved = storageEngine.retrieve(hashArg); REQUIRE(retrieved.has_value()); std::string restored(reinterpret_cast(retrieved.value().data()), retrieved.value().size()); CHECK(restored == payload); fs::remove_all(temp, ec); }