/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.sync.diffsync;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.sync.AddOperation;
import org.springframework.sync.MoveOperation;
import org.springframework.sync.Patch;
import org.springframework.sync.PatchException;
import org.springframework.sync.PatchOperation;
import org.springframework.sync.Person;
import org.springframework.sync.Todo;
import org.springframework.sync.TodoRepository;
import org.springframework.sync.diffsync.shadowstore.MapBasedShadowStore;
import org.springframework.sync.json.JsonPatchPatchConverter;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=EmbeddedDataSourceConfig.class)
@Transactional
public class DiffSyncTest {
@Autowired
private TodoRepository repository;
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@After
public void cleanup() {
repository.deleteAll();
}
//
// Apply patches - lists
//
@Test
public void patchList_emptyPatch() throws Exception {
DiffSync<Todo> sync = new DiffSync<Todo>(new MapBasedShadowStore("x"), Todo.class);
Patch patch = readJsonPatchFromResource("patch-empty");
List<Todo> todos = getTodoList();
List<Todo> patched = sync.apply(todos, patch);
assertEquals(patched, getTodoList());
// original remains unchanged
assertEquals(todos, getTodoList());
}
@Test
public void patchList_addNewItem() throws Exception {
DiffSync<Todo> sync = new DiffSync<Todo>(new MapBasedShadowStore("x"), Todo.class);
Patch patch = readJsonPatchFromResource("patch-add-new-item");
List<Todo> todos = getTodoList();
List<Todo> patched = sync.apply(todos, patch);
// original should remain unchanged
assertEquals(todos, getTodoList());
assertNotEquals(patched, todos);
assertEquals(4, patched.size());
assertEquals(todos.get(0), patched.get(0));
assertEquals(todos.get(1), patched.get(1));
assertEquals(todos.get(2), patched.get(2));
assertEquals(new Todo(null, "D", false), patched.get(3));
}
@Test
public void patchList_changeSingleEntityStatusAndDescription() throws Exception {
DiffSync<Todo> sync = new DiffSync<Todo>(new MapBasedShadowStore("x"), Todo.class);
Patch patch = readJsonPatchFromResource("patch-change-single-status-and-desc");
List<Todo> todos = getTodoList();
List<Todo> patched = sync.apply(todos, patch);
// original should remain unchanged
assertEquals(todos, getTodoList());
assertNotEquals(patched, todos);
assertEquals(3, patched.size());
assertEquals(todos.get(0), patched.get(0));
assertEquals(new Todo(2L, "BBB", true), patched.get(1));
assertEquals(todos.get(2), patched.get(2));
}
@Test
public void patchList_changeSingleEntityStatus() throws Exception {
DiffSync<Todo> sync = new DiffSync<Todo>(new MapBasedShadowStore("x"), Todo.class);
Patch patch = readJsonPatchFromResource("patch-change-single-status");
List<Todo> todos = getTodoList();
List<Todo> patched = sync.apply(todos, patch);
// original should remain unchanged
assertEquals(todos, getTodoList());
assertNotEquals(patched, todos);
assertEquals(3, patched.size());
assertEquals(todos.get(0), patched.get(0));
assertEquals(new Todo(2L, "B", true), patched.get(1));
assertEquals(todos.get(2), patched.get(2));
}
@Test
public void patchList_changeStatusAndDeleteTwoItems() throws Exception {
DiffSync<Todo> sync = new DiffSync<Todo>(new MapBasedShadowStore("x"), Todo.class);
Patch patch = readJsonPatchFromResource("patch-change-status-and-delete-two-items");
List<Todo> todos = getTodoList();
List<Todo> patched = sync.apply(todos, patch);
// original should remain unchanged
assertEquals(todos, getTodoList());
assertNotEquals(patched, todos);
assertEquals(1, patched.size());
assertEquals(new Todo(1L, "A", true), patched.get(0));
}
@Test
public void patchList_changeTwoStatusAndDescription() throws Exception {
DiffSync<Todo> sync = new DiffSync<Todo>(new MapBasedShadowStore("x"), Todo.class);
Patch patch = readJsonPatchFromResource("patch-change-two-status-and-desc");
List<Todo> todos = getTodoList();
List<Todo> patched = sync.apply(todos, patch);
// original should remain unchanged
assertEquals(todos, getTodoList());
assertNotEquals(patched, todos);
assertEquals(3, patched.size());
assertEquals(new Todo(1L, "AAA", false), patched.get(0));
assertEquals(new Todo(2L, "B", true), patched.get(1));
assertEquals(new Todo(3L, "C", false), patched.get(2));
}
@Test
public void patchList_deleteTwoItemsAndChangeStatusOnAnother() throws Exception {
DiffSync<Todo> sync = new DiffSync<Todo>(new MapBasedShadowStore("x"), Todo.class);
Patch patch = readJsonPatchFromResource("patch-delete-twoitems-and-change-status-on-another");
List<Todo> todos = getTodoList();
List<Todo> patched = sync.apply(todos, patch);
// original should remain unchanged
assertEquals(todos, getTodoList());
assertNotEquals(patched, todos);
assertEquals(1, patched.size());
assertEquals(new Todo(3L, "C", true), patched.get(0));
}
@Test
public void patchList_patchFailingOperationFirst() throws Exception {
DiffSync<Todo> sync = new DiffSync<Todo>(new MapBasedShadowStore("x"), Todo.class);
Patch patch = readJsonPatchFromResource("patch-failing-operation-first");
List<Todo> todos = getTodoList();
List<Todo> patched = null;
try {
patched = sync.apply(todos, patch);
fail();
} catch (PatchException e) {
// original should remain unchanged
assertEquals(todos, getTodoList());
assertNull(patched);
}
}
@Test
public void patchList_patchFailingOperationInMiddle() throws Exception {
DiffSync<Todo> sync = new DiffSync<Todo>(new MapBasedShadowStore("x"), Todo.class);
Patch patch = readJsonPatchFromResource("patch-failing-operation-in-middle");
List<Todo> todos = getTodoList();
List<Todo> patched = null;
try {
patched = sync.apply(todos, patch);
fail();
} catch (PatchException e) {
// original should remain unchanged
assertEquals(todos, getTodoList());
assertNull(patched);
}
}
@Test
public void patchList_manySuccessfulOperations() throws Exception {
DiffSync<Todo> sync = new DiffSync<Todo>(new MapBasedShadowStore("x"), Todo.class);
Patch patch = readJsonPatchFromResource("patch-many-successful-operations");
List<Todo> todos = getBigTodoList();
List<Todo> patched = sync.apply(todos, patch);
// original should remain unchanged
assertEquals(todos, getBigTodoList());
assertNotEquals(patched, todos);
assertEquals(6, patched.size());
assertEquals(new Todo(1L, "A", true), patched.get(0));
assertEquals(new Todo(2L, "B", true), patched.get(1));
assertEquals(new Todo(3L, "C", false), patched.get(2));
assertEquals(new Todo(4L, "C", false), patched.get(3));
assertEquals(new Todo(1L, "A", true), patched.get(4));
assertEquals(new Todo(5L, "E", false), patched.get(5));
}
@Test
public void patchList_modifyThenRemoveItem() throws Exception {
DiffSync<Todo> sync = new DiffSync<Todo>(new MapBasedShadowStore("x"), Todo.class);
Patch patch = readJsonPatchFromResource("patch-modify-then-remove-item");
List<Todo> todos = getTodoList();
List<Todo> patched = sync.apply(todos, patch);
// original should remain unchanged
assertEquals(todos, getTodoList());
assertNotEquals(patched, todos);
assertEquals(2, patched.size());
assertEquals(new Todo(1L, "A", false), patched.get(0));
assertEquals(new Todo(3L, "C", false), patched.get(1));
}
@Test
public void patchList_removeItem() throws Exception {
DiffSync<Todo> sync = new DiffSync<Todo>(new MapBasedShadowStore("x"), Todo.class);
Patch patch = readJsonPatchFromResource("patch-remove-item");
List<Todo> todos = getTodoList();
List<Todo> patched = sync.apply(todos, patch);
// original should remain unchanged
assertEquals(todos, getTodoList());
assertNotEquals(patched, todos);
assertEquals(2, patched.size());
assertEquals(new Todo(1L, "A", false), patched.get(0));
assertEquals(new Todo(3L, "C", false), patched.get(1));
}
@Test
public void patchList_removeTwoItems() throws Exception {
DiffSync<Todo> sync = new DiffSync<Todo>(new MapBasedShadowStore("x"), Todo.class);
Patch patch = readJsonPatchFromResource("patch-remove-two-items");
List<Todo> todos = getTodoList();
List<Todo> patched = sync.apply(todos, patch);
// original should remain unchanged
assertEquals(todos, getTodoList());
assertNotEquals(patched, todos);
assertEquals(1, patched.size());
assertEquals(new Todo(1L, "A", false), patched.get(0));
}
//
// Apply patches - single entity
//
@Test
public void patchEntity_emptyPatch() throws Exception {
DiffSync<Todo> sync = new DiffSync<Todo>(new MapBasedShadowStore("x"), Todo.class);
Patch patch = readJsonPatchFromResource("patch-empty");
Todo todo = new Todo(1L, "A", false);
Todo patched = sync.apply(todo, patch);
assertEquals(1L, patched.getId().longValue());
assertEquals("A", patched.getDescription());
assertFalse(patched.isComplete());
// original remains unchanged
assertEquals(1L, todo.getId().longValue());
assertEquals("A", todo.getDescription());
assertFalse(todo.isComplete());
}
@Test
public void patchEntity_booleanProperty() throws Exception {
DiffSync<Todo> sync = new DiffSync<Todo>(new MapBasedShadowStore("x"), Todo.class);
Patch patch = readJsonPatchFromResource("single-change-status");
Todo todo = new Todo(1L, "A", false);
Todo patched = sync.apply(todo, patch);
assertEquals(1L, patched.getId().longValue());
assertEquals("A", patched.getDescription());
assertTrue(patched.isComplete());
// original remains unchanged
assertEquals(1L, todo.getId().longValue());
assertEquals("A", todo.getDescription());
assertFalse(todo.isComplete());
}
@Test
public void patchEntity_stringProperty() throws Exception {
DiffSync<Todo> sync = new DiffSync<Todo>(new MapBasedShadowStore("x"), Todo.class);
Patch patch = readJsonPatchFromResource("single-change-description");
Todo todo = new Todo(1L, "A", false);
Todo patched = sync.apply(todo, patch);
assertEquals(1L, patched.getId().longValue());
assertEquals("AAA", patched.getDescription());
assertFalse(patched.isComplete());
// original remains unchanged
assertEquals(1L, todo.getId().longValue());
assertEquals("A", todo.getDescription());
assertFalse(todo.isComplete());
}
@Test
public void patchEntity_numericProperty() throws Exception {
DiffSync<Todo> sync = new DiffSync<Todo>(new MapBasedShadowStore("x"), Todo.class);
Patch patch = readJsonPatchFromResource("single-change-id");
Todo todo = new Todo(1L, "A", false);
Todo patched = sync.apply(todo, patch);
assertEquals(123L, patched.getId().longValue());
assertEquals("A", patched.getDescription());
assertFalse(patched.isComplete());
// original remains unchanged
assertEquals(1L, todo.getId().longValue());
assertEquals("A", todo.getDescription());
assertFalse(todo.isComplete());
}
@Test
public void patchEntity_stringAndBooleanProperties() throws Exception {
DiffSync<Todo> sync = new DiffSync<Todo>(new MapBasedShadowStore("x"), Todo.class);
Patch patch = readJsonPatchFromResource("single-change-status-and-desc");
Todo todo = new Todo(1L, "A", false);
Todo patched = sync.apply(todo, patch);
assertEquals(1L, patched.getId().longValue());
assertEquals("BBB", patched.getDescription());
assertTrue(patched.isComplete());
// original remains unchanged
assertEquals(1L, todo.getId().longValue());
assertEquals("A", todo.getDescription());
assertFalse(todo.isComplete());
}
@Test
public void patchEntity_moveProperty() throws Exception {
DiffSync<Person> sync = new DiffSync<Person>(new MapBasedShadowStore("x"), Person.class);
List<PatchOperation> ops = new ArrayList<PatchOperation>();
ops.add(new MoveOperation("/firstName", "/lastName"));
Patch patch = new Patch(ops);
Person person = new Person("Edmund", "Blackadder");
Person patched = sync.apply(person, patch);
assertEquals("Blackadder", patched.getFirstName());
assertNull(patched.getLastName());
}
//
// Guaranteed Delivery - Normal operations scenario
//
@Test
public void patchList_addNewItem_normal() throws Exception {
DiffSync<Todo> sync = new DiffSync<Todo>(new MapBasedShadowStore("x"), Todo.class);
Patch patch = readJsonPatchFromResource("patch-add-new-item");
VersionedPatch versionedPatch = new VersionedPatch(patch.getOperations(), 0, 0);
List<Todo> todos = getTodoList();
List<Todo> patched = sync.apply(todos, versionedPatch);
VersionedPatch diff = sync.diff(patched);
assertEquals(1, diff.getClientVersion()); // the server is acknowledge client version 1 (the client should be at that version by this time)
assertEquals(0, diff.getServerVersion()); // the server created the patch against server version 0 (but it will be 1 after the patch is created)
// original should remain unchanged
assertEquals(todos, getTodoList());
assertNotEquals(patched, todos);
assertEquals(4, patched.size());
assertEquals(todos.get(0), patched.get(0));
assertEquals(todos.get(1), patched.get(1));
assertEquals(todos.get(2), patched.get(2));
assertEquals(new Todo(null, "D", false), patched.get(3));
}
@Test
public void patchEntity_moveProperty_normal() throws Exception {
DiffSync<Person> sync = new DiffSync<Person>(new MapBasedShadowStore("x"), Person.class);
List<PatchOperation> ops = new ArrayList<PatchOperation>();
ops.add(new MoveOperation("/firstName", "/lastName"));
VersionedPatch vPatch1 = new VersionedPatch(ops, 0, 0);
Person person = new Person("Edmund", "Blackadder");
Person patched = sync.apply(person, vPatch1);
VersionedPatch diff = sync.diff(patched);
assertEquals(1, diff.getClientVersion()); // the server is acknowledge client version 1 (the client should be at that version by this time)
assertEquals(0, diff.getServerVersion()); // the server created the patch against server version 0 (but it will be 1 after the patch is created)
assertEquals("Blackadder", patched.getFirstName());
assertNull(patched.getLastName());
}
//
// Guaranteed Delivery - Duplicate packet scenario
//
@Test
public void patchList_addNewItem_duplicate() throws Exception {
DiffSync<Todo> sync = new DiffSync<Todo>(new MapBasedShadowStore("x"), Todo.class);
Patch patch = readJsonPatchFromResource("patch-add-new-item");
VersionedPatch versionedPatch = new VersionedPatch(patch.getOperations(), 0, 0);
VersionedPatch versionedPatch2 = new VersionedPatch(patch.getOperations(), 0, 0);
List<Todo> todos = getTodoList();
List<Todo> patched = sync.apply(todos, versionedPatch, versionedPatch2);
VersionedPatch diff = sync.diff(patched);
assertEquals(1, diff.getClientVersion()); // the server is acknowledge client version 1 (the client should be at that version by this time)
assertEquals(0, diff.getServerVersion()); // the server created the patch against server version 0 (but it will be 1 after the patch is created)
// original should remain unchanged
assertEquals(todos, getTodoList());
assertNotEquals(patched, todos);
assertEquals(4, patched.size());
assertEquals(todos.get(0), patched.get(0));
assertEquals(todos.get(1), patched.get(1));
assertEquals(todos.get(2), patched.get(2));
assertEquals(new Todo(null, "D", false), patched.get(3));
}
@Test
public void patchEntity_moveProperty_duplicate() throws Exception {
DiffSync<Person> sync = new DiffSync<Person>(new MapBasedShadowStore("x"), Person.class);
List<PatchOperation> ops = new ArrayList<PatchOperation>();
ops.add(new MoveOperation("/firstName", "/lastName"));
VersionedPatch vPatch1 = new VersionedPatch(ops, 0, 0);
VersionedPatch vPatch2 = new VersionedPatch(ops, 0, 0);
Person person = new Person("Edmund", "Blackadder");
Person patched = sync.apply(person, vPatch1, vPatch2);
VersionedPatch diff = sync.diff(patched);
assertEquals(1, diff.getClientVersion()); // the server is acknowledge client version 1 (the client should be at that version by this time)
assertEquals(0, diff.getServerVersion()); // the server created the patch against server version 0 (but it will be 1 after the patch is created)
assertEquals("Blackadder", patched.getFirstName());
assertNull(patched.getLastName());
}
//
// Guaranteed Delivery - Lost outbound packet scenario
//
// TODO: This is primarily a client-side case. By definition, the server never receives the patch.
// Therefore, there's nothing server-side to be tested.
// However, this case *does* apply to Spring Sync when used in an Android client.
// Therefore, tests for this scenario will need to be fleshed out.
//
// Guaranteed Delivery - Lost return packet scenario
//
@Test
public void patchList_addNewItem_lostReturn() throws Exception {
DiffSync<Todo> sync = new DiffSync<Todo>(new MapBasedShadowStore("x"), Todo.class);
// Create the list resource
List<Todo> todos = getTodoList();
// Apply an initial patch to get the server shadow's client version bumped up.
// Initially, the server shadow's server and client versions are both 0,
// matching the incoming patch's versions, so the patch is applied normally.
List<PatchOperation> ops1 = new ArrayList<PatchOperation>();
ops1.add(new AddOperation("/~", new Todo(100L, "NEW ITEM 100", false)));
VersionedPatch versionedPatch = new VersionedPatch(ops1, 0, 0);
// At this point, the client sends the patch to the server, the client puts the patch in an outbound stack,
// the client increments its shadow client version to 1, and the server calls sync.apply() to apply the patch.
List<Todo> patched = sync.apply(todos, versionedPatch);
// After the patch is applied, the server shadow versions are
// - Primary shadow: serverVersion = 0, clientVersion = 1
// - Backup shadow : serverVersion = 0, clientVersion = 1
// At this point, the server's shadow has client version 1 and server version 0
// The server then copies its current shadow to backup shadow before performing a new diff against the shadow, bumping the server version to 1 *after* the diff is performed.
// The backup shadow, having been taken before the new diff was created, still has server version 0.
// Before it performs the diff, however, it copies its current shadow to backup shadow.
// The diff was performed against the shadow whose client version 1 and server version 0, therefore the patch will have client version 1 and server version 0.
VersionedPatch lostDiff = sync.diff(patched);
// After the diff is applied, the server shadow's server version is incremented.
// - Primary shadow: serverVersion = 1, clientVersion = 1
// - Backup shadow : serverVersion = 0, clientVersion = 1
// Verify that the patch has client version 1, server version 0
assertEquals(1, lostDiff.getClientVersion());
assertEquals(0, lostDiff.getServerVersion());
// In the lost return packet scenario, the client never receives that return diff (lostDiff) or acknowledgement of the server having applied the first patch.
// The client can only assume that the server never received it (although it did).
// So it produces a new patch against its shadow (whose server version is still at 0 and client version is 1).
// It then sends both patches to the server and the server attempts to apply them both.
List<PatchOperation> ops2 = new ArrayList<PatchOperation>();
ops2.add(new AddOperation("/~", new Todo(200L, "NEW ITEM 200", false)));
VersionedPatch versionedPatch2 = new VersionedPatch(ops2, 0, 1);
patched = sync.apply(patched, versionedPatch, versionedPatch2);
// The first patch's server version is 0, which is less than the server shadow's server version of 1.
// This indicates a lost packet scenario, meaning that the client never received or applied the
// return patch from the previous cycle.
// So the server resurrects the backup shadow into the primary shadow:
// - Primary shadow: serverVersion = 0, clientVersion = 1
// - Backup shadow : serverVersion = 0, clientVersion = 1
// Then it tries to apply the first patch. Since the patch's client version is less than the shadow's client version,
// it ignores the patch as a duplicate (that was applied earlier)
// Then it tries to apply the second patch. This patch's client version is the same as the shadow's client version,
// so it applies it as with normal operation.
// After the applying the 2nd patch, the server shadow's server version is incremented.
// - Primary shadow: serverVersion = 0, clientVersion = 2
// - Backup shadow : serverVersion = 0, clientVersion = 2
// Finally, the server performs a diff against the shadow (whose server version is 0 and whose client version is 2).
// Therefore, the patch produced should have client version 2, server version 0.
// After the diff, the server version will be 1, but there's no way to verify that, except to perform another patch.
VersionedPatch diff = sync.diff(patched);
assertEquals(2, diff.getClientVersion()); // the server is acknowledging client version 1 and 2 (the client should be at that version by this time)
assertEquals(0, diff.getServerVersion()); // the server created the patch against server version 0 (but it will be 1 after the patch is created)
// After the diff is applied, the server shadow's server version is incremented.
// - Primary shadow: serverVersion = 1, clientVersion = 2
// - Backup shadow : serverVersion = 0, clientVersion = 2
// Now test that the resulting list is as expected.
// The original should remain unchanged
assertEquals(todos, getTodoList());
// The patched resource should now contain 2 additional items, one from each patch sent.
// It should *NOT* have two of the item that was added as part of the initial patch (the one that was sent twice).
assertNotEquals(patched, todos);
assertEquals(5, patched.size()); // Should only have added 2 new items. It shouldn't have added the first new item twice.
assertEquals(todos.get(0), patched.get(0));
assertEquals(todos.get(1), patched.get(1));
assertEquals(todos.get(2), patched.get(2));
assertEquals(new Todo(100L, "NEW ITEM 100", false), patched.get(3));
assertEquals(new Todo(200L, "NEW ITEM 200", false), patched.get(4));
}
@Test
public void patchEntity_moveProperty_lostReturnPacket() throws Exception {
DiffSync<Person> sync = new DiffSync<Person>(new MapBasedShadowStore("x"), Person.class);
Person person = new Person("Edmund", "Blackadder");
List<PatchOperation> ops1 = new ArrayList<PatchOperation>();
ops1.add(new MoveOperation("/firstName", "/lastName"));
VersionedPatch vPatch1 = new VersionedPatch(ops1, 0, 0);
Person patched = sync.apply(person, vPatch1);
assertEquals("Blackadder", patched.getFirstName());
assertNull(patched.getLastName());
VersionedPatch lostDiff = sync.diff(patched);
assertEquals(1, lostDiff.getClientVersion());
assertEquals(0, lostDiff.getServerVersion());
List<PatchOperation> ops2 = new ArrayList<PatchOperation>();
ops2.add(new MoveOperation("/lastName", "/firstName"));
VersionedPatch vPatch2 = new VersionedPatch(ops2, 0, 1);
patched = sync.apply(patched, vPatch1, vPatch2);
VersionedPatch diff = sync.diff(patched);
assertEquals(2, diff.getClientVersion());
assertEquals(0, diff.getServerVersion());
assertNull(patched.getFirstName());
assertEquals("Blackadder", patched.getLastName());
}
//
// private helpers
//
private List<Todo> getTodoList() {
List<Todo> todos = new ArrayList<Todo>();
todos.add(new Todo(1L, "A", false));
todos.add(new Todo(2L, "B", false));
todos.add(new Todo(3L, "C", false));
return todos;
}
private List<Todo> getBigTodoList() {
List<Todo> todos = new ArrayList<Todo>();
todos.add(new Todo(1L, "A", true));
todos.add(new Todo(2L, "B", false));
todos.add(new Todo(3L, "C", false));
todos.add(new Todo(4L, "D", false));
todos.add(new Todo(5L, "E", false));
todos.add(new Todo(6L, "F", false));
return todos;
}
private Patch readJsonPatchFromResource(String resource) throws IOException, JsonProcessingException {
return new JsonPatchPatchConverter().convert(OBJECT_MAPPER.readTree(resource(resource)));
}
private String resource(String name) throws IOException {
ClassPathResource resource = new ClassPathResource("/org/springframework/sync/" + name + ".json");
BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream()));
StringBuilder builder = new StringBuilder();
while(reader.ready()) {
builder.append(reader.readLine());
}
return builder.toString();
}
}