/*
* Copyright (C) 2015 Red Hat, Inc. and/or its affiliates.
*
* 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.jboss.errai.jpa.sync.test.server;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.persistence.TypedQuery;
import org.jboss.errai.jpa.sync.client.shared.ConflictResponse;
import org.jboss.errai.jpa.sync.client.shared.DeleteResponse;
import org.jboss.errai.jpa.sync.client.shared.IdChangeResponse;
import org.jboss.errai.jpa.sync.client.shared.NewRemoteEntityResponse;
import org.jboss.errai.jpa.sync.client.shared.SyncRequestOperation;
import org.jboss.errai.jpa.sync.client.shared.SyncResponse;
import org.jboss.errai.jpa.sync.client.shared.SyncableDataSet;
import org.jboss.errai.jpa.sync.client.shared.UpdateResponse;
import org.jboss.errai.jpa.sync.server.DataSyncServiceImpl;
import org.jboss.errai.jpa.sync.server.JavaReflectionAttributeAccessor;
import org.jboss.errai.jpa.sync.test.client.entity.SimpleEntity;
import org.junit.Before;
import org.junit.Test;
public class DataSyncServiceUnitTest extends AbstractServerSideDataSyncTest {
private final Map<String, Object> NO_PARAMS = Collections.emptyMap();
private DataSyncServiceImpl dss;
@Before
public void setupDss() {
dss = new DataSyncServiceImpl(em, new JavaReflectionAttributeAccessor());
}
@Test
public void testSendNewSimpleEntityNoConflict() {
SyncableDataSet<SimpleEntity> sds = SyncableDataSet.from("allSimpleEntities", SimpleEntity.class, NO_PARAMS);
SimpleEntity localSimpleEntity = new SimpleEntity();
SimpleEntity.setId(localSimpleEntity, 1234L); // simulating an ID we generated in the browser; Hibernate doesn't know about it
localSimpleEntity.setDate(new Timestamp(System.currentTimeMillis()));
localSimpleEntity.setInteger(42);
localSimpleEntity.setString("This was recorded on sticky tape and rust.");
List<SyncRequestOperation<SimpleEntity>> syncRequest = new ArrayList<SyncRequestOperation<SimpleEntity>>();
syncRequest.add(SyncRequestOperation.created(new SimpleEntity(localSimpleEntity)));
// now do the actual sync
List<SyncResponse<SimpleEntity>> syncResponse = dss.coldSync(sds, syncRequest);
// ensure the response is as expected
assertEquals(1, syncResponse.size());
IdChangeResponse<SimpleEntity> idChangeResponse = (IdChangeResponse<SimpleEntity>) syncResponse.get(0);
assertEquals(1234L, idChangeResponse.getOldId());
Long newId = idChangeResponse.getEntity().getId(); // we will verify this in the next stanza
// ensure the entity actually got persisted
List<SimpleEntity> queryResult = em.createQuery("SELECT se FROM SimpleEntity se", SimpleEntity.class).getResultList();
assertEquals(1, queryResult.size());
SimpleEntity.setId(localSimpleEntity, newId); // set local instance's ID to the new remote one from the response
assertEquals(localSimpleEntity.toString(), queryResult.get(0).toString());
}
@Test
public void testReceiveNewRemoteSimpleEntity() {
SimpleEntity remoteSimpleEntity = new SimpleEntity();
remoteSimpleEntity.setDate(new Timestamp(-2960391600000L));
remoteSimpleEntity.setInteger(42);
remoteSimpleEntity.setString("Mr. Watson--come here--I want to see you.");
em.persist(remoteSimpleEntity);
em.flush();
em.detach(remoteSimpleEntity);
SyncableDataSet<SimpleEntity> sds = SyncableDataSet.from("allSimpleEntities", SimpleEntity.class, NO_PARAMS);
List<SyncRequestOperation<SimpleEntity>> syncRequest = new ArrayList<SyncRequestOperation<SimpleEntity>>();
// now do the actual sync (we're starting from empty on the local (requesting) side)
List<SyncResponse<SimpleEntity>> syncResponse = dss.coldSync(sds, syncRequest);
// ensure the response is as expected
assertEquals(1, syncResponse.size());
NewRemoteEntityResponse<SimpleEntity> newRemoteEntityResponse = (NewRemoteEntityResponse<SimpleEntity>) syncResponse.get(0);
assertEquals(remoteSimpleEntity.toString(), newRemoteEntityResponse.getEntity().toString());
}
@Test
public void testUpdateBothSidesUnchanged() {
SimpleEntity remoteSimpleEntity = new SimpleEntity();
remoteSimpleEntity.setDate(new Timestamp(-2960391600000L));
remoteSimpleEntity.setInteger(42);
remoteSimpleEntity.setString("Mr. Watson--come here--I want to see you.");
em.persist(remoteSimpleEntity);
em.flush();
em.detach(remoteSimpleEntity);
SimpleEntity localSimpleEntity = new SimpleEntity(remoteSimpleEntity);
SyncableDataSet<SimpleEntity> sds = SyncableDataSet.from("allSimpleEntities", SimpleEntity.class, NO_PARAMS);
List<SyncRequestOperation<SimpleEntity>> syncRequest = new ArrayList<SyncRequestOperation<SimpleEntity>>();
syncRequest.add(SyncRequestOperation.unchanged(localSimpleEntity));
// now do the actual sync
List<SyncResponse<SimpleEntity>> syncResponse = dss.coldSync(sds, syncRequest);
// ensure the response is as expected (nothing to do)
assertEquals("Got unexpected response: " + syncResponse, 0, syncResponse.size());
}
@Test
public void testUpdateBothSidesChanged() {
SimpleEntity remoteSimpleEntity = new SimpleEntity();
remoteSimpleEntity.setDate(new Timestamp(8917200000L));
remoteSimpleEntity.setInteger(123456);
remoteSimpleEntity.setString("Houston, we've had a problem.");
em.persist(remoteSimpleEntity);
em.flush();
em.detach(remoteSimpleEntity);
SimpleEntity localEntityExpectedState = new SimpleEntity(remoteSimpleEntity);
localEntityExpectedState.setString("Go ahead, Apollo");
SimpleEntity localEntityNewState = new SimpleEntity(remoteSimpleEntity);
localEntityNewState.setString("Crosstalk");
SyncableDataSet<SimpleEntity> sds = SyncableDataSet.from("allSimpleEntities", SimpleEntity.class, NO_PARAMS);
List<SyncRequestOperation<SimpleEntity>> syncRequest = new ArrayList<SyncRequestOperation<SimpleEntity>>();
syncRequest.add(SyncRequestOperation.updated(localEntityNewState, localEntityExpectedState));
// now do the actual sync
List<SyncResponse<SimpleEntity>> syncResponse = dss.coldSync(sds, syncRequest);
// ensure the response is as expected
assertEquals(1, syncResponse.size());
ConflictResponse<SimpleEntity> conflictResponse = (ConflictResponse<SimpleEntity>) syncResponse.get(0);
assertEquals(remoteSimpleEntity.toString(), conflictResponse.getActualNew().toString());
assertEquals(localEntityExpectedState.toString(), conflictResponse.getExpected().toString());
assertEquals(localEntityNewState.toString(), conflictResponse.getRequestedNew().toString());
}
@Test
public void testUpdateRemoteSideChanged() {
SimpleEntity remoteSimpleEntity = new SimpleEntity();
remoteSimpleEntity.setDate(new Timestamp(-2960391600000L));
remoteSimpleEntity.setInteger(42);
remoteSimpleEntity.setString("Mr. Watson--come here--I want to see you.");
em.persist(remoteSimpleEntity);
em.flush();
SimpleEntity localSimpleEntity = new SimpleEntity(remoteSimpleEntity);
remoteSimpleEntity.setString("This is different");
em.flush();
em.detach(remoteSimpleEntity);
SyncableDataSet<SimpleEntity> sds = SyncableDataSet.from("allSimpleEntities", SimpleEntity.class, NO_PARAMS);
List<SyncRequestOperation<SimpleEntity>> syncRequest = new ArrayList<SyncRequestOperation<SimpleEntity>>();
syncRequest.add(SyncRequestOperation.unchanged(localSimpleEntity));
// now do the actual sync
List<SyncResponse<SimpleEntity>> syncResponse = dss.coldSync(sds, syncRequest);
// ensure the response is as expected (nothing to do)
assertEquals("Got unexpected response: " + syncResponse, 1, syncResponse.size());
UpdateResponse<SimpleEntity> updateResponse = (UpdateResponse<SimpleEntity>) syncResponse.get(0);
assertEquals(remoteSimpleEntity.toString(), updateResponse.getEntity().toString());
}
@Test
public void testUpdateRequestingSideChanged() {
SimpleEntity remoteSimpleEntity = new SimpleEntity();
remoteSimpleEntity.setDate(new Timestamp(8917200000L));
remoteSimpleEntity.setInteger(123456);
remoteSimpleEntity.setString("Houston, we've had a problem.");
em.persist(remoteSimpleEntity);
em.flush();
em.detach(remoteSimpleEntity);
SimpleEntity localEntityExpectedState = new SimpleEntity(remoteSimpleEntity);
SimpleEntity localEntityNewState = new SimpleEntity(remoteSimpleEntity);
localEntityNewState.setString("No crosstalk");
SyncableDataSet<SimpleEntity> sds = SyncableDataSet.from("allSimpleEntities", SimpleEntity.class, NO_PARAMS);
List<SyncRequestOperation<SimpleEntity>> syncRequest = new ArrayList<SyncRequestOperation<SimpleEntity>>();
syncRequest.add(SyncRequestOperation.updated(localEntityNewState, localEntityExpectedState));
// now do the actual sync
List<SyncResponse<SimpleEntity>> syncResponse = dss.coldSync(sds, syncRequest);
// ensure the response is as expected (ack of the update)
assertEquals("Got unexpected response: " + syncResponse, 1, syncResponse.size());
UpdateResponse<SimpleEntity> updateResponse = (UpdateResponse<SimpleEntity>) syncResponse.get(0);
assertEquals(localEntityNewState.toString(), updateResponse.getEntity().toString());
assertEquals(localEntityExpectedState.getVersion() + 1, updateResponse.getEntity().getVersion());
}
@Test
public void testReceiveRemoteDelete() throws Exception {
SimpleEntity localSimpleEntity = new SimpleEntity();
localSimpleEntity.setDate(new Timestamp(-2960391600000L));
localSimpleEntity.setInteger(42);
localSimpleEntity.setString("Mr. Watson--come here--I want to see you.");
SimpleEntity.setId(localSimpleEntity, 123L);
SyncableDataSet<SimpleEntity> sds = SyncableDataSet.from("allSimpleEntities", SimpleEntity.class, NO_PARAMS);
// this sync request claims we were told in the past that the server has localSimpleEntity
List<SyncRequestOperation<SimpleEntity>> syncRequest = new ArrayList<SyncRequestOperation<SimpleEntity>>();
syncRequest.add(SyncRequestOperation.unchanged(localSimpleEntity));
// now do the actual sync
List<SyncResponse<SimpleEntity>> syncResponse = dss.coldSync(sds, syncRequest);
// ensure the response is as expected (the server doesn't have the entity anymore)
assertEquals("Got unexpected response: " + syncResponse, 1, syncResponse.size());
DeleteResponse<SimpleEntity> deleteResponse = (DeleteResponse<SimpleEntity>) syncResponse.get(0);
assertEquals(localSimpleEntity.toString(), deleteResponse.getEntity().toString());
}
@Test
public void testSendLocalDelete() throws Exception {
SimpleEntity remoteSimpleEntity = new SimpleEntity();
remoteSimpleEntity.setDate(new Timestamp(123456700000L));
remoteSimpleEntity.setInteger(9);
remoteSimpleEntity.setString("You will be terminated");
em.persist(remoteSimpleEntity);
em.flush();
em.detach(remoteSimpleEntity);
SyncableDataSet<SimpleEntity> sds = SyncableDataSet.from("allSimpleEntities", SimpleEntity.class, NO_PARAMS);
// this sync request claims we were told in the past that the server has localSimpleEntity
List<SyncRequestOperation<SimpleEntity>> syncRequest = new ArrayList<SyncRequestOperation<SimpleEntity>>();
syncRequest.add(SyncRequestOperation.deleted(remoteSimpleEntity));
// now do the actual sync
List<SyncResponse<SimpleEntity>> syncResponse = dss.coldSync(sds, syncRequest);
// the server's response should acknowledge the delete operation
assertEquals("Got unexpected response: " + syncResponse, 1, syncResponse.size());
DeleteResponse<SimpleEntity> deleteResponse = (DeleteResponse<SimpleEntity>) syncResponse.get(0);
assertEquals(remoteSimpleEntity.toString(), deleteResponse.getEntity().toString());
// verify the server deleted the entity
TypedQuery<SimpleEntity> query = em.createNamedQuery("allSimpleEntities", SimpleEntity.class);
List<SimpleEntity> newQueryResult = query.getResultList();
assertTrue("Uh-oh! Entity should have been deleted! " + newQueryResult, newQueryResult.isEmpty());
}
/**
* Tests that it is harmless to say we deleted an entity that's already gone
* on the server.
*/
@Test
public void testSendLocalDeleteForRemotelyDeletedEntity() throws Exception {
SimpleEntity remoteSimpleEntity = new SimpleEntity();
remoteSimpleEntity.setDate(new Timestamp(123456700000L));
remoteSimpleEntity.setInteger(9);
remoteSimpleEntity.setString("You will be terminated");
// ensure it gets an ID assigned
em.persist(remoteSimpleEntity);
em.flush();
// now delete it
em.remove(remoteSimpleEntity);
em.flush();
em.detach(remoteSimpleEntity);
SyncableDataSet<SimpleEntity> sds = SyncableDataSet.from("allSimpleEntities", SimpleEntity.class, NO_PARAMS);
// this sync request claims we were told in the past that the server has localSimpleEntity
List<SyncRequestOperation<SimpleEntity>> syncRequest = new ArrayList<SyncRequestOperation<SimpleEntity>>();
syncRequest.add(SyncRequestOperation.deleted(remoteSimpleEntity));
// now do the actual sync (which should have no effect, since remote entity is already gone)
List<SyncResponse<SimpleEntity>> syncResponse = dss.coldSync(sds, syncRequest);
// ensure the response is empty, as expected
assertEquals("Got unexpected response: " + syncResponse, 0, syncResponse.size());
}
/**
* This tests for the case where the client has generated its own ID locally,
* and that ID is already in use on the server.
*/
@Test
public void testSendNewSimpleEntityThatHappensToHaveSameIdAsExistingRemoteEntity() {
SimpleEntity unrelatedRemoteEntity = new SimpleEntity();
unrelatedRemoteEntity.setString("Innocent bystander");
unrelatedRemoteEntity.setDate(new Timestamp(System.currentTimeMillis()));
unrelatedRemoteEntity.setInteger(2);
em.persist(unrelatedRemoteEntity);
em.flush();
SyncableDataSet<SimpleEntity> sds = SyncableDataSet.from("allSimpleEntities", SimpleEntity.class, NO_PARAMS);
SimpleEntity localSimpleEntity = new SimpleEntity();
SimpleEntity.setId(localSimpleEntity, unrelatedRemoteEntity.getId());
localSimpleEntity.setDate(new Timestamp(System.currentTimeMillis()));
localSimpleEntity.setInteger(1);
localSimpleEntity.setString("Unwitting impostor");
List<SyncRequestOperation<SimpleEntity>> syncRequest = new ArrayList<SyncRequestOperation<SimpleEntity>>();
syncRequest.add(SyncRequestOperation.created(new SimpleEntity(localSimpleEntity)));
// now do the actual sync
List<SyncResponse<SimpleEntity>> syncResponse = dss.coldSync(sds, syncRequest);
// ensure the response is as expected
assertEquals(2, syncResponse.size());
IdChangeResponse<SimpleEntity> idChangeResponse = (IdChangeResponse<SimpleEntity>) syncResponse.get(0);
assertEquals(unrelatedRemoteEntity.getId(), idChangeResponse.getOldId());
Long newId = idChangeResponse.getEntity().getId(); // we will verify this in the next stanza
// ensure the new entity actually got persisted on the server, and the innocent bystander is unharmed
List<SimpleEntity> queryResult = em.createQuery("SELECT se FROM SimpleEntity se ORDER BY se.integer", SimpleEntity.class).getResultList();
assertEquals(2, queryResult.size());
SimpleEntity.setId(localSimpleEntity, newId); // set local instance's ID to the new remote one from the response
assertEquals(localSimpleEntity.toString(), queryResult.get(0).toString());
assertSame(unrelatedRemoteEntity, queryResult.get(1));
}
}