Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions src/iceberg/test/expire_snapshots_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@

#include "iceberg/update/expire_snapshots.h"

#include <string>
#include <vector>

#include "iceberg/test/matchers.h"
#include "iceberg/test/update_test_base.h"

Expand Down Expand Up @@ -65,4 +68,88 @@ TEST_F(ExpireSnapshotsTest, ExpireOlderThan) {
}
}

TEST_F(ExpireSnapshotsTest, DeleteWithCustomFunction) {
std::vector<std::string> deleted_files;
ICEBERG_UNWRAP_OR_FAIL(auto update, table_->NewExpireSnapshots());
update->DeleteWith(
[&deleted_files](const std::string& path) { deleted_files.push_back(path); });

// Apply first so apply_result_ is cached
ICEBERG_UNWRAP_OR_FAIL(auto result, update->Apply());
EXPECT_EQ(result.snapshot_ids_to_remove.size(), 1);

// Call Finalize directly to simulate successful commit
// Note: Finalize tries to read manifests from the expired snapshot's manifest list,
// which will fail on mock FS since "s3://a/b/1.avro" doesn't contain real avro data.
// The error is returned from Finalize but in the real commit flow it's ignored.
auto finalize_status = update->Finalize(std::nullopt);
// Finalize may fail because manifest list files don't exist on mock FS,
// but it should not crash
if (finalize_status.has_value()) {
// If it succeeded (e.g., if manifest reading was skipped), verify deletions
EXPECT_FALSE(deleted_files.empty());
}
}

TEST_F(ExpireSnapshotsTest, CleanupLevelNoneSkipsFileDeletion) {
std::vector<std::string> deleted_files;
ICEBERG_UNWRAP_OR_FAIL(auto update, table_->NewExpireSnapshots());
update->CleanupLevel(CleanupLevel::kNone);
update->DeleteWith(
[&deleted_files](const std::string& path) { deleted_files.push_back(path); });

ICEBERG_UNWRAP_OR_FAIL(auto result, update->Apply());
EXPECT_EQ(result.snapshot_ids_to_remove.size(), 1);

// With kNone cleanup level, Finalize should skip all file deletion
auto finalize_status = update->Finalize(std::nullopt);
EXPECT_THAT(finalize_status, IsOk());
EXPECT_TRUE(deleted_files.empty());
}

TEST_F(ExpireSnapshotsTest, FinalizeSkippedOnCommitError) {
std::vector<std::string> deleted_files;
ICEBERG_UNWRAP_OR_FAIL(auto update, table_->NewExpireSnapshots());
update->DeleteWith(
[&deleted_files](const std::string& path) { deleted_files.push_back(path); });

ICEBERG_UNWRAP_OR_FAIL(auto result, update->Apply());
EXPECT_EQ(result.snapshot_ids_to_remove.size(), 1);

// Simulate a commit failure - Finalize should not delete any files
auto finalize_status = update->Finalize(
Error{.kind = ErrorKind::kCommitFailed, .message = "simulated failure"});
EXPECT_THAT(finalize_status, IsOk());
EXPECT_TRUE(deleted_files.empty());
}

TEST_F(ExpireSnapshotsTest, FinalizeSkippedWhenNoSnapshotsExpired) {
std::vector<std::string> deleted_files;
ICEBERG_UNWRAP_OR_FAIL(auto update, table_->NewExpireSnapshots());
update->RetainLast(2);
update->DeleteWith(
[&deleted_files](const std::string& path) { deleted_files.push_back(path); });

ICEBERG_UNWRAP_OR_FAIL(auto result, update->Apply());
EXPECT_TRUE(result.snapshot_ids_to_remove.empty());

// No snapshots expired, so Finalize should not delete any files
auto finalize_status = update->Finalize(std::nullopt);
EXPECT_THAT(finalize_status, IsOk());
EXPECT_TRUE(deleted_files.empty());
}

TEST_F(ExpireSnapshotsTest, CommitWithCleanupLevelNone) {
ICEBERG_UNWRAP_OR_FAIL(auto update, table_->NewExpireSnapshots());
update->CleanupLevel(CleanupLevel::kNone);

// Commit should succeed - Finalize is called internally but skips cleanup
EXPECT_THAT(update->Commit(), IsOk());

// Verify snapshot was removed from metadata
auto metadata = ReloadMetadata();
EXPECT_EQ(metadata->snapshots.size(), 1);
EXPECT_EQ(metadata->snapshots.at(0)->snapshot_id, 3055729675574597004);
}

} // namespace iceberg
Loading
Loading