Skip to content

Commit 149be09

Browse files
committed
Tests for JsonDiff
1 parent fcc6cad commit 149be09

File tree

3 files changed

+233
-2
lines changed

3 files changed

+233
-2
lines changed

src/main/java/com/github/fge/jsonpatch/diff2/JsonDiff.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,10 @@ private static void generateObjectDiffs(final DiffProcessor processor,
121121
final JsonPointer pointer, final ObjectNode first,
122122
final ObjectNode second)
123123
{
124-
final Set<String> firstFields = Sets.newHashSet(first.fieldNames());
125-
final Set<String> secondFields = Sets.newHashSet(second.fieldNames());
124+
final Set<String> firstFields
125+
= Sets.newTreeSet(Sets.newHashSet(first.fieldNames()));
126+
final Set<String> secondFields
127+
= Sets.newTreeSet(Sets.newHashSet(second.fieldNames()));
126128

127129
for (final String field: Sets.difference(firstFields, secondFields))
128130
processor.valueRemoved(pointer.append(field), first.get(field));
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
* Copyright (c) 2014, Francis Galiegue ([email protected])
3+
*
4+
* This software is dual-licensed under:
5+
*
6+
* - the Lesser General Public License (LGPL) version 3.0 or, at your option, any
7+
* later version;
8+
* - the Apache Software License (ASL) version 2.0.
9+
*
10+
* The text of this file and of both licenses is available at the root of this
11+
* project or, if you have the jar distribution, in directory META-INF/, under
12+
* the names LGPL-3.0.txt and ASL-2.0.txt respectively.
13+
*
14+
* Direct link to the sources:
15+
*
16+
* - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
17+
* - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
18+
*/
19+
20+
package com.github.fge.jsonpatch.diff2;
21+
22+
import com.fasterxml.jackson.databind.JsonNode;
23+
import com.github.fge.jackson.JsonLoader;
24+
import com.github.fge.jackson.JsonNumEquals;
25+
import com.github.fge.jsonpatch.JsonPatch;
26+
import com.github.fge.jsonpatch.JsonPatchException;
27+
import com.google.common.base.Equivalence;
28+
import com.google.common.base.Predicate;
29+
import com.google.common.collect.Lists;
30+
import org.testng.annotations.DataProvider;
31+
import org.testng.annotations.Test;
32+
33+
import java.io.IOException;
34+
import java.util.Iterator;
35+
import java.util.List;
36+
37+
import static org.assertj.core.api.Assertions.assertThat;
38+
39+
public final class JsonDiffTest
40+
{
41+
private static final Equivalence<JsonNode> EQUIVALENCE
42+
= JsonNumEquals.getInstance();
43+
44+
private final JsonNode testData;
45+
46+
public JsonDiffTest()
47+
throws IOException
48+
{
49+
final String resource = "/jsonpatch/diff2/diff.json";
50+
testData = JsonLoader.fromResource(resource);
51+
}
52+
53+
@DataProvider
54+
public Iterator<Object[]> getPatchesOnly()
55+
{
56+
final List<Object[]> list = Lists.newArrayList();
57+
58+
for (final JsonNode node: testData)
59+
list.add(new Object[] { node.get("first"), node.get("second") });
60+
61+
return list.iterator();
62+
}
63+
64+
@Test(dataProvider = "getPatchesOnly")
65+
public void generatedPatchAppliesCleanly(final JsonNode first,
66+
final JsonNode second)
67+
throws JsonPatchException
68+
{
69+
final JsonPatch patch = JsonDiff.asJsonPatch(first, second);
70+
final Predicate<JsonNode> predicate = EQUIVALENCE.equivalentTo(second);
71+
final JsonNode actual = patch.apply(first);
72+
73+
assertThat(predicate.apply(actual)).overridingErrorMessage(
74+
"Generated patch failed to apply\nexpected: %s\nactual: %s",
75+
second, actual
76+
).isTrue();
77+
}
78+
79+
@DataProvider
80+
public Iterator<Object[]> getLiteralPatches()
81+
{
82+
final List<Object[]> list = Lists.newArrayList();
83+
84+
for (final JsonNode node: testData) {
85+
if (!node.has("patch"))
86+
continue;
87+
list.add(new Object[] {
88+
node.get("message").textValue(), node.get("first"),
89+
node.get("second"), node.get("patch")
90+
});
91+
}
92+
93+
return list.iterator();
94+
}
95+
96+
@Test(
97+
dataProvider = "getLiteralPatches",
98+
dependsOnMethods = "generatedPatchAppliesCleanly"
99+
)
100+
public void generatedPatchesAreWhatIsExpected(final String message,
101+
final JsonNode first, final JsonNode second, final JsonNode expected)
102+
{
103+
final JsonNode actual = JsonDiff.asJson(first, second);
104+
final Predicate<JsonNode> predicate
105+
= EQUIVALENCE.equivalentTo(expected);
106+
107+
assertThat(predicate.apply(actual)).overridingErrorMessage(
108+
"patch is not what was expected\nscenario: %s\n"
109+
+ "expected: %s\nactual: %s\n", message, expected, actual
110+
).isTrue();
111+
}
112+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
[
2+
{
3+
"message": "empty patch if no changes",
4+
"first": "hello",
5+
"second": "hello",
6+
"patch": []
7+
},
8+
{
9+
"message": "numeric values are treated as mathematically equal",
10+
"first": 1,
11+
"second": 1.0,
12+
"patch": []
13+
},
14+
{
15+
"message": "array members appended use special end-of-array pointer",
16+
"first": [ 1, 2, 3 ],
17+
"second": [ 1, 2, 3, 4, 5 ],
18+
"patch": [
19+
{ "op": "add", "path": "/-", "value": 4 },
20+
{ "op": "add", "path": "/-", "value": 5 }
21+
]
22+
},
23+
{
24+
"message": "array members are correctly deleted",
25+
"first": [ 1, 2, 3 ],
26+
"second": [ 1 ],
27+
"patch": [
28+
{ "op": "remove", "path": "/1" },
29+
{ "op": "remove", "path": "/1" }
30+
]
31+
},
32+
{
33+
"message": "single object member is deleted",
34+
"first": { "a": "b", "c": "d" },
35+
"second": { "a": "b" },
36+
"patch": [
37+
{ "op": "remove", "path": "/c" }
38+
]
39+
},
40+
{
41+
"message": "added object members are in natural order",
42+
"first": { "a": 1 },
43+
"second": { "a": 1, "c": 2, "b": 3, "d": 4 },
44+
"patch": [
45+
{ "op": "add", "path": "/b", "value": 3 },
46+
{ "op": "add", "path": "/c", "value": 2 },
47+
{ "op": "add", "path": "/d", "value": 4 }
48+
]
49+
},
50+
{
51+
"message": "single object value change is replaced",
52+
"first": { "a": null },
53+
"second": { "a": 6 },
54+
"patch": [
55+
{ "op": "replace", "path": "/a", "value": 6 }
56+
]
57+
},
58+
{
59+
"message": "full value replacement is accounted for",
60+
"first": [ 1, 2, 3 ],
61+
"second": { "hello": "world" },
62+
"patch": [
63+
{ "op": "replace", "path": "", "value": { "hello": "world" } }
64+
]
65+
},
66+
{
67+
"message": "embedded object addition/replacement works",
68+
"first": {
69+
"a": "b",
70+
"c": {
71+
"d": "e"
72+
}
73+
},
74+
"second": {
75+
"a": "b",
76+
"c": {
77+
"d": 1,
78+
"e": "f"
79+
}
80+
},
81+
"patch": [
82+
{ "op": "add", "path": "/c/e", "value": "f" },
83+
{ "op": "replace", "path": "/c/d", "value": 1 }
84+
]
85+
},
86+
{
87+
"message": "embedded array addition/replacement works",
88+
"first": {
89+
"a": [ 1, 2, 3 ]
90+
},
91+
"second": {
92+
"a": [ "b", 2, 3, 4 ]
93+
},
94+
"patch": [
95+
{ "op": "replace", "path": "/a/0", "value": "b" },
96+
{ "op": "add", "path": "/a/-", "value": 4 }
97+
]
98+
},
99+
{
100+
"message": "embedded object addition/replacement works (#2)",
101+
"first": [ { "a": "b" }, "foo", { "bar": null } ],
102+
"second": [ { "a": "b", "c": "d" }, "foo", { "bar": "baz" } ],
103+
"patch": [
104+
{ "op": "add", "path": "/0/c", "value": "d" },
105+
{ "op": "replace", "path": "/2/bar", "value": "baz" }
106+
]
107+
},
108+
{
109+
"message": "embedded array addition/replacement works (#2)",
110+
"first": [ 1, [ 2, 3 ], 4 ],
111+
"second": [ "x", [ 2, 3, "y" ], 4 ],
112+
"patch": [
113+
{ "op": "replace", "path": "/0", "value": "x" },
114+
{ "op": "add", "path": "/1/-", "value": "y" }
115+
]
116+
}
117+
]

0 commit comments

Comments
 (0)