|
18 | 18 | */ |
19 | 19 |
|
20 | 20 | #include <memory> |
| 21 | +#include <optional> |
21 | 22 | #include <string> |
22 | 23 | #include <vector> |
23 | 24 |
|
|
31 | 32 | #include "iceberg/expression/literal.h" |
32 | 33 | #include "iceberg/expression/predicate.h" |
33 | 34 | #include "iceberg/schema.h" |
| 35 | +#include "iceberg/schema_field.h" |
34 | 36 | #include "iceberg/test/matchers.h" |
35 | 37 | #include "iceberg/type.h" |
36 | 38 |
|
@@ -405,4 +407,129 @@ INSTANTIATE_TEST_SUITE_P( |
405 | 407 | return info.param.name; |
406 | 408 | }); |
407 | 409 |
|
| 410 | +// --- LiteralFromJson(json, type) type-aware tests --- |
| 411 | + |
| 412 | +struct LiteralFromJsonTypedParam { |
| 413 | + std::string name; |
| 414 | + nlohmann::json json; |
| 415 | + std::shared_ptr<Type> type; |
| 416 | + TypeId expected_type_id; |
| 417 | + std::optional<std::string> expected_str; |
| 418 | +}; |
| 419 | + |
| 420 | +class LiteralFromJsonTypedTest |
| 421 | + : public ::testing::TestWithParam<LiteralFromJsonTypedParam> {}; |
| 422 | + |
| 423 | +TEST_P(LiteralFromJsonTypedTest, Parses) { |
| 424 | + const auto& p = GetParam(); |
| 425 | + ICEBERG_UNWRAP_OR_FAIL(auto lit, LiteralFromJson(p.json, p.type.get())); |
| 426 | + EXPECT_EQ(lit.type()->type_id(), p.expected_type_id); |
| 427 | + if (p.expected_str) { |
| 428 | + EXPECT_EQ(lit.ToString(), *p.expected_str); |
| 429 | + } |
| 430 | +} |
| 431 | + |
| 432 | +INSTANTIATE_TEST_SUITE_P( |
| 433 | + LiteralFromJsonTyped, LiteralFromJsonTypedTest, |
| 434 | + ::testing::Values( |
| 435 | + LiteralFromJsonTypedParam{"Boolean", nlohmann::json(true), boolean(), |
| 436 | + TypeId::kBoolean, "true"}, |
| 437 | + LiteralFromJsonTypedParam{"Int", nlohmann::json(123), int32(), TypeId::kInt, |
| 438 | + "123"}, |
| 439 | + LiteralFromJsonTypedParam{"Long", nlohmann::json(9876543210LL), int64(), |
| 440 | + TypeId::kLong, "9876543210"}, |
| 441 | + LiteralFromJsonTypedParam{"Float", nlohmann::json(1.5), float32(), TypeId::kFloat, |
| 442 | + std::nullopt}, |
| 443 | + LiteralFromJsonTypedParam{"Double", nlohmann::json(3.14), float64(), |
| 444 | + TypeId::kDouble, std::nullopt}, |
| 445 | + LiteralFromJsonTypedParam{"String", nlohmann::json("hello"), string(), |
| 446 | + TypeId::kString, std::nullopt}, |
| 447 | + LiteralFromJsonTypedParam{"DateString", nlohmann::json("2024-01-15"), date(), |
| 448 | + TypeId::kDate, std::nullopt}, |
| 449 | + LiteralFromJsonTypedParam{"Uuid", |
| 450 | + nlohmann::json("f79c3e09-677c-4bbd-a479-3f349cb785e7"), |
| 451 | + uuid(), TypeId::kUuid, std::nullopt}, |
| 452 | + LiteralFromJsonTypedParam{"Binary", nlohmann::json("deadbeef"), binary(), |
| 453 | + TypeId::kBinary, std::nullopt}, |
| 454 | + LiteralFromJsonTypedParam{"Fixed", nlohmann::json("cafebabe"), fixed(4), |
| 455 | + TypeId::kFixed, std::nullopt}, |
| 456 | + LiteralFromJsonTypedParam{"DecimalMatchingScale", nlohmann::json("123.4500"), |
| 457 | + decimal(9, 4), TypeId::kDecimal, "123.4500"}, |
| 458 | + LiteralFromJsonTypedParam{"DecimalScaleZero", nlohmann::json("2"), decimal(9, 0), |
| 459 | + TypeId::kDecimal, "2"}), |
| 460 | + [](const ::testing::TestParamInfo<LiteralFromJsonTypedParam>& info) { |
| 461 | + return info.param.name; |
| 462 | + }); |
| 463 | + |
| 464 | +struct InvalidLiteralFromJsonTypedParam { |
| 465 | + std::string name; |
| 466 | + nlohmann::json json; |
| 467 | + std::shared_ptr<Type> type; |
| 468 | +}; |
| 469 | + |
| 470 | +class InvalidLiteralFromJsonTypedTest |
| 471 | + : public ::testing::TestWithParam<InvalidLiteralFromJsonTypedParam> {}; |
| 472 | + |
| 473 | +TEST_P(InvalidLiteralFromJsonTypedTest, ReturnsError) { |
| 474 | + const auto& p = GetParam(); |
| 475 | + EXPECT_FALSE(LiteralFromJson(p.json, p.type.get()).has_value()); |
| 476 | +} |
| 477 | + |
| 478 | +INSTANTIATE_TEST_SUITE_P( |
| 479 | + LiteralFromJsonTyped, InvalidLiteralFromJsonTypedTest, |
| 480 | + ::testing::Values( |
| 481 | + InvalidLiteralFromJsonTypedParam{"BooleanTypeMismatch", nlohmann::json(42), |
| 482 | + boolean()}, |
| 483 | + InvalidLiteralFromJsonTypedParam{"DateTypeMismatch", nlohmann::json(true), |
| 484 | + date()}, |
| 485 | + InvalidLiteralFromJsonTypedParam{"UuidTypeMismatch", nlohmann::json(42), uuid()}, |
| 486 | + InvalidLiteralFromJsonTypedParam{"BinaryInvalidHex", nlohmann::json("xyz"), |
| 487 | + binary()}, |
| 488 | + InvalidLiteralFromJsonTypedParam{"FixedLengthMismatch", nlohmann::json("cafe12"), |
| 489 | + fixed(4)}, |
| 490 | + InvalidLiteralFromJsonTypedParam{"DecimalScaleMismatch", nlohmann::json("123.45"), |
| 491 | + decimal(9, 4)}, |
| 492 | + InvalidLiteralFromJsonTypedParam{"DecimalNotString", nlohmann::json(123.45), |
| 493 | + decimal(9, 2)}), |
| 494 | + [](const ::testing::TestParamInfo<InvalidLiteralFromJsonTypedParam>& info) { |
| 495 | + return info.param.name; |
| 496 | + }); |
| 497 | + |
| 498 | +struct SchemaAwarePredicateParam { |
| 499 | + std::string name; |
| 500 | + std::string field_name; |
| 501 | + std::shared_ptr<Type> field_type; |
| 502 | + nlohmann::json value; |
| 503 | +}; |
| 504 | + |
| 505 | +class SchemaAwarePredicateRoundTripTest |
| 506 | + : public ::testing::TestWithParam<SchemaAwarePredicateParam> {}; |
| 507 | + |
| 508 | +TEST_P(SchemaAwarePredicateRoundTripTest, RoundTrip) { |
| 509 | + const auto& p = GetParam(); |
| 510 | + auto schema = std::make_shared<Schema>( |
| 511 | + std::vector<SchemaField>{SchemaField::MakeOptional(1, p.field_name, p.field_type)}); |
| 512 | + nlohmann::json pred_json = {{"type", "eq"}, {"term", p.field_name}, {"value", p.value}}; |
| 513 | + ICEBERG_UNWRAP_OR_FAIL(auto expr, ExpressionFromJson(pred_json, schema.get())); |
| 514 | + ASSERT_NE(expr, nullptr); |
| 515 | +} |
| 516 | + |
| 517 | +INSTANTIATE_TEST_SUITE_P( |
| 518 | + LiteralFromJsonTyped, SchemaAwarePredicateRoundTripTest, |
| 519 | + ::testing::Values( |
| 520 | + SchemaAwarePredicateParam{"Date", "event_date", date(), "2024-01-15"}, |
| 521 | + SchemaAwarePredicateParam{"Time", "event_time", time(), "14:30:00"}, |
| 522 | + SchemaAwarePredicateParam{"Timestamp", "created_at", timestamp(), |
| 523 | + "2026-01-01T00:00:01.500"}, |
| 524 | + SchemaAwarePredicateParam{"TimestampTz", "updated_at", timestamp_tz(), |
| 525 | + "2026-01-01T00:00:01.500+00:00"}, |
| 526 | + SchemaAwarePredicateParam{"Uuid", "trace_id", uuid(), |
| 527 | + "f79c3e09-677c-4bbd-a479-3f349cb785e7"}, |
| 528 | + SchemaAwarePredicateParam{"Binary", "payload", binary(), "deadbeef"}, |
| 529 | + SchemaAwarePredicateParam{"Fixed", "hash", fixed(4), "cafebabe"}, |
| 530 | + SchemaAwarePredicateParam{"Decimal", "amount", decimal(9, 2), "123.45"}), |
| 531 | + [](const ::testing::TestParamInfo<SchemaAwarePredicateParam>& info) { |
| 532 | + return info.param.name; |
| 533 | + }); |
| 534 | + |
408 | 535 | } // namespace iceberg |
0 commit comments