Skip to content

Commit cf0fc65

Browse files
gavlyukovskiyBreus
authored andcommitted
Migrate tests to use Jackson 3
1 parent abc9a48 commit cf0fc65

19 files changed

+218
-261
lines changed

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ jmh = "1.37"
44
[libraries]
55
assertj-core = { group = "org.assertj", name = "assertj-core", version = "3.27.6" }
66
jspecify = { group = "org.jspecify", name = "jspecify", version = "1.0.0" }
7-
jackson-databind = { group = "com.fasterxml.jackson.core", name = "jackson-databind", version = "2.20.1" }
7+
jackson-databind = { group = "tools.jackson.core", name = "jackson-databind", version = "3.0.2" }
88
junit-api = { group = "org.junit.jupiter", name = "junit-jupiter-api" }
99
junit-bom = { group = "org.junit", name = "junit-bom", version = "6.0.1" }
1010
junit-platform-launcher = { group = "org.junit.platform", name = "junit-platform-launcher" }

src/jmh/java/dev/blaauwendraad/masker/json/BaselineBenchmark.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public String regexReplace(State state) {
100100

101101
@Benchmark
102102
public String jacksonParseOnly(State state) throws IOException {
103-
return ParseAndMaskUtil.DEFAULT_OBJECT_MAPPER.readTree(state.jsonString).toString();
103+
return ParseAndMaskUtil.DEFAULT_JSON_MAPPER.readTree(state.jsonString).toString();
104104
}
105105

106106
@Benchmark

src/jmh/java/dev/blaauwendraad/masker/json/InstanceCreationBenchmark.java

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
package dev.blaauwendraad.masker.json;
22

3-
import com.fasterxml.jackson.databind.ObjectMapper;
4-
import com.fasterxml.jackson.databind.node.ArrayNode;
5-
63
import dev.blaauwendraad.masker.randomgen.RandomJsonGenerator;
7-
84
import org.jspecify.annotations.NullUnmarked;
95
import org.openjdk.jmh.annotations.Benchmark;
106
import org.openjdk.jmh.annotations.BenchmarkMode;
@@ -16,9 +12,10 @@
1612
import org.openjdk.jmh.annotations.Scope;
1713
import org.openjdk.jmh.annotations.Setup;
1814
import org.openjdk.jmh.annotations.Warmup;
15+
import tools.jackson.databind.json.JsonMapper;
16+
import tools.jackson.databind.node.ArrayNode;
1917

2018
import java.io.IOException;
21-
import java.net.URL;
2219
import java.util.ArrayList;
2320
import java.util.Collections;
2421
import java.util.List;
@@ -33,7 +30,7 @@
3330
@OutputTimeUnit(TimeUnit.SECONDS)
3431
@BenchmarkMode(Mode.Throughput)
3532
public class InstanceCreationBenchmark {
36-
private static final ObjectMapper objectMapper = new ObjectMapper();
33+
private static final JsonMapper jsonMapper = new JsonMapper();
3734

3835
@org.openjdk.jmh.annotations.State(Scope.Thread)
3936
@NullUnmarked
@@ -45,15 +42,19 @@ public static class State {
4542

4643
@Setup
4744
public synchronized void setup() throws IOException {
48-
URL targetKeyFileUrl = RandomJsonGenerator.class.getResource("/target_keys.json");
49-
List<String> targetKeyList = new ArrayList<>();
50-
objectMapper.readValue(targetKeyFileUrl, ArrayNode.class).forEach(t -> targetKeyList.add(t.textValue()));
51-
if (!"ALL".equals(numberOfTargetKeys) && Integer.parseInt(numberOfTargetKeys) <= targetKeyList.size()) {
52-
Collections.shuffle(targetKeyList, new Random(RandomJsonGenerator.STATIC_RANDOM_SEED));
53-
targetKeys =
54-
targetKeyList.stream().limit(Integer.parseInt(numberOfTargetKeys)).collect(Collectors.toSet());
55-
} else {
56-
targetKeys = targetKeyList.stream().collect(Collectors.toSet());
45+
try (var stream = RandomJsonGenerator.class.getResourceAsStream("/target_keys.json")) {
46+
if (stream == null) {
47+
throw new IllegalArgumentException("File not found: target_keys.json");
48+
}
49+
List<String> targetKeyList = new ArrayList<>();
50+
jsonMapper.readValue(stream, ArrayNode.class).forEach(t -> targetKeyList.add(t.asString()));
51+
if (!"ALL".equals(numberOfTargetKeys) && Integer.parseInt(numberOfTargetKeys) <= targetKeyList.size()) {
52+
Collections.shuffle(targetKeyList, new Random(RandomJsonGenerator.STATIC_RANDOM_SEED));
53+
targetKeys =
54+
targetKeyList.stream().limit(Integer.parseInt(numberOfTargetKeys)).collect(Collectors.toSet());
55+
} else {
56+
targetKeys = targetKeyList.stream().collect(Collectors.toSet());
57+
}
5758
}
5859
}
5960
}

src/test/java/dev/blaauwendraad/masker/json/FuzzingTest.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package dev.blaauwendraad.masker.json;
22

3-
import com.fasterxml.jackson.core.JsonProcessingException;
4-
import com.fasterxml.jackson.databind.JsonNode;
53
import dev.blaauwendraad.masker.json.config.JsonMaskingConfig;
64
import dev.blaauwendraad.masker.json.path.JsonPath;
75
import dev.blaauwendraad.masker.json.util.FuzzingDurationUtil;
@@ -10,6 +8,7 @@
108
import dev.blaauwendraad.masker.randomgen.RandomJsonGeneratorConfig;
119
import org.junit.jupiter.params.ParameterizedTest;
1210
import org.junit.jupiter.params.provider.MethodSource;
11+
import tools.jackson.databind.JsonNode;
1312

1413
import java.time.Duration;
1514
import java.time.Instant;
@@ -25,7 +24,7 @@ final class FuzzingTest {
2524

2625
@ParameterizedTest
2726
@MethodSource("jsonMaskingConfigs")
28-
void fuzzingAgainstParseAndMaskUsingJackson(JsonMaskingConfig jsonMaskingConfig) throws JsonProcessingException {
27+
void fuzzingAgainstParseAndMaskUsingJackson(JsonMaskingConfig jsonMaskingConfig) {
2928
long timeLimit = FuzzingDurationUtil.determineTestTimeLimit(jsonMaskingConfigs().count());
3029
Instant startTime = Instant.now();
3130
int randomTestExecuted = 0;

src/test/java/dev/blaauwendraad/masker/json/InstanceCreationMemoryUsageTest.java

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
package dev.blaauwendraad.masker.json;
22

3-
import com.fasterxml.jackson.databind.ObjectMapper;
4-
import com.fasterxml.jackson.databind.node.ArrayNode;
53
import dev.blaauwendraad.masker.json.config.JsonMaskingConfig;
64
import dev.blaauwendraad.masker.json.config.KeyMaskingConfig;
75
import dev.blaauwendraad.masker.randomgen.RandomJsonGenerator;
86
import org.assertj.core.api.Assertions;
97
import org.junit.jupiter.api.Test;
8+
import tools.jackson.databind.json.JsonMapper;
9+
import tools.jackson.databind.node.ArrayNode;
1010

1111
import java.io.IOException;
12-
import java.net.URL;
1312
import java.util.HashSet;
1413
import java.util.Set;
1514

@@ -1587,16 +1586,11 @@ public class InstanceCreationMemoryUsageTest {
15871586
);
15881587

15891588

1590-
private static final ObjectMapper objectMapper = new ObjectMapper();
1589+
private static final JsonMapper jsonMapper = new JsonMapper();
15911590

15921591
@Test
15931592
public void defaultInstanceCreation() throws IOException {
1594-
URL targetKeyFileUrl = RandomJsonGenerator.class.getResource("/target_keys.json");
1595-
if (targetKeyFileUrl == null) {
1596-
throw new IllegalStateException("target_keys.json is not found.");
1597-
}
1598-
Set<String> targetKeys = new HashSet<>();
1599-
objectMapper.readValue(targetKeyFileUrl.openStream(), ArrayNode.class).forEach(t -> targetKeys.add(t.textValue()));
1593+
var targetKeys = loadTargetKeys();
16001594

16011595
long memoryBeforeInstanceCreationKb = getCurrentRetainedMemory();
16021596

@@ -1642,4 +1636,15 @@ private long getCurrentRetainedMemory() {
16421636
private long bytesToKb(long bytes) {
16431637
return bytes / 1024;
16441638
}
1639+
1640+
private static Set<String> loadTargetKeys() throws IOException {
1641+
try (var stream = RandomJsonGenerator.class.getResourceAsStream("/target_keys.json")) {
1642+
if (stream == null) {
1643+
throw new IllegalArgumentException("File not found: target_keys.json");
1644+
}
1645+
Set<String> targetKeys = new HashSet<>();
1646+
jsonMapper.readValue(stream, ArrayNode.class).forEach(t -> targetKeys.add(t.asString()));
1647+
return targetKeys;
1648+
}
1649+
}
16451650
}

src/test/java/dev/blaauwendraad/masker/json/JSONTestSuiteTest.java

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
package dev.blaauwendraad.masker.json;
22

3-
import com.fasterxml.jackson.core.JacksonException;
43
import dev.blaauwendraad.masker.json.config.JsonMaskingConfig;
54
import org.assertj.core.api.Assertions;
65
import org.jspecify.annotations.Nullable;
76
import org.junit.jupiter.params.ParameterizedTest;
87
import org.junit.jupiter.params.provider.Arguments;
98
import org.junit.jupiter.params.provider.MethodSource;
9+
import tools.jackson.core.JacksonException;
1010

1111
import java.io.ByteArrayInputStream;
1212
import java.io.ByteArrayOutputStream;
1313
import java.io.IOException;
1414
import java.io.InputStream;
1515
import java.io.OutputStream;
16-
import java.io.UncheckedIOException;
1716
import java.nio.charset.StandardCharsets;
1817
import java.nio.file.Files;
1918
import java.nio.file.Path;
@@ -90,13 +89,9 @@ void mustPassSuiteWithNoopMaskerShouldBeEquivalentToJackson(String testName, Jso
9089
JsonMaskerTestUtil.assertJsonMaskerApiEquivalence(jsonMasker, new String(file.originalContent, StandardCharsets.UTF_8));
9190

9291
// must be equivalent to being parsed by jackson
93-
try {
94-
assertThat(ParseAndMaskUtil.DEFAULT_OBJECT_MAPPER.readTree(actual))
95-
.as("Failed for input: " + new String(actual, StandardCharsets.UTF_8))
96-
.isEqualTo(ParseAndMaskUtil.DEFAULT_OBJECT_MAPPER.readTree(file.originalContent));
97-
} catch (IOException e) {
98-
throw new UncheckedIOException(e);
99-
}
92+
assertThat(ParseAndMaskUtil.DEFAULT_JSON_MAPPER.readTree(actual))
93+
.as("Failed for input: " + new String(actual, StandardCharsets.UTF_8))
94+
.isEqualTo(ParseAndMaskUtil.DEFAULT_JSON_MAPPER.readTree(file.originalContent));
10095
}
10196

10297
@ParameterizedTest(name = "{0}")
@@ -122,14 +117,12 @@ void mayPassSuiteWithNoopMaskerShouldNotFail(String testName, JsonTestSuiteFile
122117

123118
// if jackson can parse it - out should be equivalent
124119
try {
125-
assertThat(ParseAndMaskUtil.DEFAULT_OBJECT_MAPPER.readTree(actual))
120+
assertThat(ParseAndMaskUtil.DEFAULT_JSON_MAPPER.readTree(actual))
126121
.as("Failed for input: " + new String(actual, StandardCharsets.UTF_8))
127-
.isEqualTo(ParseAndMaskUtil.DEFAULT_OBJECT_MAPPER.readTree(file.originalContent));
122+
.isEqualTo(ParseAndMaskUtil.DEFAULT_JSON_MAPPER.readTree(file.originalContent));
128123
} catch (JacksonException e) {
129124
// JsonMasker didn't fail, but jackson can't parse it, nothing to compare
130125
// suck cases are also tested by shouldMaskAllTestCasesPredictably
131-
} catch (IOException e) {
132-
throw new UncheckedIOException(e);
133126
}
134127
}
135128

src/test/java/dev/blaauwendraad/masker/json/JsonMaskerTestUtil.java

Lines changed: 42 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
package dev.blaauwendraad.masker.json;
22

3-
import com.fasterxml.jackson.core.JsonProcessingException;
4-
import com.fasterxml.jackson.databind.JsonNode;
5-
import com.fasterxml.jackson.databind.ObjectMapper;
6-
import com.fasterxml.jackson.databind.node.ArrayNode;
73
import dev.blaauwendraad.masker.json.config.JsonMaskingConfig;
84
import dev.blaauwendraad.masker.json.config.JsonMaskingConfigTestUtil;
95
import dev.blaauwendraad.masker.json.config.KeyMaskingConfig;
106
import org.jspecify.annotations.Nullable;
117
import org.junit.jupiter.api.Assertions;
8+
import tools.jackson.databind.JsonNode;
9+
import tools.jackson.databind.json.JsonMapper;
10+
import tools.jackson.databind.node.ArrayNode;
1211

1312
import java.io.ByteArrayInputStream;
1413
import java.io.ByteArrayOutputStream;
1514
import java.io.IOException;
16-
import java.net.URL;
1715
import java.nio.charset.StandardCharsets;
1816
import java.util.ArrayList;
1917
import java.util.List;
@@ -28,70 +26,69 @@ public final class JsonMaskerTestUtil {
2826
private JsonMaskerTestUtil() {
2927
}
3028

31-
private static final ObjectMapper mapper = new ObjectMapper();
29+
private static final JsonMapper jsonMapper = new JsonMapper();
3230

3331
public static List<JsonMaskerTestInstance> getJsonMaskerTestInstancesFromFile(String fileName) throws IOException {
34-
List<JsonMaskerTestInstance> testInstances = new ArrayList<>();
35-
URL fileUrl = JsonMaskerTestUtil.class.getClassLoader().getResource(fileName);
36-
if (fileUrl == null) {
37-
throw new IllegalArgumentException("File not found: " + fileName);
38-
}
39-
ArrayNode jsonArray = mapper.readValue(fileUrl.openStream(), ArrayNode.class);
40-
for (JsonNode jsonNode : jsonArray) {
41-
JsonMaskingConfig.Builder builder = JsonMaskingConfig.builder();
42-
JsonNode jsonMaskingConfig = jsonNode.findValue("maskingConfig");
43-
if (jsonMaskingConfig != null) {
44-
applyConfig(jsonMaskingConfig, builder);
32+
try (var stream = JsonMaskerTestUtil.class.getClassLoader().getResourceAsStream(fileName)) {
33+
if (stream == null) {
34+
throw new IllegalArgumentException("File not found: " + fileName);
35+
}
36+
List<JsonMaskerTestInstance> testInstances = new ArrayList<>();
37+
ArrayNode jsonArray = jsonMapper.readValue(stream, ArrayNode.class);
38+
for (JsonNode jsonNode : jsonArray) {
39+
JsonMaskingConfig.Builder builder = JsonMaskingConfig.builder();
40+
JsonNode jsonMaskingConfig = jsonNode.findValue("maskingConfig");
41+
if (jsonMaskingConfig != null) {
42+
applyConfig(jsonMaskingConfig, builder);
43+
}
44+
JsonMaskingConfig maskingConfig = builder.build();
45+
var input = jsonNode.get("input").toPrettyString();
46+
var expectedOutput = jsonNode.get("expectedOutput").toPrettyString();
47+
testInstances.add(new JsonMaskerTestInstance(input, expectedOutput, new KeyContainsMasker(maskingConfig)));
4548
}
46-
JsonMaskingConfig maskingConfig = builder.build();
47-
var input = jsonNode.get("input").toPrettyString();
48-
var expectedOutput = jsonNode.get("expectedOutput").toPrettyString();
49-
testInstances.add(new JsonMaskerTestInstance(input, expectedOutput, new KeyContainsMasker(maskingConfig)));
49+
return testInstances;
5050
}
51-
return testInstances;
5251
}
5352

5453
private static void applyConfig(JsonNode jsonMaskingConfig, JsonMaskingConfig.Builder builder) {
55-
jsonMaskingConfig.properties().forEach(p -> {
56-
String key = p.getKey();
57-
JsonNode value = p.getValue();
54+
jsonMaskingConfig.forEachEntry((key, value) -> {
5855
switch (key) {
5956
case "maskKeys" -> StreamSupport.stream(value.spliterator(), false).forEach(node -> {
60-
if (node.isTextual()) {
61-
builder.maskKeys(Set.of(node.asText()));
57+
if (node.isString()) {
58+
builder.maskKeys(Set.of(node.asString()));
6259
} else {
63-
builder.maskKeys(asSet(node.get("keys"), JsonNode::asText), applyKeyConfig(node.get("keyMaskingConfig")));
60+
builder.maskKeys(asSet(node.get("keys"), JsonNode::asString), applyKeyConfig(node.get("keyMaskingConfig")));
6461
}
6562
});
6663
case "maskJsonPaths" -> StreamSupport.stream(value.spliterator(), false).forEach(node -> {
67-
if (node.isTextual()) {
68-
builder.maskJsonPaths(Set.of(node.asText()));
64+
if (node.isString()) {
65+
builder.maskJsonPaths(Set.of(node.asString()));
6966
} else {
70-
builder.maskJsonPaths(asSet(node.get("keys"), JsonNode::asText), applyKeyConfig(node.get("keyMaskingConfig")));
67+
builder.maskJsonPaths(asSet(node.get("keys"), JsonNode::asString), applyKeyConfig(node.get("keyMaskingConfig")));
7168
}
7269
});
73-
case "allowKeys" -> builder.allowKeys(asSet(value, JsonNode::asText));
74-
case "allowJsonPaths" -> builder.allowJsonPaths(asSet(value, JsonNode::asText));
70+
case "allowKeys" -> builder.allowKeys(asSet(value, JsonNode::asString));
71+
case "allowJsonPaths" -> builder.allowJsonPaths(asSet(value, JsonNode::asString));
7572
case "caseSensitiveTargetKeys" -> {
7673
if (value.booleanValue()) {
7774
builder.caseSensitiveTargetKeys();
7875
}
7976
}
80-
case "maskStringsWith" -> builder.maskStringsWith(value.textValue());
81-
case "maskStringCharactersWith" -> builder.maskStringCharactersWith(value.textValue());
77+
case "maskStringsWith" -> builder.maskStringsWith(value.asString());
78+
case "maskStringCharactersWith" -> builder.maskStringCharactersWith(value.asString());
8279
case "maskNumbersWith" -> {
8380
if (value.isInt()) {
8481
builder.maskNumbersWith(value.intValue());
8582
} else {
86-
builder.maskNumbersWith(value.textValue());
83+
builder.maskNumbersWith(value.asString());
8784
}
8885
}
8986
case "maskNumberDigitsWith" -> builder.maskNumberDigitsWith(value.intValue());
9087
case "maskBooleansWith" -> {
9188
if (value.isBoolean()) {
9289
builder.maskBooleansWith(value.booleanValue());
9390
}
94-
builder.maskBooleansWith(value.textValue());
91+
builder.maskBooleansWith(value.asString());
9592
}
9693
default -> throw new IllegalArgumentException("Unknown option " + key);
9794
}
@@ -100,25 +97,23 @@ private static void applyConfig(JsonNode jsonMaskingConfig, JsonMaskingConfig.Bu
10097

10198
private static KeyMaskingConfig applyKeyConfig(JsonNode jsonNode) {
10299
KeyMaskingConfig.Builder builder = KeyMaskingConfig.builder();
103-
jsonNode.properties().forEach(p -> {
104-
String key = p.getKey();
105-
JsonNode value = p.getValue();
100+
jsonNode.forEachEntry((key, value) -> {
106101
switch (key) {
107-
case "maskStringsWith" -> builder.maskStringsWith(value.textValue());
108-
case "maskStringCharactersWith" -> builder.maskStringCharactersWith(value.textValue());
102+
case "maskStringsWith" -> builder.maskStringsWith(value.asString());
103+
case "maskStringCharactersWith" -> builder.maskStringCharactersWith(value.asString());
109104
case "maskNumbersWith" -> {
110105
if (value.isInt()) {
111106
builder.maskNumbersWith(value.intValue());
112107
} else {
113-
builder.maskNumbersWith(value.textValue());
108+
builder.maskNumbersWith(value.asString());
114109
}
115110
}
116111
case "maskNumberDigitsWith" -> builder.maskNumberDigitsWith(value.intValue());
117112
case "maskBooleansWith" -> {
118113
if (value.isBoolean()) {
119114
builder.maskBooleansWith(value.booleanValue());
120115
}
121-
builder.maskBooleansWith(value.textValue());
116+
builder.maskBooleansWith(value.asString());
122117
}
123118
default -> throw new IllegalArgumentException("Unknown option " + key);
124119
}
@@ -148,13 +143,9 @@ public static void assertJsonMaskerApiEquivalence(JsonMasker jsonMasker,
148143
String minimalBufferStreamsOutput = getStreamingModeOutput(jsonMasker, input);
149144
JsonMaskingConfigTestUtil.setBufferSize(((KeyContainsMasker) jsonMasker).maskingConfig, oldBufferSize);
150145
if (pretty) {
151-
try {
152-
bytesOutput = ParseAndMaskUtil.DEFAULT_OBJECT_MAPPER.readTree(bytesOutput).toString();
153-
streamsOutput = ParseAndMaskUtil.DEFAULT_OBJECT_MAPPER.readTree(streamsOutput).toString();
154-
minimalBufferStreamsOutput = ParseAndMaskUtil.DEFAULT_OBJECT_MAPPER.readTree(minimalBufferStreamsOutput).toString();
155-
} catch (JsonProcessingException e) {
156-
throw new IllegalStateException("Failed for input: " + input, e);
157-
}
146+
bytesOutput = ParseAndMaskUtil.DEFAULT_JSON_MAPPER.readTree(bytesOutput).toString();
147+
streamsOutput = ParseAndMaskUtil.DEFAULT_JSON_MAPPER.readTree(streamsOutput).toString();
148+
minimalBufferStreamsOutput = ParseAndMaskUtil.DEFAULT_JSON_MAPPER.readTree(minimalBufferStreamsOutput).toString();
158149
}
159150
if (expectedOutput != null) {
160151
Assertions.assertEquals(expectedOutput, bytesOutput, "Failed for input: " + input);

src/test/java/dev/blaauwendraad/masker/json/NoFailingExecutionFuzzingTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package dev.blaauwendraad.masker.json;
22

3-
import com.fasterxml.jackson.databind.JsonNode;
3+
import tools.jackson.databind.JsonNode;
44
import dev.blaauwendraad.masker.json.config.JsonMaskingConfig;
55
import dev.blaauwendraad.masker.json.util.FuzzingDurationUtil;
66
import dev.blaauwendraad.masker.json.util.JsonFormatter;

0 commit comments

Comments
 (0)