Skip to content

Commit 0010d0e

Browse files
test(serde): cover FileScanTask range and spec boundaries
1 parent 66b9a53 commit 0010d0e

2 files changed

Lines changed: 96 additions & 0 deletions

File tree

‎src/iceberg/test/json_serde_test.cc‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919

2020
#include <memory>
21+
#include <optional>
2122
#include <string>
2223
#include <string_view>
2324
#include <unordered_map>
@@ -1612,6 +1613,34 @@ TEST_F(FileScanTaskJsonTest, RoundTripsHistoricalSpecWithDroppedSourceField) {
16121613
EXPECT_EQ(serialized, json);
16131614
}
16141615

1616+
TEST_F(FileScanTaskJsonTest, RejectsNonNullPartitionForDroppedSourceField) {
1617+
auto json = JavaGolden();
1618+
json["schema"]["fields"] = nlohmann::json::array();
1619+
json["data-file"]["partition"] = {7};
1620+
1621+
// The embedded schema no longer contains the source type, so neither Java nor
1622+
// C++ can decode a non-null value for this historical partition field.
1623+
auto result = FileScanTaskFromJson(json);
1624+
EXPECT_THAT(result, IsError(ErrorKind::kNotSupported));
1625+
EXPECT_THAT(result, HasErrorMessage("Unsupported type for literal JSON parsing"));
1626+
}
1627+
1628+
TEST_F(FileScanTaskJsonTest, RejectsFilesWithMismatchedEmbeddedSpecId) {
1629+
for (bool mismatch_delete_file : {false, true}) {
1630+
SCOPED_TRACE(mismatch_delete_file ? "delete-file" : "data-file");
1631+
auto json = JavaGolden();
1632+
if (mismatch_delete_file) {
1633+
json["delete-files"][0]["spec-id"] = 1;
1634+
} else {
1635+
json["data-file"]["spec-id"] = 1;
1636+
}
1637+
1638+
auto result = FileScanTaskFromJson(json);
1639+
EXPECT_THAT(result, IsError(ErrorKind::kJsonParseError));
1640+
EXPECT_THAT(result, HasErrorMessage("Invalid partition spec id"));
1641+
}
1642+
}
1643+
16151644
TEST_F(FileScanTaskJsonTest, AcceptsMissingPartitionLikeJava) {
16161645
auto json = JavaGolden();
16171646
json["data-file"].erase("partition");
@@ -1684,6 +1713,21 @@ TEST_F(FileScanTaskJsonTest, RejectsInvalidContentRolesWhenSerializing) {
16841713
EXPECT_THAT(delete_result, HasErrorMessage("delete-file"));
16851714
}
16861715

1716+
TEST_F(FileScanTaskJsonTest, RejectsDeleteFilesWithDifferentSpecIdWhenSerializing) {
1717+
for (const auto& spec_id : {std::optional<int32_t>(1), std::optional<int32_t>()}) {
1718+
SCOPED_TRACE(spec_id.value_or(-1));
1719+
auto delete_file = DeleteFileForTask();
1720+
delete_file.partition_spec_id = spec_id;
1721+
FileScanTask task(std::make_shared<DataFile>(DataFileForTask()),
1722+
{std::make_shared<DataFile>(std::move(delete_file))});
1723+
1724+
auto result = ToJson(task, specs_, schema_);
1725+
EXPECT_THAT(result, IsError(ErrorKind::kValidationFailed));
1726+
EXPECT_THAT(result, HasErrorMessage("Invalid partition spec id"));
1727+
EXPECT_THAT(result, HasErrorMessage(spec_id ? "actual = 1" : "actual = null"));
1728+
}
1729+
}
1730+
16871731
TEST_F(FileScanTaskJsonTest, RejectsInvalidPartitionValueTypeWhenSerializing) {
16881732
auto data_file = DataFileForTask();
16891733
data_file.partition = PartitionValues({Literal::String("not-an-int")});

‎src/iceberg/test/rest_json_serde_test.cc‎

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2305,6 +2305,58 @@ TEST(FileScanTasksFromJsonTest, AcceptsWholeFileStartAndLength) {
23052305
IsOk());
23062306
}
23072307

2308+
TEST(FileScanTasksFromJsonTest, NullStartAndLengthUseWholeFileDefaults) {
2309+
auto json = R"([{
2310+
"data-file": {
2311+
"content": "data",
2312+
"file-path": "s3://bucket/data/file.parquet",
2313+
"file-format": "PARQUET",
2314+
"spec-id": 0,
2315+
"partition": [],
2316+
"file-size-in-bytes": 12345,
2317+
"record-count": 100
2318+
},
2319+
"start": null,
2320+
"length": null
2321+
}])"_json;
2322+
2323+
EXPECT_THAT(FileScanTasksFromJson(json, {}, UnpartitionedSpecs(), Schema({}, 0)),
2324+
IsOk());
2325+
}
2326+
2327+
TEST(FileScanTasksFromJsonTest, RejectsInvalidRangeIntegers) {
2328+
auto valid_json = R"([{
2329+
"data-file": {
2330+
"content": "data",
2331+
"file-path": "s3://bucket/data/file.parquet",
2332+
"file-format": "PARQUET",
2333+
"spec-id": 0,
2334+
"partition": [],
2335+
"file-size-in-bytes": 12345,
2336+
"record-count": 100
2337+
},
2338+
"start": 0,
2339+
"length": 12345
2340+
}])"_json;
2341+
2342+
for (std::string_view field : {"start", "length"}) {
2343+
for (const auto& [invalid_value, expected_error] :
2344+
{std::pair<nlohmann::json, std::string_view>{0.5, "must be an integer"},
2345+
{true, "must be an integer"},
2346+
{"0", "must be an integer"},
2347+
{18446744073709551615ULL, "out of range"}}) {
2348+
SCOPED_TRACE(testing::Message() << field << "=" << invalid_value.dump());
2349+
auto json = valid_json;
2350+
json[0][field] = invalid_value;
2351+
2352+
auto result = FileScanTasksFromJson(json, {}, UnpartitionedSpecs(), Schema({}, 0));
2353+
EXPECT_THAT(result, IsError(ErrorKind::kJsonParseError));
2354+
EXPECT_THAT(result, HasErrorMessage(field));
2355+
EXPECT_THAT(result, HasErrorMessage(expected_error));
2356+
}
2357+
}
2358+
}
2359+
23082360
TEST(FileScanTasksFromJsonTest, RejectsSplitTaskWhenEitherRangeFieldDiffers) {
23092361
auto json = R"([{
23102362
"data-file": {

0 commit comments

Comments
 (0)