Add loadRecipeInfo tests

This commit is contained in:
Kirill Kamakin
2021-11-20 00:43:00 +03:00
parent 2f825361cd
commit dd33004c6f
12 changed files with 250 additions and 60 deletions

View File

@@ -2,12 +2,13 @@ package gq.kirmanak.mealie.data.auth.impl
import com.google.common.truth.Truth.assertThat
import dagger.hilt.android.testing.HiltAndroidTest
import gq.kirmanak.mealie.data.auth.impl.AuthImplTestData.TEST_PASSWORD
import gq.kirmanak.mealie.data.auth.impl.AuthImplTestData.TEST_TOKEN
import gq.kirmanak.mealie.data.auth.impl.AuthImplTestData.TEST_USERNAME
import gq.kirmanak.mealie.data.auth.impl.AuthImplTestData.body
import gq.kirmanak.mealie.data.auth.impl.AuthImplTestData.enqueueSuccessfulAuthResponse
import gq.kirmanak.mealie.data.auth.impl.AuthImplTestData.enqueueUnsuccessfulAuthResponse
import gq.kirmanak.mealie.data.test.AuthImplTestData.TEST_PASSWORD
import gq.kirmanak.mealie.data.test.AuthImplTestData.TEST_TOKEN
import gq.kirmanak.mealie.data.test.AuthImplTestData.TEST_USERNAME
import gq.kirmanak.mealie.data.test.AuthImplTestData.body
import gq.kirmanak.mealie.data.test.AuthImplTestData.enqueueSuccessfulAuthResponse
import gq.kirmanak.mealie.data.test.AuthImplTestData.enqueueUnsuccessfulAuthResponse
import gq.kirmanak.mealie.data.test.MockServerTest
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.ExperimentalSerializationApi

View File

@@ -2,11 +2,12 @@ package gq.kirmanak.mealie.data.auth.impl
import com.google.common.truth.Truth.assertThat
import dagger.hilt.android.testing.HiltAndroidTest
import gq.kirmanak.mealie.data.auth.impl.AuthImplTestData.TEST_PASSWORD
import gq.kirmanak.mealie.data.auth.impl.AuthImplTestData.TEST_TOKEN
import gq.kirmanak.mealie.data.auth.impl.AuthImplTestData.TEST_USERNAME
import gq.kirmanak.mealie.data.auth.impl.AuthImplTestData.enqueueSuccessfulAuthResponse
import gq.kirmanak.mealie.data.auth.impl.AuthImplTestData.enqueueUnsuccessfulAuthResponse
import gq.kirmanak.mealie.data.test.AuthImplTestData.TEST_PASSWORD
import gq.kirmanak.mealie.data.test.AuthImplTestData.TEST_TOKEN
import gq.kirmanak.mealie.data.test.AuthImplTestData.TEST_USERNAME
import gq.kirmanak.mealie.data.test.AuthImplTestData.enqueueSuccessfulAuthResponse
import gq.kirmanak.mealie.data.test.AuthImplTestData.enqueueUnsuccessfulAuthResponse
import gq.kirmanak.mealie.data.test.MockServerTest
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.Test

View File

@@ -2,8 +2,9 @@ package gq.kirmanak.mealie.data.auth.impl
import com.google.common.truth.Truth.assertThat
import dagger.hilt.android.testing.HiltAndroidTest
import gq.kirmanak.mealie.data.auth.impl.AuthImplTestData.TEST_TOKEN
import gq.kirmanak.mealie.data.auth.impl.AuthImplTestData.TEST_URL
import gq.kirmanak.mealie.data.test.AuthImplTestData.TEST_TOKEN
import gq.kirmanak.mealie.data.test.AuthImplTestData.TEST_URL
import gq.kirmanak.mealie.data.test.HiltRobolectricTest
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking

View File

@@ -3,8 +3,8 @@ package gq.kirmanak.mealie.data.impl
import com.google.common.truth.Truth.assertThat
import dagger.hilt.android.testing.HiltAndroidTest
import gq.kirmanak.mealie.data.auth.impl.AUTHORIZATION_HEADER
import gq.kirmanak.mealie.data.auth.impl.AuthImplTestData.TEST_TOKEN
import gq.kirmanak.mealie.data.auth.impl.MockServerTest
import gq.kirmanak.mealie.data.test.AuthImplTestData.TEST_TOKEN
import gq.kirmanak.mealie.data.test.MockServerTest
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.mockwebserver.MockResponse

View File

@@ -3,24 +3,24 @@ package gq.kirmanak.mealie.data.recipes.db
import com.google.common.truth.Truth.assertThat
import dagger.hilt.android.testing.HiltAndroidTest
import gq.kirmanak.mealie.data.MealieDb
import gq.kirmanak.mealie.data.auth.impl.HiltRobolectricTest
import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.BREAD_INGREDIENT
import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.CAKE_BREAD_RECIPE_INGREDIENT_ENTITY
import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.CAKE_RECIPE_SUMMARY_ENTITY
import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.FULL_CAKE_INFO_ENTITY
import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.FULL_PORRIDGE_INFO_ENTITY
import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.GET_CAKE_RESPONSE
import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.GET_PORRIDGE_RESPONSE
import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.MIX_CAKE_RECIPE_INSTRUCTION_ENTITY
import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.MIX_INSTRUCTION
import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.PORRIDGE_RECIPE_SUMMARY_ENTITY
import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.RECIPE_SUMMARY_CAKE
import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.RECIPE_SUMMARY_PORRIDGE
import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.TEST_RECIPE_SUMMARIES
import gq.kirmanak.mealie.data.recipes.db.entity.CategoryEntity
import gq.kirmanak.mealie.data.recipes.db.entity.CategoryRecipeEntity
import gq.kirmanak.mealie.data.recipes.db.entity.TagEntity
import gq.kirmanak.mealie.data.recipes.db.entity.TagRecipeEntity
import gq.kirmanak.mealie.data.test.HiltRobolectricTest
import gq.kirmanak.mealie.data.test.RecipeImplTestData.BREAD_INGREDIENT
import gq.kirmanak.mealie.data.test.RecipeImplTestData.CAKE_BREAD_RECIPE_INGREDIENT_ENTITY
import gq.kirmanak.mealie.data.test.RecipeImplTestData.CAKE_RECIPE_SUMMARY_ENTITY
import gq.kirmanak.mealie.data.test.RecipeImplTestData.FULL_CAKE_INFO_ENTITY
import gq.kirmanak.mealie.data.test.RecipeImplTestData.FULL_PORRIDGE_INFO_ENTITY
import gq.kirmanak.mealie.data.test.RecipeImplTestData.GET_CAKE_RESPONSE
import gq.kirmanak.mealie.data.test.RecipeImplTestData.GET_PORRIDGE_RESPONSE
import gq.kirmanak.mealie.data.test.RecipeImplTestData.MIX_CAKE_RECIPE_INSTRUCTION_ENTITY
import gq.kirmanak.mealie.data.test.RecipeImplTestData.MIX_INSTRUCTION
import gq.kirmanak.mealie.data.test.RecipeImplTestData.PORRIDGE_RECIPE_SUMMARY_ENTITY
import gq.kirmanak.mealie.data.test.RecipeImplTestData.RECIPE_SUMMARY_CAKE
import gq.kirmanak.mealie.data.test.RecipeImplTestData.RECIPE_SUMMARY_PORRIDGE
import gq.kirmanak.mealie.data.test.RecipeImplTestData.TEST_RECIPE_SUMMARIES
import kotlinx.coroutines.runBlocking
import org.junit.Test
import javax.inject.Inject

View File

@@ -0,0 +1,54 @@
package gq.kirmanak.mealie.data.recipes.impl
import com.google.common.truth.Truth.assertThat
import dagger.hilt.android.testing.HiltAndroidTest
import gq.kirmanak.mealie.data.MealieDb
import gq.kirmanak.mealie.data.recipes.RecipeRepo
import gq.kirmanak.mealie.data.recipes.db.RecipeStorage
import gq.kirmanak.mealie.data.test.MockServerWithAuthTest
import gq.kirmanak.mealie.data.test.RecipeImplTestData.FULL_CAKE_INFO_ENTITY
import gq.kirmanak.mealie.data.test.RecipeImplTestData.RECIPE_SUMMARY_CAKE
import gq.kirmanak.mealie.data.test.RecipeImplTestData.enqueueSuccessfulGetRecipe
import gq.kirmanak.mealie.data.test.RecipeImplTestData.enqueueUnsuccessfulRecipeResponse
import kotlinx.coroutines.runBlocking
import org.junit.Test
import javax.inject.Inject
@HiltAndroidTest
class RecipeRepoImplTest : MockServerWithAuthTest() {
@Inject
lateinit var subject: RecipeRepo
@Inject
lateinit var storage: RecipeStorage
@Inject
lateinit var mealieDb: MealieDb
@Test
fun `when loadRecipeInfo then loads recipe`(): Unit = runBlocking {
storage.saveRecipes(listOf(RECIPE_SUMMARY_CAKE))
mockServer.enqueueSuccessfulGetRecipe()
val actual = subject.loadRecipeInfo(1, "cake")
assertThat(actual).isEqualTo(FULL_CAKE_INFO_ENTITY)
}
@Test
fun `when loadRecipeInfo then saves to DB`(): Unit = runBlocking {
storage.saveRecipes(listOf(RECIPE_SUMMARY_CAKE))
mockServer.enqueueSuccessfulGetRecipe()
subject.loadRecipeInfo(1, "cake")
val actual = mealieDb.recipeDao().queryFullRecipeInfo(1)
assertThat(actual).isEqualTo(FULL_CAKE_INFO_ENTITY)
}
@Test
fun `when loadRecipeInfo with error then loads from DB`(): Unit = runBlocking {
storage.saveRecipes(listOf(RECIPE_SUMMARY_CAKE))
mockServer.enqueueSuccessfulGetRecipe()
subject.loadRecipeInfo(1, "cake")
mockServer.enqueueUnsuccessfulRecipeResponse()
val actual = subject.loadRecipeInfo(1, "cake")
assertThat(actual).isEqualTo(FULL_CAKE_INFO_ENTITY)
}
}

View File

@@ -5,25 +5,20 @@ import androidx.paging.LoadType.*
import com.google.common.truth.Truth.assertThat
import dagger.hilt.android.testing.HiltAndroidTest
import gq.kirmanak.mealie.data.MealieDb
import gq.kirmanak.mealie.data.auth.AuthRepo
import gq.kirmanak.mealie.data.auth.impl.AuthImplTestData.TEST_PASSWORD
import gq.kirmanak.mealie.data.auth.impl.AuthImplTestData.TEST_USERNAME
import gq.kirmanak.mealie.data.auth.impl.AuthImplTestData.enqueueSuccessfulAuthResponse
import gq.kirmanak.mealie.data.auth.impl.MockServerTest
import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.CAKE_RECIPE_SUMMARY_ENTITY
import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.PORRIDGE_RECIPE_SUMMARY_ENTITY
import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.TEST_RECIPE_ENTITIES
import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.enqueueSuccessfulRecipeSummaryResponse
import gq.kirmanak.mealie.data.recipes.RecipeImplTestData.enqueueUnsuccessfulRecipeSummaryResponse
import gq.kirmanak.mealie.data.recipes.db.entity.RecipeSummaryEntity
import gq.kirmanak.mealie.data.test.MockServerWithAuthTest
import gq.kirmanak.mealie.data.test.RecipeImplTestData.CAKE_RECIPE_SUMMARY_ENTITY
import gq.kirmanak.mealie.data.test.RecipeImplTestData.PORRIDGE_RECIPE_SUMMARY_ENTITY
import gq.kirmanak.mealie.data.test.RecipeImplTestData.TEST_RECIPE_ENTITIES
import gq.kirmanak.mealie.data.test.RecipeImplTestData.enqueueSuccessfulRecipeSummaryResponse
import gq.kirmanak.mealie.data.test.RecipeImplTestData.enqueueUnsuccessfulRecipeResponse
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Test
import javax.inject.Inject
@ExperimentalPagingApi
@HiltAndroidTest
class RecipesRemoteMediatorTest : MockServerTest() {
class RecipesRemoteMediatorTest : MockServerWithAuthTest() {
private val pagingConfig = PagingConfig(
pageSize = 2,
prefetchDistance = 5,
@@ -33,19 +28,9 @@ class RecipesRemoteMediatorTest : MockServerTest() {
@Inject
lateinit var subject: RecipesRemoteMediator
@Inject
lateinit var authRepo: AuthRepo
@Inject
lateinit var mealieDb: MealieDb
@Before
fun authenticate(): Unit = runBlocking {
mockServer.enqueueSuccessfulAuthResponse()
authRepo.authenticate(TEST_USERNAME, TEST_PASSWORD, serverUrl)
mockServer.takeRequest()
}
@Test
fun `when first load with refresh successful then result success`(): Unit = runBlocking {
mockServer.enqueueSuccessfulRecipeSummaryResponse()
@@ -86,7 +71,7 @@ class RecipesRemoteMediatorTest : MockServerTest() {
@Test
fun `when load fails then lastRequestEnd still 0`(): Unit = runBlocking {
mockServer.enqueueUnsuccessfulRecipeSummaryResponse()
mockServer.enqueueUnsuccessfulRecipeResponse()
subject.load(REFRESH, pagingState())
val actual = subject.lastRequestEnd
assertThat(actual).isEqualTo(0)
@@ -94,14 +79,14 @@ class RecipesRemoteMediatorTest : MockServerTest() {
@Test
fun `when load fails then result is error`(): Unit = runBlocking {
mockServer.enqueueUnsuccessfulRecipeSummaryResponse()
mockServer.enqueueUnsuccessfulRecipeResponse()
val actual = subject.load(REFRESH, pagingState())
assertThat(actual).isInstanceOf(RemoteMediator.MediatorResult.Error::class.java)
}
@Test
fun `when refresh then request params correct`(): Unit = runBlocking {
mockServer.enqueueUnsuccessfulRecipeSummaryResponse()
mockServer.enqueueUnsuccessfulRecipeResponse()
subject.load(REFRESH, pagingState())
val actual = mockServer.takeRequest().path
assertThat(actual).isEqualTo("/api/recipes/summary?start=0&limit=6")
@@ -123,7 +108,7 @@ class RecipesRemoteMediatorTest : MockServerTest() {
mockServer.enqueueSuccessfulRecipeSummaryResponse()
subject.load(REFRESH, pagingState())
mockServer.takeRequest()
mockServer.enqueueUnsuccessfulRecipeSummaryResponse()
mockServer.enqueueUnsuccessfulRecipeResponse()
subject.load(APPEND, pagingState())
val actual = mealieDb.recipeDao().queryAllRecipes()
assertThat(actual).isEqualTo(TEST_RECIPE_ENTITIES)

View File

@@ -1,4 +1,4 @@
package gq.kirmanak.mealie.data.auth.impl
package gq.kirmanak.mealie.data.test
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer

View File

@@ -1,16 +1,31 @@
package gq.kirmanak.mealie.data.auth.impl
package gq.kirmanak.mealie.data.test
import androidx.test.ext.junit.runners.AndroidJUnit4
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltTestApplication
import org.junit.Before
import org.junit.BeforeClass
import org.junit.Rule
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import timber.log.Timber
@RunWith(AndroidJUnit4::class)
@Config(application = HiltTestApplication::class, manifest = Config.NONE)
abstract class HiltRobolectricTest {
companion object {
@BeforeClass
@JvmStatic
fun setupTimber() {
Timber.plant(object : Timber.Tree() {
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
println(message)
t?.printStackTrace()
}
})
}
}
@get:Rule
var hiltRule = HiltAndroidRule(this)

View File

@@ -1,4 +1,4 @@
package gq.kirmanak.mealie.data.auth.impl
package gq.kirmanak.mealie.data.test
import okhttp3.mockwebserver.MockWebServer
import org.junit.After

View File

@@ -0,0 +1,21 @@
package gq.kirmanak.mealie.data.test
import gq.kirmanak.mealie.data.auth.AuthRepo
import gq.kirmanak.mealie.data.test.AuthImplTestData.TEST_PASSWORD
import gq.kirmanak.mealie.data.test.AuthImplTestData.TEST_USERNAME
import gq.kirmanak.mealie.data.test.AuthImplTestData.enqueueSuccessfulAuthResponse
import kotlinx.coroutines.runBlocking
import org.junit.Before
import javax.inject.Inject
abstract class MockServerWithAuthTest : MockServerTest() {
@Inject
lateinit var authRepo: AuthRepo
@Before
fun authenticate(): Unit = runBlocking {
mockServer.enqueueSuccessfulAuthResponse()
authRepo.authenticate(TEST_USERNAME, TEST_PASSWORD, serverUrl)
mockServer.takeRequest()
}
}

View File

@@ -1,4 +1,4 @@
package gq.kirmanak.mealie.data.recipes
package gq.kirmanak.mealie.data.test
import gq.kirmanak.mealie.data.recipes.db.entity.RecipeEntity
import gq.kirmanak.mealie.data.recipes.db.entity.RecipeIngredientEntity
@@ -106,7 +106,7 @@ object RecipeImplTestData {
enqueue(response)
}
fun MockWebServer.enqueueUnsuccessfulRecipeSummaryResponse() {
fun MockWebServer.enqueueUnsuccessfulRecipeResponse() {
val response = MockResponse()
.setBody(RECIPE_SUMMARY_UNSUCCESSFUL)
.setHeader("Content-Type", "application/json")
@@ -172,6 +172,110 @@ object RecipeImplTestData {
recipeInstructions = listOf(MIX_INSTRUCTION, BAKE_INSTRUCTION)
)
val GET_CAKE_RESPONSE_BODY = """
{
"id": 1,
"name": "Cake",
"slug": "cake",
"image": "86",
"description": "A tasty cake",
"recipeCategory": ["dessert", "tasty"],
"tags": ["gluten", "allergic"],
"rating": 4,
"dateAdded": "2021-11-13",
"dateUpdated": "2021-11-13T15:30:13",
"recipeYield": "4 servings",
"recipeIngredient": [
{
"title": "Sugar",
"note": "2 oz of white sugar",
"unit": null,
"food": null,
"disableAmount": true,
"quantity": 1
},
{
"title": "Bread",
"note": "2 oz of white bread",
"unit": null,
"food": null,
"disableAmount": false,
"quantity": 2
}
],
"recipeInstructions": [
{
"title": "Mix",
"text": "Mix the ingredients"
},
{
"title": "Bake",
"text": "Bake the ingredients"
}
],
"nutrition": {
"calories": "100",
"fatContent": "20",
"proteinContent": "30",
"carbohydrateContent": "40",
"fiberContent": "50",
"sodiumContent": "23",
"sugarContent": "53"
},
"tools": [],
"totalTime": "12 hours",
"prepTime": "1 hour",
"performTime": "4 hours",
"settings": {
"public": true,
"showNutrition": true,
"showAssets": true,
"landscapeView": true,
"disableComments": false,
"disableAmount": false
},
"assets": [],
"notes": [
{
"title": "Note title",
"text": "Note text"
},
{
"title": "Second note",
"text": "Second note text"
}
],
"orgURL": null,
"extras": {},
"comments": [
{
"text": "A new comment",
"id": 1,
"uuid": "476ebc15-f794-4eda-8380-d77bba47f839",
"recipeSlug": "test-recipe",
"dateAdded": "2021-11-19T22:13:23.862459",
"user": {
"id": 1,
"username": "kirmanak",
"admin": true
}
},
{
"text": "A second comment",
"id": 2,
"uuid": "20498eba-9639-4acd-ba0a-4829ee06915a",
"recipeSlug": "test-recipe",
"dateAdded": "2021-11-19T22:13:29.912314",
"user": {
"id": 1,
"username": "kirmanak",
"admin": true
}
}
]
}
""".trimIndent()
val GET_PORRIDGE_RESPONSE = GetRecipeResponse(
remoteId = 2,
name = "Porridge",
@@ -295,4 +399,12 @@ object RecipeImplTestData {
PORRIDGE_BOIL_RECIPE_INSTRUCTION_ENTITY,
)
)
fun MockWebServer.enqueueSuccessfulGetRecipe() {
val response = MockResponse()
.setResponseCode(200)
.setHeader("Content-Type", "application/json")
.setBody(GET_CAKE_RESPONSE_BODY)
enqueue(response)
}
}