Skip to content

Commit 16713a8

Browse files
committed
add integration test
1 parent 6beeb25 commit 16713a8

5 files changed

Lines changed: 257 additions & 2 deletions

File tree

java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcProxyUtility.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ final class BigQueryJdbcProxyUtility {
5959
new BigQueryJdbcCustomLogger(BigQueryJdbcProxyUtility.class.getName());
6060
static final String validPortRegex =
6161
"^([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$";
62+
private static final HttpTransport DEFAULT_TRANSPORT = new NetHttpTransport.Builder().build();
6263

6364
private BigQueryJdbcProxyUtility() {}
6465

@@ -144,8 +145,7 @@ static HttpTransportOptions getHttpTransportOptions(
144145
getHttpTransportFactory(
145146
proxyProperties, sslTrustStorePath, sslTrustStorePassword, callerClassName));
146147
} else {
147-
HttpTransport defaultTransport = new NetHttpTransport.Builder().build();
148-
httpTransportOptionsBuilder.setHttpTransportFactory(() -> defaultTransport);
148+
httpTransportOptionsBuilder.setHttpTransportFactory(() -> DEFAULT_TRANSPORT);
149149
}
150150

151151
if (connectTimeout != null) {
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
/*
2+
* Copyright 2026 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+
* http://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.bigquery.jdbc.it;
18+
19+
import static org.junit.jupiter.api.Assertions.assertEquals;
20+
import static org.junit.jupiter.api.Assertions.assertFalse;
21+
import static org.junit.jupiter.api.Assertions.assertTrue;
22+
23+
import com.google.cloud.bigquery.jdbc.utils.URIBuilder;
24+
import com.google.common.io.CharStreams;
25+
import com.sun.net.httpserver.HttpsConfigurator;
26+
import com.sun.net.httpserver.HttpsParameters;
27+
import com.sun.net.httpserver.HttpsServer;
28+
import java.io.File;
29+
import java.io.InputStream;
30+
import java.io.InputStreamReader;
31+
import java.io.OutputStream;
32+
import java.net.InetSocketAddress;
33+
import java.nio.charset.StandardCharsets;
34+
import java.security.KeyStore;
35+
import java.sql.Connection;
36+
import java.sql.DriverManager;
37+
import java.sql.Statement;
38+
import java.util.ArrayList;
39+
import java.util.List;
40+
import javax.net.ssl.KeyManagerFactory;
41+
import javax.net.ssl.SSLContext;
42+
import javax.net.ssl.SSLParameters;
43+
import org.junit.jupiter.api.AfterAll;
44+
import org.junit.jupiter.api.BeforeAll;
45+
import org.junit.jupiter.api.Test;
46+
47+
public class ITLocalSslValidationTest {
48+
private static final String HOST = "localhost";
49+
private static final String PASSWORD = "changeit";
50+
private static final String KEYSTORE_RESOURCE = "/localhost-keystore.jks";
51+
private static final String TRUSTSTORE_PATH = "src/test/resources/localhost-truststore.jks";
52+
private static final String SUCCESS_MARKER = "SUBPROCESS_RESULT: SUCCESS";
53+
private static final String FAILURE_MARKER_PREFIX = "SUBPROCESS_RESULT: FAILURE - ";
54+
private static final String PKIX_ERROR_MSG = "PKIX path building failed";
55+
56+
private static MockHttpsServer mockServer;
57+
private static int port;
58+
59+
public static class MockHttpsServer {
60+
private final HttpsServer server;
61+
62+
public MockHttpsServer(int port) throws Exception {
63+
server = HttpsServer.create(new InetSocketAddress(HOST, port), 0);
64+
SSLContext sslContext = SSLContext.getInstance("TLS");
65+
66+
KeyStore ks = KeyStore.getInstance("JKS");
67+
try (InputStream stream = getClass().getResourceAsStream(KEYSTORE_RESOURCE)) {
68+
if (stream == null) {
69+
throw new IllegalStateException(
70+
"Keystore resource " + KEYSTORE_RESOURCE + " not found on classpath!");
71+
}
72+
ks.load(stream, PASSWORD.toCharArray());
73+
}
74+
75+
KeyManagerFactory kmf =
76+
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
77+
kmf.init(ks, PASSWORD.toCharArray());
78+
79+
sslContext.init(kmf.getKeyManagers(), null, null);
80+
81+
server.setHttpsConfigurator(
82+
new HttpsConfigurator(sslContext) {
83+
@Override
84+
public void configure(HttpsParameters params) {
85+
try {
86+
SSLContext context = getSSLContext();
87+
SSLParameters sslParams = context.getDefaultSSLParameters();
88+
params.setSSLParameters(sslParams);
89+
} catch (Exception ex) {
90+
ex.printStackTrace();
91+
}
92+
}
93+
});
94+
95+
server.createContext(
96+
"/",
97+
exchange -> {
98+
String path = exchange.getRequestURI().getPath();
99+
String response;
100+
if (path.contains("/queries")) {
101+
response =
102+
"{\n"
103+
+ " \"kind\": \"bigquery#queryResponse\",\n"
104+
+ " \"jobComplete\": true,\n"
105+
+ " \"rows\": [],\n"
106+
+ " \"totalRows\": \"0\",\n"
107+
+ " \"schema\": {\n"
108+
+ " \"fields\": []\n"
109+
+ " }\n"
110+
+ "}";
111+
} else {
112+
response =
113+
"{\n"
114+
+ " \"kind\": \"bigquery#job\",\n"
115+
+ " \"status\": {\n"
116+
+ " \"state\": \"DONE\"\n"
117+
+ " },\n"
118+
+ " \"jobReference\": {\n"
119+
+ " \"projectId\": \"dummy\",\n"
120+
+ " \"jobId\": \"dummy-job\"\n"
121+
+ " },\n"
122+
+ " \"configuration\": {\n"
123+
+ " \"query\": {\n"
124+
+ " \"query\": \"SELECT 1\"\n"
125+
+ " }\n"
126+
+ " },\n"
127+
+ " \"statistics\": {\n"
128+
+ " \"query\": {\n"
129+
+ " \"statementType\": \"SELECT\"\n"
130+
+ " }\n"
131+
+ " }\n"
132+
+ "}";
133+
}
134+
exchange.getResponseHeaders().set("Content-Type", "application/json");
135+
exchange.sendResponseHeaders(200, response.length());
136+
try (OutputStream os = exchange.getResponseBody()) {
137+
os.write(response.getBytes());
138+
}
139+
});
140+
}
141+
142+
public void start() {
143+
server.start();
144+
}
145+
146+
public void stop() {
147+
server.stop(0);
148+
}
149+
150+
public int getPort() {
151+
return server.getAddress().getPort();
152+
}
153+
}
154+
155+
private static class ProcessResult {
156+
final int exitCode;
157+
final String stdout;
158+
159+
ProcessResult(int exitCode, String stdout) {
160+
this.exitCode = exitCode;
161+
this.stdout = stdout;
162+
}
163+
}
164+
165+
@BeforeAll
166+
public static void setUp() throws Exception {
167+
mockServer = new MockHttpsServer(0);
168+
mockServer.start();
169+
port = mockServer.getPort();
170+
}
171+
172+
@AfterAll
173+
public static void tearDown() {
174+
if (mockServer == null) {
175+
return;
176+
}
177+
mockServer.stop();
178+
}
179+
180+
private ProcessResult runSubprocess(String trustStore, String password) throws Exception {
181+
String javaHome = System.getProperty("java.home");
182+
String javaBin = javaHome + File.separator + "bin" + File.separator + "java";
183+
String classpath = System.getProperty("java.class.path");
184+
String className = ITLocalSslValidationTest.class.getCanonicalName();
185+
186+
List<String> command = new ArrayList<>();
187+
command.add(javaBin);
188+
if (trustStore != null) {
189+
command.add("-Djavax.net.ssl.trustStore=" + trustStore);
190+
}
191+
if (password != null) {
192+
command.add("-Djavax.net.ssl.trustStorePassword=" + password);
193+
}
194+
command.add("-cp");
195+
command.add(classpath);
196+
command.add(className);
197+
command.add(String.valueOf(port));
198+
199+
ProcessBuilder builder = new ProcessBuilder(command);
200+
builder.redirectErrorStream(true);
201+
Process process = builder.start();
202+
203+
String output;
204+
try (InputStreamReader reader =
205+
new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8)) {
206+
output = CharStreams.toString(reader);
207+
}
208+
209+
int exitCode = process.waitFor();
210+
return new ProcessResult(exitCode, output);
211+
}
212+
213+
@Test
214+
public void testDefaultSslFailsForSelfSigned() throws Exception {
215+
ProcessResult result = runSubprocess(null, null);
216+
assertEquals(1, result.exitCode, "Subprocess should fail. Output:\n" + result.stdout);
217+
assertTrue(result.stdout.contains(PKIX_ERROR_MSG));
218+
}
219+
220+
@Test
221+
public void testCustomTrustStoreSucceeds() throws Exception {
222+
String trustStorePath = new File(TRUSTSTORE_PATH).getAbsolutePath();
223+
ProcessResult result = runSubprocess(trustStorePath, PASSWORD);
224+
225+
assertEquals(0, result.exitCode, "Subprocess failed. Output:\n" + result.stdout);
226+
assertTrue(result.stdout.contains(SUCCESS_MARKER));
227+
assertFalse(
228+
result.stdout.contains(PKIX_ERROR_MSG),
229+
"Handshake failed with SSL error: " + result.stdout);
230+
}
231+
232+
public static void main(String[] args) {
233+
int port = Integer.parseInt(args[0]);
234+
String baseUri = "jdbc:bigquery://https://" + HOST + ":" + port + ";";
235+
String url =
236+
new URIBuilder(baseUri)
237+
.append("EndpointOverrides", "BIGQUERY=https://" + HOST + ":" + port)
238+
.append("ProjectId", "dummy")
239+
.append("OAuthType", 2)
240+
.append("OAuthAccessToken", "dummy-token")
241+
.toString();
242+
try (Connection connection = DriverManager.getConnection(url);
243+
Statement statement = connection.createStatement()) {
244+
statement.execute("SELECT 1");
245+
System.out.println(SUCCESS_MARKER);
246+
System.exit(0);
247+
} catch (Throwable e) {
248+
System.out.println(FAILURE_MARKER_PREFIX + e.getMessage());
249+
e.printStackTrace();
250+
System.exit(1);
251+
}
252+
}
253+
}

java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/it/suites/ITPresubmitTests.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.google.cloud.bigquery.jdbc.it.ITConnectionTest;
2424
import com.google.cloud.bigquery.jdbc.it.ITDatabaseMetadataTest;
2525
import com.google.cloud.bigquery.jdbc.it.ITDriverTest;
26+
import com.google.cloud.bigquery.jdbc.it.ITLocalSslValidationTest;
2627
import com.google.cloud.bigquery.jdbc.it.ITResultSetMetadataTest;
2728
import com.google.cloud.bigquery.jdbc.it.ITStatementTest;
2829
import org.junit.platform.suite.api.SelectClasses;
@@ -37,6 +38,7 @@
3738
ITConnectionPoolingTest.class,
3839
ITDatabaseMetadataTest.class,
3940
ITDriverTest.class,
41+
ITLocalSslValidationTest.class,
4042
ITResultSetMetadataTest.class,
4143
ITStatementTest.class
4244
})
2.16 KB
Binary file not shown.
927 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)