Skip to content

Commit 7a8e59a

Browse files
committed
chore: add MCP integration tests for JDBC and R2DBC pg instance
1 parent e0c22f3 commit 7a8e59a

File tree

4 files changed

+201
-1
lines changed

4 files changed

+201
-1
lines changed

.ci/cloudbuild.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ steps:
1818
env:
1919
- "IP_TYPE=${_IP_TYPE}"
2020
- "JOB_TYPE=integration"
21-
secretEnv: ["MYSQL_CONNECTION_NAME", "MYSQL_USER", "MYSQL_IAM_USER", "MYSQL_PASS", "MYSQL_DB", "POSTGRES_CONNECTION_NAME", "POSTGRES_USER", "POSTGRES_IAM_USER", "POSTGRES_PASS", "POSTGRES_DB", "POSTGRES_CAS_CONNECTION_NAME", "POSTGRES_CAS_PASS", "POSTGRES_CUSTOMER_CAS_CONNECTION_NAME", "POSTGRES_CUSTOMER_CAS_PASS", "POSTGRES_CUSTOMER_CAS_PASS_VALID_DOMAIN_NAME","POSTGRES_CUSTOMER_CAS_PASS_INVALID_DOMAIN_NAME","SQLSERVER_CONNECTION_NAME", "SQLSERVER_USER", "SQLSERVER_PASS", "SQLSERVER_DB", "QUOTA_PROJECT", "IMPERSONATED_USER"]
21+
secretEnv: ["MYSQL_CONNECTION_NAME", "MYSQL_USER", "MYSQL_IAM_USER", "MYSQL_PASS", "MYSQL_DB", "POSTGRES_CONNECTION_NAME", "POSTGRES_USER", "POSTGRES_IAM_USER", "POSTGRES_PASS", "POSTGRES_DB", "POSTGRES_MCP_CONNECTION_NAME", "POSTGRES_MCP_PASS", "POSTGRES_CAS_CONNECTION_NAME", "POSTGRES_CAS_PASS", "POSTGRES_CUSTOMER_CAS_CONNECTION_NAME", "POSTGRES_CUSTOMER_CAS_PASS", "POSTGRES_CUSTOMER_CAS_PASS_VALID_DOMAIN_NAME","POSTGRES_CUSTOMER_CAS_PASS_INVALID_DOMAIN_NAME","SQLSERVER_CONNECTION_NAME", "SQLSERVER_USER", "SQLSERVER_PASS", "SQLSERVER_DB", "QUOTA_PROJECT", "IMPERSONATED_USER"]
2222
args:
2323
- "-c"
2424
- |
@@ -45,6 +45,10 @@ availableSecrets:
4545
env: 'POSTGRES_PASS'
4646
- versionName: 'projects/$PROJECT_ID/secrets/POSTGRES_DB/versions/latest'
4747
env: 'POSTGRES_DB'
48+
- versionName: "projects/$PROJECT_ID/secrets/POSTGRES_MCP_CONNECTION_NAME/versions/latest"
49+
env: "POSTGRES_MCP_CONNECTION_NAME"
50+
- versionName: "projects/$PROJECT_ID/secrets/POSTGRES_MCP_PASS/versions/latest"
51+
env: "POSTGRES_MCP_PASS"
4852
- versionName: 'projects/$PROJECT_ID/secrets/POSTGRES_CAS_CONNECTION_NAME/versions/latest'
4953
env: 'POSTGRES_CAS_CONNECTION_NAME'
5054
- versionName: 'projects/$PROJECT_ID/secrets/POSTGRES_CAS_PASS/versions/latest'

.github/workflows/tests.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ jobs:
142142
POSTGRES_CUSTOMER_CAS_PASS:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_CUSTOMER_CAS_PASS
143143
POSTGRES_CUSTOMER_CAS_PASS_VALID_DOMAIN_NAME:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_CUSTOMER_CAS_PASS_VALID_DOMAIN_NAME
144144
POSTGRES_CUSTOMER_CAS_PASS_INVALID_DOMAIN_NAME:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_CUSTOMER_CAS_PASS_INVALID_DOMAIN_NAME
145+
POSTGRES_MCP_CONNECTION_NAME:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_MCP_CONNECTION_NAME
146+
POSTGRES_MCP_PASS:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_MCP_PASS
145147
SQLSERVER_CONNECTION_NAME:${{ vars.GOOGLE_CLOUD_PROJECT }}/SQLSERVER_CONNECTION_NAME
146148
SQLSERVER_USER:${{ vars.GOOGLE_CLOUD_PROJECT }}/SQLSERVER_USER
147149
SQLSERVER_PASS:${{ vars.GOOGLE_CLOUD_PROJECT }}/SQLSERVER_PASS
@@ -166,6 +168,8 @@ jobs:
166168
POSTGRES_CUSTOMER_CAS_PASS: "${{ steps.secrets.outputs.POSTGRES_CUSTOMER_CAS_PASS }}"
167169
POSTGRES_CUSTOMER_CAS_PASS_VALID_DOMAIN_NAME: "${{ steps.secrets.outputs.POSTGRES_CUSTOMER_CAS_PASS_VALID_DOMAIN_NAME }}"
168170
POSTGRES_CUSTOMER_CAS_PASS_INVALID_DOMAIN_NAME: "${{ steps.secrets.outputs.POSTGRES_CUSTOMER_CAS_PASS_INVALID_DOMAIN_NAME }}"
171+
POSTGRES_MCP_CONNECTION_NAME: "${{ steps.secrets.outputs.POSTGRES_MCP_CONNECTION_NAME }}"
172+
POSTGRES_MCP_PASS: "${{ steps.secrets.outputs.POSTGRES_MCP_PASS }}"
169173
SQLSERVER_CONNECTION_NAME: "${{ steps.secrets.outputs.SQLSERVER_CONNECTION_NAME }}"
170174
SQLSERVER_USER: "${{ steps.secrets.outputs.SQLSERVER_USER }}"
171175
SQLSERVER_PASS: "${{ steps.secrets.outputs.SQLSERVER_PASS }}"
@@ -249,6 +253,8 @@ jobs:
249253
POSTGRES_CUSTOMER_CAS_PASS:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_CUSTOMER_CAS_PASS
250254
POSTGRES_CUSTOMER_CAS_PASS_VALID_DOMAIN_NAME:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_CUSTOMER_CAS_PASS_VALID_DOMAIN_NAME
251255
POSTGRES_CUSTOMER_CAS_PASS_INVALID_DOMAIN_NAME:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_CUSTOMER_CAS_PASS_INVALID_DOMAIN_NAME
256+
POSTGRES_MCP_CONNECTION_NAME:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_MCP_CONNECTION_NAME
257+
POSTGRES_MCP_PASS:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_MCP_PASS
252258
SQLSERVER_CONNECTION_NAME:${{ vars.GOOGLE_CLOUD_PROJECT }}/SQLSERVER_CONNECTION_NAME
253259
SQLSERVER_USER:${{ vars.GOOGLE_CLOUD_PROJECT }}/SQLSERVER_USER
254260
SQLSERVER_PASS:${{ vars.GOOGLE_CLOUD_PROJECT }}/SQLSERVER_PASS
@@ -274,6 +280,8 @@ jobs:
274280
POSTGRES_CUSTOMER_CAS_PASS: "${{ steps.secrets.outputs.POSTGRES_CUSTOMER_CAS_PASS }}"
275281
POSTGRES_CUSTOMER_CAS_PASS_VALID_DOMAIN_NAME: "${{ steps.secrets.outputs.POSTGRES_CUSTOMER_CAS_PASS_VALID_DOMAIN_NAME }}"
276282
POSTGRES_CUSTOMER_CAS_PASS_INVALID_DOMAIN_NAME: "${{ steps.secrets.outputs.POSTGRES_CUSTOMER_CAS_PASS_INVALID_DOMAIN_NAME }}"
283+
POSTGRES_MCP_CONNECTION_NAME: "${{ steps.secrets.outputs.POSTGRES_MCP_CONNECTION_NAME }}"
284+
POSTGRES_MCP_PASS: "${{ steps.secrets.outputs.POSTGRES_MCP_PASS }}"
277285
SQLSERVER_CONNECTION_NAME: "${{ steps.secrets.outputs.SQLSERVER_CONNECTION_NAME }}"
278286
SQLSERVER_USER: "${{ steps.secrets.outputs.SQLSERVER_USER }}"
279287
SQLSERVER_PASS: "${{ steps.secrets.outputs.SQLSERVER_PASS }}"
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.sql.postgres;
18+
19+
import static com.google.common.truth.Truth.assertThat;
20+
import static com.google.common.truth.Truth.assertWithMessage;
21+
22+
import com.google.common.collect.ImmutableList;
23+
import com.zaxxer.hikari.HikariConfig;
24+
import com.zaxxer.hikari.HikariDataSource;
25+
import java.sql.*;
26+
import java.util.ArrayList;
27+
import java.util.List;
28+
import java.util.Properties;
29+
import java.util.concurrent.TimeUnit;
30+
import org.junit.Before;
31+
import org.junit.BeforeClass;
32+
import org.junit.Rule;
33+
import org.junit.Test;
34+
import org.junit.rules.Timeout;
35+
import org.junit.runner.RunWith;
36+
import org.junit.runners.JUnit4;
37+
38+
@RunWith(JUnit4.class)
39+
public class JdbcPostgresMCPIntegrationTests {
40+
41+
private static final String CONNECTION_NAME = System.getenv("POSTGRES_MCP_CONNECTION_NAME");
42+
private static final String DB_NAME = System.getenv("POSTGRES_DB");
43+
private static final String DB_USER = System.getenv("POSTGRES_USER");
44+
private static final String DB_PASSWORD = System.getenv("POSTGRES_MCP_PASS");
45+
private static final String IP_TYPE =
46+
System.getenv("IP_TYPE") == null ? "PUBLIC" : System.getenv("IP_TYPE");
47+
private static final ImmutableList<String> requiredEnvVars =
48+
ImmutableList.of("POSTGRES_USER", "POSTGRES_MCP_PASS", "POSTGRES_DB", "POSTGRES_MCP_CONNECTION_NAME");
49+
@Rule public Timeout globalTimeout = new Timeout(80, TimeUnit.SECONDS);
50+
51+
private HikariDataSource connectionPool;
52+
53+
@BeforeClass
54+
public static void checkEnvVars() {
55+
// Check that required env vars are set
56+
requiredEnvVars.forEach(
57+
(varName) ->
58+
assertWithMessage(
59+
String.format(
60+
"Environment variable '%s' must be set to perform these tests.", varName))
61+
.that(System.getenv(varName))
62+
.isNotEmpty());
63+
}
64+
65+
@Before
66+
public void setUpPool() throws SQLException {
67+
// Set up URL parameters
68+
String jdbcURL = String.format("jdbc:postgresql://db.example.com/%s", DB_NAME);
69+
Properties connProps = new Properties();
70+
connProps.setProperty("user", DB_USER);
71+
connProps.setProperty("password", DB_PASSWORD);
72+
connProps.setProperty("socketFactory", "com.google.cloud.sql.postgres.SocketFactory");
73+
connProps.setProperty("cloudSqlInstance", CONNECTION_NAME);
74+
connProps.setProperty("ipTypes", IP_TYPE);
75+
76+
// Initialize connection pool
77+
HikariConfig config = new HikariConfig();
78+
config.setJdbcUrl(jdbcURL);
79+
config.setDataSourceProperties(connProps);
80+
config.setConnectionTimeout(10000); // 10s
81+
82+
this.connectionPool = new HikariDataSource(config);
83+
}
84+
85+
@Test
86+
public void pooledConnectionTest() throws SQLException {
87+
88+
List<Timestamp> rows = new ArrayList<>();
89+
try (Connection conn = connectionPool.getConnection()) {
90+
try (PreparedStatement selectStmt = conn.prepareStatement("SELECT NOW() as TS")) {
91+
ResultSet rs = selectStmt.executeQuery();
92+
while (rs.next()) {
93+
rows.add(rs.getTimestamp("TS"));
94+
}
95+
}
96+
}
97+
assertThat(rows.size()).isEqualTo(1);
98+
}
99+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.sql.core;
18+
19+
import static com.google.common.truth.Truth.assertThat;
20+
import static com.google.common.truth.Truth.assertWithMessage;
21+
22+
import com.google.common.collect.ImmutableList;
23+
import io.r2dbc.pool.ConnectionPool;
24+
import io.r2dbc.pool.ConnectionPoolConfiguration;
25+
import io.r2dbc.spi.ConnectionFactories;
26+
import io.r2dbc.spi.ConnectionFactory;
27+
import java.time.Instant;
28+
import java.util.List;
29+
import java.util.concurrent.TimeUnit;
30+
import org.junit.Before;
31+
import org.junit.Rule;
32+
import org.junit.Test;
33+
import org.junit.rules.Timeout;
34+
import org.junit.runner.RunWith;
35+
import org.junit.runners.JUnit4;
36+
import reactor.core.publisher.Mono;
37+
38+
@RunWith(JUnit4.class)
39+
public class R2dbcPostgresMCPIntegrationTests {
40+
41+
private static final String CONNECTION_NAME = System.getenv("POSTGRES_MCP_CONNECTION_NAME");
42+
private static final String DB_NAME = System.getenv("POSTGRES_DB");
43+
private static final String DB_USER = System.getenv("POSTGRES_USER");
44+
private static final String DB_PASSWORD = System.getenv("POSTGRES_MCP_PASS");
45+
private static final String IP_TYPE =
46+
System.getenv("IP_TYPE") == null ? "PUBLIC" : System.getenv("IP_TYPE");
47+
private static final ImmutableList<String> requiredEnvVars =
48+
ImmutableList.of("POSTGRES_USER", "POSTGRES_MCP_PASS", "POSTGRES_DB", "POSTGRES_MCP_CONNECTION_NAME");
49+
@Rule public Timeout globalTimeout = new Timeout(80, TimeUnit.SECONDS);
50+
51+
private ConnectionFactory connectionPool;
52+
53+
@Before
54+
public void setUpPool() {
55+
// Check that required env vars are set
56+
requiredEnvVars.forEach(
57+
(varName) ->
58+
assertWithMessage(
59+
String.format(
60+
"Environment variable '%s' must be set to perform these tests.", varName))
61+
.that(System.getenv(varName))
62+
.isNotEmpty());
63+
64+
// Set up URL parameters
65+
String r2dbcURL =
66+
String.format(
67+
"r2dbc:gcp:postgres://%s:%s@%s/%s?ipTypes=%s",
68+
DB_USER, DB_PASSWORD, CONNECTION_NAME, DB_NAME, IP_TYPE);
69+
70+
// Initialize connection pool
71+
ConnectionFactory connectionFactory = ConnectionFactories.get(r2dbcURL);
72+
ConnectionPoolConfiguration configuration =
73+
ConnectionPoolConfiguration.builder(connectionFactory).build();
74+
75+
this.connectionPool = new ConnectionPool(configuration);
76+
}
77+
78+
@Test
79+
public void pooledConnectionTest() {
80+
List<Instant> rows =
81+
Mono.from(this.connectionPool.create())
82+
.flatMapMany(connection -> connection.createStatement("SELECT NOW() as TS").execute())
83+
.flatMap(result -> result.map((r, meta) -> r.get("TS", Instant.class)))
84+
.collectList()
85+
.block();
86+
87+
assertThat(rows.size()).isEqualTo(1);
88+
}
89+
}

0 commit comments

Comments
 (0)