Skip to content

Commit 9690529

Browse files
committed
test(json_serde): cover default-value serde in json_serde_test
Add SchemaField default-value coverage directly to json_serde_test (the binary that exercises json_serde.cc): one test pins the initial-default / write-default wire format (incl. their absence when unset), and one round-trips a field with both defaults through ToJson -> FieldFromJson for every primitive type, exercising the non-trivial date/timestamp/decimal/uuid/binary encodings the default path reuses.
1 parent 511d9c8 commit 9690529

1 file changed

Lines changed: 69 additions & 0 deletions

File tree

src/iceberg/test/json_serde_test.cc

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@
2323
#include <gtest/gtest.h>
2424
#include <nlohmann/json.hpp>
2525

26+
#include "iceberg/expression/literal.h"
2627
#include "iceberg/json_serde_internal.h"
2728
#include "iceberg/name_mapping.h"
2829
#include "iceberg/partition_spec.h"
2930
#include "iceberg/schema.h"
31+
#include "iceberg/schema_field.h"
3032
#include "iceberg/snapshot.h"
3133
#include "iceberg/sort_field.h"
3234
#include "iceberg/sort_order.h"
@@ -35,9 +37,11 @@
3537
#include "iceberg/table_update.h"
3638
#include "iceberg/test/matchers.h"
3739
#include "iceberg/transform.h"
40+
#include "iceberg/type.h"
3841
#include "iceberg/util/formatter.h" // IWYU pragma: keep
3942
#include "iceberg/util/macros.h" // IWYU pragma: keep
4043
#include "iceberg/util/timepoint.h"
44+
#include "iceberg/util/uuid.h"
4145

4246
namespace iceberg {
4347

@@ -71,6 +75,11 @@ Result<std::unique_ptr<NameMapping>> FromJsonHelper(const nlohmann::json& json)
7175
return NameMappingFromJson(json);
7276
}
7377

78+
template <>
79+
Result<std::unique_ptr<SchemaField>> FromJsonHelper(const nlohmann::json& json) {
80+
return FieldFromJson(json);
81+
}
82+
7483
// Helper function to reduce duplication in testing
7584
template <typename T>
7685
void TestJsonConversion(const T& obj, const nlohmann::json& expected_json) {
@@ -85,6 +94,66 @@ void TestJsonConversion(const T& obj, const nlohmann::json& expected_json) {
8594

8695
} // namespace
8796

97+
// Pins the wire format produced by ToJson(SchemaField) / FieldFromJson for
98+
// `initial-default` and `write-default`, including the absence of the keys when a
99+
// field carries no defaults.
100+
TEST(JsonInternalTest, SchemaFieldDefaultValues) {
101+
// Both defaults present.
102+
SchemaField with_both(/*field_id=*/1, "id", int32(), /*optional=*/false, /*doc=*/{},
103+
std::make_shared<const Literal>(Literal::Int(42)),
104+
std::make_shared<const Literal>(Literal::Int(7)));
105+
TestJsonConversion(
106+
with_both,
107+
R"({"id":1,"name":"id","required":true,"type":"int","initial-default":42,"write-default":7})"_json);
108+
109+
// Only an initial-default; write-default must not appear in the JSON.
110+
SchemaField initial_only(/*field_id=*/2, "name", string(), /*optional=*/true,
111+
/*doc=*/{},
112+
std::make_shared<const Literal>(Literal::String("n/a")),
113+
/*write_default=*/nullptr);
114+
TestJsonConversion(
115+
initial_only,
116+
R"({"id":2,"name":"name","required":false,"type":"string","initial-default":"n/a"})"_json);
117+
118+
// No defaults; neither key may appear.
119+
SchemaField no_defaults(/*field_id=*/3, "plain", int32(), /*optional=*/false);
120+
TestJsonConversion(no_defaults,
121+
R"({"id":3,"name":"plain","required":true,"type":"int"})"_json);
122+
}
123+
124+
// Round-trips a field carrying both defaults through ToJson -> FieldFromJson for
125+
// every primitive type, exercising the per-type single-value serialization the
126+
// default path reuses (date/timestamp/decimal/uuid/binary have non-trivial wire
127+
// encodings).
128+
TEST(JsonInternalTest, SchemaFieldDefaultValuesRoundTripAllTypes) {
129+
ICEBERG_UNWRAP_OR_FAIL(auto uuid_value,
130+
Uuid::FromString("f79c3e09-677c-4bbd-a479-3f349cb785e7"));
131+
std::vector<std::pair<std::shared_ptr<Type>, Literal>> cases;
132+
cases.emplace_back(boolean(), Literal::Boolean(true));
133+
cases.emplace_back(int32(), Literal::Int(-7));
134+
cases.emplace_back(int64(), Literal::Long(1234567890123LL));
135+
cases.emplace_back(float32(), Literal::Float(1.5f));
136+
cases.emplace_back(float64(), Literal::Double(2.5));
137+
cases.emplace_back(date(), Literal::Date(19738));
138+
cases.emplace_back(timestamp(), Literal::Timestamp(1719446400000000LL));
139+
cases.emplace_back(string(), Literal::String("hello"));
140+
cases.emplace_back(decimal(9, 2), Literal::Decimal(12345, 9, 2));
141+
cases.emplace_back(fixed(3), Literal::Fixed({0x01, 0x02, 0x03}));
142+
cases.emplace_back(binary(), Literal::Binary({0xDE, 0xAD, 0xBE, 0xEF}));
143+
cases.emplace_back(uuid(), Literal::UUID(uuid_value));
144+
145+
int32_t field_id = 1;
146+
for (const auto& [type, literal] : cases) {
147+
SchemaField field(field_id++, "f", type, /*optional=*/false, /*doc=*/{},
148+
std::make_shared<const Literal>(literal),
149+
std::make_shared<const Literal>(literal));
150+
auto json = ToJson(field);
151+
ICEBERG_UNWRAP_OR_FAIL(auto parsed, FieldFromJson(json));
152+
EXPECT_EQ(field, *parsed)
153+
<< "round-trip mismatch for type " << type->ToString() << ", json=" << json.dump();
154+
}
155+
}
156+
88157
TEST(JsonInternalTest, SortField) {
89158
auto identity_transform = Transform::Identity();
90159

0 commit comments

Comments
 (0)