package tests;
import com.mendix.thirdparty.org.json.JSONArray;
import com.mendix.thirdparty.org.json.JSONException;
import com.mendix.thirdparty.org.json.JSONObject;
import org.junit.Assert;
import org.junit.Test;
import com.mendix.core.Core;
import com.mendix.systemwideinterfaces.core.IContext;
import communitycommons.XPath;
import restservices.consume.ChangeLogListener;
import restservices.consume.RestConsumer;
import restservices.proxies.DataSyncState;
import restservices.proxies.HttpMethod;
import restservices.proxies.RequestResult;
import tests.proxies.Task;
import tests.proxies.TaskCopy;
public class ChangeTests extends TestBase{
private static final String ONUPDATE = "Tests.OnTaskUpdate";
private static final String ONDELETE = "Tests.OnTaskDelete";
private JSONArray getChangesJSON(IContext c, long since) throws JSONException, Exception {
return new JSONArray(RestConsumer.request(c, HttpMethod.GET, baseUrl + "changes/list?since=" + since, null, null, false).getResponseBody());
}
@Test
public void testChanges() throws Exception {
IContext c = Core.createSystemContext();
IContext c2 = Core.createSystemContext();
XPath.create(c, DataSyncState.class).contains(DataSyncState.MemberNames.CollectionUrl, baseUrl).deleteAll();
def.setEnableChangeLog(true);
def.commit();
Assert.assertEquals(0L, getChangesJSON(c2, 0).length());
Task t1 = createTask(c, "milk", false);
publishTask(c, t1, false);
JSONArray changes = getChangesJSON(c2, 1);
Assert.assertEquals(0L, changes.length());
changes = getChangesJSON(c2, 0);
Assert.assertEquals(1L, changes.length());
assertChange(changes.getJSONObject(0), t1.getNr(), false, "milk",1 );
publishTask(c, t1, false); //should not change anything
changes = getChangesJSON(c2, 0);
Assert.assertEquals(1L, changes.length());
assertChange(changes.getJSONObject(0), t1.getNr(), false, "milk",1 );
t1.setDescription("karnemelk");
publishTask(c, t1, false);
changes = getChangesJSON(c2, 0);
Assert.assertEquals(1L, changes.length());
assertChange(changes.getJSONObject(0), t1.getNr(), false, "karnemelk",2);
Assert.assertEquals(0L, XPath.create(c2, TaskCopy.class).count());
ChangeLogListener.fetch(baseUrl, ONUPDATE, ONDELETE);
Assert.assertEquals(1L, XPath.create(c2, TaskCopy.class).count());
Assert.assertEquals(t1.getNr(), XPath.create(c2, TaskCopy.class).first().getNr());
Assert.assertEquals("karnemelk", XPath.create(c2, TaskCopy.class).first().getDescription());
Task t2 = createTask(c, "twix", false);
publishTask(c, t2, false);
changes = getChangesJSON(c2, 0);
Assert.assertEquals(2L, changes.length());
assertChange(changes.getJSONObject(0), t1.getNr(), false, "karnemelk",2);
assertChange(changes.getJSONObject(1), t2.getNr(), false, "twix",3);
//check since param
changes = getChangesJSON(c2, changes.getJSONObject(0).getLong("seq"));
Assert.assertEquals(1L, changes.length());
assertChange(changes.getJSONObject(0), t2.getNr(), false, "twix",3);
//check etag and complete change object
JSONObject ch = changes.getJSONObject(0);
RequestResult resp = RestConsumer.request(c2, HttpMethod.GET, ch.getString("url"), null, null, false);
Assert.assertEquals(ch.getJSONObject("data").toString(), new JSONObject(resp.getResponseBody()).toString());
Assert.assertEquals(ch.getString("etag"), resp.getETag());
Task t3 = createTask(c, "dog", false);
publishTask(c, t3, false);
ChangeLogListener.fetch(baseUrl, ONUPDATE, ONDELETE);
Assert.assertEquals(3L, XPath.create(c2, TaskCopy.class).count());
publishTask(c, t2, true);
changes = getChangesJSON(c2, 0);
Assert.assertEquals(3L, changes.length());
assertChange(changes.getJSONObject(0), t1.getNr(), false, "karnemelk",2);
assertChange(changes.getJSONObject(1), t3.getNr(), false, "dog",4);
assertChange(changes.getJSONObject(2), t2.getNr(), true, null, 5);
//fetching should now result in 2 items
ChangeLogListener.fetch(baseUrl, ONUPDATE, ONDELETE);
Assert.assertEquals(2L, XPath.create(c2, TaskCopy.class).count());
//if we reset the state
ChangeLogListener.resetDataSyncState(baseUrl);
XPath.create(c2, TaskCopy.class).deleteAll();
//there should be nothing
Assert.assertEquals(0L, XPath.create(c2, TaskCopy.class).count());
//until we fetch again
ChangeLogListener.fetch(baseUrl, ONUPDATE, ONDELETE);
Assert.assertEquals(2L, XPath.create(c2, TaskCopy.class).count());
//if we delete all data
XPath.create(c2, TaskCopy.class).deleteAll();
Assert.assertEquals(0L, XPath.create(c2, TaskCopy.class).count());
//and fetch there should be still nothing, the tracker doesn't know after all..
ChangeLogListener.fetch(baseUrl, ONUPDATE, ONDELETE);
Assert.assertEquals(0L, XPath.create(c2, TaskCopy.class).count());
//but if we reset as well
ChangeLogListener.resetDataSyncState(baseUrl);
Assert.assertEquals(0L, XPath.create(c2, TaskCopy.class).count());
//then everything should come back :)
ChangeLogListener.fetch(baseUrl, ONUPDATE, ONDELETE);
Assert.assertEquals(2L, XPath.create(c2, TaskCopy.class).count());
}
private void assertChange(JSONObject jsonObject, Long key, boolean deleted,
String description, long rev) throws Exception {
Assert.assertEquals((long) jsonObject.getLong("key"), (long) key);
Assert.assertEquals((boolean) jsonObject.getBoolean("deleted"), deleted);
Assert.assertEquals(rev, jsonObject.getLong("seq"));
if (!deleted) {
Assert.assertEquals(jsonObject.getJSONObject("data").getString("Description"), description);
//check etag
String etag = RestConsumer.request(Core.createSystemContext(), HttpMethod.GET, baseUrl + key, null, null, false).getETag();
Assert.assertEquals(etag, jsonObject.getString("etag"));
}
}
@Test
public void testChangesFeedTimeout() throws Exception {
testChangesFeed(2);
}
@Test
public void testChangesFeedTimeoutOrAutoReconnect() throws Exception {
testChangesFeed(-2);
}
public void testChangesFeed(long timeout) throws Exception {
IContext c = Core.createSystemContext();
IContext c2 = Core.createSystemContext();
def.setEnableChangeLog(true);
def.commit();
Task t1 = createTask(c, "milk", false);
TaskCopy t2;
publishTask(c, t1, false);
ChangeLogListener.resetDataSyncState(baseUrl);
ChangeLogListener.follow(baseUrl, ONUPDATE, ONDELETE, timeout);
try {
t2 = XPath.create(c2, TaskCopy.class)
.eq(TaskCopy.MemberNames.Nr, t1.getNr())
.eq(TaskCopy.MemberNames.Description, "milk")
.firstOrWait(10000);
Assert.assertTrue(t2 != null);
t1.setDescription("karnemilk");
publishTask(c, t1, false);
t2 = XPath.create(c2, TaskCopy.class)
.eq(TaskCopy.MemberNames.Nr, t1.getNr())
.eq(TaskCopy.MemberNames.Description, "karnemilk")
.firstOrWait(10000);
Assert.assertTrue(t2 != null);
Thread.sleep(3 * Math.abs(timeout) * 1000); //initial request is over now
t1.setDescription("twix");
publishTask(c, t1, false);
t2 = XPath.create(c2, TaskCopy.class)
.eq(TaskCopy.MemberNames.Nr, t1.getNr())
.eq(TaskCopy.MemberNames.Description, "twix")
.firstOrWait(10000);
Assert.assertTrue(t2 != null);
}
finally {
ChangeLogListener.unfollow(baseUrl);
}
}
@Test
public void testAutoReconnect() throws Exception {
IContext c = Core.createSystemContext();
assertErrorcode(c, HttpMethod.GET, baseUrl + "changes/feed", 405);
Task t1 = createTask(c, "test", true);
//listen before feed is enabled
ChangeLogListener.resetDataSyncState(baseUrl);
ChangeLogListener.follow(baseUrl, ONUPDATE, ONDELETE, 5);
Thread.sleep(25000);
Object t2 = XPath.create(c, TaskCopy.class)
.eq(TaskCopy.MemberNames.Nr, t1.getNr())
.eq(TaskCopy.MemberNames.Description, "test")
.firstOrWait(1000);
Assert.assertNull(t2);
def.setEnableChangeLog(true);
def.commit();
t2 = XPath.create(c, TaskCopy.class)
.eq(TaskCopy.MemberNames.Nr, t1.getNr())
.eq(TaskCopy.MemberNames.Description, "test")
.firstOrWait(20000);
Assert.assertNotNull(t2);
}
}