Skip to content

Commit ec93ef9

Browse files
authored
JAVA-3084: Add integration test coverage for ExtraTypeCodecs (#1679)
1 parent e8b25ab commit ec93ef9

File tree

1 file changed

+298
-0
lines changed

1 file changed

+298
-0
lines changed
Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
/*
2+
* Copyright DataStax, Inc.
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+
package com.datastax.oss.driver.core.type.codec;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
20+
import com.datastax.oss.driver.api.core.CqlSession;
21+
import com.datastax.oss.driver.api.core.Version;
22+
import com.datastax.oss.driver.api.core.cql.BoundStatement;
23+
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
24+
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
25+
import com.datastax.oss.driver.api.core.type.codec.ExtraTypeCodecs;
26+
import com.datastax.oss.driver.api.core.type.codec.TypeCodec;
27+
import com.datastax.oss.driver.api.core.type.codec.TypeCodecs;
28+
import com.datastax.oss.driver.api.testinfra.ccm.CcmRule;
29+
import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement;
30+
import com.datastax.oss.driver.api.testinfra.requirement.BackendType;
31+
import com.datastax.oss.driver.api.testinfra.session.SessionRule;
32+
import com.datastax.oss.driver.categories.ParallelizableTests;
33+
import com.fasterxml.jackson.annotation.JsonCreator;
34+
import com.fasterxml.jackson.annotation.JsonProperty;
35+
import com.fasterxml.jackson.databind.ObjectMapper;
36+
import java.time.Instant;
37+
import java.time.LocalDateTime;
38+
import java.time.ZoneId;
39+
import java.time.ZonedDateTime;
40+
import java.time.temporal.ChronoUnit;
41+
import java.util.ArrayList;
42+
import java.util.Arrays;
43+
import java.util.List;
44+
import java.util.Objects;
45+
import java.util.Optional;
46+
import java.util.UUID;
47+
import java.util.stream.Stream;
48+
import org.junit.Assume;
49+
import org.junit.BeforeClass;
50+
import org.junit.ClassRule;
51+
import org.junit.Test;
52+
import org.junit.experimental.categories.Category;
53+
import org.junit.rules.RuleChain;
54+
import org.junit.rules.TestRule;
55+
56+
@Category(ParallelizableTests.class)
57+
public class ExtraTypeCodecsIT {
58+
59+
private static final CcmRule CCM_RULE = CcmRule.getInstance();
60+
61+
private static final SessionRule<CqlSession> SESSION_RULE = SessionRule.builder(CCM_RULE).build();
62+
63+
@ClassRule
64+
public static final TestRule CHAIN = RuleChain.outerRule(CCM_RULE).around(SESSION_RULE);
65+
66+
private enum TableField {
67+
cql_text("text_value", "text"),
68+
cql_int("integer_value", "int"),
69+
cql_vector("vector_value", "vector<float, 3>"),
70+
cql_list_of_text("list_of_text_value", "list<text>"),
71+
cql_timestamp("timestamp_value", "timestamp"),
72+
cql_boolean("boolean_value", "boolean"),
73+
;
74+
75+
final String name;
76+
final String ty;
77+
78+
TableField(String name, String ty) {
79+
this.name = name;
80+
this.ty = ty;
81+
}
82+
83+
private String definition() {
84+
return String.format("%s %s", name, ty);
85+
}
86+
}
87+
88+
@BeforeClass
89+
public static void setupSchema() {
90+
List<String> fieldDefinitions = new ArrayList<>();
91+
fieldDefinitions.add("key uuid PRIMARY KEY");
92+
Stream.of(TableField.values())
93+
.forEach(
94+
tf -> {
95+
// TODO: Move this check to BackendRequirementRule once JAVA-3069 is resolved.
96+
if (tf == TableField.cql_vector
97+
&& CCM_RULE.getCassandraVersion().compareTo(Version.parse("5.0")) < 0) {
98+
// don't add vector type before cassandra version 5.0
99+
return;
100+
}
101+
fieldDefinitions.add(tf.definition());
102+
});
103+
SESSION_RULE
104+
.session()
105+
.execute(
106+
SimpleStatement.builder(
107+
String.format(
108+
"CREATE TABLE IF NOT EXISTS extra_type_codecs_it (%s)",
109+
String.join(", ", fieldDefinitions)))
110+
.setExecutionProfile(SESSION_RULE.slowProfile())
111+
.build());
112+
}
113+
114+
private <T> void insertAndRead(TableField field, T value, TypeCodec<T> codec) {
115+
CqlSession session = SESSION_RULE.session();
116+
// write value under new key using provided codec
117+
UUID key = UUID.randomUUID();
118+
119+
PreparedStatement preparedInsert =
120+
session.prepare(
121+
SimpleStatement.builder(
122+
String.format(
123+
"INSERT INTO extra_type_codecs_it (key, %s) VALUES (?, ?)", field.name))
124+
.build());
125+
BoundStatement boundInsert =
126+
preparedInsert
127+
.boundStatementBuilder()
128+
.setUuid("key", key)
129+
.set(field.name, value, codec)
130+
.build();
131+
session.execute(boundInsert);
132+
133+
// read value using provided codec and assert result
134+
PreparedStatement preparedSelect =
135+
session.prepare(
136+
SimpleStatement.builder(
137+
String.format("SELECT %s FROM extra_type_codecs_it WHERE key = ?", field.name))
138+
.build());
139+
BoundStatement boundSelect = preparedSelect.boundStatementBuilder().setUuid("key", key).build();
140+
assertThat(session.execute(boundSelect).one().get(field.name, codec)).isEqualTo(value);
141+
}
142+
143+
@Test
144+
public void enum_names_of() {
145+
insertAndRead(
146+
TableField.cql_text, TestEnum.value1, ExtraTypeCodecs.enumNamesOf(TestEnum.class));
147+
}
148+
149+
@Test
150+
public void enum_ordinals_of() {
151+
insertAndRead(
152+
TableField.cql_int, TestEnum.value1, ExtraTypeCodecs.enumOrdinalsOf(TestEnum.class));
153+
}
154+
155+
// Also requires -Dccm.branch=vsearch and the ability to build that branch locally
156+
@BackendRequirement(type = BackendType.CASSANDRA, minInclusive = "5.0.0")
157+
@Test
158+
public void float_to_vector_array() {
159+
// @BackRequirement on test methods that use @ClassRule to configure CcmRule require @Rule
160+
// BackendRequirementRule included with fix JAVA-3069. Until then we will ignore this test with
161+
// an assume.
162+
Assume.assumeTrue(
163+
"Requires Cassandra 5.0 or greater",
164+
CCM_RULE.getCassandraVersion().compareTo(Version.parse("5.0")) >= 0);
165+
insertAndRead(
166+
TableField.cql_vector,
167+
new float[] {1.1f, 0f, Float.NaN},
168+
ExtraTypeCodecs.floatVectorToArray(3));
169+
}
170+
171+
@Test
172+
public void json_java_class() {
173+
insertAndRead(
174+
TableField.cql_text,
175+
new TestJsonAnnotatedPojo("example", Arrays.asList(1, 2, 3)),
176+
ExtraTypeCodecs.json(TestJsonAnnotatedPojo.class));
177+
}
178+
179+
@Test
180+
public void json_java_class_and_object_mapper() {
181+
insertAndRead(
182+
TableField.cql_text,
183+
TestPojo.create(1, "abc", "def"),
184+
ExtraTypeCodecs.json(TestPojo.class, new ObjectMapper()));
185+
}
186+
187+
@Test
188+
public void list_to_array_of() {
189+
insertAndRead(
190+
TableField.cql_list_of_text,
191+
new String[] {"hello", "kitty"},
192+
ExtraTypeCodecs.listToArrayOf(TypeCodecs.TEXT));
193+
}
194+
195+
@Test
196+
public void local_timestamp_at() {
197+
ZoneId systemZoneId = ZoneId.systemDefault();
198+
insertAndRead(
199+
TableField.cql_timestamp,
200+
LocalDateTime.now(systemZoneId).truncatedTo(ChronoUnit.MILLIS),
201+
ExtraTypeCodecs.localTimestampAt(systemZoneId));
202+
}
203+
204+
@Test
205+
public void optional_of() {
206+
insertAndRead(
207+
TableField.cql_boolean, Optional.empty(), ExtraTypeCodecs.optionalOf(TypeCodecs.BOOLEAN));
208+
insertAndRead(
209+
TableField.cql_boolean, Optional.of(true), ExtraTypeCodecs.optionalOf(TypeCodecs.BOOLEAN));
210+
}
211+
212+
@Test
213+
public void timestamp_at() {
214+
ZoneId systemZoneId = ZoneId.systemDefault();
215+
insertAndRead(
216+
TableField.cql_timestamp,
217+
Instant.now().truncatedTo(ChronoUnit.MILLIS),
218+
ExtraTypeCodecs.timestampAt(systemZoneId));
219+
}
220+
221+
@Test
222+
public void timestamp_millis_at() {
223+
ZoneId systemZoneId = ZoneId.systemDefault();
224+
insertAndRead(
225+
TableField.cql_timestamp,
226+
Instant.now().toEpochMilli(),
227+
ExtraTypeCodecs.timestampMillisAt(systemZoneId));
228+
}
229+
230+
@Test
231+
public void zoned_timestamp_at() {
232+
ZoneId systemZoneId = ZoneId.systemDefault();
233+
insertAndRead(
234+
TableField.cql_timestamp,
235+
ZonedDateTime.now(systemZoneId).truncatedTo(ChronoUnit.MILLIS),
236+
ExtraTypeCodecs.zonedTimestampAt(systemZoneId));
237+
}
238+
239+
private enum TestEnum {
240+
value1,
241+
value2,
242+
value3,
243+
}
244+
245+
// Public for JSON serialization
246+
public static final class TestJsonAnnotatedPojo {
247+
public final String info;
248+
public final List<Integer> values;
249+
250+
@JsonCreator
251+
public TestJsonAnnotatedPojo(
252+
@JsonProperty("info") String info, @JsonProperty("values") List<Integer> values) {
253+
this.info = info;
254+
this.values = values;
255+
}
256+
257+
@Override
258+
public boolean equals(Object o) {
259+
if (this == o) return true;
260+
if (o == null || getClass() != o.getClass()) return false;
261+
TestJsonAnnotatedPojo testJsonAnnotatedPojo = (TestJsonAnnotatedPojo) o;
262+
return Objects.equals(info, testJsonAnnotatedPojo.info)
263+
&& Objects.equals(values, testJsonAnnotatedPojo.values);
264+
}
265+
266+
@Override
267+
public int hashCode() {
268+
return Objects.hash(info, values);
269+
}
270+
}
271+
272+
public static final class TestPojo {
273+
public int id;
274+
public String[] messages;
275+
276+
public static TestPojo create(int id, String... messages) {
277+
TestPojo obj = new TestPojo();
278+
obj.id = id;
279+
obj.messages = messages;
280+
return obj;
281+
}
282+
283+
@Override
284+
public boolean equals(Object o) {
285+
if (this == o) return true;
286+
if (o == null || getClass() != o.getClass()) return false;
287+
TestPojo testPojo = (TestPojo) o;
288+
return id == testPojo.id && Arrays.equals(messages, testPojo.messages);
289+
}
290+
291+
@Override
292+
public int hashCode() {
293+
int result = Objects.hash(id);
294+
result = 31 * result + Arrays.hashCode(messages);
295+
return result;
296+
}
297+
}
298+
}

0 commit comments

Comments
 (0)