LCOV - code coverage report
Current view: top level - backend/engine/semantic/stubs - keys_test.cc (source / functions) Coverage Total Hit
Test: _coverage_report.dat Lines: 100.0 % 75 75
Test Date: 2026-07-02 21:01:18 Functions: 100.0 % 8 8

            Line data    Source code
       1              : #include "backend/engine/semantic/stubs/keys.h"
       2              : 
       3              : #include <string>
       4              : #include <vector>
       5              : 
       6              : #include "absl/status/status.h"
       7              : #include "absl/status/statusor.h"
       8              : #include "backend/engine/semantic/error.h"
       9              : #include "backend/engine/semantic/value.h"
      10              : #include "gmock/gmock.h"
      11              : #include "googlesql/public/type.h"
      12              : #include "googlesql/public/type.pb.h"
      13              : #include "googlesql/public/value.h"
      14              : #include "gtest/gtest.h"
      15              : 
      16              : namespace bigquery_emulator {
      17              : namespace backend {
      18              : namespace engine {
      19              : namespace semantic {
      20              : namespace stubs {
      21              : namespace {
      22              : 
      23              : using ::testing::HasSubstr;
      24              : 
      25            1 : TEST(KeysNewKeysetTest, ReturnsSentinelBytesForStringArg) {
      26              :   // The contract pins the sentinel byte-for-byte so client-library
      27              :   // probes can assert on `KEYS.NEW_KEYSET(...)`'s exact return
      28              :   // (e.g. checking the leading `bigquery-emulator:keyset:v1:`
      29              :   // marker before round-tripping through any downstream consumer).
      30              :   // The trailing segment echoes the requested key type so the
      31              :   // sentinel still carries the salient input parameter -- BigQuery
      32              :   // documents that the returned BYTES depend on the key type, and
      33              :   // the placeholder preserves that observable difference.
      34            1 :   Value aead_key = Value::String("AEAD_AES_GCM_256");
      35            1 :   auto r = KeysNewKeyset({aead_key});
      36            2 :   ASSERT_TRUE(r.ok()) << r.status();
      37            1 :   EXPECT_EQ(r->type_kind(), ::googlesql::TYPE_BYTES);
      38            1 :   EXPECT_EQ(r->bytes_value(), "bigquery-emulator:keyset:v1:AEAD_AES_GCM_256");
      39              : 
      40            1 :   Value deterministic_key =
      41            1 :       Value::String("DETERMINISTIC_AEAD_AES_SIV_CMAC_256");
      42            1 :   auto other = KeysNewKeyset({deterministic_key});
      43            2 :   ASSERT_TRUE(other.ok()) << other.status();
      44            1 :   EXPECT_EQ(other->bytes_value(),
      45            1 :             "bigquery-emulator:keyset:v1:DETERMINISTIC_AEAD_AES_SIV_CMAC_256");
      46              :   // The two BigQuery-distinct inputs surface as two distinct BYTES
      47              :   // outputs. A future regression that hard-codes a single byte
      48              :   // string for every input would silently approximate the
      49              :   // BigQuery contract; this assertion catches it.
      50            1 :   EXPECT_NE(r->bytes_value(), other->bytes_value());
      51            1 : }
      52              : 
      53            1 : TEST(KeysNewKeysetTest, NullStringPropagatesToNullBytes) {
      54              :   // Standard BigQuery scalar contract: NULL input -> NULL output of
      55              :   // the documented return type. The semantic executor's SAFE-mode
      56              :   // unwrap relies on the function itself producing a typed NULL
      57              :   // (NULL BYTES) rather than `Value::Null()` so the surrounding
      58              :   // projection's column-type tag remains BYTES.
      59            1 :   Value null_key = Value::NullString();
      60            1 :   auto r = KeysNewKeyset({null_key});
      61            2 :   ASSERT_TRUE(r.ok()) << r.status();
      62            1 :   EXPECT_TRUE(r->is_null());
      63            1 :   EXPECT_EQ(r->type_kind(), ::googlesql::TYPE_BYTES);
      64            1 : }
      65              : 
      66            1 : TEST(KeysNewKeysetTest, RejectsWrongArity) {
      67              :   // 0 or 2+ arguments is a caller bug. The analyzer should reject
      68              :   // it before the dispatch even fires; this is defense-in-depth so
      69              :   // a downstream caller invoking the helper directly gets a clean
      70              :   // INVALID_ARGUMENT instead of an out-of-bounds access.
      71            1 :   auto zero = KeysNewKeyset({});
      72            1 :   EXPECT_EQ(zero.status().code(), absl::StatusCode::kInvalidArgument);
      73            1 :   EXPECT_THAT(std::string(zero.status().message()),
      74            1 :               HasSubstr("KEYS.NEW_KEYSET"));
      75              : 
      76            1 :   Value key_a = Value::String("a");
      77            1 :   Value key_b = Value::String("b");
      78            1 :   auto two = KeysNewKeyset({key_a, key_b});
      79            1 :   EXPECT_EQ(two.status().code(), absl::StatusCode::kInvalidArgument);
      80            1 : }
      81              : 
      82            1 : TEST(KeysNewKeysetTest, RejectsNonStringArg) {
      83              :   // Wrong type also surfaces INVALID_ARGUMENT. The analyzer
      84              :   // normally inserts an implicit cast from the literal kind to the
      85              :   // declared signature kind; if this branch fires the caller has
      86              :   // bypassed the analyzer (or hit a future overload the stub does
      87              :   // not yet model) and we surface a clean failure rather than
      88              :   // emitting a sentinel keyed on an INT64.
      89            1 :   Value wrong_type = Value::Int64(7);
      90            1 :   auto r = KeysNewKeyset({wrong_type});
      91            1 :   EXPECT_EQ(r.status().code(), absl::StatusCode::kInvalidArgument);
      92            1 :   EXPECT_THAT(std::string(r.status().message()), HasSubstr("STRING"));
      93            1 : }
      94              : 
      95            1 : TEST(KeysKeysetLengthTest, ReturnsOneForAnyNonNullBytes) {
      96              :   // BigQuery's `NEW_KEYSET` always returns a single-key keyset;
      97              :   // the stub pins `KEYSET_LENGTH` to the matching answer. The
      98              :   // value is identical for the sentinel BYTES the emulator emits
      99              :   // and any other BYTES the caller might pass -- the local-stub
     100              :   // posture is "accept-and-return-shaped-answer", not "validate
     101              :   // the keyset envelope".
     102            1 :   Value sentinel = Value::Bytes("bigquery-emulator:keyset:v1:AEAD_AES_GCM_256");
     103            1 :   auto from_sentinel = KeysKeysetLength({sentinel});
     104            2 :   ASSERT_TRUE(from_sentinel.ok()) << from_sentinel.status();
     105            1 :   EXPECT_EQ(from_sentinel->type_kind(), ::googlesql::TYPE_INT64);
     106            1 :   EXPECT_EQ(from_sentinel->int64_value(), 1);
     107              : 
     108            1 :   Value arbitrary = Value::Bytes("\x01\x02\x03");
     109            1 :   auto from_arbitrary = KeysKeysetLength({arbitrary});
     110            2 :   ASSERT_TRUE(from_arbitrary.ok()) << from_arbitrary.status();
     111            1 :   EXPECT_EQ(from_arbitrary->int64_value(), 1);
     112              : 
     113            1 :   Value empty = Value::Bytes("");
     114            1 :   auto from_empty = KeysKeysetLength({empty});
     115            2 :   ASSERT_TRUE(from_empty.ok()) << from_empty.status();
     116            1 :   EXPECT_EQ(from_empty->int64_value(), 1);
     117            1 : }
     118              : 
     119            1 : TEST(KeysKeysetLengthTest, NullBytesPropagatesToNullInt64) {
     120              :   // Same NULL-propagation contract as the NEW_KEYSET stub. NULL ->
     121              :   // NULL INT64 so the projection column type tag stays INT64.
     122            1 :   Value null_keyset = Value::NullBytes();
     123            1 :   auto r = KeysKeysetLength({null_keyset});
     124            2 :   ASSERT_TRUE(r.ok()) << r.status();
     125            1 :   EXPECT_TRUE(r->is_null());
     126            1 :   EXPECT_EQ(r->type_kind(), ::googlesql::TYPE_INT64);
     127            1 : }
     128              : 
     129            1 : TEST(KeysKeysetLengthTest, RejectsWrongArity) {
     130            1 :   auto zero = KeysKeysetLength({});
     131            1 :   EXPECT_EQ(zero.status().code(), absl::StatusCode::kInvalidArgument);
     132            1 :   EXPECT_THAT(std::string(zero.status().message()),
     133            1 :               HasSubstr("KEYS.KEYSET_LENGTH"));
     134              : 
     135            1 :   Value key_a = Value::Bytes("a");
     136            1 :   Value key_b = Value::Bytes("b");
     137            1 :   auto two = KeysKeysetLength({key_a, key_b});
     138            1 :   EXPECT_EQ(two.status().code(), absl::StatusCode::kInvalidArgument);
     139            1 : }
     140              : 
     141            1 : TEST(KeysKeysetLengthTest, RejectsNonBytesArg) {
     142              :   // Wrong type -> INVALID_ARGUMENT. Same reasoning as
     143              :   // `RejectsNonStringArg` on the NEW_KEYSET stub.
     144            1 :   Value not_bytes = Value::String("not-bytes");
     145            1 :   auto r = KeysKeysetLength({not_bytes});
     146            1 :   EXPECT_EQ(r.status().code(), absl::StatusCode::kInvalidArgument);
     147            1 :   EXPECT_THAT(std::string(r.status().message()), HasSubstr("BYTES"));
     148            1 : }
     149              : 
     150              : }  // namespace
     151              : }  // namespace stubs
     152              : }  // namespace semantic
     153              : }  // namespace engine
     154              : }  // namespace backend
     155              : }  // namespace bigquery_emulator
        

Generated by: LCOV version 2.0-1