package com.rapidftr.repository; import android.database.Cursor; import com.rapidftr.CustomTestRunner; import com.rapidftr.RapidFtrApplication; import com.rapidftr.database.Database; import com.rapidftr.database.DatabaseSession; import com.rapidftr.database.ShadowSQLiteHelper; import com.rapidftr.forms.FormField; import com.rapidftr.forms.FormSection; import com.rapidftr.forms.FormSectionTest; import com.rapidftr.model.Child; import com.rapidftr.model.History; import com.rapidftr.model.User; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import static com.rapidftr.CustomTestRunner.createUser; import static com.rapidftr.model.History.HISTORIES; import static com.rapidftr.utils.JSONMatcher.equalJSONIgnoreOrder; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsEqual.equalTo; import static org.hamcrest.core.IsNot.not; import static org.junit.Assert.*; import static org.junit.matchers.JUnitMatchers.hasItem; import static org.junit.matchers.JUnitMatchers.hasItems; import static org.mockito.Mockito.*; @RunWith(CustomTestRunner.class) public class ChildRepositoryTest { public DatabaseSession session; public ChildRepository repository; private List<FormField> highlightedFormFields; private RapidFtrApplication rapidFtrApplication; @Before public void setupSession() throws IOException { session = new ShadowSQLiteHelper("test_database").getSession(); rapidFtrApplication = (RapidFtrApplication) Robolectric.getShadowApplication().getApplicationContext(); User user = new User("userName", "password", true, "http://1.2.3.4"); rapidFtrApplication.setCurrentUser(user); repository = new ChildRepository("user1", session, rapidFtrApplication); highlightedFormFields = new ArrayList<FormField>(); List<FormSection> formSections = FormSectionTest.loadFormSectionsFromClassPathResource(); for (FormSection formSection : formSections) { highlightedFormFields.addAll(formSection.getOrderedHighLightedFields()); } } @Test public void shouldCreateChildRecordAndNotSetLastUpdatedAt() throws JSONException { repository.createOrUpdate(new Child("id1", "user1", null)); assertThat(repository.size(), equalTo(1)); } @Test public void shouldCreateChildRecordAndSetCreatedAt() throws Exception { repository.createOrUpdate(new Child("id1", "user1", "{ 'test1' : 'value1' }")); JSONObject childJsonValues = repository.get("id1").values(); JSONArray histories = (JSONArray) childJsonValues.get(History.HISTORIES); JSONObject changes = (JSONObject) ((JSONObject) histories.get(0)).get("changes"); assert(((JSONObject) changes.get("child")).has(History.CREATED)); } @Test public void shouldUpdateChildRecordIfIdAlreadyExistsAndSetLastUpdateAt() throws Exception { ChildRepository repository = spy(new ChildRepository("user1", session, rapidFtrApplication)); repository.createOrUpdate(new Child("id1", "user1", "{ 'test1' : 'value1', 'test2' : 0, 'test3' : [ '1', 2, '3' ] }")); String updateString = "{ 'test1' : 'value1' }"; String expectedString = "{'created_by':'user1','test1':'value1','unique_identifier':'id1','last_updated_at':'LAST_UPDATED_AT'}"; doReturn("LAST_UPDATED_AT").when(repository).getTimeStamp(); repository.createOrUpdate(new Child("id1", "user1", updateString)); Child child = repository.get("id1"); assertThat(child.getUniqueId(), equalTo("id1")); JSONObject values = child.values(); values.remove(History.HISTORIES); assertThat(values, equalJSONIgnoreOrder(expectedString)); assertThat(child.getLastUpdatedAt(), is("LAST_UPDATED_AT")); } @Test public void shouldUpdateChildRecordWithHistory() throws Exception { repository.createOrUpdate(new Child("idx", "user1", "{ 'test1' : 'value1', 'test2' : 0, 'test3' : [ '1', 2, '3' ] }")); Child child = repository.get("idx"); JSONObject childJsonValues = child.values(); assertEquals(1, childJsonValues.getJSONArray(History.HISTORIES).length()); child.put("test1", "value2"); repository.createOrUpdate(child); childJsonValues = repository.get("idx").values(); assertEquals(2, childJsonValues.getJSONArray(History.HISTORIES).length()); } @Test public void shouldSaveInternalIdAndRevDuringChildCreation() throws JSONException { Child child1 = new ChildBuilder().withName("tester").withCreatedBy("user1").withUniqueId("abcd1234").build(); Child child2 = new ChildBuilder().withInternalId("59cd40f39ab6aa791f73885e3bdd99f9").withName("tester").withUniqueId("1234abcd").withRev("4-b011946150a16b0d2c6271aed05e2abe").withCreatedBy("user1").build(); repository.createOrUpdate(child1); repository.createOrUpdate(child2); HashMap<String, String> allIdsAndRevs = repository.getAllIdsAndRevs(); assertEquals(2, allIdsAndRevs.size()); assertEquals("", allIdsAndRevs.get("")); assertEquals("4-b011946150a16b0d2c6271aed05e2abe", allIdsAndRevs.get("59cd40f39ab6aa791f73885e3bdd99f9")); child1.put("_id", "dfb2031ebfcbef39dccdb468f5200edc"); child1.put("_rev", "5-1ed26a0e5072830a9064361a570684f6"); repository.createOrUpdateWithoutHistory(child1); allIdsAndRevs = repository.getAllIdsAndRevs(); assertEquals(2, allIdsAndRevs.size()); assertEquals("4-b011946150a16b0d2c6271aed05e2abe", allIdsAndRevs.get("59cd40f39ab6aa791f73885e3bdd99f9")); assertEquals("5-1ed26a0e5072830a9064361a570684f6", allIdsAndRevs.get("dfb2031ebfcbef39dccdb468f5200edc")); } @Test public void shouldGetCorrectlyDeserializesData() throws JSONException, IOException { Child child1 = new Child("id1", "user1", "{ 'test1' : 'value1', 'test2' : 0, 'test3' : [ '1', 2, '3' ] }"); repository.createOrUpdate(child1); Child child2 = repository.get("id1"); assertThat(child1.values(), equalJSONIgnoreOrder(child2.values())); assertNotNull(child2.getLastUpdatedAt()); } @Test public void shouldCorrectlyGetSyncedState() throws JSONException, IOException { Child syncedChild = new Child("syncedID", "user1", null, true); Child unsyncedChild = new Child("unsyncedID", "user1", null, false); repository.createOrUpdate(syncedChild); repository.createOrUpdate(unsyncedChild); assertThat(repository.get("syncedID").isSynced(), is(true)); assertThat(repository.get("unsyncedID").isSynced(), is(false)); } @Test(expected = NullPointerException.class) public void getShouldThrowExceptionIfRecordDoesNotExist() throws JSONException { repository.get("blah"); } @Test public void shouldNotReturnChildrenCreatedByOtherUnAuthorizedUsers() throws Exception { User user1 = createUser("user1"); user1.setVerified(false); User user2 = createUser("user2"); user2.setVerified(false); RapidFtrApplication.getApplicationInstance().setCurrentUser(user1); Child child1 = new Child("id1", user1.getUserName(), "{ 'name' : 'child1', 'test2' : 0, 'test3' : [ '1', 2, '3' ] }"); Child child2 = new Child("id2", user2.getUserName(), "{ 'name' : 'child2', 'test2' : 0, 'test3' : [ '1', 2, '3' ] }"); repository.createOrUpdate(child1); repository.createOrUpdate(child2); List<Child> children = repository.getFirstPageOfChildrenMatchingString("hild"); assertEquals(1, children.size()); } @Test public void shouldReturnChildRecordsGivenListOfIds() throws Exception { Child child1 = new Child("id1", "user1", "{ 'name' : 'child1', 'test2' : 0, 'test3' : [ '1', 2, '3' ] }"); Child child2 = new Child("id2", "user2", "{ 'name' : 'child2', 'test2' : 0, 'test3' : [ '1', 2, '3' ] }"); Child child3 = new Child("id3", "user3", "{ 'name' : 'child3', 'test2' : 'child1', 'test3' : [ '1', 2, '3' ] }"); Child child4 = new Child("child1", "user4", "{ 'name' : 'child4', 'test2' : 'test2', 'test3' : [ '1', 2, '3' ] }"); repository.createOrUpdate(child1); repository.createOrUpdate(child2); repository.createOrUpdate(child3); repository.createOrUpdate(child4); ArrayList<String> listOfIds = new ArrayList<String>(); listOfIds.add("id1"); listOfIds.add("id2"); listOfIds.add("id3"); listOfIds.add("child1"); List<Child> children = repository.getChildrenByIds(listOfIds); assertEquals(4, children.size()); assertTrue(children.contains(child1)); assertTrue(children.contains(child2)); assertTrue(children.contains(child3)); assertTrue(children.contains(child4)); } @Test public void shouldReturnFirstPage() throws JSONException { session = mock(DatabaseSession.class); repository = spy(new ChildRepository("user1", session, rapidFtrApplication)); doReturn(new ArrayList<Child>()).when(repository).toChildren(any(Cursor.class)); repository.getRecordsForFirstPage(); String sql = "SELECT child_json, synced FROM children WHERE child_owner='user1' ORDER BY id LIMIT 30"; verify(session, times(1)).rawQuery(sql, null); } @Test public void shouldReturnRecordsBetweenSpecifiedLimits() throws JSONException { session = mock(DatabaseSession.class); repository = spy(new ChildRepository("user1", session, rapidFtrApplication)); doReturn(new ArrayList<Child>()).when(repository).toChildren(any(Cursor.class)); repository.getRecordsBetween(1, 10); String sql = "SELECT child_json, synced FROM children WHERE child_owner='user1' ORDER BY id LIMIT 9 OFFSET 1"; verify(session, times(1)).rawQuery(sql, null); } @Test public void shouldQueryForFirstThirtyMatches() throws JSONException { session = mock(DatabaseSession.class); repository = spy(new ChildRepository("user1", session, rapidFtrApplication)); doReturn(new ArrayList<Child>()).when(repository).toChildren(any(Cursor.class)); repository.getFirstPageOfChildrenMatchingString("john"); PaginatedSearchQueryBuilder queryBuilder = new PaginatedSearchQueryBuilder( RapidFtrApplication.getApplicationInstance(), "john"); String sql = queryBuilder.queryForMatchingChildrenFirstPage(); verify(session, times(1)).rawQuery(sql, null); } @Test public void shouldQueryForMatchingChildrenBetweenSpecifiedLimits() throws JSONException { session = mock(DatabaseSession.class); repository = spy(new ChildRepository("user1", session, rapidFtrApplication)); doReturn(new ArrayList<Child>()).when(repository).toChildren(any(Cursor.class)); repository.getChildrenMatchingStringBetween("john", 1, 10); PaginatedSearchQueryBuilder queryBuilder = new PaginatedSearchQueryBuilder( RapidFtrApplication.getApplicationInstance(), "john"); String sql = queryBuilder.queryForMatchingChildrenBetweenPages(1, 10); verify(session, times(1)).rawQuery(sql, null); } @Test public void shouldCorrectlyGetSyncedStateWhenGettingAllRecordsInFirstPage() throws JSONException, IOException { Child syncedChild = new Child("syncedID", "user1", null, true); Child unsyncedChild = new Child("unsyncedID", "user1", null, false); repository.createOrUpdate(syncedChild); repository.createOrUpdate(unsyncedChild); List<Child> all = repository.getRecordsForFirstPage(); assertThat(all.get(0).isSynced(), is(true)); assertThat(all.get(1).isSynced(), is(false)); } @Test public void shouldReturnsAllRecords() throws JSONException, IOException { Child child1 = new Child("id1", "user1", "{'name':'last_user'}"); Child child2 = new Child("id2", "user1", "{'name':'first_user'}"); repository.createOrUpdate(child1); repository.createOrUpdate(child2); List<Child> children = repository.getRecordsForFirstPage(); assertThat(children.size(), equalTo(2)); assertThat(child2.getInternalId(), equalTo(children.get(0).getInternalId())); assertThat(child1.getInternalId(), equalTo(children.get(1).getInternalId())); } @Test public void shouldOnlyReturnsOwnRecordsWhenGettingAll() throws JSONException { Child child1 = new Child("id1", "user1", null); repository.createOrUpdate(child1); ChildRepository anotherUsersRepository = new ChildRepository("user2", session, rapidFtrApplication); Child child2 = new Child("id2", "user2", null); anotherUsersRepository.createOrUpdate(child2); assertThat(repository.allCreatedByCurrentUser(), not(hasItem(child2))); assertThat(anotherUsersRepository.allCreatedByCurrentUser(), not(hasItem(child1))); } @Test public void shouldReturnAllUnSyncedRecords() throws JSONException { Child child1 = new Child("id1", "user1", null); Child child2 = new Child("id2", "user1", null, true); repository.createOrUpdate(child1); repository.createOrUpdate(child2); List<Child> children = repository.toBeSynced(); assertThat(children.size(), equalTo(1)); assertThat(children, hasItems(child1)); } @Test public void shouldReturnAllUnSyncedRecordsForGivenUser() throws JSONException { Child child1 = new Child("id1", "user1", null); Child child2 = new Child("id2", "user2", null); repository.createOrUpdate(child1); repository.createOrUpdate(child2); List<Child> children = repository.currentUsersUnsyncedRecords(); assertThat(children.size(), equalTo(1)); assertThat(children, hasItems(child1)); } @Test public void shouldReturnTrueWhenAChildWithTheGivenIdExistsInTheDatabase() { assertThat(repository.exists("1234"), is(false)); } @Test public void shouldReturnFalseWhenAChildWithTheGivenIdDoesNotExistInTheDatabase() throws JSONException { Child child1 = new Child("iAmARealChild", "user1", null); repository.createOrUpdate(child1); assertThat(repository.exists("iAmARealChild"), is(true)); } @Test public void shouldUpdateAnExstingChild() throws JSONException { Child child = new Child("id1", "user1", "{ 'test1' : 'value1', 'test2' : 0, 'test3' : [ '1', 2, '3' ] }"); repository.createOrUpdate(child); child.put(Database.ChildTableColumn.owner.getColumnName(), "new owner"); child.put("someNewField", "someNewValue"); repository.createOrUpdateWithoutHistory(child); Child updatedChild = repository.get("id1"); assertThat((String) updatedChild.get(Database.ChildTableColumn.owner.getColumnName()), is("new owner")); assertThat(updatedChild.get("someNewField").toString(), is("someNewValue")); } @Test public void shouldAddHistoriesIfChildHasBeenUpdated() throws JSONException { Child existingChild = new Child("id", "user1", "{'name' : 'oldname'}"); repository.createOrUpdate(existingChild); Child updatedChild = new Child("id", "user1", "{'name' : 'updatedname'}"); repository.createOrUpdate(updatedChild); Child savedChild = repository.get(updatedChild.getUniqueId()); assertTrue(savedChild.get(HISTORIES).toString().matches(".*\"changes\":\\{.*\"name\":\\{(\"to\":\"updatedname\"|\"from\":\"oldname\"),(\"from\":\"oldname\"|\"to\":\"updatedname\")\\}.*")); } @Test public void shouldReturnChildrenWithTheGivenInternalIds() throws JSONException { Child child1 = new Child("id1", "user1", "{ 'name' : 'child1', 'test2' : 0, '_id' : 'ae0fc' }"); Child child2 = new Child("id2", "user1", "{ 'name' : 'child2', 'test2' : 0, '_id' : 'b32fa' }"); repository.createOrUpdate(child1); repository.createOrUpdate(child2); List<String> internalIds = new ArrayList<String>(); internalIds.add("ae0fc"); internalIds.add("b32fa"); List<Child> children = repository.getAllWithInternalIds(internalIds); assertEquals(2, children.size()); assertTrue(children.contains(child1)); assertTrue(children.contains(child2)); } @Test public void shouldNotReturnAnyChildGivenInternalIdsDontMatch() throws JSONException { Child child1 = new Child("id1", "user1", "{ 'name' : 'child1', 'test2' : 0, '_id' : 'ae0fc' }"); Child child2 = new Child("id2", "user1", "{ 'name' : 'child2', 'test2' : 0, '_id' : 'b32fa' }"); repository.createOrUpdate(child1); repository.createOrUpdate(child2); List<String> internalIds = new ArrayList<String>(); internalIds.add("000fc"); internalIds.add("1243fa"); List<Child> children = repository.getAllWithInternalIds(internalIds); assertEquals(0, children.size()); } @Test public void shouldOnlyReturnChildrenWithMatchingInternalIds() throws JSONException { Child child1 = new Child("id1", "user1", "{ 'name' : 'child1', 'test2' : 0, '_id' : 'ae0fc' }"); Child child2 = new Child("id2", "user1", "{ 'name' : 'child2', 'test2' : 0, '_id' : 'b32fa' }"); Child child3 = new Child("id3", "user1", "{ 'name' : 'child3', 'test2' : 0, '_id' : 'c42fa' }"); repository.createOrUpdate(child1); repository.createOrUpdate(child2); repository.createOrUpdate(child3); List<String> internalIds = new ArrayList<String>(); internalIds.add("ae0fc"); internalIds.add("1443fa"); internalIds.add("c42fa"); List<Child> children = repository.getAllWithInternalIds(internalIds); assertEquals(2, children.size()); assertTrue(children.contains(child1)); assertTrue(children.contains(child3)); } @Test public void shouldRetrieveAllIdsAndRevs() throws JSONException { Child child1 = new ChildBuilder() .withInternalId("dfb2031ebfcbef39dccdb468f5200edc") .withName("tester") .withRev("5-1ed26a0e5072830a9064361a570684f6") .withCreatedBy("user1") .withUniqueId("abc123") .build(); Child child2 = new ChildBuilder() .withInternalId("59cd40f39ab6aa791f73885e3bdd99f9") .withName("tester") .withRev("4-b011946150a16b0d2c6271aed05e2abe") .withCreatedBy("user1") .withUniqueId("bcs234") .build(); repository.createOrUpdate(child1); repository.createOrUpdate(child2); HashMap<String, String> allIdsAndRevs = repository.getAllIdsAndRevs(); assertEquals(2, allIdsAndRevs.size()); assertEquals("5-1ed26a0e5072830a9064361a570684f6", allIdsAndRevs.get("dfb2031ebfcbef39dccdb468f5200edc")); assertEquals("4-b011946150a16b0d2c6271aed05e2abe", allIdsAndRevs.get("59cd40f39ab6aa791f73885e3bdd99f9")); } @Test public void shouldDeleteAllRecordOfAGivenUser() throws JSONException { Child syncedChild = new Child("syncedID", "user1", null, true); Child unSyncedChild = new Child("unsyncedID", "user1", null, false); repository.createOrUpdate(syncedChild); repository.createOrUpdate(unSyncedChild); repository.deleteChildrenByOwner(); assertEquals(0, repository.getRecordsForFirstPage().size()); } @Test public void shouldCreateNewChildWithoutHistory() throws JSONException { Child child = new Child("syncedID", "user1", null, true); repository.createOrUpdateWithoutHistory(child); Child savedChild = repository.get(child.getUniqueId()); assertNotNull(savedChild); assertFalse(savedChild.has(HISTORIES)); } @Test public void shouldUpdateExistingChildWithoutHistory() throws JSONException { Child child = new Child("syncedID", "user1", null, true); repository.createOrUpdateWithoutHistory(child); child.put("more_stuff", "some_more_stuff"); repository.createOrUpdateWithoutHistory(child); Child savedChild = repository.get(child.getUniqueId()); assertNotNull(savedChild); assertFalse(savedChild.has(HISTORIES)); assertEquals("some_more_stuff", savedChild.get("more_stuff")); } public class ChildBuilder { Child child = new Child(); public ChildBuilder withName(String name) throws JSONException { child.put(Database.ChildTableColumn.name.getColumnName(), name); return this; } public ChildBuilder withInternalId(String id) throws JSONException { child.put(Database.ChildTableColumn.internal_id.getColumnName(), id); return this; } public ChildBuilder withRev(String rev) throws JSONException { child.put(Database.ChildTableColumn.internal_rev.getColumnName(), rev); return this; } public ChildBuilder withCreatedBy(String createdBy) throws JSONException { child.put(Database.ChildTableColumn.created_by.getColumnName(), createdBy); return this; } public ChildBuilder withUniqueId(String uniqueId) throws JSONException { child.put(Database.ChildTableColumn.unique_identifier.getColumnName(), uniqueId); return this; } public Child build() { return child; } } }