-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add index to local entities #6261
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ import org.hamcrest.Matchers.contains | |
import org.hamcrest.Matchers.containsInAnyOrder | ||
import org.hamcrest.Matchers.equalTo | ||
import org.junit.Test | ||
import org.odk.collect.android.entities.support.EntitySameAsMatcher.Companion.sameEntityAs | ||
import org.odk.collect.entities.storage.EntitiesRepository | ||
import org.odk.collect.entities.storage.Entity | ||
|
||
|
@@ -16,8 +17,8 @@ abstract class EntitiesRepositoryTest { | |
fun `#getLists returns lists for saved entities`() { | ||
val repository = buildSubject() | ||
|
||
val wine = Entity("wines", "1", "Léoville Barton 2008") | ||
val whisky = Entity("whiskys", "2", "Lagavulin 16") | ||
val wine = Entity.New("wines", "1", "Léoville Barton 2008") | ||
val whisky = Entity.New("whiskys", "2", "Lagavulin 16") | ||
repository.save(wine) | ||
repository.save(whisky) | ||
|
||
|
@@ -28,97 +29,97 @@ abstract class EntitiesRepositoryTest { | |
fun `#getEntities returns entities for list`() { | ||
val repository = buildSubject() | ||
|
||
val wine = Entity("wines", "1", "Léoville Barton 2008") | ||
val whisky = Entity("whiskys", "2", "Lagavulin 16") | ||
val wine = Entity.New("wines", "1", "Léoville Barton 2008") | ||
val whisky = Entity.New("whiskys", "2", "Lagavulin 16") | ||
repository.save(wine) | ||
repository.save(whisky) | ||
|
||
val wines = repository.getEntities("wines") | ||
assertThat(wines.size, equalTo(1)) | ||
assertThat(wines[0], equalTo(wine)) | ||
assertThat(wines[0], sameEntityAs(wine)) | ||
|
||
val whiskys = repository.getEntities("whiskys") | ||
assertThat(whiskys.size, equalTo(1)) | ||
assertThat(whiskys[0], equalTo(whisky)) | ||
assertThat(whiskys[0], sameEntityAs(whisky)) | ||
} | ||
|
||
@Test | ||
fun `#save updates existing entity with matching id`() { | ||
val repository = buildSubject() | ||
|
||
val wine = Entity("wines", "1", "Léoville Barton 2008", version = 1) | ||
val wine = Entity.New("wines", "1", "Léoville Barton 2008", version = 1) | ||
repository.save(wine) | ||
|
||
val updatedWine = Entity("wines", wine.id, "Léoville Barton 2009", version = 2) | ||
val updatedWine = Entity.New("wines", wine.id, "Léoville Barton 2009", version = 2) | ||
repository.save(updatedWine) | ||
|
||
val wines = repository.getEntities("wines") | ||
assertThat(wines, contains(updatedWine)) | ||
assertThat(wines, contains(sameEntityAs(updatedWine))) | ||
} | ||
|
||
@Test | ||
fun `#save creates entity with matching id in different list`() { | ||
val repository = buildSubject() | ||
|
||
val wine = Entity("wines", "1", "Léoville Barton 2008", version = 1) | ||
val wine = Entity.New("wines", "1", "Léoville Barton 2008", version = 1) | ||
repository.save(wine) | ||
|
||
val updatedWine = Entity("whisky", wine.id, "Edradour 10", version = 2) | ||
val updatedWine = Entity.New("whisky", wine.id, "Edradour 10", version = 2) | ||
repository.save(updatedWine) | ||
|
||
val wines = repository.getEntities("wines") | ||
assertThat(wines, contains(wine)) | ||
assertThat(wines, contains(sameEntityAs(wine))) | ||
val whiskys = repository.getEntities("whisky") | ||
assertThat(whiskys, contains(updatedWine)) | ||
assertThat(whiskys, contains(sameEntityAs(updatedWine))) | ||
} | ||
|
||
@Test | ||
fun `#save updates existing entity with matching id and version`() { | ||
val repository = buildSubject() | ||
|
||
val wine = Entity("wines", "1", "Léoville Barton 2008", version = 1) | ||
val wine = Entity.New("wines", "1", "Léoville Barton 2008", version = 1) | ||
repository.save(wine) | ||
|
||
val updatedWine = wine.copy(label = "Léoville Barton 2009") | ||
repository.save(updatedWine) | ||
|
||
val wines = repository.getEntities("wines") | ||
assertThat(wines, contains(updatedWine)) | ||
assertThat(wines, contains(sameEntityAs(updatedWine))) | ||
} | ||
|
||
@Test | ||
fun `#save updates state on existing entity when it is offline`() { | ||
val repository = buildSubject() | ||
|
||
val wine = Entity("wines", "1", "Léoville Barton 2008", state = Entity.State.OFFLINE) | ||
val wine = Entity.New("wines", "1", "Léoville Barton 2008", state = Entity.State.OFFLINE) | ||
repository.save(wine) | ||
|
||
val updatedWine = wine.copy(state = Entity.State.ONLINE) | ||
repository.save(updatedWine) | ||
|
||
val wines = repository.getEntities("wines") | ||
assertThat(wines, contains(updatedWine)) | ||
assertThat(wines, contains(sameEntityAs(updatedWine))) | ||
} | ||
|
||
@Test | ||
fun `#save does not update state on existing entity when it is online`() { | ||
val repository = buildSubject() | ||
|
||
val wine = Entity("wines", "1", "Léoville Barton 2008", state = Entity.State.ONLINE) | ||
val wine = Entity.New("wines", "1", "Léoville Barton 2008", state = Entity.State.ONLINE) | ||
repository.save(wine) | ||
|
||
val updatedWine = wine.copy(state = Entity.State.OFFLINE) | ||
repository.save(updatedWine) | ||
|
||
val wines = repository.getEntities("wines") | ||
assertThat(wines, contains(wine)) | ||
assertThat(wines, contains(sameEntityAs(wine))) | ||
} | ||
|
||
@Test | ||
fun `#save adds new properties`() { | ||
val repository = buildSubject() | ||
|
||
val wine = Entity( | ||
val wine = Entity.New( | ||
"wines", | ||
"1", | ||
"Léoville Barton 2008", | ||
|
@@ -127,7 +128,7 @@ abstract class EntitiesRepositoryTest { | |
) | ||
repository.save(wine) | ||
|
||
val updatedWine = Entity( | ||
val updatedWine = Entity.New( | ||
"wines", | ||
wine.id, | ||
"Léoville Barton 2008", | ||
|
@@ -145,7 +146,7 @@ abstract class EntitiesRepositoryTest { | |
fun `#save updates existing properties`() { | ||
val repository = buildSubject() | ||
|
||
val wine = Entity( | ||
val wine = Entity.New( | ||
"wines", | ||
"1", | ||
"Léoville Barton 2008", | ||
|
@@ -154,7 +155,7 @@ abstract class EntitiesRepositoryTest { | |
) | ||
repository.save(wine) | ||
|
||
val updatedWine = Entity( | ||
val updatedWine = Entity.New( | ||
"wines", | ||
wine.id, | ||
"Léoville Barton 2008", | ||
|
@@ -172,7 +173,7 @@ abstract class EntitiesRepositoryTest { | |
fun `#save does not update existing label if new one is null`() { | ||
val repository = buildSubject() | ||
|
||
val wine = Entity( | ||
val wine = Entity.New( | ||
"wines", | ||
"1", | ||
"Léoville Barton 2008", | ||
|
@@ -181,7 +182,7 @@ abstract class EntitiesRepositoryTest { | |
) | ||
repository.save(wine) | ||
|
||
val updatedWine = Entity( | ||
val updatedWine = Entity.New( | ||
"wines", | ||
wine.id, | ||
null, | ||
|
@@ -204,16 +205,16 @@ abstract class EntitiesRepositoryTest { | |
repository.addList("blah") | ||
assertThat(repository.getLists(), containsInAnyOrder("wines", "blah")) | ||
|
||
repository.save(Entity("wines", "blah", "Blah")) | ||
repository.save(Entity.New("wines", "blah", "Blah")) | ||
assertThat(repository.getLists(), containsInAnyOrder("wines", "blah")) | ||
} | ||
|
||
@Test | ||
fun `#clear deletes all entities`() { | ||
val repository = buildSubject() | ||
|
||
val wine = Entity("wines", "1", "Léoville Barton 2008") | ||
val whisky = Entity("whiskys", "2", "Lagavulin 16") | ||
val wine = Entity.New("wines", "1", "Léoville Barton 2008") | ||
val whisky = Entity.New("whiskys", "2", "Lagavulin 16") | ||
repository.save(wine) | ||
repository.save(whisky) | ||
|
||
|
@@ -227,13 +228,55 @@ abstract class EntitiesRepositoryTest { | |
fun `#save can save multiple entities`() { | ||
val repository = buildSubject() | ||
|
||
val wine = Entity("wines", "1", "Léoville Barton 2008") | ||
val whisky = Entity("whiskys", "2", "Lagavulin 16") | ||
val wine = Entity.New("wines", "1", "Léoville Barton 2008") | ||
val whisky = Entity.New("whiskys", "2", "Lagavulin 16") | ||
repository.save(wine, whisky) | ||
|
||
assertThat(repository.getLists(), containsInAnyOrder("wines", "whiskys")) | ||
} | ||
|
||
@Test | ||
fun `#save assigns an index to each entity in insert order when saving multiple entities`() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They don't strictly need to be in insert order most likely, but this behaviour is much easier to test, and will mean the resulting multiplicity within form entry will match the order in the CSVs. |
||
val first = Entity.New("wines", "1", "Léoville Barton 2008") | ||
val second = Entity.New("wines", "2", "Pontet Canet 2014") | ||
|
||
val repository = buildSubject() | ||
repository.save(first, second) | ||
|
||
val entities = repository.getEntities("wines") | ||
assertThat(entities[0].index, equalTo(0)) | ||
assertThat(entities[1].index, equalTo(1)) | ||
} | ||
|
||
@Test | ||
fun `#save assigns an index to each entity in insert order when saving single entities`() { | ||
val first = Entity.New("wines", "1", "Léoville Barton 2008") | ||
val second = Entity.New("wines", "2", "Pontet Canet 2014") | ||
|
||
val repository = buildSubject() | ||
repository.save(first) | ||
repository.save(second) | ||
|
||
val entities = repository.getEntities("wines") | ||
assertThat(entities[0].index, equalTo(0)) | ||
assertThat(entities[1].index, equalTo(1)) | ||
} | ||
|
||
@Test | ||
fun `#save does not change index when updating an existing entity`() { | ||
seadowg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
val repository = buildSubject() | ||
|
||
val first = Entity.New("wines", "1", "Léoville Barton 2008") | ||
val second = Entity.New("wines", "2", "Pontet Canet 2014") | ||
repository.save(first, second) | ||
assertThat(repository.getEntities("wines")[0].index, equalTo(0)) | ||
|
||
val updatedWine = first.copy(label = "Léoville Barton 2009") | ||
repository.save(updatedWine) | ||
|
||
assertThat(repository.getEntities("wines")[0].index, equalTo(0)) | ||
} | ||
|
||
@Test | ||
fun `#addList adds a list with no entities`() { | ||
val repository = buildSubject() | ||
|
@@ -247,12 +290,34 @@ abstract class EntitiesRepositoryTest { | |
fun `#delete removes an entity`() { | ||
val repository = buildSubject() | ||
|
||
val leoville = Entity("wines", "1", "Léoville Barton 2008") | ||
val canet = Entity("wines", "2", "Pontet-Canet 2014") | ||
val leoville = Entity.New("wines", "1", "Léoville Barton 2008") | ||
val canet = Entity.New("wines", "2", "Pontet-Canet 2014") | ||
repository.save(leoville, canet) | ||
|
||
repository.delete("1") | ||
|
||
assertThat(repository.getEntities("wines"), containsInAnyOrder(canet)) | ||
assertThat(repository.getEntities("wines"), containsInAnyOrder(sameEntityAs(canet))) | ||
} | ||
|
||
@Test | ||
fun `#delete updates index values so that they are always in sequence and start at 0`() { | ||
val repository = buildSubject() | ||
|
||
val leoville = Entity.New("wines", "1", "Léoville Barton 2008") | ||
val canet = Entity.New("wines", "2", "Pontet-Canet 2014") | ||
val gloria = Entity.New("wines", "3", "Chateau Gloria 2016") | ||
repository.save(leoville, canet, gloria) | ||
|
||
repository.delete("1") | ||
|
||
var wines = repository.getEntities("wines") | ||
assertThat(wines[0].index, equalTo(0)) | ||
assertThat(wines[1].index, equalTo(1)) | ||
|
||
repository.save(leoville) | ||
wines = repository.getEntities("wines") | ||
assertThat(wines[0].index, equalTo(0)) | ||
assertThat(wines[1].index, equalTo(1)) | ||
assertThat(wines[2].index, equalTo(2)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Our future "query" APIs would just filter the result of this.
This sort of thing is obviously going to be much more awkward to implement in SQL, but I think we have some options. Worst case, we'll need to do more work in form download (or in some new background job kicked off after form download) to give ourselves an "index".