From 8d18183bc0a7d301233812fac131d17570c625ec Mon Sep 17 00:00:00 2001 From: Adetunji Dahunsi Date: Sun, 27 Mar 2022 11:54:28 -0400 Subject: [PATCH] Sort news resources by descending publish date, add NewsResourceDao tests Change-Id: Ib783ed1f4e0ce83c5add82e561f50f89de4faac9 --- core-database/build.gradle | 13 ++ .../core/database/dao/NewsResourceDaoTest.kt | 184 ++++++++++++++++++ .../core/database/dao/NewsResourceDao.kt | 18 +- 3 files changed, 206 insertions(+), 9 deletions(-) create mode 100644 core-database/src/androidTest/java/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDaoTest.kt diff --git a/core-database/build.gradle b/core-database/build.gradle index b602f3527..9847803ba 100644 --- a/core-database/build.gradle +++ b/core-database/build.gradle @@ -34,6 +34,8 @@ android { ksp { arg("room.schemaLocation", "$projectDir/schemas") } + + testInstrumentationRunner "com.google.samples.apps.nowinandroid.core.testing.NiaTestRunner" } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -56,4 +58,15 @@ dependencies { implementation libs.hilt.android kapt libs.hilt.compiler + + androidTestImplementation project(':core-testing') + + // androidx.test is forcing JUnit, 4.12. This forces it to use 4.13 + configurations.configureEach { + resolutionStrategy { + force libs.junit4 + // Temporary workaround for https://issuetracker.google.com/174733673 + force 'org.objenesis:objenesis:2.6' + } + } } \ No newline at end of file diff --git a/core-database/src/androidTest/java/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDaoTest.kt b/core-database/src/androidTest/java/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDaoTest.kt new file mode 100644 index 000000000..2fb5c7b3c --- /dev/null +++ b/core-database/src/androidTest/java/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDaoTest.kt @@ -0,0 +1,184 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.nowinandroid.core.database.dao + +import android.content.Context +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import com.google.samples.apps.nowinandroid.core.database.NiADatabase +import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceEntity +import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceTopicCrossRef +import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity +import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel +import com.google.samples.apps.nowinandroid.core.database.model.episodeEntityShell +import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.test.runTest +import kotlinx.datetime.Instant +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test + +class NewsResourceDaoTest { + + private lateinit var newsResourceDao: NewsResourceDao + private lateinit var episodeDao: EpisodeDao + private lateinit var topicDao: TopicDao + private lateinit var db: NiADatabase + + @Before + fun createDb() { + val context = ApplicationProvider.getApplicationContext() + db = Room.inMemoryDatabaseBuilder( + context, + NiADatabase::class.java + ).build() + newsResourceDao = db.newsResourceDao() + episodeDao = db.episodeDao() + topicDao = db.topicDao() + } + + @Test + fun newsResourceDao_fetches_items_by_descending_publish_date() = runTest { + val newsResourceEntities = listOf( + testNewsResource( + id = 0, + millisSinceEpoch = 0, + ), + testNewsResource( + id = 1, + millisSinceEpoch = 3, + ), + testNewsResource( + id = 2, + millisSinceEpoch = 1, + ), + testNewsResource( + id = 3, + millisSinceEpoch = 2, + ), + ) + val episodeEntityShells = newsResourceEntities + .map(NewsResourceEntity::episodeEntityShell) + .distinct() + + episodeDao.saveEpisodeEntities( + episodeEntityShells + ) + newsResourceDao.saveNewsResourceEntities( + newsResourceEntities + ) + + val savedNewsResourceEntities = newsResourceDao.getNewsResourcesStream() + .first() + + assertEquals( + listOf(3L, 2L, 1L, 0L), + savedNewsResourceEntities.map { + it.asExternalModel().publishDate.toEpochMilliseconds() + } + ) + } + + @Test + fun newsResourceDao_filters_items_by_topic_ids_by_descending_publish_date() = runTest { + val topicEntities = listOf( + testTopicEntity( + id = 1, + name = "1" + ), + testTopicEntity( + id = 2, + name = "2" + ), + ) + val newsResourceEntities = listOf( + testNewsResource( + id = 0, + millisSinceEpoch = 0, + ), + testNewsResource( + id = 1, + millisSinceEpoch = 3, + ), + testNewsResource( + id = 2, + millisSinceEpoch = 1, + ), + testNewsResource( + id = 3, + millisSinceEpoch = 2, + ), + ) + val episodeEntityShells = newsResourceEntities + .map(NewsResourceEntity::episodeEntityShell) + .distinct() + val newsResourceTopicCrossRefEntities = topicEntities.mapIndexed { index, topicEntity -> + NewsResourceTopicCrossRef( + newsResourceId = index, + topicId = topicEntity.id + ) + } + + topicDao.saveTopics( + topicEntities + ) + episodeDao.saveEpisodeEntities( + episodeEntityShells + ) + newsResourceDao.saveNewsResourceEntities( + newsResourceEntities + ) + newsResourceDao.saveTopicCrossRefEntities( + newsResourceTopicCrossRefEntities + ) + + val filteredNewsResources = newsResourceDao.getNewsResourcesStream( + filterTopicIds = topicEntities + .map(TopicEntity::id) + .toSet() + ).first() + + assertEquals( + listOf(1, 0), + filteredNewsResources.map { it.entity.id } + ) + } +} + +private fun testTopicEntity( + id: Int = 0, + name: String +) = TopicEntity( + id = id, + name = name, + description = "", +) + +private fun testNewsResource( + id: Int = 0, + millisSinceEpoch: Long = 0 +) = NewsResourceEntity( + id = id, + episodeId = 0, + title = "", + content = "", + url = "", + headerImageUrl = "", + publishDate = Instant.fromEpochMilliseconds(millisSinceEpoch), + type = NewsResourceType.DAC, +) diff --git a/core-database/src/main/java/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDao.kt b/core-database/src/main/java/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDao.kt index 1bea8c6cc..0d5fbe98e 100644 --- a/core-database/src/main/java/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDao.kt +++ b/core-database/src/main/java/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDao.kt @@ -33,21 +33,21 @@ import kotlinx.coroutines.flow.Flow interface NewsResourceDao { @Query( value = """ - SELECT * FROM news_resources - ORDER BY publish_date + SELECT * FROM news_resources + ORDER BY publish_date DESC """ ) fun getNewsResourcesStream(): Flow> @Query( value = """ - SELECT * FROM news_resources - WHERE id in - ( - SELECT news_resource_id FROM news_resources_topics - WHERE topic_id IN (:filterTopicIds) - ) - ORDER BY publish_date + SELECT * FROM news_resources + WHERE id in + ( + SELECT news_resource_id FROM news_resources_topics + WHERE topic_id IN (:filterTopicIds) + ) + ORDER BY publish_date DESC """ ) fun getNewsResourcesStream(filterTopicIds: Set): Flow>