• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.credentialmanager
17 
18 import java.time.Instant
19 import android.graphics.drawable.Drawable
20 import com.android.credentialmanager.model.get.CredentialEntryInfo
21 import com.android.credentialmanager.model.get.ActionEntryInfo
22 import com.android.credentialmanager.model.get.AuthenticationEntryInfo
23 import com.android.credentialmanager.model.Request
24 import androidx.test.filters.SmallTest
25 import androidx.test.ext.junit.runners.AndroidJUnit4
26 import org.junit.Test
27 import org.mockito.kotlin.mock
28 import org.junit.runner.RunWith
29 import com.android.credentialmanager.model.CredentialType
30 import com.google.common.truth.Truth.assertThat
31 import com.android.credentialmanager.ui.mappers.toGet
32 import com.android.credentialmanager.model.get.ProviderInfo
33 import com.android.credentialmanager.CredentialSelectorUiState.Get.MultipleEntry.PerNameEntries
34 
35 /** Unit tests for [CredentialSelectorUiStateGetMapper]. */
36 @SmallTest
37 @RunWith(AndroidJUnit4::class)
38 class CredentialSelectorUiStateGetMapperTest {
39 
40     private val mDrawable = mock<Drawable>()
41 
42     private val actionEntryInfo =
43         ActionEntryInfo(
44             providerId = "",
45             entryKey = "",
46             entrySubkey = "",
47             pendingIntent = null,
48             fillInIntent = null,
49             title = "title",
50             icon = mDrawable,
51             subTitle = "subtitle",
52         )
53 
54     private val authenticationEntryInfo =
55         AuthenticationEntryInfo(
56             providerId = "",
57             entryKey = "",
58             entrySubkey = "",
59             pendingIntent = null,
60             fillInIntent = null,
61             title = "title",
62             providerDisplayName = "",
63             icon = mDrawable,
64             isUnlockedAndEmpty = true,
65             isLastUnlocked = true
66         )
67 
68     private val passkeyCredentialEntryInfo =
69         createCredentialEntryInfo(credentialType = CredentialType.PASSKEY, userName = "userName")
70 
71     private val unknownCredentialEntryInfo =
72         createCredentialEntryInfo(credentialType = CredentialType.UNKNOWN, userName = "userName2")
73 
74     private val passwordCredentialEntryInfo =
75         createCredentialEntryInfo(credentialType = CredentialType.PASSWORD, userName = "userName")
76 
77     private val recentlyUsedPasskeyCredential =
78         createCredentialEntryInfo(credentialType =
79     CredentialType.PASSKEY, lastUsedTimeMillis = 2L, userName = "userName")
80 
81     private val recentlyUsedPasswordCredential =
82         createCredentialEntryInfo(credentialType =
83     CredentialType.PASSWORD, lastUsedTimeMillis = 2L, userName = "userName")
84 
85     private val credentialList1 = listOf(
86         passkeyCredentialEntryInfo,
87         passwordCredentialEntryInfo
88     )
89 
90     private val credentialList2 = listOf(
91         passkeyCredentialEntryInfo,
92         passwordCredentialEntryInfo,
93         recentlyUsedPasskeyCredential,
94         unknownCredentialEntryInfo,
95         recentlyUsedPasswordCredential
96     )
97 
98     @Test
On primary screen, just one account returns SingleEntrynull99     fun `On primary screen, just one account returns SingleEntry`() {
100         val getCredentialUiState = Request.Get(
101             token = null,
102             resultReceiver = null,
103             providerInfos = listOf(createProviderInfo(credentialList1))).toGet(isPrimary = true)
104 
105         assertThat(getCredentialUiState).isEqualTo(
106             CredentialSelectorUiState.Get.SingleEntry(passkeyCredentialEntryInfo)
107         ) // prefer passkey over password for selected credential
108     }
109 
110     @Test
On primary screen, multiple accounts returns MultipleEntryPrimaryScreennull111     fun `On primary screen, multiple accounts returns MultipleEntryPrimaryScreen`() {
112         val getCredentialUiState = Request.Get(
113             token = null,
114             resultReceiver = null,
115             providerInfos = listOf(createProviderInfo(listOf(passkeyCredentialEntryInfo,
116                 unknownCredentialEntryInfo)))).toGet(isPrimary = true)
117 
118         assertThat(getCredentialUiState).isEqualTo(
119             CredentialSelectorUiState.Get.MultipleEntryPrimaryScreen(
120                 sortedEntries = listOf(
121                     passkeyCredentialEntryInfo, // userName
122                     unknownCredentialEntryInfo // userName2
123                 ),
124                 icon = mDrawable,
125                 authenticationEntryList = listOf(authenticationEntryInfo)
126             )) // prefer passkey from account 1, then unknown from account 2
127     }
128 
129     @Test
On secondary screen, a MultipleEntry is returnednull130     fun `On secondary screen, a MultipleEntry is returned`() {
131         val getCredentialUiState = Request.Get(
132             token = null,
133             resultReceiver = null,
134             providerInfos = listOf(createProviderInfo(credentialList1))).toGet(isPrimary = false)
135 
136         assertThat(getCredentialUiState).isEqualTo(
137             CredentialSelectorUiState.Get.MultipleEntry(
138                 listOf(PerNameEntries("userName", listOf(
139                     passkeyCredentialEntryInfo,
140                     passwordCredentialEntryInfo))
141                 ),
142                 listOf(actionEntryInfo),
143                 listOf(authenticationEntryInfo)
144             ))
145     }
146 
147     @Test
Returned multiple entry is sorted by credentialType and lastUsedTimeMillisnull148     fun `Returned multiple entry is sorted by credentialType and lastUsedTimeMillis`() {
149         val getCredentialUiState = Request.Get(
150             token = null,
151             resultReceiver = null,
152             providerInfos = listOf(createProviderInfo(credentialList1),
153                 createProviderInfo(credentialList2))).toGet(isPrimary = false)
154 
155         assertThat(getCredentialUiState).isEqualTo(
156             CredentialSelectorUiState.Get.MultipleEntry(
157                 listOf(
158                     PerNameEntries("userName",
159                         listOf(
160                             recentlyUsedPasskeyCredential, // from provider 2
161                             passkeyCredentialEntryInfo, // from provider 1 or 2
162                             passkeyCredentialEntryInfo, // from provider 1 or 2
163                             recentlyUsedPasswordCredential, // from provider 2
164                             passwordCredentialEntryInfo, // from provider 1 or 2
165                             passwordCredentialEntryInfo, // from provider 1 or 2
166                         )),
167                     PerNameEntries("userName2", listOf(unknownCredentialEntryInfo)),
168                 ),
169                 listOf(actionEntryInfo, actionEntryInfo),
170                 listOf(authenticationEntryInfo, authenticationEntryInfo)
171             )
172         )
173     }
174 
175     @Test
Returned multiple entry is grouped by display name if presentnull176     fun `Returned multiple entry is grouped by display name if present`() {
177         val testCred1 = createCredentialEntryInfo(displayName = "testDisplayName",
178             userName = "testUserName", credentialType = CredentialType.PASSWORD)
179         val testCred2 = createCredentialEntryInfo(displayName = "testDisplayName",
180             userName = "testUserName", credentialType = CredentialType.PASSKEY)
181         val getCredentialUiState = Request.Get(
182             token = null,
183             resultReceiver = null,
184             providerInfos = listOf(createProviderInfo(credentialList1),
185                 createProviderInfo(credentialList2),
186                 createProviderInfo(listOf(testCred1, testCred2))))
187             .toGet(isPrimary = false)
188 
189         assertThat(getCredentialUiState).isEqualTo(
190             CredentialSelectorUiState.Get.MultipleEntry(
191                 listOf(
192                     PerNameEntries("userName",
193                         listOf(
194                             recentlyUsedPasskeyCredential, // from provider 2
195                             passkeyCredentialEntryInfo, // from provider 1 or 2
196                             passkeyCredentialEntryInfo, // from provider 1 or 2
197                             recentlyUsedPasswordCredential, // from provider 2
198                             passwordCredentialEntryInfo, // from provider 1 or 2
199                             passwordCredentialEntryInfo, // from provider 1 or 2
200                         )),
201                     PerNameEntries("userName2", listOf(unknownCredentialEntryInfo)),
202                     PerNameEntries("testDisplayName", listOf(testCred2, testCred1)),
203                 ),
204                 listOf(actionEntryInfo, actionEntryInfo, actionEntryInfo),
205                 listOf(authenticationEntryInfo, authenticationEntryInfo, authenticationEntryInfo)
206             )
207         )
208     }
209 
createCredentialEntryInfonull210     fun createCredentialEntryInfo(
211         userName: String,
212         displayName: String? = null,
213         credentialType: CredentialType = CredentialType.PASSKEY,
214         lastUsedTimeMillis: Long = 0L
215     ): CredentialEntryInfo =
216         CredentialEntryInfo(
217             providerId = "",
218             entryKey = "",
219             entrySubkey = "",
220             pendingIntent = null,
221             fillInIntent = null,
222             credentialType = credentialType,
223             rawCredentialType = "",
224             credentialTypeDisplayName = "",
225             providerDisplayName = "",
226             userName = userName,
227             displayName = displayName,
228             icon = mDrawable,
229             shouldTintIcon = false,
230             lastUsedTimeMillis = Instant.ofEpochMilli(lastUsedTimeMillis),
231             isAutoSelectable = true,
232             entryGroupId = "",
233             isDefaultIconPreferredAsSingleProvider = false,
234             affiliatedDomain = "",
235         )
236 
237     fun createProviderInfo(credentials: List<CredentialEntryInfo> = listOf()): ProviderInfo =
238         ProviderInfo(
239             id = "providerInfo",
240             icon = mDrawable,
241             displayName = "displayName",
242             credentialEntryList = credentials,
243             authenticationEntryList = listOf(authenticationEntryInfo),
244             remoteEntry = null,
245             actionEntryList = listOf(actionEntryInfo)
246         )
247 }
248