Line data Source code
1 : // Tests for the BigQuery polyfill UDF library registrar.
2 : //
3 : // The registrar's contract is:
4 : //
5 : // * `RegisterAll(conn)` returns OK against a fresh, healthy
6 : // `duckdb_connection`.
7 : // * After `RegisterAll`, every UDF the registrar promises to
8 : // install is callable via plain DuckDB SQL.
9 : // * `RegisterAll(nullptr)` returns INVALID_ARGUMENT (the executor
10 : // contract guarantees a non-null connection, but the registrar
11 : // defends in depth so a future caller that forgets to check
12 : // `duckdb_connect`'s return code does not crash inside DuckDB).
13 : // * Registration failure (e.g. a malformed `CREATE MACRO` body
14 : // landing in a future commit) propagates out as a non-OK status
15 : // -- the registrar never partially registers and silently
16 : // pretends to succeed. The `RegisterAll(nullptr)` case stands in
17 : // for the failure shape today; per-family tests cover the
18 : // malformed-macro case in their own files.
19 :
20 : #include "backend/engine/duckdb/udf/registrar.h"
21 :
22 : #include "absl/status/status.h"
23 : #include "duckdb.h"
24 : #include "gtest/gtest.h"
25 :
26 : namespace bigquery_emulator {
27 : namespace backend {
28 : namespace engine {
29 : namespace duckdb {
30 : namespace udf {
31 : namespace {
32 :
33 : // Test fixture that owns a fresh, in-memory DuckDB database and
34 : // connection per TEST_F. Mirrors the per-query connection lifecycle
35 : // `DuckDbExecutor` uses, so the registrar runs in the same
36 : // environment it sees at runtime.
37 : class RegistrarTest : public ::testing::Test {
38 : protected:
39 3 : void SetUp() override {
40 3 : ASSERT_EQ(::duckdb_open(nullptr, &db_), ::DuckDBSuccess);
41 3 : ASSERT_EQ(::duckdb_connect(db_, &conn_), ::DuckDBSuccess);
42 3 : }
43 :
44 3 : void TearDown() override {
45 3 : if (conn_ != nullptr) ::duckdb_disconnect(&conn_);
46 3 : if (db_ != nullptr) ::duckdb_close(&db_);
47 3 : }
48 :
49 : ::duckdb_database db_ = nullptr;
50 : ::duckdb_connection conn_ = nullptr;
51 : };
52 :
53 1 : TEST_F(RegistrarTest, NullConnectionReturnsInvalidArgument) {
54 1 : absl::Status s = RegisterAll(nullptr);
55 2 : EXPECT_EQ(s.code(), absl::StatusCode::kInvalidArgument) << s;
56 1 : }
57 :
58 1 : TEST_F(RegistrarTest, FreshConnectionRegistersWithoutError) {
59 1 : absl::Status s = RegisterAll(conn_);
60 2 : EXPECT_TRUE(s.ok()) << s;
61 1 : }
62 :
63 1 : TEST_F(RegistrarTest, IdempotentAcrossMultipleCalls) {
64 : // Subsequent commits use `CREATE OR REPLACE MACRO`; the registrar
65 : // must remain idempotent so the executor can reuse a connection
66 : // (a regression that switched to `CREATE MACRO` would surface here
67 : // as DuckDB's "macro already exists" error).
68 1 : EXPECT_TRUE(RegisterAll(conn_).ok());
69 1 : EXPECT_TRUE(RegisterAll(conn_).ok());
70 1 : }
71 :
72 : } // namespace
73 : } // namespace udf
74 : } // namespace duckdb
75 : } // namespace engine
76 : } // namespace backend
77 : } // namespace bigquery_emulator
|