|
|
@ -34,6 +34,7 @@ import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel
|
|
|
|
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource
|
|
|
|
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource
|
|
|
|
import com.google.samples.apps.nowinandroid.core.datastore.test.testUserPreferencesDataStore
|
|
|
|
import com.google.samples.apps.nowinandroid.core.datastore.test.testUserPreferencesDataStore
|
|
|
|
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
|
|
|
|
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
|
|
|
|
|
|
|
|
import com.google.samples.apps.nowinandroid.core.model.data.Topic
|
|
|
|
import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList
|
|
|
|
import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList
|
|
|
|
import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource
|
|
|
|
import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource
|
|
|
|
import com.google.samples.apps.nowinandroid.core.testing.notifications.TestNotifier
|
|
|
|
import com.google.samples.apps.nowinandroid.core.testing.notifications.TestNotifier
|
|
|
@ -46,6 +47,7 @@ import org.junit.Rule
|
|
|
|
import org.junit.Test
|
|
|
|
import org.junit.Test
|
|
|
|
import org.junit.rules.TemporaryFolder
|
|
|
|
import org.junit.rules.TemporaryFolder
|
|
|
|
import kotlin.test.assertEquals
|
|
|
|
import kotlin.test.assertEquals
|
|
|
|
|
|
|
|
import kotlin.test.assertTrue
|
|
|
|
|
|
|
|
|
|
|
|
class OfflineFirstNewsRepositoryTest {
|
|
|
|
class OfflineFirstNewsRepositoryTest {
|
|
|
|
|
|
|
|
|
|
|
@ -53,6 +55,8 @@ class OfflineFirstNewsRepositoryTest {
|
|
|
|
|
|
|
|
|
|
|
|
private lateinit var subject: OfflineFirstNewsRepository
|
|
|
|
private lateinit var subject: OfflineFirstNewsRepository
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private lateinit var niaPreferencesDataSource: NiaPreferencesDataSource
|
|
|
|
|
|
|
|
|
|
|
|
private lateinit var newsResourceDao: TestNewsResourceDao
|
|
|
|
private lateinit var newsResourceDao: TestNewsResourceDao
|
|
|
|
|
|
|
|
|
|
|
|
private lateinit var topicDao: TestTopicDao
|
|
|
|
private lateinit var topicDao: TestTopicDao
|
|
|
@ -68,17 +72,19 @@ class OfflineFirstNewsRepositoryTest {
|
|
|
|
|
|
|
|
|
|
|
|
@Before
|
|
|
|
@Before
|
|
|
|
fun setup() {
|
|
|
|
fun setup() {
|
|
|
|
|
|
|
|
niaPreferencesDataSource = NiaPreferencesDataSource(
|
|
|
|
|
|
|
|
tmpFolder.testUserPreferencesDataStore(testScope),
|
|
|
|
|
|
|
|
)
|
|
|
|
newsResourceDao = TestNewsResourceDao()
|
|
|
|
newsResourceDao = TestNewsResourceDao()
|
|
|
|
topicDao = TestTopicDao()
|
|
|
|
topicDao = TestTopicDao()
|
|
|
|
network = TestNiaNetworkDataSource()
|
|
|
|
network = TestNiaNetworkDataSource()
|
|
|
|
notifier = TestNotifier()
|
|
|
|
notifier = TestNotifier()
|
|
|
|
synchronizer = TestSynchronizer(
|
|
|
|
synchronizer = TestSynchronizer(
|
|
|
|
NiaPreferencesDataSource(
|
|
|
|
niaPreferencesDataSource,
|
|
|
|
tmpFolder.testUserPreferencesDataStore(testScope),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
subject = OfflineFirstNewsRepository(
|
|
|
|
subject = OfflineFirstNewsRepository(
|
|
|
|
|
|
|
|
niaPreferencesDataSource = niaPreferencesDataSource,
|
|
|
|
newsResourceDao = newsResourceDao,
|
|
|
|
newsResourceDao = newsResourceDao,
|
|
|
|
topicDao = topicDao,
|
|
|
|
topicDao = topicDao,
|
|
|
|
network = network,
|
|
|
|
network = network,
|
|
|
@ -130,6 +136,8 @@ class OfflineFirstNewsRepositoryTest {
|
|
|
|
@Test
|
|
|
|
@Test
|
|
|
|
fun offlineFirstNewsRepository_sync_pulls_from_network() =
|
|
|
|
fun offlineFirstNewsRepository_sync_pulls_from_network() =
|
|
|
|
testScope.runTest {
|
|
|
|
testScope.runTest {
|
|
|
|
|
|
|
|
// User has not onboarded
|
|
|
|
|
|
|
|
niaPreferencesDataSource.setShouldHideOnboarding(false)
|
|
|
|
subject.syncWith(synchronizer)
|
|
|
|
subject.syncWith(synchronizer)
|
|
|
|
|
|
|
|
|
|
|
|
val newsResourcesFromNetwork = network.getNewsResources()
|
|
|
|
val newsResourcesFromNetwork = network.getNewsResources()
|
|
|
@ -151,16 +159,16 @@ class OfflineFirstNewsRepositoryTest {
|
|
|
|
actual = synchronizer.getChangeListVersions().newsResourceVersion,
|
|
|
|
actual = synchronizer.getChangeListVersions().newsResourceVersion,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// Notifier should have been called with new news resources
|
|
|
|
// Notifier should not have been called
|
|
|
|
assertEquals(
|
|
|
|
assertTrue(notifier.addedNewsResources.isEmpty())
|
|
|
|
expected = newsResourcesFromDb.map(NewsResource::id).sorted(),
|
|
|
|
|
|
|
|
actual = notifier.addedNewsResources.first().map(NewsResource::id).sorted(),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
@Test
|
|
|
|
fun offlineFirstNewsRepository_sync_deletes_items_marked_deleted_on_network() =
|
|
|
|
fun offlineFirstNewsRepository_sync_deletes_items_marked_deleted_on_network() =
|
|
|
|
testScope.runTest {
|
|
|
|
testScope.runTest {
|
|
|
|
|
|
|
|
// User has not onboarded
|
|
|
|
|
|
|
|
niaPreferencesDataSource.setShouldHideOnboarding(false)
|
|
|
|
|
|
|
|
|
|
|
|
val newsResourcesFromNetwork = network.getNewsResources()
|
|
|
|
val newsResourcesFromNetwork = network.getNewsResources()
|
|
|
|
.map(NetworkNewsResource::asEntity)
|
|
|
|
.map(NetworkNewsResource::asEntity)
|
|
|
|
.map(NewsResourceEntity::asExternalModel)
|
|
|
|
.map(NewsResourceEntity::asExternalModel)
|
|
|
@ -198,17 +206,16 @@ class OfflineFirstNewsRepositoryTest {
|
|
|
|
actual = synchronizer.getChangeListVersions().newsResourceVersion,
|
|
|
|
actual = synchronizer.getChangeListVersions().newsResourceVersion,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// Notifier should have been called with news resources from network that are not
|
|
|
|
// Notifier should not have been called
|
|
|
|
// deleted
|
|
|
|
assertTrue(notifier.addedNewsResources.isEmpty())
|
|
|
|
assertEquals(
|
|
|
|
|
|
|
|
expected = (newsResourcesFromNetwork.map(NewsResource::id) - deletedItems).sorted(),
|
|
|
|
|
|
|
|
actual = notifier.addedNewsResources.first().map(NewsResource::id).sorted(),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
@Test
|
|
|
|
fun offlineFirstNewsRepository_incremental_sync_pulls_from_network() =
|
|
|
|
fun offlineFirstNewsRepository_incremental_sync_pulls_from_network() =
|
|
|
|
testScope.runTest {
|
|
|
|
testScope.runTest {
|
|
|
|
|
|
|
|
// User has not onboarded
|
|
|
|
|
|
|
|
niaPreferencesDataSource.setShouldHideOnboarding(false)
|
|
|
|
|
|
|
|
|
|
|
|
// Set news version to 7
|
|
|
|
// Set news version to 7
|
|
|
|
synchronizer.updateChangeListVersions {
|
|
|
|
synchronizer.updateChangeListVersions {
|
|
|
|
copy(newsResourceVersion = 7)
|
|
|
|
copy(newsResourceVersion = 7)
|
|
|
@ -244,11 +251,8 @@ class OfflineFirstNewsRepositoryTest {
|
|
|
|
actual = synchronizer.getChangeListVersions().newsResourceVersion,
|
|
|
|
actual = synchronizer.getChangeListVersions().newsResourceVersion,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// Notifier should have been called with only added news resources from network
|
|
|
|
// Notifier should not have been called
|
|
|
|
assertEquals(
|
|
|
|
assertTrue(notifier.addedNewsResources.isEmpty())
|
|
|
|
expected = newsResourcesFromNetwork.map(NewsResource::id).sorted(),
|
|
|
|
|
|
|
|
actual = notifier.addedNewsResources.first().map(NewsResource::id).sorted(),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
@Test
|
|
|
@ -283,4 +287,70 @@ class OfflineFirstNewsRepositoryTest {
|
|
|
|
.sortedBy(NewsResourceTopicCrossRef::toString),
|
|
|
|
.sortedBy(NewsResourceTopicCrossRef::toString),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
|
|
fun offlineFirstNewsRepository_sends_notifications_for_newly_synced_news_that_is_followed() =
|
|
|
|
|
|
|
|
testScope.runTest {
|
|
|
|
|
|
|
|
// User has onboarded
|
|
|
|
|
|
|
|
niaPreferencesDataSource.setShouldHideOnboarding(true)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val networkNewsResources = network.getNewsResources()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Follow roughly half the topics
|
|
|
|
|
|
|
|
val followedTopicIds = networkNewsResources
|
|
|
|
|
|
|
|
.flatMap(NetworkNewsResource::topicEntityShells)
|
|
|
|
|
|
|
|
.mapNotNull { topic ->
|
|
|
|
|
|
|
|
when (topic.id.chars().sum() % 2) {
|
|
|
|
|
|
|
|
0 -> topic.id
|
|
|
|
|
|
|
|
else -> null
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
.toSet()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Set followed topics
|
|
|
|
|
|
|
|
niaPreferencesDataSource.setFollowedTopicIds(followedTopicIds)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
subject.syncWith(synchronizer)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val followedNewsResourceIdsFromNetwork = networkNewsResources
|
|
|
|
|
|
|
|
.filter { (it.topics intersect followedTopicIds).isNotEmpty() }
|
|
|
|
|
|
|
|
.map(NetworkNewsResource::id)
|
|
|
|
|
|
|
|
.sorted()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Notifier should have been called with only news resources that have topics
|
|
|
|
|
|
|
|
// that the user follows
|
|
|
|
|
|
|
|
assertEquals(
|
|
|
|
|
|
|
|
expected = followedNewsResourceIdsFromNetwork,
|
|
|
|
|
|
|
|
actual = notifier.addedNewsResources.first().map(NewsResource::id).sorted(),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
|
|
fun offlineFirstNewsRepository_does_not_send_notifications_for_existing_news_resources() =
|
|
|
|
|
|
|
|
testScope.runTest {
|
|
|
|
|
|
|
|
// User has onboarded
|
|
|
|
|
|
|
|
niaPreferencesDataSource.setShouldHideOnboarding(true)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val networkNewsResources = network.getNewsResources()
|
|
|
|
|
|
|
|
.map(NetworkNewsResource::asEntity)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val newsResources = networkNewsResources
|
|
|
|
|
|
|
|
.map(NewsResourceEntity::asExternalModel)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Prepopulate dao with news resources
|
|
|
|
|
|
|
|
newsResourceDao.upsertNewsResources(networkNewsResources)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val followedTopicIds = newsResources
|
|
|
|
|
|
|
|
.flatMap(NewsResource::topics)
|
|
|
|
|
|
|
|
.map(Topic::id)
|
|
|
|
|
|
|
|
.toSet()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Follow all topics
|
|
|
|
|
|
|
|
niaPreferencesDataSource.setFollowedTopicIds(followedTopicIds)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
subject.syncWith(synchronizer)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Notifier should not have been called bc all news resources existed previously
|
|
|
|
|
|
|
|
assertTrue(notifier.addedNewsResources.isEmpty())
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|