Skip to content

Commit

Permalink
test: Failing test to reproduce #62
Browse files Browse the repository at this point in the history
  • Loading branch information
Joseph Cooper committed Mar 10, 2023
1 parent 608a764 commit 537b985
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 53 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.omricat.maplibrarian.firebase

object FirebaseEmulatorConnection {

// Connects to host computer from Android emulator
const val HOST = "10.0.2.2"

const val AUTH_PORT = 9099

const val FIRESTORE_PORT = 8080

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package com.omricat.maplibrarian.firebase

import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.getOrThrow
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.firestore.FirebaseFirestore
import com.omricat.maplibrarian.auth.EmailPasswordCredential
import com.omricat.maplibrarian.chartlist.FirebaseChartsService
import com.omricat.maplibrarian.firebase.auth.FirebaseAuthEmulatorRestApi
import com.omricat.maplibrarian.firebase.auth.FirebaseAuthService
import com.omricat.maplibrarian.firebase.charts.FirebaseFirestoreRestApi
import com.omricat.maplibrarian.model.DbChartModel
import com.omricat.maplibrarian.model.UnsavedChartModel
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.BeforeClass
import org.junit.Test

@OptIn(ExperimentalCoroutinesApi::class)
class FirebaseEndToEndTest {

@Before
fun clearData() {
authApi.deleteAllUsers()
firestoreApi.deleteAllData()
}

private val testCredential = EmailPasswordCredential("[email protected]", "password")

@Test
fun addUser_addCharts_queryCharts() = runTest {
val testDispatcherProvider = TestDispatcherProvider(testScheduler)
val userRepository = FirebaseAuthService(firebaseAuthInstance, testDispatcherProvider)

val chartsRepository = FirebaseChartsService(firestoreInstance, testDispatcherProvider)

val createUserResult = userRepository.createUser(testCredential)

val user = createUserResult.getOrThrow { AssertionError(it) }

val addChartResult =
chartsRepository.addNewChart(user, UnsavedChartModel(user.id, "New map"))

assert(addChartResult is Ok<DbChartModel>)

val queryChartResult =
chartsRepository.chartsListForUser(user)

queryChartResult.getOrThrow { AssertionError(it) }.run {
assert(size == 1)
assert(first().title == "New map")
}
}

companion object Fixtures {

@JvmStatic lateinit var firestoreInstance: FirebaseFirestore

@JvmStatic lateinit var firestoreApi: FirebaseFirestoreRestApi

@JvmStatic lateinit var firebaseAuthInstance: FirebaseAuth

@JvmStatic lateinit var authApi: FirebaseAuthEmulatorRestApi

@JvmStatic
@BeforeClass
fun setup() {
firestoreInstance =
FirebaseFirestore.getInstance(TestFixtures.app).apply {
useEmulator(
FirebaseEmulatorConnection.HOST,
FirebaseEmulatorConnection.FIRESTORE_PORT
)
}

firestoreApi =
FirebaseFirestoreRestApi(
TestFixtures.projectId,
TestFixtures.emulatorBaseUrl(FirebaseEmulatorConnection.FIRESTORE_PORT)
)

firebaseAuthInstance =
FirebaseAuth.getInstance(TestFixtures.app).apply {
useEmulator(
FirebaseEmulatorConnection.HOST,
FirebaseEmulatorConnection.AUTH_PORT
)
}
authApi =
FirebaseAuthEmulatorRestApi(
TestFixtures.projectId,
TestFixtures.emulatorBaseUrl(FirebaseEmulatorConnection.AUTH_PORT)
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.omricat.maplibrarian.firebase

import com.omricat.maplibrarian.utils.DispatcherProvider
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestCoroutineScheduler

@OptIn(ExperimentalCoroutinesApi::class)
class TestDispatcherProvider(scheduler: TestCoroutineScheduler) : DispatcherProvider {
private val dispatcher = StandardTestDispatcher(scheduler)
override val default: CoroutineDispatcher
get() = dispatcher
override val io: CoroutineDispatcher
get() = dispatcher
override val main: CoroutineDispatcher
get() = dispatcher
override val unconfined: CoroutineDispatcher
get() = dispatcher
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.omricat.maplibrarian.firebase

import androidx.test.core.app.ApplicationProvider
import com.google.firebase.FirebaseApp
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Builder

object TestFixtures {
val app: FirebaseApp by lazy {
FirebaseApp.initializeApp(ApplicationProvider.getApplicationContext())
?: error("Failed to initialize FirebaseApp")
}

val projectId: String
get() = app.options.projectId ?: error("Can't get projectId from FirebaseApp options")

fun emulatorBaseUrl(port: Int): HttpUrl =
Builder().host(FirebaseEmulatorConnection.HOST).port(port).scheme("http").build()
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,21 @@ package com.omricat.maplibrarian.firebase.auth

import okhttp3.HttpUrl
import retrofit2.Call
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.create
import retrofit2.http.DELETE
import retrofit2.http.Path

class FirebaseAuthEmulatorRestApi(private val projectId: String, baseUrl: HttpUrl) {

private interface RetrofitEmulatorApi {
private interface AuthEmulatorApi {

@DELETE("/emulator/v1/projects/{project-id}/accounts")
fun deleteAllUsers(@Path("project-id") projectId: String): Call<Unit>
}

private val wrappedApi: RetrofitEmulatorApi =
private val wrappedApi: AuthEmulatorApi =
Retrofit.Builder().baseUrl(baseUrl).build().create()
fun deleteAllUsers() {
wrappedApi.deleteAllUsers(projectId).execute()
}
fun deleteAllUsers(): Response<Unit> = wrappedApi.deleteAllUsers(projectId).execute()
}
Original file line number Diff line number Diff line change
@@ -1,85 +1,57 @@
package com.omricat.maplibrarian.firebase.auth

import androidx.test.core.app.ApplicationProvider
import com.github.michaelbull.result.Ok
import com.google.firebase.FirebaseApp
import com.google.firebase.auth.FirebaseAuth
import com.omricat.maplibrarian.auth.EmailPasswordCredential
import com.omricat.maplibrarian.firebase.FirebaseEmulatorConnection
import com.omricat.maplibrarian.firebase.TestDispatcherProvider
import com.omricat.maplibrarian.firebase.TestFixtures
import com.omricat.maplibrarian.model.User
import com.omricat.maplibrarian.utils.DispatcherProvider
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestCoroutineScheduler
import kotlinx.coroutines.test.runTest
import okhttp3.HttpUrl
import org.junit.Before
import org.junit.BeforeClass
import org.junit.Test

// Connects to host computer from Android emulator
private const val FIREBASE_EMULATOR_HOST = "10.0.2.2"

private const val FIREBASE_EMULATOR_AUTH_PORT = 9099

@OptIn(ExperimentalCoroutinesApi::class)
class FirebaseAuthServiceTest {

@Before
fun clearUsers() {
emulatorAPI.deleteAllUsers()
authApi.deleteAllUsers()
}

@Test
fun addUserSucceeds() {
val credential = EmailPasswordCredential("[email protected]", "password")
private val testCredential = EmailPasswordCredential("[email protected]", "password")

runTest {
val repository =
FirebaseAuthService(firebaseAuthInstance, TestDispatcherProvider(testScheduler))
val createUserResult = repository.createUser(credential)
assert(createUserResult is Ok<User>)
}
@Test
fun addUserSucceeds() = runTest {
val repository =
FirebaseAuthService(firebaseAuthInstance, TestDispatcherProvider(testScheduler))
val createUserResult = repository.createUser(testCredential)
assert(createUserResult is Ok<User>)
}

companion object Fixtures {

@JvmStatic lateinit var firebaseAuthInstance: FirebaseAuth

@JvmStatic lateinit var emulatorAPI: FirebaseAuthEmulatorRestApi
@JvmStatic lateinit var authApi: FirebaseAuthEmulatorRestApi

@JvmStatic
@BeforeClass
fun setUp() {
val app: FirebaseApp =
FirebaseApp.initializeApp(ApplicationProvider.getApplicationContext())
?: error("Failed to initialize FirebaseApp")
firebaseAuthInstance =
FirebaseAuth.getInstance(app).apply {
useEmulator(FIREBASE_EMULATOR_HOST, FIREBASE_EMULATOR_AUTH_PORT)
FirebaseAuth.getInstance(TestFixtures.app).apply {
useEmulator(
FirebaseEmulatorConnection.HOST,
FirebaseEmulatorConnection.AUTH_PORT
)
}
emulatorAPI =
authApi =
FirebaseAuthEmulatorRestApi(
app.options.projectId!!,
HttpUrl.Builder()
.host(FIREBASE_EMULATOR_HOST)
.port(FIREBASE_EMULATOR_AUTH_PORT)
.scheme("http")
.build()
TestFixtures.projectId,
TestFixtures.emulatorBaseUrl(FirebaseEmulatorConnection.AUTH_PORT)
)
}
}
}

@OptIn(ExperimentalCoroutinesApi::class)
class TestDispatcherProvider(scheduler: TestCoroutineScheduler) : DispatcherProvider {
private val dispatcher = StandardTestDispatcher(scheduler)
override val default: CoroutineDispatcher
get() = dispatcher
override val io: CoroutineDispatcher
get() = dispatcher
override val main: CoroutineDispatcher
get() = dispatcher
override val unconfined: CoroutineDispatcher
get() = dispatcher
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.omricat.maplibrarian.firebase.charts

import okhttp3.HttpUrl
import retrofit2.Call
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.create
import retrofit2.http.DELETE
import retrofit2.http.Path

class FirebaseFirestoreRestApi(private val projectId: String, baseUrl: HttpUrl) {

private interface FirestoreEmulatorApi {

@DELETE("/emulator/v1/projects/{project-id}/databases/(default)/documents")
fun deleteDefaultDatabase(@Path("project-id") projectId: String): Call<Unit>
}

private val wrappedApi: FirestoreEmulatorApi =
Retrofit.Builder().baseUrl(baseUrl).build().create()

fun deleteAllData(): Response<Unit> = wrappedApi.deleteDefaultDatabase(projectId).execute()
}

0 comments on commit 537b985

Please sign in to comment.