Create API token when signing in
This commit is contained in:
@@ -5,4 +5,6 @@ interface AuthDataSource {
|
||||
* Tries to acquire authentication token using the provided credentials
|
||||
*/
|
||||
suspend fun authenticate(username: String, password: String): String
|
||||
|
||||
suspend fun createApiToken(name: String): String
|
||||
}
|
||||
@@ -10,9 +10,5 @@ interface AuthRepo {
|
||||
|
||||
suspend fun getAuthHeader(): String?
|
||||
|
||||
suspend fun requireAuthHeader(): String
|
||||
|
||||
suspend fun logout()
|
||||
|
||||
suspend fun invalidateAuthHeader()
|
||||
}
|
||||
@@ -9,12 +9,4 @@ interface AuthStorage {
|
||||
suspend fun setAuthHeader(authHeader: String?)
|
||||
|
||||
suspend fun getAuthHeader(): String?
|
||||
|
||||
suspend fun setEmail(email: String?)
|
||||
|
||||
suspend fun getEmail(): String?
|
||||
|
||||
suspend fun setPassword(password: String?)
|
||||
|
||||
suspend fun getPassword(): String?
|
||||
}
|
||||
@@ -4,7 +4,9 @@ import gq.kirmanak.mealient.data.auth.AuthDataSource
|
||||
import gq.kirmanak.mealient.data.baseurl.ServerInfoRepo
|
||||
import gq.kirmanak.mealient.data.baseurl.ServerVersion
|
||||
import gq.kirmanak.mealient.datasource.v0.MealieDataSourceV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.CreateApiTokenRequestV0
|
||||
import gq.kirmanak.mealient.datasource.v1.MealieDataSourceV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.CreateApiTokenRequestV1
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@@ -15,14 +17,20 @@ class AuthDataSourceImpl @Inject constructor(
|
||||
private val v1Source: MealieDataSourceV1,
|
||||
) : AuthDataSource {
|
||||
|
||||
private suspend fun getVersion(): ServerVersion = serverInfoRepo.getVersion()
|
||||
|
||||
private suspend fun getUrl(): String = serverInfoRepo.requireUrl()
|
||||
|
||||
override suspend fun authenticate(
|
||||
username: String,
|
||||
password: String,
|
||||
): String {
|
||||
val baseUrl = serverInfoRepo.requireUrl()
|
||||
return when (serverInfoRepo.getVersion()) {
|
||||
ServerVersion.V0 -> v0Source.authenticate(baseUrl, username, password)
|
||||
ServerVersion.V1 -> v1Source.authenticate(baseUrl, username, password)
|
||||
}
|
||||
): String = when (getVersion()) {
|
||||
ServerVersion.V0 -> v0Source.authenticate(getUrl(), username, password)
|
||||
ServerVersion.V1 -> v1Source.authenticate(getUrl(), username, password)
|
||||
}
|
||||
|
||||
override suspend fun createApiToken(name: String): String = when (getVersion()) {
|
||||
ServerVersion.V0 -> v0Source.createApiToken(getUrl(), CreateApiTokenRequestV0(name))
|
||||
ServerVersion.V1 -> v1Source.createApiToken(getUrl(), CreateApiTokenRequestV1(name)).token
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import gq.kirmanak.mealient.data.auth.AuthDataSource
|
||||
import gq.kirmanak.mealient.data.auth.AuthRepo
|
||||
import gq.kirmanak.mealient.data.auth.AuthStorage
|
||||
import gq.kirmanak.mealient.datasource.AuthenticationProvider
|
||||
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
@@ -24,34 +23,20 @@ class AuthRepoImpl @Inject constructor(
|
||||
override suspend fun authenticate(email: String, password: String) {
|
||||
logger.v { "authenticate() called with: email = $email, password = $password" }
|
||||
val token = authDataSource.authenticate(email, password)
|
||||
val header = AUTH_HEADER_FORMAT.format(token)
|
||||
authStorage.setAuthHeader(header)
|
||||
authStorage.setEmail(email)
|
||||
authStorage.setPassword(password)
|
||||
authStorage.setAuthHeader(AUTH_HEADER_FORMAT.format(token))
|
||||
val apiToken = authDataSource.createApiToken(API_TOKEN_NAME)
|
||||
authStorage.setAuthHeader(AUTH_HEADER_FORMAT.format(apiToken))
|
||||
}
|
||||
|
||||
override suspend fun getAuthHeader(): String? = authStorage.getAuthHeader()
|
||||
|
||||
override suspend fun requireAuthHeader(): String = checkNotNull(getAuthHeader()) {
|
||||
"Auth header is null when it was required"
|
||||
}
|
||||
|
||||
override suspend fun logout() {
|
||||
logger.v { "logout() called" }
|
||||
authStorage.setEmail(null)
|
||||
authStorage.setPassword(null)
|
||||
authStorage.setAuthHeader(null)
|
||||
}
|
||||
|
||||
override suspend fun invalidateAuthHeader() {
|
||||
logger.v { "invalidateAuthHeader() called" }
|
||||
val email = authStorage.getEmail() ?: return
|
||||
val password = authStorage.getPassword() ?: return
|
||||
runCatchingExceptCancel { authenticate(email, password) }
|
||||
.onFailure { logout() } // Clear all known values to avoid reusing them
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val AUTH_HEADER_FORMAT = "Bearer %s"
|
||||
private const val API_TOKEN_NAME = "Mealient"
|
||||
}
|
||||
}
|
||||
@@ -32,14 +32,6 @@ class AuthStorageImpl @Inject constructor(
|
||||
|
||||
override suspend fun getAuthHeader(): String? = getString(AUTH_HEADER_KEY)
|
||||
|
||||
override suspend fun setEmail(email: String?) = putString(EMAIL_KEY, email)
|
||||
|
||||
override suspend fun getEmail(): String? = getString(EMAIL_KEY)
|
||||
|
||||
override suspend fun setPassword(password: String?) = putString(PASSWORD_KEY, password)
|
||||
|
||||
override suspend fun getPassword(): String? = getString(PASSWORD_KEY)
|
||||
|
||||
private suspend fun putString(
|
||||
key: String,
|
||||
value: String?
|
||||
@@ -57,11 +49,5 @@ class AuthStorageImpl @Inject constructor(
|
||||
companion object {
|
||||
@VisibleForTesting
|
||||
const val AUTH_HEADER_KEY = "authHeader"
|
||||
|
||||
@VisibleForTesting
|
||||
const val EMAIL_KEY = "email"
|
||||
|
||||
@VisibleForTesting
|
||||
const val PASSWORD_KEY = "password"
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import gq.kirmanak.mealient.data.auth.AuthRepo
|
||||
import gq.kirmanak.mealient.data.auth.AuthStorage
|
||||
import gq.kirmanak.mealient.data.baseurl.ServerInfoRepo
|
||||
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
|
||||
import gq.kirmanak.mealient.test.AuthImplTestData.TEST_API_AUTH_HEADER
|
||||
import gq.kirmanak.mealient.test.AuthImplTestData.TEST_API_TOKEN
|
||||
import gq.kirmanak.mealient.test.AuthImplTestData.TEST_AUTH_HEADER
|
||||
import gq.kirmanak.mealient.test.AuthImplTestData.TEST_BASE_URL
|
||||
import gq.kirmanak.mealient.test.AuthImplTestData.TEST_PASSWORD
|
||||
@@ -51,13 +53,15 @@ class AuthRepoImplTest : BaseUnitTest() {
|
||||
@Test
|
||||
fun `when authenticate successfully then saves to storage`() = runTest {
|
||||
coEvery { serverInfoRepo.getVersion() } returns TEST_SERVER_VERSION_V0
|
||||
coEvery { dataSource.authenticate(eq(TEST_USERNAME), eq(TEST_PASSWORD)) } returns TEST_TOKEN
|
||||
coEvery { dataSource.authenticate(any(), any()) } returns TEST_TOKEN
|
||||
coEvery { serverInfoRepo.requireUrl() } returns TEST_BASE_URL
|
||||
coEvery { dataSource.createApiToken(any()) } returns TEST_API_TOKEN
|
||||
subject.authenticate(TEST_USERNAME, TEST_PASSWORD)
|
||||
coVerifyAll {
|
||||
coVerify {
|
||||
dataSource.authenticate(eq(TEST_USERNAME), eq(TEST_PASSWORD))
|
||||
storage.setAuthHeader(TEST_AUTH_HEADER)
|
||||
storage.setEmail(TEST_USERNAME)
|
||||
storage.setPassword(TEST_PASSWORD)
|
||||
dataSource.createApiToken(eq("Mealient"))
|
||||
storage.setAuthHeader(TEST_API_AUTH_HEADER)
|
||||
}
|
||||
confirmVerified(storage)
|
||||
}
|
||||
@@ -71,50 +75,9 @@ class AuthRepoImplTest : BaseUnitTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when logout then removes email, password and header`() = runTest {
|
||||
fun `when logout expect header removal`() = runTest {
|
||||
subject.logout()
|
||||
coVerifyAll {
|
||||
storage.setEmail(null)
|
||||
storage.setPassword(null)
|
||||
storage.setAuthHeader(null)
|
||||
}
|
||||
coVerify { storage.setAuthHeader(null) }
|
||||
confirmVerified(storage)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when invalidate then does not authenticate without email`() = runTest {
|
||||
coEvery { storage.getEmail() } returns null
|
||||
coEvery { storage.getPassword() } returns TEST_PASSWORD
|
||||
subject.invalidateAuthHeader()
|
||||
confirmVerified(dataSource)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when invalidate then does not authenticate without password`() = runTest {
|
||||
coEvery { storage.getEmail() } returns TEST_USERNAME
|
||||
coEvery { storage.getPassword() } returns null
|
||||
subject.invalidateAuthHeader()
|
||||
confirmVerified(dataSource)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when invalidate with credentials then calls authenticate`() = runTest {
|
||||
coEvery { storage.getEmail() } returns TEST_USERNAME
|
||||
coEvery { storage.getPassword() } returns TEST_PASSWORD
|
||||
coEvery { serverInfoRepo.getVersion() } returns TEST_SERVER_VERSION_V0
|
||||
coEvery { serverInfoRepo.requireUrl() } returns TEST_BASE_URL
|
||||
coEvery { dataSource.authenticate(eq(TEST_USERNAME), eq(TEST_PASSWORD)) } returns TEST_TOKEN
|
||||
subject.invalidateAuthHeader()
|
||||
coVerifyAll { dataSource.authenticate(eq(TEST_USERNAME), eq(TEST_PASSWORD)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when invalidate with credentials and auth fails then clears email`() = runTest {
|
||||
coEvery { storage.getEmail() } returns "invalid"
|
||||
coEvery { storage.getPassword() } returns ""
|
||||
coEvery { serverInfoRepo.requireUrl() } returns TEST_BASE_URL
|
||||
coEvery { dataSource.authenticate(any(), any()) } throws RuntimeException()
|
||||
subject.invalidateAuthHeader()
|
||||
coVerify { storage.setEmail(null) }
|
||||
}
|
||||
}
|
||||
@@ -8,12 +8,8 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import gq.kirmanak.mealient.data.auth.AuthStorage
|
||||
import gq.kirmanak.mealient.data.auth.impl.AuthStorageImpl.Companion.AUTH_HEADER_KEY
|
||||
import gq.kirmanak.mealient.data.auth.impl.AuthStorageImpl.Companion.EMAIL_KEY
|
||||
import gq.kirmanak.mealient.data.auth.impl.AuthStorageImpl.Companion.PASSWORD_KEY
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.test.AuthImplTestData.TEST_AUTH_HEADER
|
||||
import gq.kirmanak.mealient.test.AuthImplTestData.TEST_PASSWORD
|
||||
import gq.kirmanak.mealient.test.AuthImplTestData.TEST_USERNAME
|
||||
import gq.kirmanak.mealient.test.FakeLogger
|
||||
import gq.kirmanak.mealient.test.HiltRobolectricTest
|
||||
import io.mockk.MockKAnnotations
|
||||
@@ -55,16 +51,4 @@ class AuthStorageImplTest : HiltRobolectricTest() {
|
||||
fun `when authHeader is observed then sends null if nothing saved`() = runTest {
|
||||
assertThat(subject.authHeaderFlow.first()).isEqualTo(null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when setEmail then edits shared preferences`() = runTest {
|
||||
subject.setEmail(TEST_USERNAME)
|
||||
assertThat(sharedPreferences.getString(EMAIL_KEY, null)).isEqualTo(TEST_USERNAME)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when getPassword then reads shared preferences`() = runTest {
|
||||
sharedPreferences.edit(commit = true) { putString(PASSWORD_KEY, TEST_PASSWORD) }
|
||||
assertThat(subject.getPassword()).isEqualTo(TEST_PASSWORD)
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@ object AuthImplTestData {
|
||||
const val TEST_BASE_URL = "https://example.com/"
|
||||
const val TEST_TOKEN = "TEST_TOKEN"
|
||||
const val TEST_AUTH_HEADER = "Bearer TEST_TOKEN"
|
||||
const val TEST_API_TOKEN = "TEST_API_TOKEN"
|
||||
const val TEST_API_AUTH_HEADER = "Bearer TEST_API_TOKEN"
|
||||
const val TEST_VERSION = "v0.5.6"
|
||||
val TEST_SERVER_VERSION_V0 = ServerVersion.V0
|
||||
val TEST_SERVER_VERSION_V1 = ServerVersion.V1
|
||||
|
||||
@@ -4,4 +4,6 @@ interface AuthenticationProvider {
|
||||
|
||||
suspend fun getAuthHeader(): String?
|
||||
|
||||
suspend fun logout()
|
||||
|
||||
}
|
||||
@@ -12,21 +12,30 @@ import javax.inject.Provider
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
// TODO has to be interceptor, otherwise only public recipes are visible
|
||||
class MealieAuthenticator @Inject constructor(
|
||||
private val authenticationProvider: Provider<AuthenticationProvider>,
|
||||
private val authenticationProviderProvider: Provider<AuthenticationProvider>,
|
||||
) : Authenticator {
|
||||
|
||||
private val authenticationProvider: AuthenticationProvider
|
||||
get() = authenticationProviderProvider.get()
|
||||
|
||||
override fun authenticate(route: Route?, response: Response): Request? {
|
||||
val supportsBearer = response.challenges().any { it.scheme == BEARER_SCHEME }
|
||||
val request = response.request
|
||||
return if (supportsBearer && request.header(HEADER_NAME) == null) {
|
||||
getAuthHeader()?.let { request.copyWithHeader(HEADER_NAME, it) }
|
||||
} else {
|
||||
null // Either Bearer is not supported or we've already tried to authenticate
|
||||
return when {
|
||||
request.header(HEADER_NAME) != null -> {
|
||||
logout()
|
||||
null
|
||||
}
|
||||
supportsBearer -> getAuthHeader()?.let { request.copyWithHeader(HEADER_NAME, it) }
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAuthHeader() = runBlocking { authenticationProvider.get().getAuthHeader() }
|
||||
private fun getAuthHeader() = runBlocking { authenticationProvider.getAuthHeader() }
|
||||
|
||||
private fun logout() = runBlocking { authenticationProvider.logout() }
|
||||
|
||||
companion object {
|
||||
@VisibleForTesting
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package gq.kirmanak.mealient.datasource.v0
|
||||
|
||||
import gq.kirmanak.mealient.datasource.v0.models.AddRecipeRequestV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.CreateApiTokenRequestV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.GetRecipeResponseV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.GetRecipeSummaryResponseV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.ParseRecipeURLRequestV0
|
||||
@@ -41,4 +42,9 @@ interface MealieDataSourceV0 {
|
||||
baseUrl: String,
|
||||
request: ParseRecipeURLRequestV0,
|
||||
): String
|
||||
|
||||
suspend fun createApiToken(
|
||||
baseUrl: String,
|
||||
request: CreateApiTokenRequestV0,
|
||||
): String
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import gq.kirmanak.mealient.datasource.NetworkError
|
||||
import gq.kirmanak.mealient.datasource.NetworkRequestWrapper
|
||||
import gq.kirmanak.mealient.datasource.decode
|
||||
import gq.kirmanak.mealient.datasource.v0.models.AddRecipeRequestV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.CreateApiTokenRequestV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.ErrorDetailV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.GetRecipeResponseV0
|
||||
import gq.kirmanak.mealient.datasource.v0.models.GetRecipeSummaryResponseV0
|
||||
@@ -86,7 +87,15 @@ class MealieDataSourceV0Impl @Inject constructor(
|
||||
): String = networkRequestWrapper.makeCallAndHandleUnauthorized(
|
||||
block = { service.createRecipeFromURL("$baseUrl/api/recipes/create-url", request) },
|
||||
logMethod = { "parseRecipeFromURL" },
|
||||
logParameters = { "baseUrl = $baseUrl, request = $request" }
|
||||
logParameters = { "baseUrl = $baseUrl, request = $request" },
|
||||
)
|
||||
|
||||
override suspend fun createApiToken(
|
||||
baseUrl: String,
|
||||
request: CreateApiTokenRequestV0,
|
||||
): String = networkRequestWrapper.makeCallAndHandleUnauthorized(
|
||||
block = { service.createApiToken("$baseUrl/api/users/api-tokens", request) },
|
||||
logMethod = { "createApiToken" },
|
||||
logParameters = { "baseUrl = $baseUrl, request = $request" }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -41,4 +41,10 @@ interface MealieServiceV0 {
|
||||
@Url url: String,
|
||||
@Body request: ParseRecipeURLRequestV0,
|
||||
): String
|
||||
|
||||
@POST
|
||||
suspend fun createApiToken(
|
||||
@Url url: String,
|
||||
@Body request: CreateApiTokenRequestV0,
|
||||
): String
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package gq.kirmanak.mealient.datasource.v0.models
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class CreateApiTokenRequestV0(
|
||||
@SerialName("name") val name: String,
|
||||
)
|
||||
@@ -1,5 +1,7 @@
|
||||
package gq.kirmanak.mealient.datasource.v1
|
||||
|
||||
import gq.kirmanak.mealient.datasource.v1.models.CreateApiTokenRequestV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.CreateApiTokenResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.CreateRecipeRequestV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetRecipeResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetRecipeSummaryResponseV1
|
||||
@@ -48,4 +50,9 @@ interface MealieDataSourceV1 {
|
||||
baseUrl: String,
|
||||
request: ParseRecipeURLRequestV1,
|
||||
): String
|
||||
|
||||
suspend fun createApiToken(
|
||||
baseUrl: String,
|
||||
request: CreateApiTokenRequestV1,
|
||||
): CreateApiTokenResponseV1
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package gq.kirmanak.mealient.datasource.v1
|
||||
import gq.kirmanak.mealient.datasource.NetworkError
|
||||
import gq.kirmanak.mealient.datasource.NetworkRequestWrapper
|
||||
import gq.kirmanak.mealient.datasource.decode
|
||||
import gq.kirmanak.mealient.datasource.v1.models.CreateApiTokenRequestV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.CreateApiTokenResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.CreateRecipeRequestV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.ErrorDetailV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetRecipeResponseV1
|
||||
@@ -98,8 +100,15 @@ class MealieDataSourceV1Impl @Inject constructor(
|
||||
block = { service.createRecipeFromURL("$baseUrl/api/recipes/create-url", request) },
|
||||
logMethod = { "parseRecipeFromURL" },
|
||||
logParameters = { "baseUrl = $baseUrl, request = $request" }
|
||||
|
||||
)
|
||||
|
||||
override suspend fun createApiToken(
|
||||
baseUrl: String,
|
||||
request: CreateApiTokenRequestV1
|
||||
): CreateApiTokenResponseV1 = networkRequestWrapper.makeCallAndHandleUnauthorized(
|
||||
block = { service.createApiToken("$baseUrl/api/users/api-tokens", request) },
|
||||
logMethod = { "createApiToken" },
|
||||
logParameters = { "baseUrl = $baseUrl, request = $request" }
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -47,4 +47,10 @@ interface MealieServiceV1 {
|
||||
@Url url: String,
|
||||
@Body request: ParseRecipeURLRequestV1,
|
||||
): String
|
||||
|
||||
@POST
|
||||
suspend fun createApiToken(
|
||||
@Url url: String,
|
||||
@Body request: CreateApiTokenRequestV1,
|
||||
): CreateApiTokenResponseV1
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package gq.kirmanak.mealient.datasource.v1.models
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class CreateApiTokenRequestV1(
|
||||
@SerialName("name") val name: String,
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
package gq.kirmanak.mealient.datasource.v1.models
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class CreateApiTokenResponseV1(
|
||||
@SerialName("token") val token: String,
|
||||
)
|
||||
@@ -17,7 +17,7 @@ class MealieAuthenticatorTest : BaseUnitTest() {
|
||||
|
||||
private lateinit var subject: MealieAuthenticator
|
||||
|
||||
@MockK
|
||||
@MockK(relaxUnitFun = true)
|
||||
lateinit var authenticationProvider: AuthenticationProvider
|
||||
|
||||
@Before
|
||||
@@ -48,11 +48,18 @@ class MealieAuthenticatorTest : BaseUnitTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when no auth header was set expect authenticate to return null`() {
|
||||
fun `when an auth header was set expect authenticate to return null`() {
|
||||
val response = buildResponse(authHeader = "token")
|
||||
assertThat(subject.authenticate(null, response)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when an auth header was set expect authenticate to logout`() {
|
||||
val response = buildResponse(authHeader = "token")
|
||||
subject.authenticate(null, response)
|
||||
coVerify { authenticationProvider.logout() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when auth header exists expect authenticate to return request`() {
|
||||
coEvery { authenticationProvider.getAuthHeader() } returns "token"
|
||||
|
||||
Reference in New Issue
Block a user