Line data Source code
1 : // DryRun and validation tests for `QueryService`.
2 :
3 : #include "frontend/handlers/query_test_fixture.h"
4 :
5 : namespace bigquery_emulator {
6 : namespace frontend {
7 : namespace {
8 1 : TEST_F(QueryServiceTest, DryRunSelect1ReturnsSingleInt64Column) {
9 1 : v1::QueryRequest req = MakeRequest("SELECT 1");
10 1 : v1::DryRunResponse resp;
11 1 : auto status = service_->DryRun(nullptr, &req, &resp);
12 2 : ASSERT_TRUE(status.ok()) << status.error_message();
13 1 : ASSERT_EQ(resp.schema().fields_size(), 1);
14 1 : EXPECT_EQ(resp.schema().fields(0).type(), "INT64");
15 1 : EXPECT_EQ(resp.estimated_bytes_processed(), 0);
16 1 : }
17 :
18 1 : TEST_F(QueryServiceTest, DryRunSelectMultipleConstantsReturnsAllColumns) {
19 1 : v1::QueryRequest req = MakeRequest(
20 1 : "SELECT 1 AS one, 'hello' AS greeting, CAST(3.14 AS FLOAT64) AS pi");
21 1 : v1::DryRunResponse resp;
22 1 : auto status = service_->DryRun(nullptr, &req, &resp);
23 2 : ASSERT_TRUE(status.ok()) << status.error_message();
24 1 : ASSERT_EQ(resp.schema().fields_size(), 3);
25 1 : EXPECT_EQ(resp.schema().fields(0).name(), "one");
26 1 : EXPECT_EQ(resp.schema().fields(0).type(), "INT64");
27 1 : EXPECT_EQ(resp.schema().fields(1).name(), "greeting");
28 1 : EXPECT_EQ(resp.schema().fields(1).type(), "STRING");
29 1 : EXPECT_EQ(resp.schema().fields(2).name(), "pi");
30 1 : EXPECT_EQ(resp.schema().fields(2).type(), "FLOAT64");
31 1 : }
32 :
33 1 : TEST_F(QueryServiceTest, DryRunSelectFromTableReturnsTableSchema) {
34 1 : CreatePeopleTable();
35 1 : v1::QueryRequest req = MakeRequest("SELECT id, name, tags FROM ds.t");
36 1 : v1::DryRunResponse resp;
37 1 : auto status = service_->DryRun(nullptr, &req, &resp);
38 2 : ASSERT_TRUE(status.ok()) << status.error_message();
39 1 : ASSERT_EQ(resp.schema().fields_size(), 3);
40 1 : EXPECT_EQ(resp.schema().fields(0).name(), "id");
41 1 : EXPECT_EQ(resp.schema().fields(0).type(), "INT64");
42 1 : EXPECT_EQ(resp.schema().fields(1).name(), "name");
43 1 : EXPECT_EQ(resp.schema().fields(1).type(), "STRING");
44 1 : EXPECT_EQ(resp.schema().fields(2).name(), "tags");
45 1 : EXPECT_EQ(resp.schema().fields(2).type(), "STRING");
46 1 : EXPECT_EQ(resp.schema().fields(2).mode(), "REPEATED");
47 1 : }
48 :
49 1 : TEST_F(QueryServiceTest, DryRunSyntaxErrorIsInvalidArgument) {
50 1 : v1::QueryRequest req = MakeRequest("SELECT FROM");
51 1 : v1::DryRunResponse resp;
52 1 : auto status = service_->DryRun(nullptr, &req, &resp);
53 2 : EXPECT_EQ(status.error_code(), ::grpc::StatusCode::INVALID_ARGUMENT)
54 2 : << status.error_message();
55 1 : EXPECT_FALSE(status.error_message().empty());
56 : // Error message must carry a `line:column:` prefix so the gateway
57 : // can extract it for the BigQuery REST error envelope.
58 2 : EXPECT_NE(status.error_message().find(':'), std::string::npos)
59 2 : << status.error_message();
60 1 : }
61 :
62 1 : TEST_F(QueryServiceTest, DryRunUnknownTableIsInvalidArgument) {
63 : // Name-resolution errors surface as `INVALID_ARGUMENT` from
64 : // GoogleSQL even though the catalog status was `NOT_FOUND`. The
65 : // analyzer wraps it: `Table not found: ds.missing`. The gateway
66 : // maps both to BigQuery's `invalidQuery` reason.
67 1 : v1::QueryRequest req = MakeRequest("SELECT * FROM ds.missing");
68 1 : v1::DryRunResponse resp;
69 1 : auto status = service_->DryRun(nullptr, &req, &resp);
70 2 : EXPECT_NE(status.error_code(), ::grpc::StatusCode::OK)
71 2 : << status.error_message();
72 2 : EXPECT_NE(status.error_message().find("missing"), std::string::npos)
73 2 : << status.error_message();
74 1 : }
75 :
76 1 : TEST_F(QueryServiceTest, DryRunEmptySqlIsInvalidArgument) {
77 1 : v1::QueryRequest req = MakeRequest("");
78 1 : v1::DryRunResponse resp;
79 1 : auto status = service_->DryRun(nullptr, &req, &resp);
80 2 : EXPECT_EQ(status.error_code(), ::grpc::StatusCode::INVALID_ARGUMENT)
81 2 : << status.error_message();
82 1 : }
83 :
84 1 : TEST_F(QueryServiceTest, DryRunUseLegacySqlIsInvalidArgument) {
85 1 : v1::QueryRequest req = MakeRequest("SELECT 1");
86 1 : req.set_use_legacy_sql(true);
87 1 : v1::DryRunResponse resp;
88 1 : auto status = service_->DryRun(nullptr, &req, &resp);
89 2 : EXPECT_EQ(status.error_code(), ::grpc::StatusCode::INVALID_ARGUMENT)
90 2 : << status.error_message();
91 1 : }
92 :
93 1 : TEST_F(QueryServiceTest, DryRunMissingProjectIsInvalidArgument) {
94 1 : v1::QueryRequest req;
95 1 : req.set_sql("SELECT 1");
96 1 : v1::DryRunResponse resp;
97 1 : auto status = service_->DryRun(nullptr, &req, &resp);
98 2 : EXPECT_EQ(status.error_code(), ::grpc::StatusCode::INVALID_ARGUMENT)
99 2 : << status.error_message();
100 1 : }
101 :
102 1 : TEST(QueryServiceWithoutStorageTest, DryRunReturnsFailedPrecondition) {
103 1 : QueryService service(/*storage=*/nullptr);
104 1 : v1::QueryRequest req;
105 1 : req.set_project_id("proj-test");
106 1 : req.set_sql("SELECT 1");
107 1 : v1::DryRunResponse resp;
108 1 : auto status = service.DryRun(nullptr, &req, &resp);
109 2 : EXPECT_EQ(status.error_code(), ::grpc::StatusCode::FAILED_PRECONDITION)
110 2 : << status.error_message();
111 1 : }
112 : } // namespace
113 : } // namespace frontend
114 : } // namespace bigquery_emulator
|