Skip to content

Commit f475894

Browse files
cursoragentsimbo1905
andcommitted
Issue #130 Add json-transforms golden tests and ported fixtures
Co-authored-by: simbo1905 <simbo1905@60hertz.com>
1 parent 32f60d9 commit f475894

File tree

87 files changed

+1226
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+1226
-0
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package json.java21.transforms;
2+
3+
import jdk.sandbox.java.util.json.Json;
4+
import jdk.sandbox.java.util.json.JsonValue;
5+
import org.junit.jupiter.params.ParameterizedTest;
6+
import org.junit.jupiter.params.provider.Arguments;
7+
import org.junit.jupiter.params.provider.MethodSource;
8+
9+
import java.io.IOException;
10+
import java.nio.file.Files;
11+
import java.nio.file.Path;
12+
import java.util.List;
13+
import java.util.logging.Logger;
14+
import java.util.stream.Stream;
15+
16+
import static org.assertj.core.api.Assertions.assertThat;
17+
18+
public final class JsonTransformGoldenFilesTest extends JsonTransformsLoggingConfig {
19+
20+
private static final Logger LOG = Logger.getLogger(JsonTransformGoldenFilesTest.class.getName());
21+
22+
@ParameterizedTest(name = "{0} - {1}")
23+
@MethodSource("inputs")
24+
void goldenFiles(String category, String testName) throws IOException {
25+
LOG.info(() -> "TEST: goldenFiles category=" + category + " testName=" + testName);
26+
27+
final var base = Path.of(System.getProperty("jsontransforms.test.resources"));
28+
final var dir = base.resolve("Inputs").resolve(category);
29+
30+
final var sourceName = testName.substring(0, testName.lastIndexOf('.'));
31+
final var sourcePath = dir.resolve(sourceName + ".Source.json");
32+
final var transformPath = dir.resolve(testName + ".Transform.json");
33+
final var expectedPath = dir.resolve(testName + ".Expected.json");
34+
35+
final JsonValue source = parseFile(sourcePath);
36+
final JsonValue transform = parseFile(transformPath);
37+
final JsonValue expected = parseFile(expectedPath);
38+
39+
final var program = JsonTransform.parse(transform);
40+
final var actual = program.run(source);
41+
42+
assertThat(actual).isEqualTo(expected);
43+
}
44+
45+
static Stream<Arguments> inputs() throws IOException {
46+
final var base = Path.of(System.getProperty("jsontransforms.test.resources"));
47+
final var inputsBase = base.resolve("Inputs");
48+
final List<String> categories = List.of("Default", "Remove", "Rename", "Replace", "Merge");
49+
50+
Stream<Arguments> all = Stream.empty();
51+
for (final var category : categories) {
52+
final var dir = inputsBase.resolve(category);
53+
try (var stream = Files.list(dir)) {
54+
final var args = stream
55+
.filter(p -> p.getFileName().toString().endsWith(".Transform.json"))
56+
.map(p -> p.getFileName().toString())
57+
.map(name -> name.substring(0, name.length() - ".Transform.json".length()))
58+
.sorted()
59+
.map(testName -> Arguments.of(category, testName));
60+
all = Stream.concat(all, args);
61+
}
62+
}
63+
return all;
64+
}
65+
66+
private static JsonValue parseFile(Path path) throws IOException {
67+
final var text = Files.readString(path);
68+
return Json.parse(stripBom(text));
69+
}
70+
71+
private static String stripBom(String s) {
72+
if (s != null && !s.isEmpty() && s.charAt(0) == '\uFEFF') {
73+
return s.substring(1);
74+
}
75+
return s;
76+
}
77+
}
78+
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package json.java21.transforms;
2+
3+
import jdk.sandbox.java.util.json.Json;
4+
import org.junit.jupiter.api.Test;
5+
6+
import java.util.logging.Logger;
7+
8+
import static org.assertj.core.api.Assertions.assertThat;
9+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
10+
11+
public final class JsonTransformValidationTest extends JsonTransformsLoggingConfig {
12+
13+
private static final Logger LOG = Logger.getLogger(JsonTransformValidationTest.class.getName());
14+
15+
@Test
16+
void invalidVerb() {
17+
LOG.info(() -> "TEST: invalidVerb");
18+
19+
var source = Json.parse("{\"A\":1}");
20+
var transform = Json.parse("{\"@jdt.invalid\":false}");
21+
22+
assertThatThrownBy(() -> JsonTransform.parse(transform).run(source))
23+
.isInstanceOf(JsonTransformException.class);
24+
}
25+
26+
@Test
27+
void invalidVerbValue() {
28+
LOG.info(() -> "TEST: invalidVerbValue");
29+
30+
var source = Json.parse("{\"A\":1}");
31+
var transform = Json.parse("{\"@jdt.remove\":10}");
32+
33+
assertThatThrownBy(() -> JsonTransform.parse(transform).run(source))
34+
.isInstanceOf(JsonTransformException.class);
35+
}
36+
37+
@Test
38+
void invalidAttribute() {
39+
LOG.info(() -> "TEST: invalidAttribute");
40+
41+
var source = Json.parse("{\"A\":1}");
42+
var transform = Json.parse("{\"@jdt.replace\": {\"@jdt.invalid\": false}}");
43+
44+
assertThatThrownBy(() -> JsonTransform.parse(transform).run(source))
45+
.isInstanceOf(JsonTransformException.class);
46+
}
47+
48+
@Test
49+
void missingAttribute() {
50+
LOG.info(() -> "TEST: missingAttribute");
51+
52+
var source = Json.parse("{\"A\":1}");
53+
var transform = Json.parse("{\"@jdt.rename\": {\"@jdt.path\": \"$.A\"}}");
54+
55+
assertThatThrownBy(() -> JsonTransform.parse(transform).run(source))
56+
.isInstanceOf(JsonTransformException.class);
57+
}
58+
59+
@Test
60+
void mixedAttributes() {
61+
LOG.info(() -> "TEST: mixedAttributes");
62+
63+
var source = Json.parse("{\"A\":1}");
64+
var transform = Json.parse("{\"@jdt.rename\": {\"@jdt.path\": \"$.A\", \"@jdt.value\": \"Astar\", \"NotAttribute\": true}}");
65+
66+
assertThatThrownBy(() -> JsonTransform.parse(transform).run(source))
67+
.isInstanceOf(JsonTransformException.class);
68+
}
69+
70+
@Test
71+
void wrongAttributeValueType() {
72+
LOG.info(() -> "TEST: wrongAttributeValueType");
73+
74+
var source = Json.parse("{\"A\":1}");
75+
var transform = Json.parse("{\"@jdt.remove\": {\"@jdt.path\": false}}");
76+
77+
assertThatThrownBy(() -> JsonTransform.parse(transform).run(source))
78+
.isInstanceOf(JsonTransformException.class);
79+
}
80+
81+
@Test
82+
void removeNonExistentNodeByPathIsNoOp() {
83+
LOG.info(() -> "TEST: removeNonExistentNodeByPathIsNoOp");
84+
85+
var source = Json.parse("{\"A\":1}");
86+
var transform = Json.parse("{\"@jdt.remove\": {\"@jdt.path\": \"$.B\"}}");
87+
88+
var result = JsonTransform.parse(transform).run(source);
89+
assertThat(result).isEqualTo(source);
90+
}
91+
92+
@Test
93+
void removeRootThrows() {
94+
LOG.info(() -> "TEST: removeRootThrows");
95+
96+
var source = Json.parse("{\"A\":1}");
97+
var transform = Json.parse("{\"@jdt.remove\": true}");
98+
99+
assertThatThrownBy(() -> JsonTransform.parse(transform).run(source))
100+
.isInstanceOf(JsonTransformException.class);
101+
}
102+
103+
@Test
104+
void invalidRenameMappingValue() {
105+
LOG.info(() -> "TEST: invalidRenameMappingValue");
106+
107+
var source = Json.parse("{\"A\":1}");
108+
var transform = Json.parse("{\"@jdt.rename\": {\"A\": 10}}");
109+
110+
assertThatThrownBy(() -> JsonTransform.parse(transform).run(source))
111+
.isInstanceOf(JsonTransformException.class);
112+
}
113+
114+
@Test
115+
void renameNonExistentNodeIsNoOp() {
116+
LOG.info(() -> "TEST: renameNonExistentNodeIsNoOp");
117+
118+
var source = Json.parse("{\"A\":1}");
119+
var transform = Json.parse("{\"@jdt.rename\": {\"B\": \"Bstar\"}}");
120+
121+
var result = JsonTransform.parse(transform).run(source);
122+
assertThat(result).isEqualTo(source);
123+
}
124+
125+
@Test
126+
void replaceRootWithPrimitiveThrows() {
127+
LOG.info(() -> "TEST: replaceRootWithPrimitiveThrows");
128+
129+
var source = Json.parse("{\"A\":1}");
130+
var transform = Json.parse("{\"@jdt.replace\": 10}");
131+
132+
assertThatThrownBy(() -> JsonTransform.parse(transform).run(source))
133+
.isInstanceOf(JsonTransformException.class);
134+
}
135+
}
136+
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package json.java21.transforms;
2+
3+
import org.junit.jupiter.api.BeforeAll;
4+
5+
import java.nio.file.Path;
6+
import java.nio.file.Paths;
7+
import java.util.Locale;
8+
import java.util.logging.Handler;
9+
import java.util.logging.Level;
10+
import java.util.logging.Logger;
11+
12+
/// Base class for json-transforms tests that configures JUL logging from system properties.
13+
/// All test classes should extend this class to enable consistent logging behavior.
14+
public class JsonTransformsLoggingConfig {
15+
16+
@BeforeAll
17+
static void enableJulDebug() {
18+
final var log = Logger.getLogger(JsonTransformsLoggingConfig.class.getName());
19+
final var root = Logger.getLogger("");
20+
final var levelProp = System.getProperty("java.util.logging.ConsoleHandler.level");
21+
22+
Level targetLevel = Level.INFO;
23+
if (levelProp != null) {
24+
try {
25+
targetLevel = Level.parse(levelProp.trim());
26+
} catch (IllegalArgumentException ex) {
27+
try {
28+
targetLevel = Level.parse(levelProp.trim().toUpperCase(Locale.ROOT));
29+
} catch (IllegalArgumentException ignored) {
30+
log.warning(() -> "Unrecognized logging level from 'java.util.logging.ConsoleHandler.level': " + levelProp);
31+
}
32+
}
33+
}
34+
35+
if (root.getLevel() == null || root.getLevel().intValue() > targetLevel.intValue()) {
36+
root.setLevel(targetLevel);
37+
}
38+
for (Handler handler : root.getHandlers()) {
39+
final var handlerLevel = handler.getLevel();
40+
if (handlerLevel == null || handlerLevel.intValue() > targetLevel.intValue()) {
41+
handler.setLevel(targetLevel);
42+
}
43+
}
44+
45+
final var prop = System.getProperty("jsontransforms.test.resources");
46+
if (prop == null || prop.isBlank()) {
47+
Path base = Paths.get("src", "test", "resources").toAbsolutePath();
48+
System.setProperty("jsontransforms.test.resources", base.toString());
49+
log.config(() -> "jsontransforms.test.resources set to " + base);
50+
}
51+
}
52+
}
53+
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"A": [
3+
{ "Added": true }
4+
],
5+
"B": [
6+
{
7+
"Name": "B1",
8+
"Value": 1
9+
},
10+
{
11+
"Name": "B2",
12+
"Value": 2
13+
},
14+
{
15+
"Name": "B2",
16+
"Value": 3
17+
}
18+
],
19+
"C": [
20+
{},
21+
{ "Empty": false },
22+
{ "Array": [ { "Inception": true } ] },
23+
{ "Empty": false }
24+
],
25+
"D": {
26+
"D1": [
27+
{ "Value": 1 },
28+
{ "Value": 0 }
29+
]
30+
}
31+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"A": [
3+
{ "Added": true }
4+
],
5+
"B": [
6+
{
7+
"Name": "B2",
8+
"Value": 2
9+
},
10+
{
11+
"Name": "B2",
12+
"Value": 3
13+
}
14+
],
15+
"C": [
16+
{ "Empty": false }
17+
],
18+
"D": {
19+
"D1": [ { "Value": 0 } ]
20+
}
21+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"A": {
3+
"Replaced": true
4+
},
5+
"B": [
6+
{
7+
"Name": "B1",
8+
"Value": 1
9+
}
10+
],
11+
"C": "Replaced",
12+
"D": {
13+
"D1": null
14+
}
15+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"A": {
3+
"Replaced": true
4+
},
5+
"C": "Replaced",
6+
"D": {
7+
"D1": null
8+
}
9+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"A": [],
3+
"B": [
4+
{
5+
"Name": "B1",
6+
"Value": 1
7+
}
8+
],
9+
"C": [
10+
{},
11+
{ "Empty": false },
12+
{ "Array": [ { "Inception": true } ] }
13+
],
14+
"D": {
15+
"D1": [ { "Value": 1 } ]
16+
}
17+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"A": {
3+
"A1": "New"
4+
},
5+
"B": {
6+
"B1": 10,
7+
"B2": 2,
8+
"B3": 30
9+
},
10+
"C": {
11+
"C1": {
12+
"C11": true,
13+
"C12": false,
14+
"C13": [
15+
{ "Name": "C131" },
16+
{ "Name": "C132" }
17+
]
18+
},
19+
"C2": 2,
20+
"C3": {
21+
"Added": true
22+
}
23+
}
24+
}

0 commit comments

Comments
 (0)