Line data Source code
1 : // Matrix coverage for ParseParameterValue wire forms (plan 04).
2 : #include "absl/time/time.h"
3 : #include "backend/engine/semantic/value.h"
4 : #include "googlesql/public/numeric_value.h"
5 : #include "googlesql/public/type.h"
6 : #include "googlesql/public/value.h"
7 : #include "gtest/gtest.h"
8 :
9 : namespace bigquery_emulator {
10 : namespace backend {
11 : namespace engine {
12 : namespace semantic {
13 : namespace {
14 :
15 : struct ParameterMatrixCase {
16 : const char* type_kind;
17 : const char* type_json;
18 : const char* value_json;
19 : bool should_succeed;
20 : const char* label;
21 : };
22 :
23 1 : std::vector<ParameterMatrixCase> AcceptedMatrixCases() {
24 1 : return {
25 1 : {"TIMESTAMP",
26 1 : nullptr,
27 1 : "2026-06-22T10:00:00",
28 1 : true,
29 1 : "timestamp_naive_iso_t"},
30 1 : {"TIMESTAMP", nullptr, "2026-06-22 10:00:00", true, "timestamp_space"},
31 1 : {"TIMESTAMP", nullptr, "2026-06-22T10:00:00Z", true, "timestamp_z"},
32 1 : {"TIMESTAMP",
33 1 : nullptr,
34 1 : "2026-06-22T10:00:00+00:00",
35 1 : true,
36 1 : "timestamp_offset"},
37 1 : {"TIMESTAMP",
38 1 : nullptr,
39 1 : "2026-06-22T10:00:00.123456",
40 1 : true,
41 1 : "timestamp_fractional"},
42 1 : {"TIMESTAMP", nullptr, "2026-06-22", true, "timestamp_date_only"},
43 1 : {"DATE", nullptr, "2020-06-15", true, "date_iso"},
44 1 : {"DATETIME", nullptr, "2020-06-15 12:30:45", true, "datetime_space"},
45 1 : {"DATETIME", nullptr, "2020-06-15T12:30:45", true, "datetime_iso_t"},
46 1 : {"TIME", nullptr, "12:30:45", true, "time_hms"},
47 1 : {"TIME", nullptr, "12:30:45.123456", true, "time_fractional"},
48 1 : {"NUMERIC", nullptr, "3.14159", true, "numeric"},
49 1 : {"BIGNUMERIC",
50 1 : nullptr,
51 1 : "99999999999999999999999999999.999999999",
52 1 : true,
53 1 : "bignumeric"},
54 1 : {"BOOL", nullptr, "true", true, "bool_true"},
55 1 : {"BOOL", nullptr, "false", true, "bool_false"},
56 1 : {"BYTES", nullptr, "\"SGVsbG8=\"", true, "bytes_base64"},
57 1 : {"ARRAY", "STRING", R"(["a","b"])", true, "array_string"},
58 1 : {"STRUCT", "x:INT64,y:STRING", R"(["1","foo"])", true, "struct_mixed"},
59 1 : };
60 1 : }
61 :
62 1 : std::vector<ParameterMatrixCase> RejectedMatrixCases() {
63 1 : return {
64 1 : {"TIMESTAMP", nullptr, "not-a-timestamp", false, "timestamp_garbage"},
65 1 : {"DATE", nullptr, "2020-13-45", false, "date_invalid"},
66 1 : {"BOOL", nullptr, "yes", false, "bool_garbage"},
67 1 : {"BYTES", nullptr, "\"!!!\"", false, "bytes_invalid_base64"},
68 1 : };
69 1 : }
70 :
71 : class ParameterMatrixTest : public testing::TestWithParam<ParameterMatrixCase> {
72 : };
73 :
74 22 : TEST_P(ParameterMatrixTest, ParseParameterValue) {
75 22 : const ParameterMatrixCase& c = GetParam();
76 22 : absl::string_view type_json = c.type_json != nullptr ? c.type_json : "";
77 22 : auto result = ParseParameterValue(c.value_json, c.type_kind, type_json);
78 22 : if (c.should_succeed) {
79 36 : ASSERT_TRUE(result.ok()) << c.label << ": " << result.status();
80 18 : } else {
81 8 : ASSERT_FALSE(result.ok()) << c.label;
82 4 : }
83 22 : }
84 :
85 : INSTANTIATE_TEST_SUITE_P(
86 : Accepted,
87 : ParameterMatrixTest,
88 : testing::ValuesIn(AcceptedMatrixCases()),
89 : [](const testing::TestParamInfo<ParameterMatrixCase>& info) {
90 : return info.param.label;
91 : });
92 :
93 : INSTANTIATE_TEST_SUITE_P(
94 : Rejected,
95 : ParameterMatrixTest,
96 : testing::ValuesIn(RejectedMatrixCases()),
97 : [](const testing::TestParamInfo<ParameterMatrixCase>& info) {
98 : return info.param.label;
99 : });
100 :
101 1 : TEST(ValueParametersTest, TimestampNaiveIsoDefaultsToUtc) {
102 1 : auto v = ParseParameterValue("2026-06-22T10:00:00", "TIMESTAMP");
103 2 : ASSERT_TRUE(v.ok()) << v.status();
104 1 : EXPECT_EQ(absl::ToUnixSeconds(v->ToTime()), 1782122400);
105 1 : }
106 :
107 1 : TEST(ValueParametersTest, DateRoundTrips) {
108 1 : auto v = ParseParameterValue("2020-06-15", "DATE");
109 2 : ASSERT_TRUE(v.ok()) << v.status();
110 1 : EXPECT_EQ(v->type_kind(), ::googlesql::TYPE_DATE);
111 1 : EXPECT_FALSE(v->is_null());
112 1 : }
113 :
114 1 : TEST(ValueParametersTest, DatetimeSpaceForm) {
115 1 : auto v = ParseParameterValue("2020-06-15 12:30:45", "DATETIME");
116 2 : ASSERT_TRUE(v.ok()) << v.status();
117 1 : EXPECT_EQ(v->type_kind(), ::googlesql::TYPE_DATETIME);
118 1 : }
119 :
120 1 : TEST(ValueParametersTest, TimeFractionalSeconds) {
121 1 : auto v = ParseParameterValue("12:30:45.123456", "TIME");
122 2 : ASSERT_TRUE(v.ok()) << v.status();
123 1 : EXPECT_EQ(v->type_kind(), ::googlesql::TYPE_TIME);
124 1 : }
125 :
126 1 : TEST(ValueParametersTest, BytesBase64RoundTrips) {
127 1 : auto v = ParseParameterValue("\"SGVsbG8=\"", "BYTES");
128 2 : ASSERT_TRUE(v.ok()) << v.status();
129 1 : EXPECT_EQ(v->bytes_value(), "Hello");
130 1 : }
131 :
132 1 : TEST(ValueParametersTest, NumericAndBigNumeric) {
133 1 : auto n = ParseParameterValue("1.5", "NUMERIC");
134 2 : ASSERT_TRUE(n.ok()) << n.status();
135 1 : auto bn = ParseParameterValue("99999999999999999999999999999.999999999",
136 1 : "BIGNUMERIC");
137 2 : ASSERT_TRUE(bn.ok()) << bn.status();
138 1 : }
139 :
140 : } // namespace
141 : } // namespace semantic
142 : } // namespace engine
143 : } // namespace backend
144 : } // namespace bigquery_emulator
|