OXYGENHUB
# c1b5d82f16cd oxygenhub-android / app / src / test / java / io / oxygenhub / android / secondfactor / ApprovalsViewModelTest.kt
3.2 KiB · last changed This phone as the second factor: a Keystore key opened by fingerprint, face or PIN approves sign-ins and sensitive changes on the web by the number the browser shows (closes #32) 3 days ago source blame history raw
1
package io.oxygenhub.android.secondfactor
2
3
import io.oxygenhub.android.FakeHubApi
4
import io.oxygenhub.android.MainDispatcherRule
5
import io.oxygenhub.android.api.HubException
6
import io.oxygenhub.android.api.PendingApproval
7
import kotlinx.coroutines.test.runTest
8
import org.junit.Assert.assertEquals
9
import org.junit.Assert.assertNull
10
import org.junit.Assert.assertTrue
11
import org.junit.Rule
12
import org.junit.Test
13
14
class ApprovalsViewModelTest {
15
    @get:Rule val main = MainDispatcherRule()
16
17
    private val signIn = PendingApproval("ab12", "signin", "Sign in from Firefox on Linux, 203.0.113.9", listOf(12, 47, 83))
18
19
    @Test
20
    fun theMessageIsTheOneTheHubChecks() {
21
        // oxygenhub's auth::phone::message, byte for byte.
22
        assertEquals("oxygenhub approval v1\nab12\n47\napprove", ApprovalMessage.of("ab12", 47, true).decodeToString())
23
        assertEquals("oxygenhub approval v1\nab12\n47\ndeny", ApprovalMessage.of("ab12", 47, false).decodeToString())
24
        assertEquals("00ff10", byteArrayOf(0, -1, 16).toHex())
25
    }
26
27
    @Test
28
    fun aWaitingApprovalIsShownAndApprovingSignsTheNumberPicked() = runTest {
29
        val api = FakeHubApi()
30
        api.approvals = { listOf(signIn) }
31
        val vm = ApprovalsViewModel(api)
32
        vm.check()
33
        assertEquals(signIn, vm.waiting.value)
34
        var signed: String? = null
35
        vm.approve(signIn, 47) { m -> signed = m.decodeToString(); byteArrayOf(1, 2) }
36
        assertEquals("oxygenhub approval v1\nab12\n47\napprove", signed)
37
        assertEquals(listOf(Triple("ab12", true, 47L)), api.answers)
38
        assertTrue(api.calls.contains("answer ab12 true 47 true"))
39
        assertNull("answered: not shown again", vm.waiting.value)
40
        assertEquals("Approved: the browser is signing in.", vm.said.value)
41
        vm.check()
42
        assertNull("still listed until the hub catches up, but not shown again", vm.waiting.value)
43
    }
44
45
    @Test
46
    fun backingOutOfThePromptSendsNothing() = runTest {
47
        val api = FakeHubApi()
48
        api.approvals = { listOf(signIn) }
49
        val vm = ApprovalsViewModel(api)
50
        vm.check()
51
        vm.approve(signIn, 47) { null }
52
        assertTrue(api.answers.isEmpty())
53
        assertEquals(signIn, vm.waiting.value)
54
        assertTrue(vm.said.value!!.startsWith("Not approved"))
55
    }
56
57
    @Test
58
    fun aWrongNumberAndANoAreSaidPlainly() = runTest {
59
        val api = FakeHubApi()
60
        api.approvals = { listOf(signIn) }
61
        api.answerState = { n -> if (n == 47L) "approved" else "denied" }
62
        val vm = ApprovalsViewModel(api)
63
        vm.check()
64
        vm.approve(signIn, 12) { byteArrayOf(1) }
65
        assertTrue(vm.said.value!!.contains("not the number"))
66
        val other = signIn.copy(id = "cd34")
67
        api.approvals = { listOf(other) }
68
        vm.check()
69
        vm.deny(other)
70
        assertEquals(Triple("cd34", false, 0L), api.answers.last())
71
        assertTrue(vm.said.value!!.startsWith("Refused"))
72
    }
73
74
    @Test
75
    fun aHubThatCannotSayLeavesNothingShown() = runTest {
76
        val api = FakeHubApi()
77
        api.approvals = { throw HubException.Http(404, "no such API route") }
78
        val vm = ApprovalsViewModel(api)
79
        vm.check()
80
        assertNull(vm.waiting.value)
81
    }
82
}