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

            Line data    Source code
       1              : // Unit tests for `script::ExecuteAssert`.
       2              : //
       3              : // We drive a real `AnalyzeStatement` against a tiny `SimpleCatalog`
       4              : // (mirroring `executor_test.cc`) and run `ExecuteAssert` over the
       5              : // analyzer's resolved `ResolvedAssertStmt`. Tests pin every branch
       6              : // of the BigQuery-documented `ASSERT` contract:
       7              : //
       8              : //   * `ASSERT TRUE` -- OK.
       9              : //   * `ASSERT FALSE` -- INVALID_ARGUMENT carrying the
      10              : //     `kInvalidArgument` semantic-error reason and the default
      11              : //     "Assertion failed" message.
      12              : //   * `ASSERT FALSE AS '<msg>'` -- same code, message includes
      13              : //     `<msg>`.
      14              : //   * `ASSERT NULL` -- INVALID_ARGUMENT (NULL is not TRUE).
      15              : //   * Expression evaluation failures (e.g. `ASSERT 1.0 / 0 > 0`)
      16              : //     propagate the underlying semantic error reason (here:
      17              : //     `kDivisionByZero`).
      18              : 
      19              : #include "backend/engine/semantic/script/assert_stmt.h"
      20              : 
      21              : #include <memory>
      22              : #include <string>
      23              : 
      24              : #include "absl/status/status.h"
      25              : #include "absl/status/statusor.h"
      26              : #include "backend/engine/engine.h"
      27              : #include "backend/engine/semantic/error.h"
      28              : #include "backend/engine/semantic/script/script_driver.h"
      29              : #include "googlesql/public/analyzer.h"
      30              : #include "googlesql/public/analyzer_options.h"
      31              : #include "googlesql/public/analyzer_output.h"
      32              : #include "googlesql/public/builtin_function_options.h"
      33              : #include "googlesql/public/catalog.h"
      34              : #include "googlesql/public/language_options.h"
      35              : #include "googlesql/public/options.pb.h"
      36              : #include "googlesql/public/simple_catalog.h"
      37              : #include "googlesql/public/types/type_factory.h"
      38              : #include "googlesql/resolved_ast/resolved_ast.h"
      39              : #include "gtest/gtest.h"
      40              : 
      41              : namespace bigquery_emulator {
      42              : namespace backend {
      43              : namespace engine {
      44              : namespace semantic {
      45              : namespace script {
      46              : namespace {
      47              : 
      48              : // Build an analyzer options bundle that supports every statement
      49              : // kind (ASSERT is not in the default supported set) and enables
      50              : // the maximum language feature surface so the resolver accepts the
      51              : // `AS '<msg>'` form.
      52            6 : ::googlesql::AnalyzerOptions MakeAssertAnalyzerOptions() {
      53            6 :   ::googlesql::LanguageOptions language;
      54            6 :   language.EnableMaximumLanguageFeatures();
      55            6 :   language.set_product_mode(::googlesql::PRODUCT_EXTERNAL);
      56            6 :   language.SetSupportsAllStatementKinds();
      57            6 :   ::googlesql::AnalyzerOptions options(language);
      58            6 :   options.CreateDefaultArenasIfNotSet();
      59            6 :   return options;
      60            6 : }
      61              : 
      62              : class AssertStmtTest : public ::testing::Test {
      63              :  protected:
      64            6 :   void SetUp() override {
      65            6 :     type_factory_ = std::make_unique<::googlesql::TypeFactory>();
      66            6 :     catalog_ = std::make_unique<::googlesql::SimpleCatalog>(
      67            6 :         "assert_catalog", type_factory_.get());
      68            6 :     catalog_->AddBuiltinFunctions(
      69            6 :         ::googlesql::BuiltinFunctionOptions::AllReleasedFunctions());
      70            6 :   }
      71              : 
      72            6 :   const ::googlesql::ResolvedAssertStmt* Analyze(absl::string_view sql) {
      73            6 :     last_output_.reset();
      74            6 :     auto options = MakeAssertAnalyzerOptions();
      75            6 :     absl::Status s = ::googlesql::AnalyzeStatement(
      76            6 :         sql, options, catalog_.get(), type_factory_.get(), &last_output_);
      77           12 :     EXPECT_TRUE(s.ok()) << s;
      78            6 :     if (!s.ok() || last_output_ == nullptr) return nullptr;
      79            6 :     const ::googlesql::ResolvedStatement* stmt = nullptr;
      80            6 :     stmt = last_output_->resolved_statement();
      81            6 :     EXPECT_NE(stmt, nullptr);
      82            6 :     if (stmt == nullptr) return nullptr;
      83            6 :     EXPECT_EQ(stmt->node_kind(), ::googlesql::RESOLVED_ASSERT_STMT);
      84            6 :     return stmt->GetAs<::googlesql::ResolvedAssertStmt>();
      85            6 :   }
      86              : 
      87            6 :   QueryRequest MakeRequest(absl::string_view sql) {
      88            6 :     QueryRequest req;
      89            6 :     req.project_id = "test-project";
      90            6 :     req.sql = std::string(sql);
      91            6 :     return req;
      92            6 :   }
      93              : 
      94              :   std::unique_ptr<::googlesql::TypeFactory> type_factory_{};
      95              :   std::unique_ptr<::googlesql::SimpleCatalog> catalog_{};
      96              :   std::unique_ptr<const ::googlesql::AnalyzerOutput> last_output_{};
      97              : };
      98              : 
      99            1 : TEST_F(AssertStmtTest, AssertTrueIsNoop) {
     100            1 :   const auto* stmt = Analyze("ASSERT TRUE");
     101            1 :   ASSERT_NE(stmt, nullptr);
     102            1 :   ScriptDriver driver;
     103            1 :   absl::Status status =
     104            1 :       ExecuteAssert(MakeRequest("ASSERT TRUE"), *stmt, driver);
     105            2 :   EXPECT_TRUE(status.ok()) << status;
     106            1 : }
     107              : 
     108            1 : TEST_F(AssertStmtTest, AssertFalseSurfacesInvalidArgument) {
     109            1 :   const auto* stmt = Analyze("ASSERT FALSE");
     110            1 :   ASSERT_NE(stmt, nullptr);
     111            1 :   ScriptDriver driver;
     112            1 :   absl::Status status =
     113            1 :       ExecuteAssert(MakeRequest("ASSERT FALSE"), *stmt, driver);
     114            1 :   ASSERT_FALSE(status.ok());
     115            1 :   EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument);
     116            1 :   EXPECT_EQ(GetSemanticErrorReason(status),
     117            1 :             SemanticErrorReason::kInvalidArgument);
     118            2 :   EXPECT_NE(status.message().find("Assertion failed"), std::string::npos)
     119            2 :       << status.message();
     120            1 : }
     121              : 
     122            1 : TEST_F(AssertStmtTest, AssertFalseWithDescriptionIncludesIt) {
     123            1 :   const std::string sql = "ASSERT FALSE AS 'predicate must hold'";
     124            1 :   const auto* stmt = Analyze(sql);
     125            1 :   ASSERT_NE(stmt, nullptr);
     126            1 :   ScriptDriver driver;
     127            1 :   absl::Status status = ExecuteAssert(MakeRequest(sql), *stmt, driver);
     128            1 :   ASSERT_FALSE(status.ok());
     129            1 :   EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument);
     130            2 :   EXPECT_NE(status.message().find("predicate must hold"), std::string::npos)
     131            2 :       << status.message();
     132            1 : }
     133              : 
     134            1 : TEST_F(AssertStmtTest, AssertNullTreatedAsFailure) {
     135            1 :   const std::string sql = "ASSERT CAST(NULL AS BOOL)";
     136            1 :   const auto* stmt = Analyze(sql);
     137            1 :   ASSERT_NE(stmt, nullptr);
     138            1 :   ScriptDriver driver;
     139            1 :   absl::Status status = ExecuteAssert(MakeRequest(sql), *stmt, driver);
     140            1 :   ASSERT_FALSE(status.ok());
     141            1 :   EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument);
     142            1 : }
     143              : 
     144            1 : TEST_F(AssertStmtTest, AssertWithComparisonOnTruePredicate) {
     145            1 :   const std::string sql = "ASSERT 1 + 1 = 2";
     146            1 :   const auto* stmt = Analyze(sql);
     147            1 :   ASSERT_NE(stmt, nullptr);
     148            1 :   ScriptDriver driver;
     149            1 :   EXPECT_TRUE(ExecuteAssert(MakeRequest(sql), *stmt, driver).ok());
     150            1 : }
     151              : 
     152            1 : TEST_F(AssertStmtTest, AssertPropagatesEvaluationError) {
     153              :   // The expression evaluator surfaces a structured division-by-zero
     154              :   // error; ASSERT propagates it verbatim so the caller sees the
     155              :   // same envelope they would have seen for `SELECT 1.0 / 0`.
     156            1 :   const std::string sql = "ASSERT 1.0 / 0 > 0";
     157            1 :   const auto* stmt = Analyze(sql);
     158            1 :   ASSERT_NE(stmt, nullptr);
     159            1 :   ScriptDriver driver;
     160            1 :   absl::Status status = ExecuteAssert(MakeRequest(sql), *stmt, driver);
     161            1 :   ASSERT_FALSE(status.ok());
     162            1 :   EXPECT_EQ(GetSemanticErrorReason(status),
     163            1 :             SemanticErrorReason::kDivisionByZero);
     164            1 : }
     165              : 
     166              : }  // namespace
     167              : }  // namespace script
     168              : }  // namespace semantic
     169              : }  // namespace engine
     170              : }  // namespace backend
     171              : }  // namespace bigquery_emulator
        

Generated by: LCOV version 2.0-1