LCOV - code coverage report
Current view: top level - backend/schema - googlesql_to_bq_test.cc (source / functions) Coverage Total Hit
Test: _coverage_report.dat Lines: 97.6 % 124 121
Test Date: 2026-07-02 21:01:18 Functions: 100.0 % 7 7

            Line data    Source code
       1              : // Unit tests for the googlesql::Type -> proto FieldSchema reflector.
       2              : //
       3              : // We exercise the mapping by constructing GoogleSQL types via a
       4              : // `TypeFactory` (the same path the analyzer uses) and asserting on
       5              : // the resulting `FieldSchema` proto. Tests cover every BigQuery
       6              : // scalar, ARRAY/STRUCT containers, and the error cases for kinds we
       7              : // do not yet model.
       8              : 
       9              : #include "backend/schema/googlesql_to_bq.h"
      10              : 
      11              : #include <memory>
      12              : #include <string>
      13              : #include <utility>
      14              : #include <vector>
      15              : 
      16              : #include "absl/status/status.h"
      17              : #include "absl/strings/string_view.h"
      18              : #include "googlesql/public/type.h"
      19              : #include "googlesql/public/types/struct_type.h"
      20              : #include "googlesql/public/types/type_factory.h"
      21              : #include "gtest/gtest.h"
      22              : #include "proto/emulator.pb.h"
      23              : 
      24              : namespace bigquery_emulator {
      25              : namespace backend {
      26              : namespace schema {
      27              : namespace {
      28              : 
      29              : using ::googlesql::StructType;
      30              : using ::googlesql::Type;
      31              : using ::googlesql::TypeFactory;
      32              : 
      33              : class TypeReflectionTest : public ::testing::Test {
      34              :  protected:
      35              :   TypeFactory factory_{};
      36              : };
      37              : 
      38            1 : TEST_F(TypeReflectionTest, ScalarsRoundTripToBigQueryNames) {
      39            1 :   struct Case {
      40            1 :     const Type* type = nullptr;
      41            1 :     absl::string_view expected;
      42            1 :   };
      43            1 :   const Case cases[] = {
      44            1 :       {factory_.get_bool(), "BOOL"},
      45            1 :       {factory_.get_int64(), "INT64"},
      46            1 :       {factory_.get_double(), "FLOAT64"},
      47            1 :       {factory_.get_string(), "STRING"},
      48            1 :       {factory_.get_bytes(), "BYTES"},
      49            1 :       {factory_.get_date(), "DATE"},
      50            1 :       {factory_.get_time(), "TIME"},
      51            1 :       {factory_.get_datetime(), "DATETIME"},
      52            1 :       {factory_.get_timestamp(), "TIMESTAMP"},
      53            1 :       {factory_.get_numeric(), "NUMERIC"},
      54            1 :       {factory_.get_bignumeric(), "BIGNUMERIC"},
      55            1 :       {factory_.get_json(), "JSON"},
      56            1 :       {factory_.get_geography(), "GEOGRAPHY"},
      57            1 :   };
      58           13 :   for (const Case& c : cases) {
      59           13 :     v1::FieldSchema out;
      60           13 :     absl::Status status = TypeToFieldSchema(c.type, "col", &out);
      61           26 :     ASSERT_TRUE(status.ok()) << status.message();
      62           13 :     EXPECT_EQ(out.name(), "col");
      63           13 :     EXPECT_EQ(out.type(), c.expected);
      64           13 :     EXPECT_EQ(out.mode(), "");
      65           13 :     EXPECT_EQ(out.fields_size(), 0);
      66           13 :   }
      67            1 : }
      68              : 
      69            1 : TEST_F(TypeReflectionTest, ArrayOfScalarBecomesRepeated) {
      70            1 :   const Type* array_type = nullptr;
      71            1 :   ASSERT_TRUE(factory_.MakeArrayType(factory_.get_string(), &array_type).ok());
      72            1 :   v1::FieldSchema out;
      73            1 :   ASSERT_TRUE(TypeToFieldSchema(array_type, "tags", &out).ok());
      74            1 :   EXPECT_EQ(out.name(), "tags");
      75            1 :   EXPECT_EQ(out.type(), "STRING");
      76            1 :   EXPECT_EQ(out.mode(), "REPEATED");
      77            1 : }
      78              : 
      79            1 : TEST_F(TypeReflectionTest, NestedStructRecursesIntoFields) {
      80            1 :   std::vector<StructType::StructField> inner_fields = {
      81            1 :       {"city", factory_.get_string()},
      82            1 :       {"zip", factory_.get_int64()},
      83            1 :   };
      84            1 :   const StructType* inner = nullptr;
      85            1 :   ASSERT_TRUE(factory_.MakeStructType(inner_fields, &inner).ok());
      86            1 :   std::vector<StructType::StructField> outer_fields = {
      87            1 :       {"name", factory_.get_string()},
      88            1 :       {"address", inner},
      89            1 :   };
      90            1 :   const StructType* outer = nullptr;
      91            1 :   ASSERT_TRUE(factory_.MakeStructType(outer_fields, &outer).ok());
      92              : 
      93            1 :   v1::FieldSchema out;
      94            1 :   ASSERT_TRUE(TypeToFieldSchema(outer, "person", &out).ok());
      95            1 :   EXPECT_EQ(out.name(), "person");
      96            1 :   EXPECT_EQ(out.type(), "STRUCT");
      97            1 :   ASSERT_EQ(out.fields_size(), 2);
      98            1 :   EXPECT_EQ(out.fields(0).name(), "name");
      99            1 :   EXPECT_EQ(out.fields(0).type(), "STRING");
     100            1 :   EXPECT_EQ(out.fields(1).name(), "address");
     101            1 :   EXPECT_EQ(out.fields(1).type(), "STRUCT");
     102            1 :   ASSERT_EQ(out.fields(1).fields_size(), 2);
     103            1 :   EXPECT_EQ(out.fields(1).fields(0).name(), "city");
     104            1 :   EXPECT_EQ(out.fields(1).fields(0).type(), "STRING");
     105            1 :   EXPECT_EQ(out.fields(1).fields(1).name(), "zip");
     106            1 :   EXPECT_EQ(out.fields(1).fields(1).type(), "INT64");
     107            1 : }
     108              : 
     109            1 : TEST_F(TypeReflectionTest, ArrayOfStructPropagatesNestedFields) {
     110            1 :   std::vector<StructType::StructField> fields = {
     111            1 :       {"id", factory_.get_int64()},
     112            1 :       {"label", factory_.get_string()},
     113            1 :   };
     114            1 :   const StructType* st = nullptr;
     115            1 :   ASSERT_TRUE(factory_.MakeStructType(fields, &st).ok());
     116            1 :   const Type* arr = nullptr;
     117            1 :   ASSERT_TRUE(factory_.MakeArrayType(st, &arr).ok());
     118              : 
     119            1 :   v1::FieldSchema out;
     120            1 :   ASSERT_TRUE(TypeToFieldSchema(arr, "items", &out).ok());
     121            1 :   EXPECT_EQ(out.name(), "items");
     122            1 :   EXPECT_EQ(out.type(), "STRUCT");
     123            1 :   EXPECT_EQ(out.mode(), "REPEATED");
     124            1 :   ASSERT_EQ(out.fields_size(), 2);
     125            1 :   EXPECT_EQ(out.fields(0).type(), "INT64");
     126            1 :   EXPECT_EQ(out.fields(1).type(), "STRING");
     127            1 : }
     128              : 
     129            1 : TEST_F(TypeReflectionTest, StructFieldWithRepeatedArraySetsMode) {
     130            1 :   const Type* arr = nullptr;
     131            1 :   ASSERT_TRUE(factory_.MakeArrayType(factory_.get_string(), &arr).ok());
     132            1 :   std::vector<StructType::StructField> fields = {
     133            1 :       {"tags", arr},
     134            1 :       {"count", factory_.get_int64()},
     135            1 :   };
     136            1 :   const StructType* st = nullptr;
     137            1 :   ASSERT_TRUE(factory_.MakeStructType(fields, &st).ok());
     138              : 
     139            1 :   v1::FieldSchema out;
     140            1 :   ASSERT_TRUE(TypeToFieldSchema(st, "rec", &out).ok());
     141            1 :   ASSERT_EQ(out.fields_size(), 2);
     142            1 :   EXPECT_EQ(out.fields(0).name(), "tags");
     143            1 :   EXPECT_EQ(out.fields(0).type(), "STRING");
     144            1 :   EXPECT_EQ(out.fields(0).mode(), "REPEATED");
     145            1 :   EXPECT_EQ(out.fields(1).name(), "count");
     146            1 :   EXPECT_EQ(out.fields(1).type(), "INT64");
     147            1 :   EXPECT_EQ(out.fields(1).mode(), "");
     148            1 : }
     149              : 
     150            1 : TEST_F(TypeReflectionTest, ArrayOfArrayIsRejected) {
     151            1 :   const Type* inner = nullptr;
     152            1 :   ASSERT_TRUE(factory_.MakeArrayType(factory_.get_int64(), &inner).ok());
     153            1 :   const Type* outer = nullptr;
     154              :   // GoogleSQL's MakeArrayType refuses ARRAY<ARRAY<...>> with an
     155              :   // InvalidArgument; we mirror that signal here. If a future
     156              :   // GoogleSQL release ever loosens the restriction, this test will
     157              :   // fail and we want to keep the BigQuery side rejecting it
     158              :   // explicitly anyway.
     159            1 :   absl::Status make = factory_.MakeArrayType(inner, &outer);
     160            1 :   if (make.ok()) {
     161            0 :     v1::FieldSchema out;
     162            0 :     EXPECT_EQ(TypeToFieldSchema(outer, "bad", &out).code(),
     163            0 :               absl::StatusCode::kInvalidArgument);
     164            1 :   } else {
     165            1 :     EXPECT_EQ(make.code(), absl::StatusCode::kInvalidArgument);
     166            1 :   }
     167            1 : }
     168              : 
     169            1 : TEST_F(TypeReflectionTest, NullTypeAndNullOutputAreInvalid) {
     170            1 :   v1::FieldSchema out;
     171            1 :   EXPECT_EQ(TypeToFieldSchema(nullptr, "x", &out).code(),
     172            1 :             absl::StatusCode::kInvalidArgument);
     173            1 :   EXPECT_EQ(TypeToFieldSchema(factory_.get_int64(), "x", nullptr).code(),
     174            1 :             absl::StatusCode::kInvalidArgument);
     175            1 : }
     176              : 
     177              : }  // namespace
     178              : }  // namespace schema
     179              : }  // namespace backend
     180              : }  // namespace bigquery_emulator
        

Generated by: LCOV version 2.0-1