| /* |
| * 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"); |
| EXPECT_EQ(TableRequirement::Kind::kAssertUUID, requirement.kind()); |
| 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); |
| EXPECT_EQ(TableRequirement::Kind::kAssertCurrentSchemaID, requirement.kind()); |
| 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")); |
| } |
| |
| TEST(TableRequirementTest, AssertDoesNotExist) { |
| // Success - table does not exist (null metadata) |
| table::AssertDoesNotExist requirement; |
| EXPECT_EQ(TableRequirement::Kind::kAssertDoesNotExist, requirement.kind()); |
| 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); |
| EXPECT_EQ(TableRequirement::Kind::kAssertRefSnapshotID, requirement.kind()); |
| 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); |
| EXPECT_EQ(TableRequirement::Kind::kAssertLastAssignedFieldId, requirement.kind()); |
| 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); |
| EXPECT_EQ(TableRequirement::Kind::kAssertLastAssignedPartitionId, requirement.kind()); |
| 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); |
| EXPECT_EQ(TableRequirement::Kind::kAssertDefaultSpecID, requirement.kind()); |
| 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); |
| EXPECT_EQ(TableRequirement::Kind::kAssertDefaultSortOrderID, requirement.kind()); |
| 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 |