Skip to content

Commit fb3d1f6

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 fb3d1f6

File tree

1 file changed

+236
-0
lines changed

1 file changed

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

0 commit comments

Comments
 (0)