Skip to content

Commit a5af8c9

Browse files
committed
test: add tests for using multiplexed sessions
Add a couple of tests to verify that multiplexed sessions are being used by the JDBC driver when the environment variable has been set.
1 parent c13a120 commit a5af8c9

File tree

1 file changed

+235
-0
lines changed

1 file changed

+235
-0
lines changed
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
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+
* 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.spanner.jdbc;
18+
19+
import static org.junit.Assert.assertEquals;
20+
import static org.junit.Assert.assertNotNull;
21+
import static org.junit.Assert.assertTrue;
22+
import static org.junit.Assume.assumeTrue;
23+
24+
import com.google.cloud.spanner.MockServerHelper;
25+
import com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult;
26+
import com.google.cloud.spanner.connection.AbstractMockServerTest;
27+
import com.google.common.base.Strings;
28+
import com.google.protobuf.ListValue;
29+
import com.google.protobuf.Value;
30+
import com.google.spanner.v1.ExecuteSqlRequest;
31+
import com.google.spanner.v1.ResultSetMetadata;
32+
import com.google.spanner.v1.ResultSetStats;
33+
import com.google.spanner.v1.StructType;
34+
import com.google.spanner.v1.StructType.Field;
35+
import com.google.spanner.v1.Type;
36+
import com.google.spanner.v1.TypeCode;
37+
import java.sql.Connection;
38+
import java.sql.DriverManager;
39+
import java.sql.ResultSet;
40+
import java.sql.SQLException;
41+
import java.sql.Statement;
42+
import java.util.Collections;
43+
import java.util.Map;
44+
import org.junit.Before;
45+
import org.junit.BeforeClass;
46+
import org.junit.Test;
47+
import org.junit.runner.RunWith;
48+
import org.junit.runners.JUnit4;
49+
50+
@RunWith(JUnit4.class)
51+
public class MultiplexedSessionsTest extends AbstractMockServerTest {
52+
private static final String PROCESS_ENVIRONMENT = "java.lang.ProcessEnvironment";
53+
private static final String ENVIRONMENT = "theUnmodifiableEnvironment";
54+
private static final String SOURCE_MAP = "m";
55+
private static final Object STATIC_METHOD = null;
56+
private static final Class<?> UMODIFIABLE_MAP_CLASS =
57+
Collections.unmodifiableMap(Collections.emptyMap()).getClass();
58+
private static final Class<?> MAP_CLASS = Map.class;
59+
60+
private static boolean setEnvVar = false;
61+
62+
private String query;
63+
private String dml;
64+
private String dmlReturning;
65+
66+
@SuppressWarnings("unchecked")
67+
private static Map<String, String> getModifiableEnvironment() throws Exception {
68+
Class<?> environmentClass = Class.forName(PROCESS_ENVIRONMENT);
69+
java.lang.reflect.Field environmentField = environmentClass.getDeclaredField(ENVIRONMENT);
70+
assertNotNull(environmentField);
71+
environmentField.setAccessible(true);
72+
73+
Object unmodifiableEnvironmentMap = environmentField.get(STATIC_METHOD);
74+
assertNotNull(unmodifiableEnvironmentMap);
75+
assertTrue(UMODIFIABLE_MAP_CLASS.isAssignableFrom(unmodifiableEnvironmentMap.getClass()));
76+
77+
java.lang.reflect.Field underlyingMapField =
78+
unmodifiableEnvironmentMap.getClass().getDeclaredField(SOURCE_MAP);
79+
underlyingMapField.setAccessible(true);
80+
Object underlyingMap = underlyingMapField.get(unmodifiableEnvironmentMap);
81+
assertNotNull(underlyingMap);
82+
assertTrue(MAP_CLASS.isAssignableFrom(underlyingMap.getClass()));
83+
84+
return (Map<String, String>) underlyingMap;
85+
}
86+
87+
@BeforeClass
88+
public static void setEnvVars() throws Exception {
89+
// Java versions 8 and lower start with 1. (1.8, 1.7 etc.).
90+
// Higher versions start with the major version number.
91+
// So this effectively verifies that the test is running on Java 8.
92+
assumeTrue(System.getProperty("java.version", "undefined").startsWith("1."));
93+
94+
if (Strings.isNullOrEmpty(System.getenv("GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS_FOR_RW"))) {
95+
Map<String, String> env = getModifiableEnvironment();
96+
env.put("GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS_FOR_RW", "true");
97+
setEnvVar = true;
98+
}
99+
}
100+
101+
@BeforeClass
102+
public static void clearEnvVars() throws Exception {
103+
if (setEnvVar) {
104+
Map<String, String> env = getModifiableEnvironment();
105+
env.remove("GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS_FOR_RW");
106+
}
107+
}
108+
109+
@Before
110+
public void setupResults() {
111+
query = "select * from my_table";
112+
dml = "insert into my_table (id, value) values (1, 'One')";
113+
String DML_THEN_RETURN_ID = dml + "\nTHEN RETURN `id`";
114+
dmlReturning = "insert into my_table (id, value) values (1, 'One') THEN RETURN *";
115+
116+
super.setupResults();
117+
118+
com.google.spanner.v1.ResultSet resultSet =
119+
com.google.spanner.v1.ResultSet.newBuilder()
120+
.setMetadata(
121+
ResultSetMetadata.newBuilder()
122+
.setRowType(
123+
StructType.newBuilder()
124+
.addFields(
125+
Field.newBuilder()
126+
.setType(Type.newBuilder().setCode(TypeCode.INT64).build())
127+
.setName("id")
128+
.build())
129+
.addFields(
130+
Field.newBuilder()
131+
.setType(Type.newBuilder().setCode(TypeCode.STRING).build())
132+
.setName("value")
133+
.build())
134+
.build())
135+
.build())
136+
.addRows(
137+
ListValue.newBuilder()
138+
.addValues(Value.newBuilder().setStringValue("1").build())
139+
.addValues(Value.newBuilder().setStringValue("One").build())
140+
.build())
141+
.build();
142+
com.google.spanner.v1.ResultSet returnIdResultSet =
143+
com.google.spanner.v1.ResultSet.newBuilder()
144+
.setMetadata(
145+
ResultSetMetadata.newBuilder()
146+
.setRowType(
147+
StructType.newBuilder()
148+
.addFields(
149+
Field.newBuilder()
150+
.setType(Type.newBuilder().setCode(TypeCode.INT64).build())
151+
.setName("id")
152+
.build())
153+
.build())
154+
.build())
155+
.addRows(
156+
ListValue.newBuilder()
157+
.addValues(Value.newBuilder().setStringValue("1").build())
158+
.build())
159+
.build();
160+
mockSpanner.putStatementResult(
161+
StatementResult.query(com.google.cloud.spanner.Statement.of(query), resultSet));
162+
mockSpanner.putStatementResult(
163+
StatementResult.update(com.google.cloud.spanner.Statement.of(dml), 1L));
164+
mockSpanner.putStatementResult(
165+
StatementResult.query(
166+
com.google.cloud.spanner.Statement.of(dmlReturning),
167+
resultSet
168+
.toBuilder()
169+
.setStats(ResultSetStats.newBuilder().setRowCountExact(1L).build())
170+
.build()));
171+
mockSpanner.putStatementResult(
172+
StatementResult.query(
173+
com.google.cloud.spanner.Statement.of(DML_THEN_RETURN_ID),
174+
returnIdResultSet
175+
.toBuilder()
176+
.setStats(ResultSetStats.newBuilder().setRowCountExact(1L).build())
177+
.build()));
178+
}
179+
180+
private String createUrl() {
181+
return String.format(
182+
"jdbc:cloudspanner://localhost:%d/projects/%s/instances/%s/databases/%s?usePlainText=true",
183+
getPort(), "proj", "inst", "db");
184+
}
185+
186+
@Override
187+
protected Connection createJdbcConnection() throws SQLException {
188+
return DriverManager.getConnection(createUrl());
189+
}
190+
191+
@Test
192+
public void testStatementExecuteQuery() throws SQLException {
193+
try (Connection connection = createJdbcConnection();
194+
Statement statement = connection.createStatement()) {
195+
try (ResultSet resultSet = statement.executeQuery(query)) {
196+
//noinspection StatementWithEmptyBody
197+
while (resultSet.next()) {}
198+
}
199+
}
200+
assertEquals(1, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class));
201+
ExecuteSqlRequest request = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0);
202+
assertTrue(MockServerHelper.getSession(mockSpanner, request.getSession()).getMultiplexed());
203+
}
204+
205+
@Test
206+
public void testStatementExecuteUpdate() throws SQLException {
207+
try (Connection connection = createJdbcConnection();
208+
Statement statement = connection.createStatement()) {
209+
assertEquals(1, statement.executeUpdate(dml));
210+
}
211+
assertEquals(1, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class));
212+
ExecuteSqlRequest request = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0);
213+
assertTrue(MockServerHelper.getSession(mockSpanner, request.getSession()).getMultiplexed());
214+
assertTrue(request.hasTransaction());
215+
assertTrue(request.getTransaction().hasBegin());
216+
assertTrue(request.getTransaction().getBegin().hasReadWrite());
217+
}
218+
219+
@Test
220+
public void testStatementExecuteQueryDmlReturning() throws SQLException {
221+
try (Connection connection = createJdbcConnection();
222+
Statement statement = connection.createStatement()) {
223+
try (ResultSet resultSet = statement.executeQuery(dmlReturning)) {
224+
//noinspection StatementWithEmptyBody
225+
while (resultSet.next()) {}
226+
}
227+
}
228+
assertEquals(1, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class));
229+
ExecuteSqlRequest request = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0);
230+
assertTrue(MockServerHelper.getSession(mockSpanner, request.getSession()).getMultiplexed());
231+
assertTrue(request.hasTransaction());
232+
assertTrue(request.getTransaction().hasBegin());
233+
assertTrue(request.getTransaction().getBegin().hasReadWrite());
234+
}
235+
}

0 commit comments

Comments
 (0)