This is an automated email from the ASF dual-hosted git repository.

gangwu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-cpp.git


The following commit(s) were added to refs/heads/main by this push:
     new 52b9c5e  refactor(test): reorganize table metadata test suite (#343)
52b9c5e is described below

commit 52b9c5ea875580060c14d947a797fa1308211206
Author: Li Feiyang <[email protected]>
AuthorDate: Wed Nov 26 11:04:01 2025 +0800

    refactor(test): reorganize table metadata test suite (#343)
---
 src/iceberg/table_requirements.cc               |   1 -
 src/iceberg/test/CMakeLists.txt                 |   4 +-
 src/iceberg/test/meson.build                    |   2 +
 src/iceberg/test/table_metadata_builder_test.cc | 405 +++---------------------
 src/iceberg/test/table_requirement_test.cc      | 203 ++++++++++++
 src/iceberg/test/table_update_test.cc           |  89 ++++++
 6 files changed, 346 insertions(+), 358 deletions(-)

diff --git a/src/iceberg/table_requirements.cc 
b/src/iceberg/table_requirements.cc
index aae874e..fcb744a 100644
--- a/src/iceberg/table_requirements.cc
+++ b/src/iceberg/table_requirements.cc
@@ -19,7 +19,6 @@
 
 #include "iceberg/table_requirements.h"
 
-#include "iceberg/exception.h"
 #include "iceberg/table_metadata.h"
 #include "iceberg/table_update.h"
 
diff --git a/src/iceberg/test/CMakeLists.txt b/src/iceberg/test/CMakeLists.txt
index 302789e..f0c6027 100644
--- a/src/iceberg/test/CMakeLists.txt
+++ b/src/iceberg/test/CMakeLists.txt
@@ -82,8 +82,10 @@ add_iceberg_test(table_test
                  json_internal_test.cc
                  pending_update_test.cc
                  schema_json_test.cc
-                 table_metadata_builder_test.cc
                  table_test.cc
+                 table_metadata_builder_test.cc
+                 table_requirement_test.cc
+                 table_update_test.cc
                  test_common.cc)
 
 add_iceberg_test(expression_test
diff --git a/src/iceberg/test/meson.build b/src/iceberg/test/meson.build
index 6fbe82d..4e5d30a 100644
--- a/src/iceberg/test/meson.build
+++ b/src/iceberg/test/meson.build
@@ -48,7 +48,9 @@ iceberg_tests = {
             'json_internal_test.cc',
             'schema_json_test.cc',
             'table_metadata_builder_test.cc',
+            'table_requirement_test.cc',
             'table_test.cc',
+            'table_update_test.cc',
             'test_common.cc',
         ),
     },
diff --git a/src/iceberg/test/table_metadata_builder_test.cc 
b/src/iceberg/test/table_metadata_builder_test.cc
index 9b98048..f49a60d 100644
--- a/src/iceberg/test/table_metadata_builder_test.cc
+++ b/src/iceberg/test/table_metadata_builder_test.cc
@@ -20,62 +20,40 @@
 #include <memory>
 #include <string>
 
-#include <gmock/gmock-matchers.h>
 #include <gtest/gtest.h>
 
 #include "iceberg/partition_spec.h"
+#include "iceberg/snapshot.h"
 #include "iceberg/sort_order.h"
 #include "iceberg/table_metadata.h"
-#include "iceberg/table_requirement.h"
-#include "iceberg/table_requirements.h"
 #include "iceberg/table_update.h"
 #include "iceberg/test/matchers.h"
 
 namespace iceberg {
 
-// Helper functions to reduce test boilerplate
 namespace {
 
-// Generate requirements and return them
-std::vector<std::unique_ptr<TableRequirement>> GenerateRequirements(
-    const TableUpdate& update, const TableMetadata* base) {
-  TableUpdateContext context(base, /*is_replace=*/false);
-  EXPECT_THAT(update.GenerateRequirements(context), IsOk());
-
-  auto requirements = context.Build();
-  EXPECT_THAT(requirements, IsOk());
-  return std::move(requirements.value());
+// Helper function to create base metadata for tests
+std::unique_ptr<TableMetadata> CreateBaseMetadata() {
+  auto metadata = std::make_unique<TableMetadata>();
+  metadata->format_version = 2;
+  metadata->table_uuid = "test-uuid-1234";
+  metadata->location = "s3://bucket/test";
+  metadata->last_sequence_number = 0;
+  metadata->last_updated_ms = TimePointMs{std::chrono::milliseconds(1000)};
+  metadata->last_column_id = 0;
+  metadata->default_spec_id = PartitionSpec::kInitialSpecId;
+  metadata->last_partition_id = 0;
+  metadata->current_snapshot_id = Snapshot::kInvalidSnapshotId;
+  metadata->default_sort_order_id = SortOrder::kInitialSortOrderId;
+  metadata->next_row_id = TableMetadata::kInitialRowId;
+  return metadata;
 }
 
 }  // namespace
 
-// Test fixture for TableMetadataBuilder tests
-class TableMetadataBuilderTest : public ::testing::Test {
- protected:
-  void SetUp() override {
-    // Create a base metadata for update tests
-    base_metadata_ = std::make_unique<TableMetadata>();
-    base_metadata_->format_version = 2;
-    base_metadata_->table_uuid = "test-uuid-1234";
-    base_metadata_->location = "s3://bucket/test";
-    base_metadata_->last_sequence_number = 0;
-    base_metadata_->last_updated_ms = 
TimePointMs{std::chrono::milliseconds(1000)};
-    base_metadata_->last_column_id = 0;
-    base_metadata_->default_spec_id = PartitionSpec::kInitialSpecId;
-    base_metadata_->last_partition_id = 0;
-    base_metadata_->current_snapshot_id = Snapshot::kInvalidSnapshotId;
-    base_metadata_->default_sort_order_id = SortOrder::kInitialSortOrderId;
-    base_metadata_->next_row_id = TableMetadata::kInitialRowId;
-  }
-
-  std::unique_ptr<TableMetadata> base_metadata_;
-};
-
-// ============================================================================
-// TableMetadataBuilder - Basic Construction Tests
-// ============================================================================
-
-TEST_F(TableMetadataBuilderTest, BuildFromEmpty) {
+// test construction of TableMetadataBuilder
+TEST(TableMetadataBuilderTest, BuildFromEmpty) {
   auto builder = TableMetadataBuilder::BuildFromEmpty(2);
   ASSERT_NE(builder, nullptr);
 
@@ -91,8 +69,9 @@ TEST_F(TableMetadataBuilderTest, BuildFromEmpty) {
   EXPECT_EQ(metadata->current_snapshot_id, Snapshot::kInvalidSnapshotId);
 }
 
-TEST_F(TableMetadataBuilderTest, BuildFromExisting) {
-  auto builder = TableMetadataBuilder::BuildFrom(base_metadata_.get());
+TEST(TableMetadataBuilderTest, BuildFromExisting) {
+  auto base = CreateBaseMetadata();
+  auto builder = TableMetadataBuilder::BuildFrom(base.get());
   ASSERT_NE(builder, nullptr);
 
   ICEBERG_UNWRAP_OR_FAIL(auto metadata, builder->Build());
@@ -103,345 +82,59 @@ TEST_F(TableMetadataBuilderTest, BuildFromExisting) {
   EXPECT_EQ(metadata->location, "s3://bucket/test");
 }
 
-// ============================================================================
-// TableMetadataBuilder - AssignUUID Tests
-// ============================================================================
-
-TEST_F(TableMetadataBuilderTest, AssignUUIDForNewTable) {
+// Test AssignUUID method
+TEST(TableMetadataBuilderTest, AssignUUID) {
+  // Assign UUID for new table
   auto builder = TableMetadataBuilder::BuildFromEmpty(2);
   builder->AssignUUID("new-uuid-5678");
-
   ICEBERG_UNWRAP_OR_FAIL(auto metadata, builder->Build());
   EXPECT_EQ(metadata->table_uuid, "new-uuid-5678");
-}
 
-TEST_F(TableMetadataBuilderTest, AssignUUIDAndUpdateExisting) {
-  auto builder = TableMetadataBuilder::BuildFrom(base_metadata_.get());
+  // Update existing table's UUID
+  auto base = CreateBaseMetadata();
+  builder = TableMetadataBuilder::BuildFrom(base.get());
   builder->AssignUUID("updated-uuid-9999");
-
-  ICEBERG_UNWRAP_OR_FAIL(auto metadata, builder->Build());
+  ICEBERG_UNWRAP_OR_FAIL(metadata, builder->Build());
   EXPECT_EQ(metadata->table_uuid, "updated-uuid-9999");
-}
 
-TEST_F(TableMetadataBuilderTest, AssignUUIDWithEmptyUUID) {
-  auto builder = TableMetadataBuilder::BuildFromEmpty(2);
+  // Empty UUID should fail
+  builder = TableMetadataBuilder::BuildFromEmpty(2);
   builder->AssignUUID("");
-
   ASSERT_THAT(builder->Build(), HasErrorMessage("Cannot assign empty UUID"));
-}
 
-TEST_F(TableMetadataBuilderTest, AssignUUIDWithSameUUID) {
-  auto builder = TableMetadataBuilder::BuildFrom(base_metadata_.get());
-  builder->AssignUUID("test-uuid-1234");  // Same UUID
-
-  ICEBERG_UNWRAP_OR_FAIL(auto metadata, builder->Build());
+  // Assign same UUID (no-op)
+  base = CreateBaseMetadata();
+  builder = TableMetadataBuilder::BuildFrom(base.get());
+  builder->AssignUUID("test-uuid-1234");
+  ICEBERG_UNWRAP_OR_FAIL(metadata, builder->Build());
   EXPECT_EQ(metadata->table_uuid, "test-uuid-1234");
-}
 
-TEST_F(TableMetadataBuilderTest, AssignUUIDWithAutoGenerate) {
-  auto builder = TableMetadataBuilder::BuildFromEmpty(2);
-  builder->AssignUUID();  // Auto-generate
-
-  ICEBERG_UNWRAP_OR_FAIL(auto metadata, builder->Build());
+  // Auto-generate UUID
+  builder = TableMetadataBuilder::BuildFromEmpty(2);
+  builder->AssignUUID();
+  ICEBERG_UNWRAP_OR_FAIL(metadata, builder->Build());
   EXPECT_FALSE(metadata->table_uuid.empty());
-}
 
-TEST_F(TableMetadataBuilderTest, AssignUUIDAndCaseInsensitiveComparison) {
-  base_metadata_->table_uuid = "TEST-UUID-ABCD";
-  auto builder = TableMetadataBuilder::BuildFrom(base_metadata_.get());
+  // Case insensitive comparison
+  base = CreateBaseMetadata();
+  base->table_uuid = "TEST-UUID-ABCD";
+  builder = TableMetadataBuilder::BuildFrom(base.get());
   builder->AssignUUID("test-uuid-abcd");  // Different case - should be no-op
-
-  ICEBERG_UNWRAP_OR_FAIL(auto metadata, builder->Build());
+  ICEBERG_UNWRAP_OR_FAIL(metadata, builder->Build());
   EXPECT_EQ(metadata->table_uuid, "TEST-UUID-ABCD");  // Original case 
preserved
 }
 
-// ============================================================================
-// TableUpdate - ApplyTo Tests
-// ============================================================================
-
-TEST_F(TableMetadataBuilderTest, TableUpdateWithAssignUUID) {
+// Test applying TableUpdate to builder
+TEST(TableMetadataBuilderTest, ApplyUpdate) {
+  // Apply AssignUUID update
   auto builder = TableMetadataBuilder::BuildFromEmpty(2);
-
   table::AssignUUID update("apply-uuid");
   update.ApplyTo(*builder);
+  // TODO(Li Feiyang): Add more update and `apply` once other build methods are
+  // implemented
 
   ICEBERG_UNWRAP_OR_FAIL(auto metadata, builder->Build());
   EXPECT_EQ(metadata->table_uuid, "apply-uuid");
 }
 
-// ============================================================================
-// TableUpdate - GenerateRequirements Tests
-// ============================================================================
-
-TEST_F(TableMetadataBuilderTest,
-       TableUpdateWithAssignUUIDAndGenerateRequirementsForNewTable) {
-  table::AssignUUID update("new-uuid");
-
-  auto requirements = GenerateRequirements(update, nullptr);
-  EXPECT_TRUE(requirements.empty());  // No requirements for new table
-}
-
-TEST_F(TableMetadataBuilderTest,
-       TableUpdateWithAssignUUIDAndGenerateRequirementsForExistingTable) {
-  table::AssignUUID update("new-uuid");
-
-  auto requirements = GenerateRequirements(update, base_metadata_.get());
-  EXPECT_EQ(requirements.size(), 1);  // Should generate AssertUUID requirement
-}
-
-TEST_F(TableMetadataBuilderTest,
-       TableUpdateWithAssignUUIDAndGenerateRequirementsWithEmptyUUID) {
-  base_metadata_->table_uuid = "";
-  table::AssignUUID update("new-uuid");
-
-  auto requirements = GenerateRequirements(update, base_metadata_.get());
-  EXPECT_TRUE(requirements.empty());  // No requirement when base has no UUID
-}
-
-// ============================================================================
-// TableRequirement - Validate Tests
-// ============================================================================
-
-TEST_F(TableMetadataBuilderTest, TableRequirementAssertUUIDSuccess) {
-  table::AssertUUID requirement("test-uuid-1234");
-
-  ASSERT_THAT(requirement.Validate(base_metadata_.get()), IsOk());
-}
-
-TEST_F(TableMetadataBuilderTest, TableRequirementAssertUUIDMismatch) {
-  table::AssertUUID requirement("wrong-uuid");
-
-  auto status = requirement.Validate(base_metadata_.get());
-  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
-  EXPECT_THAT(status, HasErrorMessage("UUID does not match"));
-}
-
-TEST_F(TableMetadataBuilderTest, TableRequirementAssertUUIDNullBase) {
-  table::AssertUUID requirement("any-uuid");
-
-  auto status = requirement.Validate(nullptr);
-  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
-  EXPECT_THAT(status, HasErrorMessage("metadata is missing"));
-}
-
-TEST_F(TableMetadataBuilderTest, TableRequirementAssertUUIDCaseInsensitive) {
-  base_metadata_->table_uuid = "TEST-UUID-1234";
-  table::AssertUUID requirement("test-uuid-1234");
-
-  ASSERT_THAT(requirement.Validate(base_metadata_.get()), IsOk());
-}
-
-TEST_F(TableMetadataBuilderTest, TableRequirementAssertCurrentSchemaIDSuccess) 
{
-  base_metadata_->current_schema_id = 5;
-  table::AssertCurrentSchemaID requirement(5);
-
-  ASSERT_THAT(requirement.Validate(base_metadata_.get()), IsOk());
-}
-
-TEST_F(TableMetadataBuilderTest, 
TableRequirementAssertCurrentSchemaIDMismatch) {
-  base_metadata_->current_schema_id = 5;
-  table::AssertCurrentSchemaID requirement(10);
-
-  auto status = requirement.Validate(base_metadata_.get());
-  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
-  EXPECT_THAT(status, HasErrorMessage("schema ID does not match"));
-}
-
-TEST_F(TableMetadataBuilderTest, 
TableRequirementAssertCurrentSchemaIDNullBase) {
-  table::AssertCurrentSchemaID requirement(5);
-
-  auto status = requirement.Validate(nullptr);
-  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
-  EXPECT_THAT(status, HasErrorMessage("metadata is missing"));
-}
-
-TEST_F(TableMetadataBuilderTest, TableRequirementAssertCurrentSchemaIDNotSet) {
-  base_metadata_->current_schema_id = std::nullopt;
-  table::AssertCurrentSchemaID requirement(5);
-
-  auto status = requirement.Validate(base_metadata_.get());
-  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
-  EXPECT_THAT(status, HasErrorMessage("schema ID is not set"));
-}
-
-TEST_F(TableMetadataBuilderTest, TableRequirementAssertDoesNotExistSuccess) {
-  table::AssertDoesNotExist requirement;
-
-  ASSERT_THAT(requirement.Validate(nullptr), IsOk());
-}
-
-TEST_F(TableMetadataBuilderTest, 
TableRequirementAssertDoesNotExistTableExists) {
-  table::AssertDoesNotExist requirement;
-
-  auto status = requirement.Validate(base_metadata_.get());
-  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
-  EXPECT_THAT(status, HasErrorMessage("table already exists"));
-}
-
-TEST_F(TableMetadataBuilderTest, TableRequirementAssertRefSnapshotIDSuccess) {
-  auto ref = std::make_shared<SnapshotRef>();
-  ref->snapshot_id = 100;
-  ref->retention = SnapshotRef::Branch{};
-  base_metadata_->refs["main"] = ref;
-
-  table::AssertRefSnapshotID requirement("main", 100);
-
-  ASSERT_THAT(requirement.Validate(base_metadata_.get()), IsOk());
-}
-
-TEST_F(TableMetadataBuilderTest, TableRequirementAssertRefSnapshotIDMismatch) {
-  auto ref = std::make_shared<SnapshotRef>();
-  ref->snapshot_id = 100;
-  ref->retention = SnapshotRef::Branch{};
-  base_metadata_->refs["main"] = ref;
-
-  table::AssertRefSnapshotID requirement("main", 200);
-
-  auto status = requirement.Validate(base_metadata_.get());
-  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
-  EXPECT_THAT(status, HasErrorMessage("has changed"));
-}
-
-TEST_F(TableMetadataBuilderTest, 
TableRequirementAssertRefSnapshotIDRefMissing) {
-  table::AssertRefSnapshotID requirement("missing-ref", 100);
-
-  auto status = requirement.Validate(base_metadata_.get());
-  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
-  EXPECT_THAT(status, HasErrorMessage("is missing"));
-}
-
-// Removed TableRequirementAssertRefSnapshotIDNullBase test
-// Java implementation doesn't check for null base, so passing nullptr would 
cause
-// undefined behavior This matches Java's assumption that base is never null 
when Validate
-// is called
-
-TEST_F(TableMetadataBuilderTest, 
TableRequirementAssertRefSnapshotIDNulloptSuccess) {
-  // Ref should not exist, and it doesn't
-  table::AssertRefSnapshotID requirement("nonexistent", std::nullopt);
-
-  ASSERT_THAT(requirement.Validate(base_metadata_.get()), IsOk());
-}
-
-TEST_F(TableMetadataBuilderTest, 
TableRequirementAssertRefSnapshotIDNulloptButExists) {
-  auto ref = std::make_shared<SnapshotRef>();
-  ref->snapshot_id = 100;
-  ref->retention = SnapshotRef::Branch{};
-  base_metadata_->refs["main"] = ref;
-
-  table::AssertRefSnapshotID requirement("main", std::nullopt);
-
-  auto status = requirement.Validate(base_metadata_.get());
-  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
-  EXPECT_THAT(status, HasErrorMessage("created concurrently"));
-}
-
-TEST_F(TableMetadataBuilderTest, 
TableRequirementAssertLastAssignedFieldIdSuccess) {
-  base_metadata_->last_column_id = 10;
-  table::AssertLastAssignedFieldId requirement(10);
-
-  ASSERT_THAT(requirement.Validate(base_metadata_.get()), IsOk());
-}
-
-TEST_F(TableMetadataBuilderTest, 
TableRequirementAssertLastAssignedFieldIdMismatch) {
-  base_metadata_->last_column_id = 10;
-  table::AssertLastAssignedFieldId requirement(15);
-
-  auto status = requirement.Validate(base_metadata_.get());
-  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
-  EXPECT_THAT(status, HasErrorMessage("last assigned field ID does not 
match"));
-}
-
-TEST_F(TableMetadataBuilderTest, 
TableRequirementAssertLastAssignedFieldIdNullBase) {
-  table::AssertLastAssignedFieldId requirement(10);
-
-  EXPECT_THAT(requirement.Validate(nullptr), IsOk());
-}
-
-TEST_F(TableMetadataBuilderTest, 
TableRequirementAssertLastAssignedPartitionIdSuccess) {
-  base_metadata_->last_partition_id = 5;
-  table::AssertLastAssignedPartitionId requirement(5);
-
-  ASSERT_THAT(requirement.Validate(base_metadata_.get()), IsOk());
-}
-
-TEST_F(TableMetadataBuilderTest, 
TableRequirementAssertLastAssignedPartitionIdMismatch) {
-  base_metadata_->last_partition_id = 5;
-  table::AssertLastAssignedPartitionId requirement(8);
-
-  auto status = requirement.Validate(base_metadata_.get());
-  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
-  EXPECT_THAT(status, HasErrorMessage("last assigned partition ID does not 
match"));
-}
-
-TEST_F(TableMetadataBuilderTest, 
TableRequirementAssertLastAssignedPartitionIdNullBase) {
-  table::AssertLastAssignedPartitionId requirement(5);
-
-  auto status = requirement.Validate(nullptr);
-  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
-  EXPECT_THAT(status, HasErrorMessage("metadata is missing"));
-}
-
-TEST_F(TableMetadataBuilderTest, TableRequirementAssertDefaultSpecIDSuccess) {
-  base_metadata_->default_spec_id = 3;
-  table::AssertDefaultSpecID requirement(3);
-
-  ASSERT_THAT(requirement.Validate(base_metadata_.get()), IsOk());
-}
-
-TEST_F(TableMetadataBuilderTest, TableRequirementAssertDefaultSpecIDMismatch) {
-  base_metadata_->default_spec_id = 3;
-  table::AssertDefaultSpecID requirement(7);
-
-  auto status = requirement.Validate(base_metadata_.get());
-  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
-  EXPECT_THAT(status, HasErrorMessage("spec changed"));
-}
-
-TEST_F(TableMetadataBuilderTest, 
TableRequirementAssertDefaultSortOrderIDSuccess) {
-  base_metadata_->default_sort_order_id = 2;
-  table::AssertDefaultSortOrderID requirement(2);
-
-  ASSERT_THAT(requirement.Validate(base_metadata_.get()), IsOk());
-}
-
-TEST_F(TableMetadataBuilderTest, 
TableRequirementAssertDefaultSortOrderIDMismatch) {
-  base_metadata_->default_sort_order_id = 2;
-  table::AssertDefaultSortOrderID requirement(4);
-
-  auto status = requirement.Validate(base_metadata_.get());
-  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
-  EXPECT_THAT(status, HasErrorMessage("sort order changed"));
-}
-
-// ============================================================================
-// Integration Tests - End-to-End Workflow
-// ============================================================================
-
-TEST_F(TableMetadataBuilderTest, IntegrationCreateTableWithUUID) {
-  auto builder = TableMetadataBuilder::BuildFromEmpty(2);
-  builder->AssignUUID("integration-test-uuid");
-
-  ICEBERG_UNWRAP_OR_FAIL(auto metadata, builder->Build());
-  EXPECT_EQ(metadata->table_uuid, "integration-test-uuid");
-  EXPECT_EQ(metadata->format_version, 2);
-}
-
-TEST_F(TableMetadataBuilderTest, IntegrationOptimisticConcurrencyControl) {
-  table::AssignUUID update("new-uuid");
-
-  // Generate and validate requirements
-  auto requirements = GenerateRequirements(update, base_metadata_.get());
-  for (const auto& req : requirements) {
-    auto val_status = req->Validate(base_metadata_.get());
-    ASSERT_THAT(val_status, IsOk()) << "Requirement validation failed";
-  }
-
-  // Apply update and build
-  auto builder = TableMetadataBuilder::BuildFrom(base_metadata_.get());
-  update.ApplyTo(*builder);
-
-  ICEBERG_UNWRAP_OR_FAIL(auto metadata, builder->Build());
-  ASSERT_NE(metadata, nullptr);
-}
-
 }  // namespace iceberg
diff --git a/src/iceberg/test/table_requirement_test.cc 
b/src/iceberg/test/table_requirement_test.cc
new file mode 100644
index 0000000..24af87d
--- /dev/null
+++ b/src/iceberg/test/table_requirement_test.cc
@@ -0,0 +1,203 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "iceberg/table_requirement.h"
+
+#include <memory>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include "iceberg/snapshot.h"
+#include "iceberg/table_metadata.h"
+#include "iceberg/test/matchers.h"
+
+namespace iceberg {
+
+TEST(TableRequirementTest, AssertUUID) {
+  auto base = std::make_unique<TableMetadata>();
+  base->table_uuid = "test-uuid-1234";
+
+  // Success - UUID matches
+  table::AssertUUID requirement("test-uuid-1234");
+  ASSERT_THAT(requirement.Validate(base.get()), IsOk());
+
+  // UUID mismatch
+  table::AssertUUID wrong_uuid("wrong-uuid");
+  auto status = wrong_uuid.Validate(base.get());
+  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
+  EXPECT_THAT(status, HasErrorMessage("UUID does not match"));
+
+  // Null base metadata
+  table::AssertUUID any_uuid("any-uuid");
+  status = any_uuid.Validate(nullptr);
+  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
+  EXPECT_THAT(status, HasErrorMessage("metadata is missing"));
+
+  // Case insensitive UUID comparison
+  base->table_uuid = "TEST-UUID-1234";
+  table::AssertUUID lowercase_uuid("test-uuid-1234");
+  ASSERT_THAT(lowercase_uuid.Validate(base.get()), IsOk());
+}
+
+TEST(TableRequirementTest, AssertCurrentSchemaID) {
+  auto base = std::make_unique<TableMetadata>();
+  base->current_schema_id = 5;
+
+  // Success - schema ID matches
+  table::AssertCurrentSchemaID requirement(5);
+  ASSERT_THAT(requirement.Validate(base.get()), IsOk());
+
+  // Schema ID mismatch
+  table::AssertCurrentSchemaID wrong_id(10);
+  auto status = wrong_id.Validate(base.get());
+  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
+  EXPECT_THAT(status, HasErrorMessage("schema ID does not match"));
+
+  // Null base metadata
+  table::AssertCurrentSchemaID req_for_null(5);
+  status = req_for_null.Validate(nullptr);
+  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
+  EXPECT_THAT(status, HasErrorMessage("metadata is missing"));
+
+  // Schema ID not set
+  base->current_schema_id = std::nullopt;
+  table::AssertCurrentSchemaID req_for_nullopt(5);
+  status = req_for_nullopt.Validate(base.get());
+  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
+  EXPECT_THAT(status, HasErrorMessage("schema ID is not set"));
+}
+
+TEST(TableRequirementTest, AssertDoesNotExist) {
+  // Success - table does not exist (null metadata)
+  table::AssertDoesNotExist requirement;
+  ASSERT_THAT(requirement.Validate(nullptr), IsOk());
+
+  // Table already exists
+  auto base = std::make_unique<TableMetadata>();
+  auto status = requirement.Validate(base.get());
+  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
+  EXPECT_THAT(status, HasErrorMessage("table already exists"));
+}
+
+TEST(TableRequirementTest, AssertRefSnapshotID) {
+  auto base = std::make_unique<TableMetadata>();
+  auto ref = std::make_shared<SnapshotRef>();
+  ref->snapshot_id = 100;
+  ref->retention = SnapshotRef::Branch{};
+  base->refs["main"] = ref;
+
+  // Success - ref snapshot ID matches
+  table::AssertRefSnapshotID requirement("main", 100);
+  ASSERT_THAT(requirement.Validate(base.get()), IsOk());
+
+  // Snapshot ID mismatch
+  table::AssertRefSnapshotID wrong_id("main", 200);
+  auto status = wrong_id.Validate(base.get());
+  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
+  EXPECT_THAT(status, HasErrorMessage("has changed"));
+
+  // Ref missing
+  table::AssertRefSnapshotID missing_ref("missing-ref", 100);
+  status = missing_ref.Validate(base.get());
+  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
+  EXPECT_THAT(status, HasErrorMessage("is missing"));
+
+  // Ref should not exist and doesn't (nullopt snapshot ID)
+  table::AssertRefSnapshotID nonexistent("nonexistent", std::nullopt);
+  ASSERT_THAT(nonexistent.Validate(base.get()), IsOk());
+
+  // Ref should not exist but does (nullopt snapshot ID but ref exists)
+  table::AssertRefSnapshotID exists_but_shouldnt("main", std::nullopt);
+  status = exists_but_shouldnt.Validate(base.get());
+  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
+  EXPECT_THAT(status, HasErrorMessage("created concurrently"));
+}
+
+TEST(TableRequirementTest, AssertLastAssignedFieldId) {
+  auto base = std::make_unique<TableMetadata>();
+  base->last_column_id = 10;
+
+  // Success - field ID matches
+  table::AssertLastAssignedFieldId requirement(10);
+  ASSERT_THAT(requirement.Validate(base.get()), IsOk());
+
+  // Field ID mismatch
+  table::AssertLastAssignedFieldId wrong_id(15);
+  auto status = wrong_id.Validate(base.get());
+  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
+  EXPECT_THAT(status, HasErrorMessage("last assigned field ID does not 
match"));
+
+  // Null base metadata (should succeed)
+  table::AssertLastAssignedFieldId req_for_null(10);
+  EXPECT_THAT(req_for_null.Validate(nullptr), IsOk());
+}
+
+TEST(TableRequirementTest, AssertLastAssignedPartitionId) {
+  auto base = std::make_unique<TableMetadata>();
+  base->last_partition_id = 5;
+
+  // Success - partition ID matches
+  table::AssertLastAssignedPartitionId requirement(5);
+  ASSERT_THAT(requirement.Validate(base.get()), IsOk());
+
+  // Partition ID mismatch
+  table::AssertLastAssignedPartitionId wrong_id(8);
+  auto status = wrong_id.Validate(base.get());
+  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
+  EXPECT_THAT(status, HasErrorMessage("last assigned partition ID does not 
match"));
+
+  // Null base metadata
+  table::AssertLastAssignedPartitionId req_for_null(5);
+  status = req_for_null.Validate(nullptr);
+  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
+  EXPECT_THAT(status, HasErrorMessage("metadata is missing"));
+}
+
+TEST(TableRequirementTest, AssertDefaultSpecID) {
+  auto base = std::make_unique<TableMetadata>();
+  base->default_spec_id = 3;
+
+  // Success - spec ID matches
+  table::AssertDefaultSpecID requirement(3);
+  ASSERT_THAT(requirement.Validate(base.get()), IsOk());
+
+  // Spec ID mismatch
+  table::AssertDefaultSpecID wrong_id(7);
+  auto status = wrong_id.Validate(base.get());
+  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
+  EXPECT_THAT(status, HasErrorMessage("spec changed"));
+}
+
+TEST(TableRequirementTest, AssertDefaultSortOrderID) {
+  auto base = std::make_unique<TableMetadata>();
+  base->default_sort_order_id = 2;
+
+  // Success - sort order ID matches
+  table::AssertDefaultSortOrderID requirement(2);
+  ASSERT_THAT(requirement.Validate(base.get()), IsOk());
+
+  // Sort order ID mismatch
+  table::AssertDefaultSortOrderID wrong_id(4);
+  auto status = wrong_id.Validate(base.get());
+  EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
+  EXPECT_THAT(status, HasErrorMessage("sort order changed"));
+}
+
+}  // namespace iceberg
diff --git a/src/iceberg/test/table_update_test.cc 
b/src/iceberg/test/table_update_test.cc
new file mode 100644
index 0000000..9607db5
--- /dev/null
+++ b/src/iceberg/test/table_update_test.cc
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "iceberg/table_update.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "iceberg/partition_spec.h"
+#include "iceberg/snapshot.h"
+#include "iceberg/sort_order.h"
+#include "iceberg/table_metadata.h"
+#include "iceberg/table_requirement.h"
+#include "iceberg/table_requirements.h"
+#include "iceberg/test/matchers.h"
+
+namespace iceberg {
+
+namespace {
+
+// Helper function to generate requirements
+std::vector<std::unique_ptr<TableRequirement>> GenerateRequirements(
+    const TableUpdate& update, const TableMetadata* base) {
+  TableUpdateContext context(base, /*is_replace=*/false);
+  EXPECT_THAT(update.GenerateRequirements(context), IsOk());
+
+  auto requirements = context.Build();
+  EXPECT_THAT(requirements, IsOk());
+  return std::move(requirements.value());
+}
+
+// Helper function to create base metadata for tests
+std::unique_ptr<TableMetadata> CreateBaseMetadata() {
+  auto metadata = std::make_unique<TableMetadata>();
+  metadata->format_version = 2;
+  metadata->table_uuid = "test-uuid-1234";
+  metadata->location = "s3://bucket/test";
+  metadata->last_sequence_number = 0;
+  metadata->last_updated_ms = TimePointMs{std::chrono::milliseconds(1000)};
+  metadata->last_column_id = 0;
+  metadata->default_spec_id = PartitionSpec::kInitialSpecId;
+  metadata->last_partition_id = 0;
+  metadata->current_snapshot_id = Snapshot::kInvalidSnapshotId;
+  metadata->default_sort_order_id = SortOrder::kInitialSortOrderId;
+  metadata->next_row_id = TableMetadata::kInitialRowId;
+  return metadata;
+}
+
+}  // namespace
+
+// Test GenerateRequirements for AssignUUID update
+TEST(TableUpdateTest, AssignUUIDGenerateRequirements) {
+  table::AssignUUID update("new-uuid");
+
+  // New table - no requirements
+  auto new_table_reqs = GenerateRequirements(update, nullptr);
+  EXPECT_TRUE(new_table_reqs.empty());
+
+  // Existing table - should generate AssertUUID requirement
+  auto base = CreateBaseMetadata();
+  auto existing_table_reqs = GenerateRequirements(update, base.get());
+  EXPECT_EQ(existing_table_reqs.size(), 1);
+
+  // Existing table with empty UUID - no requirements
+  base->table_uuid = "";
+  auto empty_uuid_reqs = GenerateRequirements(update, base.get());
+  EXPECT_TRUE(empty_uuid_reqs.empty());
+}
+
+}  // namespace iceberg

Reply via email to