private static <X> String stringify(SyncRequestOperation<X> op) { return "[" + op.getType() + ": expected=" + op.getExpectedState() + ", desired=" + op.getEntity() + "]"; }
public void testDeleteFromClient() { SimpleEntity entity = new SimpleEntity(); entity.setString("the string value"); entity.setDate(new Timestamp(1234567L)); entity.setInteger(9999); ErraiEntityManager esem = csm.getExpectedStateEm(); ErraiEntityManager dsem = csm.getDesiredStateEm(); // first persist the expected state esem.persist(entity); esem.flush(); esem.clear(); List<SyncRequestOperation<SimpleEntity>> expectedClientRequests = new ArrayList<SyncRequestOperation<SimpleEntity>>(); expectedClientRequests.add(SyncRequestOperation.deleted(entity)); // assuming no conflict, the server deletes the entity and generates the appropriate response List<SyncResponse<SimpleEntity>> fakeServerResponses = new ArrayList<SyncResponse<SimpleEntity>>(); fakeServerResponses.add(new DeleteResponse<SimpleEntity>(entity)); performColdSync(expectedClientRequests, fakeServerResponses); assertNull(esem.find(SimpleEntity.class, entity.getId())); assertNull(dsem.find(SimpleEntity.class, entity.getId())); }
// a hybrid of "new entity from server" and "id change from server" // the scenario is that we have a local entity with, say, ID 100 // and the server tells us "here's a new entity. its ID is 100!" // so we have to move our existing entity out of the way before accepting the remote one. public void testNewEntityWithConflictingIdFromServer() { SimpleEntity newRemote = new SimpleEntity(); newRemote.setString("new entity from server"); newRemote.setDate(new Timestamp(1234567L)); newRemote.setInteger(9999); SimpleEntity.setId(newRemote, 100L); SimpleEntity existingLocal = new SimpleEntity(); existingLocal.setString("existing local entity"); existingLocal.setDate(new Timestamp(7654321L)); existingLocal.setInteger(8888); SimpleEntity.setId(existingLocal, 100L); ErraiEntityManager esem = csm.getExpectedStateEm(); ErraiEntityManager dsem = csm.getDesiredStateEm(); dsem.persist(existingLocal); dsem.flush(); assertNull(esem.find(SimpleEntity.class, existingLocal.getId())); assertEquals( dsem.find(SimpleEntity.class, existingLocal.getId()).toString(), existingLocal.toString()); List<SyncRequestOperation<SimpleEntity>> expectedClientRequests = new ArrayList<SyncRequestOperation<SimpleEntity>>(); expectedClientRequests.add(SyncRequestOperation.created(existingLocal)); // note that the mock // server will ignore // this for the purpose // of this test List<SyncResponse<SimpleEntity>> fakeServerResponses = new ArrayList<SyncResponse<SimpleEntity>>(); fakeServerResponses.add(new NewRemoteEntityResponse<SimpleEntity>(newRemote)); performColdSync(expectedClientRequests, fakeServerResponses); // now the ID of existingLocal (still managed by dsem) should not be 100 anymore assertFalse( "Existing id " + existingLocal.getId() + " should not be the same as new object's ID " + newRemote.getId(), existingLocal.getId() == newRemote.getId()); assertSame(existingLocal, dsem.find(SimpleEntity.class, existingLocal.getId())); assertEquals(newRemote.toString(), dsem.find(SimpleEntity.class, newRemote.getId()).toString()); assertEquals(newRemote.toString(), esem.find(SimpleEntity.class, newRemote.getId()).toString()); }
public void testDeleteFromServer() { SimpleEntity newEntity = new SimpleEntity(); newEntity.setString("the string value"); newEntity.setDate(new Timestamp(1234567L)); newEntity.setInteger(9999); ErraiEntityManager esem = csm.getExpectedStateEm(); ErraiEntityManager dsem = csm.getDesiredStateEm(); // persist this as both the "expected state" from the server and the "desired state" on the // client SimpleEntity originalEntityState = esem.merge(newEntity); esem.flush(); esem.clear(); dsem.persist(originalEntityState); dsem.flush(); dsem.clear(); List<SyncRequestOperation<SimpleEntity>> expectedClientRequests = new ArrayList<SyncRequestOperation<SimpleEntity>>(); expectedClientRequests.add(SyncRequestOperation.unchanged(originalEntityState)); // now cook up a server response that says it got deleted List<SyncResponse<SimpleEntity>> fakeServerResponses = new ArrayList<SyncResponse<SimpleEntity>>(); fakeServerResponses.add(new DeleteResponse<SimpleEntity>(newEntity)); performColdSync(expectedClientRequests, fakeServerResponses); assertNull(esem.find(SimpleEntity.class, newEntity.getId())); assertNull(dsem.find(SimpleEntity.class, newEntity.getId())); // finally, ensure the deleted entity is not stuck in the REMOVED state // (should be NEW or DETACHED; we can verify by trying to merge it) try { esem.merge(newEntity); } catch (IllegalArgumentException e) { fail("Merging removed entity failed: " + e); } try { dsem.merge(newEntity); } catch (IllegalArgumentException e) { fail("Merging removed entity failed: " + e); } }
public void testIdChangeFromServer() { SimpleEntity entity = new SimpleEntity(); entity.setString("the string value"); entity.setDate(new Timestamp(1234567L)); entity.setInteger(9999); ErraiEntityManager esem = csm.getExpectedStateEm(); ErraiEntityManager dsem = csm.getDesiredStateEm(); SimpleEntity originalLocalState = dsem.merge(entity); long originalId = originalLocalState.getId(); dsem.flush(); dsem.detach(originalLocalState); assertNull(esem.find(SimpleEntity.class, originalId)); assertEquals(dsem.find(SimpleEntity.class, originalId).toString(), entity.toString()); // Now change the ID and tell the ClientSyncManager it happened long newId = originalLocalState.getId() + 100; SimpleEntity.setId(entity, newId); List<SyncRequestOperation<SimpleEntity>> expectedClientRequests = new ArrayList<SyncRequestOperation<SimpleEntity>>(); expectedClientRequests.add(SyncRequestOperation.created(originalLocalState)); List<SyncResponse<SimpleEntity>> fakeServerResponses = new ArrayList<SyncResponse<SimpleEntity>>(); fakeServerResponses.add(new IdChangeResponse<SimpleEntity>(originalId, entity)); performColdSync(expectedClientRequests, fakeServerResponses); assertNull(esem.find(SimpleEntity.class, originalId)); assertNull(dsem.find(SimpleEntity.class, originalId)); SimpleEntity changedEntityExpected = esem.find(SimpleEntity.class, newId); SimpleEntity changedEntityDesired = dsem.find(SimpleEntity.class, newId); assertEquals(changedEntityExpected.toString(), entity.toString()); assertEquals(changedEntityDesired.toString(), entity.toString()); assertNotSame(changedEntityDesired, changedEntityExpected); }
public void testUpdateFromClient() { SimpleEntity entity = new SimpleEntity(); entity.setString("the string value"); entity.setDate(new Timestamp(1234567L)); entity.setInteger(9999); ErraiEntityManager esem = csm.getExpectedStateEm(); ErraiEntityManager dsem = csm.getDesiredStateEm(); // first persist the expected state SimpleEntity originalEntityState = esem.merge(entity); esem.flush(); esem.clear(); // now make a change and persist the desired state SimpleEntity.setId(entity, originalEntityState.getId()); entity.setString("this has been updated"); dsem.persist(entity); dsem.flush(); dsem.clear(); List<SyncRequestOperation<SimpleEntity>> expectedClientRequests = new ArrayList<SyncRequestOperation<SimpleEntity>>(); expectedClientRequests.add(SyncRequestOperation.updated(entity, originalEntityState)); // the server will respond with confirmation of the update List<SyncResponse<SimpleEntity>> fakeServerResponses = new ArrayList<SyncResponse<SimpleEntity>>(); fakeServerResponses.add(new UpdateResponse<SimpleEntity>(entity)); performColdSync(expectedClientRequests, fakeServerResponses); SimpleEntity changedEntityExpected = esem.find(SimpleEntity.class, entity.getId()); SimpleEntity changedEntityDesired = dsem.find(SimpleEntity.class, entity.getId()); assertEquals(changedEntityExpected.toString(), entity.toString()); assertEquals(changedEntityDesired.toString(), entity.toString()); assertNotSame(changedEntityDesired, changedEntityExpected); }
public void testUpdateFromServer() { SimpleEntity newEntity = new SimpleEntity(); newEntity.setString("the string value"); newEntity.setDate(new Timestamp(1234567L)); newEntity.setInteger(9999); ErraiEntityManager esem = csm.getExpectedStateEm(); ErraiEntityManager dsem = csm.getDesiredStateEm(); // persist this as both the "expected state" from the server and the "desired state" on the // client SimpleEntity originalEntityState = esem.merge(newEntity); esem.flush(); esem.clear(); dsem.persist(originalEntityState); dsem.flush(); dsem.clear(); List<SyncRequestOperation<SimpleEntity>> expectedClientRequests = new ArrayList<SyncRequestOperation<SimpleEntity>>(); expectedClientRequests.add(SyncRequestOperation.unchanged(originalEntityState)); // now cook up a server response that says something changed SimpleEntity.setId(newEntity, originalEntityState.getId()); newEntity.setString("a new string value"); newEntity.setInteger(110011); List<SyncResponse<SimpleEntity>> fakeServerResponses = new ArrayList<SyncResponse<SimpleEntity>>(); fakeServerResponses.add(new UpdateResponse<SimpleEntity>(newEntity)); performColdSync(expectedClientRequests, fakeServerResponses); SimpleEntity changedEntityExpected = esem.find(SimpleEntity.class, newEntity.getId()); SimpleEntity changedEntityDesired = dsem.find(SimpleEntity.class, newEntity.getId()); assertEquals(changedEntityExpected.toString(), newEntity.toString()); assertEquals(changedEntityDesired.toString(), newEntity.toString()); assertNotSame(changedEntityDesired, changedEntityExpected); }
public void testConcurrentSyncRequestsRejected() { SimpleEntity entity = new SimpleEntity(); entity.setString("the string value"); entity.setDate(new Timestamp(1234567L)); entity.setInteger(9999); ErraiEntityManager esem = csm.getExpectedStateEm(); ErraiEntityManager dsem = csm.getDesiredStateEm(); // first persist the desired state SimpleEntity clientEntity = dsem.merge(entity); dsem.flush(); dsem.clear(); Long originalId = clientEntity.getId(); List<SyncRequestOperation<SimpleEntity>> expectedClientRequests = new ArrayList<SyncRequestOperation<SimpleEntity>>(); expectedClientRequests.add(SyncRequestOperation.created(clientEntity)); // the server creates the entity with a different ID and notifies us of the change List<SyncResponse<SimpleEntity>> fakeServerResponses = new ArrayList<SyncResponse<SimpleEntity>>(); SimpleEntity.setId(entity, 1010L); fakeServerResponses.add(new IdChangeResponse<SimpleEntity>(originalId, entity)); Runnable doDuringSync = new Runnable() { boolean alreadyRunning = false; @Override public void run() { if (alreadyRunning) { fail("Detected recursive call to coldSync()"); } alreadyRunning = true; // at this point, ClientSyncManager is in the middle of a coldSync call. for safety, it // is // required to fail. try { csm.coldSync( "allSimpleEntities", SimpleEntity.class, Collections.<String, Object>emptyMap(), new RemoteCallback<List<SyncResponse<SimpleEntity>>>() { @Override public void callback(List<SyncResponse<SimpleEntity>> response) { fail("this recursive call to coldSync must not succeed"); } }, new ErrorCallback<List<SyncResponse<SimpleEntity>>>() { @Override public boolean error( List<SyncResponse<SimpleEntity>> message, Throwable throwable) { fail("this recursive call to coldSync should have failed synchronously"); throw new AssertionError(); } }); fail("recursive call to coldSync() failed to throw an exception"); } catch (IllegalStateException ex) { System.out.println( "Got expected IllegalStateException. Returning normally so client state assertions can run."); // expected } } }; performColdSync(expectedClientRequests, fakeServerResponses, doDuringSync); // now ensure the results of the original sync request were not harmed assertFalse(csm.isSyncInProgress()); assertEquals(esem.find(SimpleEntity.class, entity.getId()).toString(), entity.toString()); assertEquals(dsem.find(SimpleEntity.class, entity.getId()).toString(), entity.toString()); assertNull(esem.find(SimpleEntity.class, originalId)); assertNull(dsem.find(SimpleEntity.class, originalId)); }