Skip to content

Commit 2be51dc

Browse files
committed
test(source/cloudsqlpg): create MCP integration tests
1 parent c4a9912 commit 2be51dc

File tree

1 file changed

+196
-0
lines changed

1 file changed

+196
-0
lines changed
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package cloudsqlpg
16+
17+
import (
18+
"context"
19+
"fmt"
20+
"net"
21+
"os"
22+
"regexp"
23+
"strings"
24+
"testing"
25+
"time"
26+
27+
"cloud.google.com/go/cloudsqlconn"
28+
"github.com/google/uuid"
29+
"github.com/googleapis/genai-toolbox/internal/testutils"
30+
"github.com/googleapis/genai-toolbox/tests"
31+
"github.com/jackc/pgx/v5/pgxpool"
32+
)
33+
34+
var (
35+
CloudSQLPostgresSourceType = "cloud-sql-postgres"
36+
CloudSQLPostgresToolType = "postgres-sql"
37+
CloudSQLPostgresProject = os.Getenv("CLOUD_SQL_POSTGRES_PROJECT")
38+
CloudSQLPostgresRegion = os.Getenv("CLOUD_SQL_POSTGRES_REGION")
39+
CloudSQLPostgresInstance = os.Getenv("CLOUD_SQL_POSTGRES_INSTANCE")
40+
CloudSQLPostgresDatabase = os.Getenv("CLOUD_SQL_POSTGRES_DATABASE")
41+
CloudSQLPostgresUser = os.Getenv("CLOUD_SQL_POSTGRES_USER")
42+
CloudSQLPostgresPass = os.Getenv("CLOUD_SQL_POSTGRES_PASS")
43+
)
44+
45+
func getCloudSQLPgVars(t *testing.T) map[string]any {
46+
switch "" {
47+
case CloudSQLPostgresProject:
48+
t.Fatal("'CLOUD_SQL_POSTGRES_PROJECT' not set")
49+
case CloudSQLPostgresRegion:
50+
t.Fatal("'CLOUD_SQL_POSTGRES_REGION' not set")
51+
case CloudSQLPostgresInstance:
52+
t.Fatal("'CLOUD_SQL_POSTGRES_INSTANCE' not set")
53+
case CloudSQLPostgresDatabase:
54+
t.Fatal("'CLOUD_SQL_POSTGRES_DATABASE' not set")
55+
case CloudSQLPostgresUser:
56+
t.Fatal("'CLOUD_SQL_POSTGRES_USER' not set")
57+
case CloudSQLPostgresPass:
58+
t.Fatal("'CLOUD_SQL_POSTGRES_PASS' not set")
59+
}
60+
61+
return map[string]any{
62+
"type": CloudSQLPostgresSourceType,
63+
"project": CloudSQLPostgresProject,
64+
"instance": CloudSQLPostgresInstance,
65+
"region": CloudSQLPostgresRegion,
66+
"database": CloudSQLPostgresDatabase,
67+
"user": CloudSQLPostgresUser,
68+
"password": CloudSQLPostgresPass,
69+
}
70+
}
71+
72+
// Copied over from cloud_sql_pg.go
73+
func initCloudSQLPgConnectionPool(project, region, instance, ip_type, user, pass, dbname string) (*pgxpool.Pool, error) {
74+
// Configure the driver to connect to the database
75+
dsn := fmt.Sprintf("user=%s password=%s dbname=%s sslmode=disable", user, pass, dbname)
76+
config, err := pgxpool.ParseConfig(dsn)
77+
if err != nil {
78+
return nil, fmt.Errorf("unable to parse connection uri: %w", err)
79+
}
80+
81+
// Create a new dialer with options
82+
dialOpts, err := tests.GetCloudSQLDialOpts(ip_type)
83+
if err != nil {
84+
return nil, err
85+
}
86+
d, err := cloudsqlconn.NewDialer(context.Background(), cloudsqlconn.WithDefaultDialOptions(dialOpts...))
87+
if err != nil {
88+
return nil, fmt.Errorf("unable to parse connection uri: %w", err)
89+
}
90+
91+
// Tell the driver to use the Cloud SQL Go Connector to create connections
92+
i := fmt.Sprintf("%s:%s:%s", project, region, instance)
93+
config.ConnConfig.DialFunc = func(ctx context.Context, _ string, instance string) (net.Conn, error) {
94+
return d.Dial(ctx, i)
95+
}
96+
97+
// Interact with the driver directly as you normally would
98+
pool, err := pgxpool.NewWithConfig(context.Background(), config)
99+
if err != nil {
100+
return nil, err
101+
}
102+
return pool, nil
103+
}
104+
105+
func TestCloudSQLPgListTools(t *testing.T) {
106+
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
107+
defer cancel()
108+
109+
args := []string{"--prebuilt", "cloud-sql-postgres"}
110+
111+
cmd, cleanup, err := tests.StartCmd(ctx, map[string]any{}, args...)
112+
if err != nil {
113+
t.Fatalf("command initialization returned an error: %v", err)
114+
}
115+
defer cleanup()
116+
117+
waitCtx, cancelWait := context.WithTimeout(ctx, 10*time.Second)
118+
defer cancelWait()
119+
out, err := testutils.WaitForString(waitCtx, regexp.MustCompile(`Server ready to serve`), cmd.Out)
120+
if err != nil {
121+
t.Logf("toolbox command logs: \n%s", out)
122+
t.Fatalf("toolbox didn't start successfully: %v", err)
123+
}
124+
125+
// We expect standard Postgres tools to be listed
126+
_, tools, err := tests.GetMCPToolsList(t, nil)
127+
if err != nil {
128+
t.Fatalf("failed to get tools list: %v", err)
129+
}
130+
131+
if len(tools) == 0 {
132+
t.Errorf("expected tools to be listed, got none")
133+
}
134+
}
135+
136+
func TestCloudSQLPgCallTool(t *testing.T) {
137+
getCloudSQLPgVars(t)
138+
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
139+
defer cancel()
140+
141+
pool, err := initCloudSQLPgConnectionPool(CloudSQLPostgresProject, CloudSQLPostgresRegion, CloudSQLPostgresInstance, "public", CloudSQLPostgresUser, CloudSQLPostgresPass, CloudSQLPostgresDatabase)
142+
if err != nil {
143+
t.Fatalf("unable to create Cloud SQL connection pool: %s", err)
144+
}
145+
defer pool.Close()
146+
147+
uniqueID := strings.ReplaceAll(uuid.New().String(), "-", "")
148+
149+
args := []string{"--prebuilt", "cloud-sql-postgres"}
150+
151+
cmd, cleanup, err := tests.StartCmd(ctx, map[string]any{}, args...)
152+
if err != nil {
153+
t.Fatalf("command initialization returned an error: %v", err)
154+
}
155+
defer cleanup()
156+
157+
waitCtx, cancelWait := context.WithTimeout(ctx, 10*time.Second)
158+
defer cancelWait()
159+
out, err := testutils.WaitForString(waitCtx, regexp.MustCompile(`Server ready to serve`), cmd.Out)
160+
if err != nil {
161+
t.Logf("toolbox command logs: \n%s", out)
162+
t.Fatalf("toolbox didn't start successfully: %v", err)
163+
}
164+
165+
// Run shared Postgres tests
166+
tests.RunMCPPostgresListViewsTest(t, ctx, pool)
167+
tests.RunMCPPostgresListSchemasTest(t, ctx, pool, CloudSQLPostgresUser, uniqueID)
168+
tests.RunMCPPostgresListActiveQueriesTest(t, ctx, pool)
169+
tests.RunMCPPostgresListAvailableExtensionsTest(t)
170+
tests.RunMCPPostgresListInstalledExtensionsTest(t)
171+
tests.RunMCPPostgresDatabaseOverviewTest(t, ctx, pool)
172+
tests.RunMCPPostgresListTriggersTest(t, ctx, pool)
173+
tests.RunMCPPostgresListIndexesTest(t, ctx, pool)
174+
tests.RunMCPPostgresListSequencesTest(t, ctx, pool)
175+
tests.RunMCPPostgresLongRunningTransactionsTest(t, ctx, pool)
176+
tests.RunMCPPostgresListLocksTest(t, ctx, pool)
177+
tests.RunMCPPostgresReplicationStatsTest(t, ctx, pool)
178+
tests.RunMCPPostgresGetColumnCardinalityTest(t, ctx, pool)
179+
tests.RunMCPPostgresListTableStatsTest(t, ctx, pool)
180+
tests.RunMCPPostgresListPublicationTablesTest(t, ctx, pool)
181+
tests.RunMCPPostgresListTableSpacesTest(t)
182+
tests.RunMCPPostgresListPgSettingsTest(t, ctx, pool)
183+
tests.RunMCPPostgresListDatabaseStatsTest(t, ctx, pool)
184+
tests.RunMCPPostgresListRolesTest(t, ctx, pool)
185+
tests.RunMCPPostgresListStoredProcedureTest(t, ctx, pool)
186+
187+
toolsToTest := map[string]string{
188+
"list_autovacuum_configurations": `{}`,
189+
"list_memory_configurations": `{}`,
190+
"list_top_bloated_tables": `{"limit": 10}`,
191+
"list_replication_slots": `{}`,
192+
"list_invalid_indexes": `{}`,
193+
"get_query_plan": `{"query": "SELECT 1"}`,
194+
}
195+
tests.RunMCPStatementToolsTest(t, toolsToTest)
196+
}

0 commit comments

Comments
 (0)