e04ed9c211
CF: Deploy Dev Docs / deploy (push) Has been cancelled
Sync Labels / build (push) Has been cancelled
tests / unit tests (macos-latest) (push) Has been cancelled
tests / unit tests (windows-latest) (push) Has been cancelled
tests / unit tests (ubuntu-latest) (push) Has been cancelled
142 lines
3.4 KiB
Go
142 lines
3.4 KiB
Go
// Copyright 2026 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package bigquerycommon_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/googleapis/mcp-toolbox/internal/tools/bigquery/bigquerycommon"
|
|
)
|
|
|
|
func TestValidTableID(t *testing.T) {
|
|
tcs := []struct {
|
|
in string
|
|
valid bool
|
|
}{
|
|
// Allowed: dataset.table
|
|
{"my_dataset.my_table", true},
|
|
{"ds.t", true},
|
|
{"ds_1.tbl_2", true},
|
|
|
|
// Allowed: project.dataset.table
|
|
{"proj.ds.tbl", true},
|
|
{"PROJ.DS.TBL", true},
|
|
{"my_project.my_dataset.my_table", true},
|
|
|
|
// Rejected: hyphens (not valid in dataset/table IDs).
|
|
{"my-project.my-dataset.my_table", false},
|
|
{"my-project.my_dataset.my-table", false},
|
|
{"my-project-123.dataset.table", true},
|
|
|
|
// Rejected: only one component (no dot)
|
|
{"my_dataset", false},
|
|
{"", false},
|
|
|
|
// Rejected: too many dots (4+ parts)
|
|
{"a.b.c.d", false},
|
|
|
|
// Rejected: injection characters
|
|
{"dataset.table`", false},
|
|
{"dataset.table` UNION ALL SELECT 1 --", false},
|
|
{"dataset.table'; DROP TABLE x --", false},
|
|
{"dataset.table\n", false},
|
|
{"dataset.table ", false},
|
|
{" dataset.table", false},
|
|
{"dataset.table\t", false},
|
|
|
|
// Rejected: backtick (closes identifier in SQL)
|
|
{"ds.`table`", false},
|
|
|
|
// Rejected: SQL metacharacters
|
|
{"ds.table--", false},
|
|
{"ds.table/*", false},
|
|
{"ds.table;", false},
|
|
}
|
|
for _, tc := range tcs {
|
|
if got := bigquerycommon.ValidTableID(tc.in); got != tc.valid {
|
|
t.Errorf("ValidTableID(%q) = %v, want %v", tc.in, got, tc.valid)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestValidColumnName(t *testing.T) {
|
|
tcs := []struct {
|
|
in string
|
|
valid bool
|
|
}{
|
|
// Allowed: simple identifiers.
|
|
{"sales", true},
|
|
{"sales_col", true},
|
|
{"_internal", true},
|
|
{"Col1", true},
|
|
{"A", true},
|
|
{"is_test", true},
|
|
{"timestamp_col", true},
|
|
|
|
// Rejected: empty string.
|
|
{"", false},
|
|
|
|
// Rejected: leading digit.
|
|
{"1col", false},
|
|
|
|
// Rejected: SQL injection characters.
|
|
{"col'", false},
|
|
{"col`", false},
|
|
{"col; DROP TABLE x", false},
|
|
{"col UNION SELECT", false},
|
|
{"col--", false},
|
|
{"col/*", false},
|
|
{"col(", false},
|
|
{"col)", false},
|
|
{"col/col2", false},
|
|
|
|
// Rejected: whitespace.
|
|
{"col name", false},
|
|
{" col", false},
|
|
{"col ", false},
|
|
|
|
// Rejected: dots (not valid in unquoted column names).
|
|
{"ds.col", false},
|
|
}
|
|
for _, tc := range tcs {
|
|
if got := bigquerycommon.ValidColumnName(tc.in); got != tc.valid {
|
|
t.Errorf("ValidColumnName(%q) = %v, want %v", tc.in, got, tc.valid)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestStripSingleQuotes(t *testing.T) {
|
|
tcs := []struct {
|
|
in string
|
|
want string
|
|
}{
|
|
{"'hello'", "hello"},
|
|
{"hello", "hello"},
|
|
{"'hello", "'hello"},
|
|
{"hello'", "hello'"},
|
|
{"''", ""},
|
|
{"'", "'"},
|
|
{"", ""},
|
|
{"'a'", "a"},
|
|
{"'abc'", "abc"},
|
|
{"''abc''", "'abc'"},
|
|
}
|
|
for _, tc := range tcs {
|
|
if got := bigquerycommon.StripSingleQuotes(tc.in); got != tc.want {
|
|
t.Errorf("StripSingleQuotes(%q) = %q, want %q", tc.in, got, tc.want)
|
|
}
|
|
}
|
|
}
|