-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #25 from PeriodPals/feat/authentication/model
Define Model for Supabase authentication
- Loading branch information
Showing
6 changed files
with
289 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
app/src/main/java/com/android/periodpals/model/auth/AuthModel.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.android.periodpals.model.auth | ||
|
||
interface AuthModel { | ||
|
||
suspend fun login( | ||
userEmail: String, | ||
userPassword: String, | ||
onSuccess: () -> Unit, | ||
onFailure: (Exception) -> Unit, | ||
) | ||
|
||
suspend fun register( | ||
userEmail: String, | ||
userPassword: String, | ||
onSuccess: () -> Unit, | ||
onFailure: (Exception) -> Unit, | ||
) | ||
|
||
suspend fun logout(onSuccess: () -> Unit, onFailure: (Exception) -> Unit) | ||
|
||
suspend fun isUserLoggedIn(token: String, onSuccess: () -> Unit, onFailure: (Exception) -> Unit) | ||
} |
83 changes: 83 additions & 0 deletions
83
app/src/main/java/com/android/periodpals/model/auth/AuthModelSupabase.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package com.android.periodpals.model.auth | ||
|
||
import android.util.Log | ||
import io.github.jan.supabase.SupabaseClient | ||
import io.github.jan.supabase.auth.Auth | ||
import io.github.jan.supabase.auth.SignOutScope | ||
import io.github.jan.supabase.auth.providers.builtin.Email | ||
|
||
private const val TAG = "AuthModelSupabase" | ||
|
||
class AuthModelSupabase( | ||
private val supabase: SupabaseClient, | ||
private val pluginManagerWrapper: PluginManagerWrapper = | ||
PluginManagerWrapperImpl(supabase.pluginManager), | ||
) : AuthModel { | ||
|
||
private val supabaseAuth: Auth = pluginManagerWrapper.getAuthPlugin() | ||
|
||
override suspend fun register( | ||
userEmail: String, | ||
userPassword: String, | ||
onSuccess: () -> Unit, | ||
onFailure: (Exception) -> Unit, | ||
) { | ||
try { | ||
supabaseAuth.signUpWith(Email) { | ||
email = userEmail | ||
password = userPassword | ||
} | ||
Log.d(TAG, "register: successfully registered the user") | ||
onSuccess() | ||
} catch (e: Exception) { | ||
Log.d(TAG, "register: failed to register the user: ${e.message}") | ||
onFailure(e) | ||
} | ||
} | ||
|
||
override suspend fun login( | ||
userEmail: String, | ||
userPassword: String, | ||
onSuccess: () -> Unit, | ||
onFailure: (Exception) -> Unit, | ||
) { | ||
try { | ||
supabaseAuth.signInWith(Email) { | ||
email = userEmail | ||
password = userPassword | ||
} | ||
Log.d(TAG, "login: successfully logged in the user") | ||
onSuccess() | ||
} catch (e: Exception) { | ||
Log.d(TAG, "login: failed to log in the user: ${e.message}") | ||
onFailure(e) | ||
} | ||
} | ||
|
||
override suspend fun logout(onSuccess: () -> Unit, onFailure: (Exception) -> Unit) { | ||
try { | ||
supabaseAuth.signOut(SignOutScope.LOCAL) | ||
Log.d(TAG, "logout: successfully logged out the user") | ||
onSuccess() | ||
} catch (e: Exception) { | ||
Log.d(TAG, "logout: failed to log out the user: ${e.message}") | ||
onFailure(e) | ||
} | ||
} | ||
|
||
override suspend fun isUserLoggedIn( | ||
token: String, | ||
onSuccess: () -> Unit, | ||
onFailure: (Exception) -> Unit, | ||
) { | ||
try { | ||
supabaseAuth.retrieveUser(token) | ||
supabaseAuth.refreshCurrentSession() // will throw an error if the user is not logged in | ||
Log.d(TAG, "isUserLoggedIn: user is logged in") | ||
onSuccess() | ||
} catch (e: Exception) { | ||
Log.d(TAG, "isUserLoggedIn: user is not logged in: ${e.message}") | ||
onFailure(e) | ||
} | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
app/src/main/java/com/android/periodpals/model/auth/PluginManagerWrapper.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.android.periodpals.model.auth | ||
|
||
import io.github.jan.supabase.auth.Auth | ||
import io.github.jan.supabase.plugins.PluginManager | ||
|
||
interface PluginManagerWrapper { | ||
fun getAuthPlugin(): Auth | ||
} | ||
|
||
class PluginManagerWrapperImpl(private val pluginManager: PluginManager) : PluginManagerWrapper { | ||
override fun getAuthPlugin(): Auth { | ||
return pluginManager.getPlugin(Auth) | ||
} | ||
} |
157 changes: 157 additions & 0 deletions
157
app/src/test/java/com/android/periodpals/model/auth/AuthModelSupabaseTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
package com.android.periodpals.model.auth | ||
|
||
import io.github.jan.supabase.SupabaseClient | ||
import io.github.jan.supabase.auth.Auth | ||
import io.github.jan.supabase.auth.AuthConfig | ||
import io.github.jan.supabase.auth.deepLinkOrNull | ||
import io.github.jan.supabase.auth.providers.builtin.Email | ||
import junit.framework.TestCase.fail | ||
import kotlinx.coroutines.runBlocking | ||
import org.junit.Before | ||
import org.junit.Test | ||
import org.mockito.Mock | ||
import org.mockito.Mockito.anyString | ||
import org.mockito.Mockito.doThrow | ||
import org.mockito.Mockito.mock | ||
import org.mockito.Mockito.`when` | ||
import org.mockito.MockitoAnnotations | ||
import org.mockito.kotlin.any | ||
import org.mockito.kotlin.anyOrNull | ||
|
||
class AuthModelSupabaseTest { | ||
|
||
@Mock private lateinit var supabaseClient: SupabaseClient | ||
|
||
@Mock private lateinit var pluginManagerWrapper: PluginManagerWrapper | ||
|
||
@Mock private lateinit var auth: Auth | ||
|
||
@Mock private lateinit var authConfig: AuthConfig | ||
|
||
private lateinit var authModel: AuthModelSupabase | ||
|
||
@Before | ||
fun setUp() { | ||
MockitoAnnotations.openMocks(this) | ||
auth = mock(Auth::class.java) | ||
|
||
`when`(auth.config).thenReturn(authConfig) | ||
`when`(pluginManagerWrapper.getAuthPlugin()).thenReturn(auth) | ||
`when`(authConfig.deepLinkOrNull).thenReturn("https://example.com") | ||
authModel = AuthModelSupabase(supabaseClient, pluginManagerWrapper) | ||
} | ||
|
||
@Test | ||
fun `register success`() = runBlocking { | ||
`when`(auth.signUpWith(any<Email>(), anyOrNull(), any())).thenAnswer {} | ||
|
||
var successCalled = false | ||
authModel.register( | ||
"test@example.com", | ||
"password", | ||
{ successCalled = true }, | ||
{ fail("Should not call onFailure") }, | ||
) | ||
|
||
assert(successCalled) | ||
} | ||
|
||
@Test | ||
fun `register failure`() = runBlocking { | ||
val exception = RuntimeException("Registration failed") | ||
doThrow(exception).`when`(auth).signUpWith(any<Email>(), anyOrNull(), any()) | ||
|
||
var failureCalled = false | ||
authModel.register( | ||
"test@example.com", | ||
"password", | ||
{ fail("Should not call onSuccess") }, | ||
{ failureCalled = true }, | ||
) | ||
|
||
assert(failureCalled) | ||
} | ||
|
||
@Test | ||
fun `login success`() = runBlocking { | ||
`when`(auth.signInWith(Email)).thenReturn(Unit) | ||
|
||
var successCalled = false | ||
authModel.login( | ||
"test@example.com", | ||
"password", | ||
{ successCalled = true }, | ||
{ fail("Should not call onFailure") }, | ||
) | ||
|
||
assert(successCalled) | ||
} | ||
|
||
@Test | ||
fun `login failure`() = runBlocking { | ||
val exception = RuntimeException("Login failed") | ||
doThrow(exception).`when`(auth).signInWith(any<Email>(), anyOrNull(), any()) | ||
|
||
var failureCalled = false | ||
authModel.login( | ||
"test@example.com", | ||
"password", | ||
{ fail("Should not call onSuccess") }, | ||
{ failureCalled = true }, | ||
) | ||
|
||
assert(failureCalled) | ||
} | ||
|
||
@Test | ||
fun `logout success`() = runBlocking { | ||
`when`(auth.signOut(any())).thenReturn(Unit) | ||
|
||
var successCalled = false | ||
authModel.logout({ successCalled = true }, { fail("Should not call onFailure") }) | ||
|
||
assert(successCalled) | ||
} | ||
|
||
@Test | ||
fun `logout failure`() = runBlocking { | ||
val exception = RuntimeException("Logout failed") | ||
doThrow(exception).`when`(auth).signOut(any()) | ||
|
||
var failureCalled = false | ||
authModel.logout({ fail("Should not call onSuccess") }, { failureCalled = true }) | ||
|
||
assert(failureCalled) | ||
} | ||
|
||
@Test | ||
fun `isUserLoggedIn success`() = runBlocking { | ||
`when`(auth.retrieveUser(anyString())).thenReturn(null) | ||
`when`(auth.refreshCurrentSession()).thenReturn(Unit) | ||
|
||
var successCalled = false | ||
authModel.isUserLoggedIn( | ||
"token", | ||
{ successCalled = true }, | ||
{ fail("Should not call onFailure") }, | ||
) | ||
|
||
assert(successCalled) | ||
} | ||
|
||
@Test | ||
fun `isUserLoggedIn failure`() = runBlocking { | ||
val exception = RuntimeException("User not logged in") | ||
doThrow(exception).`when`(auth).retrieveUser(anyString()) | ||
doThrow(exception).`when`(auth).refreshCurrentSession() | ||
|
||
var failureCalled = false | ||
authModel.isUserLoggedIn( | ||
"token", | ||
{ fail("Should not call onSuccess") }, | ||
{ failureCalled = true }, | ||
) | ||
|
||
assert(failureCalled) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters