OXYGENHUB
# 6e2af63ae616 oxygenhub-android / app / src / test / java / io / oxygenhub / android / ui / SignInViewModelTest.kt
4.4 KiB · last changed Sign-in, dashboard, repositories, issues and inbox screens, adaptive (refs #2) 11 days ago source blame history raw
1
package io.oxygenhub.android.ui
2
3
import io.oxygenhub.android.FakeHubApi
4
import io.oxygenhub.android.MainDispatcherRule
5
import io.oxygenhub.android.api.DeviceAuth
6
import io.oxygenhub.android.api.DeviceCode
7
import io.oxygenhub.android.api.DeviceFlowException
8
import io.oxygenhub.android.api.HubException
9
import io.oxygenhub.android.api.TokenGrant
10
import io.oxygenhub.android.api.User
11
import io.oxygenhub.android.auth.MemorySessionStore
12
import io.oxygenhub.android.auth.Session
13
import io.oxygenhub.android.auth.SessionManager
14
import io.oxygenhub.android.ui.signin.SignInState
15
import io.oxygenhub.android.ui.signin.SignInViewModel
16
import kotlinx.coroutines.CompletableDeferred
17
import org.junit.Assert.assertEquals
18
import org.junit.Assert.assertNull
19
import org.junit.Assert.assertTrue
20
import org.junit.Rule
21
import org.junit.Test
22
23
class SignInViewModelTest {
24
    @get:Rule val main = MainDispatcherRule()
25
26
    private val code = DeviceCode("oxydev_x", "BCDF-GHJK", "https://hub.example/device", "https://hub.example/device?code=BCDF-GHJK")
27
    private val grant = TokenGrant("oxy_new", scope = "repo:read user:read", username = "andymccall")
28
29
    /** A device flow whose token arrives when the test says. */
30
    private inner class FakeAuth : DeviceAuth {
31
        val token = CompletableDeferred<TokenGrant>()
32
        var me: () -> User = { User("andymccall") }
33
        var asked: String? = null
34
        override suspend fun requestCode(client: String, scopes: List<String>): DeviceCode {
35
            asked = client
36
            return code
37
        }
38
        override suspend fun awaitToken(code: DeviceCode): TokenGrant = token.await()
39
        override suspend fun whoAmI(token: String): User = me()
40
    }
41
42
    private val store = MemorySessionStore()
43
    private val sessions = SessionManager(store) { _, _ -> FakeHubApi() }
44
    private val auth = FakeAuth()
45
    private val hubs = mutableListOf<String>()
46
47
    private fun vm() = SignInViewModel(sessions, { hubs += it; auth }, "OxygenHub for Android on Test", "https://oxygenhub.io")
48
49
    @Test
50
    fun theDefaultHubOrTheLastOne() {
51
        assertEquals("https://oxygenhub.io", vm().hub.value)
52
        store.lastHub = "https://hub.example"
53
        assertEquals("https://hub.example", vm().hub.value)
54
    }
55
56
    @Test
57
    fun signsInOnceApproved() {
58
        val vm = vm()
59
        vm.setHub("hub.example/")
60
        vm.signIn()
61
        assertEquals(listOf("https://hub.example"), hubs)
62
        assertEquals("https://hub.example", vm.hub.value)
63
        assertEquals("OxygenHub for Android on Test", auth.asked)
64
        assertEquals(SignInState.Approving(code), vm.state.value)
65
        assertNull(sessions.session.value)
66
67
        auth.token.complete(grant)
68
        assertEquals(Session("https://hub.example", "andymccall", "oxy_new", listOf("repo:read", "user:read")), sessions.session.value)
69
        assertEquals(store.load(), sessions.session.value)
70
        assertEquals(SignInState.Idle(), vm.state.value)
71
    }
72
73
    @Test
74
    fun aTokenWithoutUserReadUsesTheNameTheGrantGives() {
75
        auth.me = { throw HubException.Http(403, "this token lacks the user:read scope") }
76
        val vm = vm()
77
        vm.signIn()
78
        auth.token.complete(grant.copy(scope = "repo:read"))
79
        assertEquals("andymccall", sessions.session.value?.username)
80
        assertEquals(listOf("repo:read"), sessions.session.value?.scopes)
81
    }
82
83
    @Test
84
    fun aBadAddressIsRefusedHere() {
85
        val vm = vm()
86
        vm.setHub("http://insecure.example")
87
        vm.signIn()
88
        assertTrue(hubs.isEmpty())
89
        assertTrue((vm.state.value as SignInState.Idle).error!!.contains("HTTPS"))
90
    }
91
92
    @Test
93
    fun aDenialIsSaid() {
94
        val vm = vm()
95
        vm.signIn()
96
        auth.token.completeExceptionally(DeviceFlowException.Denied())
97
        assertEquals(SignInState.Idle("The sign-in was denied on the web."), vm.state.value)
98
        assertNull(sessions.session.value)
99
    }
100
101
    @Test
102
    fun anUnreachableHubIsSaid() {
103
        val vm = vm()
104
        vm.signIn()
105
        auth.token.completeExceptionally(HubException.Network(java.io.IOException("no route")))
106
        assertEquals(SignInState.Idle("Can't reach the hub. Check your connection and try again."), vm.state.value)
107
    }
108
109
    @Test
110
    fun cancelling() {
111
        val vm = vm()
112
        vm.signIn()
113
        vm.cancel()
114
        assertEquals(SignInState.Idle(), vm.state.value)
115
        auth.token.complete(grant)
116
        assertNull(sessions.session.value)
117
    }
118
}