BIT-897 Decrypt sync response (#181)

This commit is contained in:
Ramsey Smith
2023-10-31 08:06:36 -06:00
committed by GitHub
parent d0dacfb4ed
commit 10a20f626d
27 changed files with 1814 additions and 47 deletions

View File

@@ -2,7 +2,7 @@ package com.x8bit.bitwarden.data.platform.base
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import com.x8bit.bitwarden.data.platform.datasource.network.core.ResultCallAdapterFactory
import kotlinx.serialization.json.Json
import com.x8bit.bitwarden.data.platform.datasource.network.di.PlatformNetworkModule
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.mockwebserver.MockWebServer
import org.junit.After
@@ -13,12 +13,14 @@ import retrofit2.Retrofit
*/
abstract class BaseServiceTest {
private val json = PlatformNetworkModule.providesJson()
protected val server = MockWebServer().apply { start() }
protected val retrofit: Retrofit = Retrofit.Builder()
.baseUrl(server.url("/").toString())
.addCallAdapterFactory(ResultCallAdapterFactory())
.addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
.build()
@After

View File

@@ -0,0 +1,134 @@
package com.x8bit.bitwarden.data.vault.datasource.network.model
import java.time.LocalDateTime
/**
* Create a mock [SyncResponseJson.Cipher] with a given [number].
*/
fun createMockCipher(number: Int): SyncResponseJson.Cipher =
SyncResponseJson.Cipher(
id = "mockId-$number",
organizationId = "mockOrganizationId-$number",
folderId = "mockFolderId-$number",
collectionIds = listOf("mockCollectionId-$number"),
name = "mockName-$number",
notes = "mockNotes-$number",
type = CipherTypeJson.LOGIN,
login = createMockLogin(number = number),
creationDate = LocalDateTime.parse("2023-10-27T12:00:00"),
deletedDate = LocalDateTime.parse("2023-10-27T12:00:00"),
revisionDate = LocalDateTime.parse("2023-10-27T12:00:00"),
attachments = listOf(createMockAttachment(number = number)),
card = createMockCard(number = number),
fields = listOf(createMockField(number = number)),
identity = createMockIdentity(number = number),
isFavorite = false,
passwordHistory = listOf(createMockPasswordHistory(number = number)),
reprompt = CipherRepromptTypeJson.NONE,
secureNote = createMockSecureNote(),
shouldEdit = false,
shouldOrganizationUseTotp = false,
shouldViewPassword = false,
)
/**
* Create a mock [SyncResponseJson.Cipher.Identity] with a given [number].
*/
fun createMockIdentity(number: Int): SyncResponseJson.Cipher.Identity =
SyncResponseJson.Cipher.Identity(
firstName = "mockFirstName-$number",
middleName = "mockMiddleName-$number",
lastName = "mockLastName-$number",
passportNumber = "mockPassportNumber-$number",
country = "mockCountry-$number",
address1 = "mockAddress1-$number",
address2 = "mockAddress2-$number",
address3 = "mockAddress3-$number",
city = "mockCity-$number",
postalCode = "mockPostalCode-$number",
title = "mockTitle-$number",
ssn = "mockSsn-$number",
phone = "mockPhone-$number",
company = "mockCompany-$number",
licenseNumber = "mockLicenseNumber-$number",
state = "mockState-$number",
email = "mockEmail-$number",
username = "mockUsername-$number",
)
/**
* Create a mock [SyncResponseJson.Cipher.Attachment] with a given [number].
*/
fun createMockAttachment(number: Int): SyncResponseJson.Cipher.Attachment =
SyncResponseJson.Cipher.Attachment(
fileName = "mockFileName-$number",
size = 1,
sizeName = "mockSizeName-$number",
id = "mockId-$number",
url = "mockUrl-$number",
key = "mockKey-$number",
)
/**
* Create a mock [SyncResponseJson.Cipher.Card] with a given [number].
*/
fun createMockCard(number: Int): SyncResponseJson.Cipher.Card =
SyncResponseJson.Cipher.Card(
number = "mockNumber-$number",
expMonth = "mockExpMonth-$number",
code = "mockCode-$number",
expirationYear = "mockExpirationYear-$number",
cardholderName = "mockCardholderName-$number",
brand = "mockBrand-$number",
)
/**
* Create a mock [SyncResponseJson.Cipher.PasswordHistory] with a given [number].
*/
fun createMockPasswordHistory(number: Int): SyncResponseJson.Cipher.PasswordHistory =
SyncResponseJson.Cipher.PasswordHistory(
password = "mockPassword-$number",
lastUsedDate = LocalDateTime.parse("2023-10-27T12:00:00"),
)
/**
* Create a mock [SyncResponseJson.Cipher.SecureNote].
*/
fun createMockSecureNote(): SyncResponseJson.Cipher.SecureNote =
SyncResponseJson.Cipher.SecureNote(
type = SecureNoteTypeJson.GENERIC,
)
/**
* Create a mock [SyncResponseJson.Cipher.Field] with a given [number].
*/
fun createMockField(number: Int): SyncResponseJson.Cipher.Field =
SyncResponseJson.Cipher.Field(
linkedIdType = LinkedIdTypeJson.LOGIN_USERNAME,
name = "mockName-$number",
type = FieldTypeJson.HIDDEN,
value = "mockValue-$number",
)
/**
* Create a mock [SyncResponseJson.Cipher.Login] with a given [number].
*/
fun createMockLogin(number: Int): SyncResponseJson.Cipher.Login =
SyncResponseJson.Cipher.Login(
username = "mockUsername-$number",
password = "mockPassword-$number",
passwordRevisionDate = LocalDateTime.parse("2023-10-27T12:00:00"),
shouldAutofillOnPageLoad = false,
uri = "mockUri-$number",
uris = listOf(createMockUri(number = number)),
totp = "mockTotp-$number",
)
/**
* Create a mock [SyncResponseJson.Cipher.Login.Uri] with a given [number].
*/
fun createMockUri(number: Int): SyncResponseJson.Cipher.Login.Uri =
SyncResponseJson.Cipher.Login.Uri(
uri = "mockUri-$number",
uriMatchType = UriMatchTypeJson.HOST,
)

View File

@@ -0,0 +1,14 @@
package com.x8bit.bitwarden.data.vault.datasource.network.model
/**
* Create a mock [SyncResponseJson.Collection] with a given [number].
*/
fun createMockCollection(number: Int): SyncResponseJson.Collection =
SyncResponseJson.Collection(
organizationId = "mockOrganizationId-$number",
shouldHidePasswords = false,
name = "mockName-$number",
externalId = "mockExternalId-$number",
isReadOnly = false,
id = "mockId-$number",
)

View File

@@ -0,0 +1,22 @@
package com.x8bit.bitwarden.data.vault.datasource.network.model
/**
* Create a mock [SyncResponseJson.Domains] with a given [number].
*/
fun createMockDomains(number: Int): SyncResponseJson.Domains =
SyncResponseJson.Domains(
globalEquivalentDomains = listOf(
SyncResponseJson.Domains.GlobalEquivalentDomain(
isExcluded = false,
domains = listOf(
"mockDomain-$number",
),
type = 1,
),
),
equivalentDomains = listOf(
listOf(
"mockEquivalentDomain-$number",
),
),
)

View File

@@ -0,0 +1,13 @@
package com.x8bit.bitwarden.data.vault.datasource.network.model
import java.time.LocalDateTime
/**
* Create a mock [SyncResponseJson.Folder] with a given [number].
*/
fun createMockFolder(number: Int): SyncResponseJson.Folder =
SyncResponseJson.Folder(
id = "mockId-$number",
name = "mockName-$number",
revisionDate = LocalDateTime.parse("2023-10-27T12:00:00"),
)

View File

@@ -0,0 +1,12 @@
package com.x8bit.bitwarden.data.vault.datasource.network.model
/**
* Create a mock [SyncResponseJson.Policy] with a given [number].
*/
fun createMockPolicy(number: Int): SyncResponseJson.Policy =
SyncResponseJson.Policy(
organizationId = "mockOrganizationId-$number",
id = "mockId-$number",
type = PolicyTypeJson.MASTER_PASSWORD,
isEnabled = false,
)

View File

@@ -0,0 +1,115 @@
package com.x8bit.bitwarden.data.vault.datasource.network.model
import java.time.LocalDateTime
/**
* Create a mock [SyncResponseJson.Profile] with a given [number].
*/
fun createMockProfile(number: Int): SyncResponseJson.Profile =
SyncResponseJson.Profile(
providerOrganizations = listOf(createMockOrganization(number = number)),
isPremiumFromOrganization = false,
shouldForcePasswordReset = false,
avatarColor = "mockAvatarColor-$number",
isEmailVerified = false,
isTwoFactorEnabled = false,
privateKey = "mockPrivateKey-$number",
isPremium = false,
culture = "mockCulture-$number",
name = "mockName-$number",
organizations = listOf(createMockOrganization(number = number)),
shouldUseKeyConnector = false,
id = "mockId-$number",
masterPasswordHint = "mockMasterPasswordHint-$number",
email = "mockEmail-$number",
key = "mockKey-$number",
securityStamp = "mockSecurityStamp-$number",
providers = listOf(createMockProvider(number = number)),
)
/**
* Create a mock [SyncResponseJson.Profile.Organization] with a given [number].
*/
fun createMockOrganization(number: Int): SyncResponseJson.Profile.Organization =
SyncResponseJson.Profile.Organization(
shouldUsePolicies = false,
keyConnectorUrl = "mockKeyConnectorUrl-$number",
type = 1,
seats = 1,
isEnabled = false,
providerType = 1,
isResetPasswordEnrolled = false,
shouldUseSecretsManager = false,
maxCollections = 1,
isSelfHost = false,
shouldUseKeyConnector = false,
permissions = createMockPermissions(),
hasPublicAndPrivateKeys = false,
providerId = "mockProviderId-$number",
id = "mockId-$number",
shouldUseGroups = false,
shouldUseDirectory = false,
key = "mockKey-$number",
providerName = "mockProviderName-$number",
shouldUsersGetPremium = false,
maxStorageGb = 1,
identifier = "mockIdentifier-$number",
shouldUseSso = false,
shouldUseCustomPermissions = false,
isFamilySponsorshipAvailable = false,
shouldUseResetPassword = false,
planProductType = 1,
accessSecretsManager = false,
use2fa = false,
familySponsorshipToDelete = false,
userId = "mockUserId-$number",
shouldUseActivateAutofillPolicy = false,
shouldUseEvents = false,
familySponsorshipFriendlyName = "mockFamilySponsorshipFriendlyName-$number",
isKeyConnectorEnabled = false,
shouldUseTotp = false,
familySponsorshipLastSyncDate = LocalDateTime.parse("2023-10-27T12:00:00"),
shouldUseScim = false,
name = "mockName-$number",
shouldUseApi = false,
isSsoBound = false,
familySponsorshipValidUntil = LocalDateTime.parse("2023-10-27T12:00:00"),
status = 1,
)
/**
* Create a mock [SyncResponseJson.Profile.Permissions].
*/
fun createMockPermissions(): SyncResponseJson.Profile.Permissions =
SyncResponseJson.Profile.Permissions(
shouldManageGroups = false,
shouldManageResetPassword = false,
shouldAccessReports = false,
shouldManagePolicies = false,
shouldDeleteAnyCollection = false,
shouldManageSso = false,
shouldDeleteAssignedCollections = false,
shouldManageUsers = false,
shouldManageScim = false,
shouldAccessImportExport = false,
shouldEditAnyCollection = false,
shouldAccessEventLogs = false,
shouldCreateNewCollections = false,
shouldEditAssignedCollections = false,
)
/**
* Create a mock [SyncResponseJson.Profile.Provider] with a given [number].
*/
fun createMockProvider(number: Int): SyncResponseJson.Profile.Provider =
SyncResponseJson.Profile.Provider(
shouldUseEvents = false,
permissions = createMockPermissions(),
name = "mockName-$number",
id = "mockId-$number",
type = 1,
userId = "mockUserId-$number",
key = "mockKey-$number",
isEnabled = false,
status = 1,
)

View File

@@ -0,0 +1,37 @@
package com.x8bit.bitwarden.data.vault.datasource.network.model
import java.time.LocalDateTime
fun createMockSend(number: Int): SyncResponseJson.Send =
SyncResponseJson.Send(
accessCount = 1,
notes = "mockNotes-$number",
revisionDate = LocalDateTime.parse("2023-10-27T12:00:00"),
maxAccessCount = 1,
shouldHideEmail = false,
type = SendTypeJson.FILE,
accessId = "mockAccessId-$number",
password = "mockPassword-$number",
file = createMockFile(number = 1),
deletionDate = LocalDateTime.parse("2023-10-27T12:00:00"),
name = "mockName-$number",
isDisabled = false,
id = "mockId-$number",
text = createMockText(number = number),
key = "mockKey-$number",
expirationDate = LocalDateTime.parse("2023-10-27T12:00:00"),
)
fun createMockFile(number: Int): SyncResponseJson.Send.File =
SyncResponseJson.Send.File(
fileName = "mockFileName-$number",
size = 1,
sizeName = "mockSizeName-$number",
id = "mockId-$number",
)
fun createMockText(number: Int): SyncResponseJson.Send.Text =
SyncResponseJson.Send.Text(
isHidden = false,
text = "mockText-$number",
)

View File

@@ -0,0 +1,375 @@
package com.x8bit.bitwarden.data.vault.datasource.network.service
import com.x8bit.bitwarden.data.platform.base.BaseServiceTest
import com.x8bit.bitwarden.data.vault.datasource.network.api.SyncApi
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockCipher
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockCollection
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockDomains
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockFolder
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockPolicy
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockProfile
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockSend
import kotlinx.coroutines.test.runTest
import okhttp3.mockwebserver.MockResponse
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import retrofit2.create
class SyncServiceTest : BaseServiceTest() {
private val syncApi: SyncApi = retrofit.create()
private val syncService: SyncService = SyncServiceImpl(
syncApi = syncApi,
)
@Test
fun `sync should return the correct response`() = runTest {
server.enqueue(MockResponse().setBody(SYNC_SUCCESS_JSON))
val result = syncService.sync()
assertEquals(SYNC_SUCCESS, result.getOrThrow())
}
}
private const val SYNC_SUCCESS_JSON = """
{
"profile": {
"id": "mockId-1",
"name": "mockName-1",
"email": "mockEmail-1",
"emailVerified": false,
"premium": false,
"premiumFromOrganization": false,
"masterPasswordHint": "mockMasterPasswordHint-1",
"culture": "mockCulture-1",
"twoFactorEnabled": false,
"key": "mockKey-1",
"privateKey": "mockPrivateKey-1",
"securityStamp": "mockSecurityStamp-1",
"forcePasswordReset": false,
"usesKeyConnector": false,
"avatarColor": "mockAvatarColor-1",
"organizations": [
{
"usePolicies": false,
"keyConnectorUrl": "mockKeyConnectorUrl-1",
"type": 1,
"seats": 1,
"enabled": false,
"providerType": 1,
"resetPasswordEnrolled": false,
"useSecretsManager": false,
"maxCollections": 1,
"selfHost": false,
"useKeyConnector": false,
"permissions": {
"manageGroups": false,
"manageResetPassword": false,
"accessReports": false,
"managePolicies": false,
"deleteAnyCollection": false,
"manageSso": false,
"deleteAssignedCollections": false,
"manageUsers": false,
"manageScim": false,
"accessImportExport": false,
"editAnyCollection": false,
"accessEventLogs": false,
"createNewCollections": false,
"editAssignedCollections": false
},
"hasPublicAndPrivateKeys": false,
"providerId": "mockProviderId-1",
"id": "mockId-1",
"useGroups": false,
"useDirectory": false,
"key": "mockKey-1",
"providerName": "mockProviderName-1",
"usersGetPremium": false,
"maxStorageGb": 1,
"identifier": "mockIdentifier-1",
"useSso": false,
"useCustomPermissions": false,
"familySponsorshipAvailable": false,
"useResetPassword": false,
"planProductType": 1,
"accessSecretsManager": false,
"use2fa": false,
"familySponsorshipToDelete": false,
"userId": "mockUserId-1",
"useActivateAutofillPolicy": false,
"useEvents": false,
"familySponsorshipFriendlyName": "mockFamilySponsorshipFriendlyName-1",
"keyConnectorEnabled": false,
"useTotp": false,
"familySponsorshipLastSyncDate": "2023-10-27T12:00:00.00Z",
"useScim": false,
"name": "mockName-1",
"useApi": false,
"ssoBound": false,
"familySponsorshipValidUntil": "2023-10-27T12:00:00.00Z",
"status": 1
}
],
"providers": [
{
"useEvents": false,
"permissions": {
"manageGroups": false,
"manageResetPassword": false,
"accessReports": false,
"managePolicies": false,
"deleteAnyCollection": false,
"manageSso": false,
"deleteAssignedCollections": false,
"manageUsers": false,
"manageScim": false,
"accessImportExport": false,
"editAnyCollection": false,
"accessEventLogs": false,
"createNewCollections": false,
"editAssignedCollections": false
},
"name": "mockName-1",
"id": "mockId-1",
"type": 1,
"userId": "mockUserId-1",
"key": "mockKey-1",
"enabled": false,
"status": 1
}
],
"providerOrganizations": [
{
"usePolicies": false,
"keyConnectorUrl": "mockKeyConnectorUrl-1",
"type": 1,
"seats": 1,
"enabled": false,
"providerType": 1,
"resetPasswordEnrolled": false,
"useSecretsManager": false,
"maxCollections": 1,
"selfHost": false,
"useKeyConnector": false,
"permissions": {
"manageGroups": false,
"manageResetPassword": false,
"accessReports": false,
"managePolicies": false,
"deleteAnyCollection": false,
"manageSso": false,
"deleteAssignedCollections": false,
"manageUsers": false,
"manageScim": false,
"accessImportExport": false,
"editAnyCollection": false,
"accessEventLogs": false,
"createNewCollections": false,
"editAssignedCollections": false
},
"hasPublicAndPrivateKeys": false,
"providerId": "mockProviderId-1",
"id": "mockId-1",
"useGroups": false,
"useDirectory": false,
"key": "mockKey-1",
"providerName": "mockProviderName-1",
"usersGetPremium": false,
"maxStorageGb": 1,
"identifier": "mockIdentifier-1",
"useSso": false,
"useCustomPermissions": false,
"familySponsorshipAvailable": false,
"useResetPassword": false,
"planProductType": 1,
"accessSecretsManager": false,
"use2fa": false,
"familySponsorshipToDelete": false,
"userId": "mockUserId-1",
"useActivateAutofillPolicy": false,
"useEvents": false,
"familySponsorshipFriendlyName": "mockFamilySponsorshipFriendlyName-1",
"keyConnectorEnabled": false,
"useTotp": false,
"familySponsorshipLastSyncDate": "2023-10-27T12:00:00.00Z",
"useScim": false,
"name": "mockName-1",
"useApi": false,
"ssoBound": false,
"familySponsorshipValidUntil": "2023-10-27T12:00:00.00Z",
"status": 1
}
]
},
"folders": [
{
"revisionDate": "2023-10-27T12:00:00.00Z",
"name": "mockName-1",
"id": "mockId-1"
}
],
"collections": [
{
"organizationId": "mockOrganizationId-1",
"hidePasswords": false,
"name": "mockName-1",
"externalId": "mockExternalId-1",
"readOnly": false,
"id": "mockId-1"
}
],
"ciphers": [
{
"notes": "mockNotes-1",
"attachments": [
{
"fileName": "mockFileName-1",
"size": 1,
"sizeName": "mockSizeName-1",
"id": "mockId-1",
"url": "mockUrl-1",
"key": "mockKey-1"
}
],
"organizationUseTotp": false,
"reprompt": 0,
"edit": false,
"passwordHistory": [
{
"password": "mockPassword-1",
"lastUsedDate": "2023-10-27T12:00:00.00Z"
}
],
"revisionDate": "2023-10-27T12:00:00.00Z",
"type": 1,
"login": {
"uris": [
{
"match": 1,
"uri": "mockUri-1"
}
],
"totp": "mockTotp-1",
"password": "mockPassword-1",
"passwordRevisionDate": "2023-10-27T12:00:00.00Z",
"autofillOnPageLoad": false,
"uri": "mockUri-1",
"username": "mockUsername-1"
},
"creationDate": "2023-10-27T12:00:00.00Z",
"secureNote": {
"type": 0
},
"folderId": "mockFolderId-1",
"organizationId": "mockOrganizationId-1",
"deletedDate": "2023-10-27T12:00:00.00Z",
"identity": {
"passportNumber": "mockPassportNumber-1",
"lastName": "mockLastName-1",
"address3": "mockAddress3-1",
"address2": "mockAddress2-1",
"city": "mockCity-1",
"country": "mockCountry-1",
"address1": "mockAddress1-1",
"postalCode": "mockPostalCode-1",
"title": "mockTitle-1",
"ssn": "mockSsn-1",
"firstName": "mockFirstName-1",
"phone": "mockPhone-1",
"middleName": "mockMiddleName-1",
"company": "mockCompany-1",
"licenseNumber": "mockLicenseNumber-1",
"state": "mockState-1",
"email": "mockEmail-1",
"username": "mockUsername-1"
},
"collectionIds": [
"mockCollectionId-1"
],
"name": "mockName-1",
"id": "mockId-1"
"fields": [
{
"linkedId": 100,
"name": "mockName-1",
"type": 1,
"value": "mockValue-1"
}
],
"viewPassword": false,
"favorite": false,
"card": {
"number": "mockNumber-1",
"expMonth": "mockExpMonth-1",
"code": "mockCode-1",
"expYear": "mockExpirationYear-1",
"cardholderName": "mockCardholderName-1",
"brand": "mockBrand-1"
}
}
],
"domains": {
"equivalentDomains": [
[
"mockEquivalentDomain-1"
]
],
"globalEquivalentDomains": [
{
"type": 1,
"domains": [
"mockDomain-1"
],
"excluded": false
}
]
},
"policies": [
{
"organizationId": "mockOrganizationId-1",
"id": "mockId-1",
"type": 1,
"enabled": false
}
],
"sends": [
{
"accessCount": 1,
"notes": "mockNotes-1",
"revisionDate": "2023-10-27T12:00:00.00Z",
"maxAccessCount": 1,
"hideEmail": false,
"type": 1,
"accessId": "mockAccessId-1",
"password": "mockPassword-1",
"file": {
"fileName": "mockFileName-1",
"size": 1,
"sizeName": "mockSizeName-1",
"id": "mockId-1"
},
"deletionDate": "2023-10-27T12:00:00.00Z",
"name": "mockName-1",
"disabled": false,
"id": "mockId-1",
"text": {
"hidden": false,
"text": "mockText-1"
},
"key": "mockKey-1",
"expirationDate": "2023-10-27T12:00:00.00Z"
}
]
}
"""
private val SYNC_SUCCESS = SyncResponseJson(
folders = listOf(createMockFolder(number = 1)),
collections = listOf(createMockCollection(number = 1)),
profile = createMockProfile(number = 1),
ciphers = listOf(createMockCipher(number = 1)),
policies = listOf(createMockPolicy(number = 1)),
domains = createMockDomains(number = 1),
sends = listOf(createMockSend(number = 1)),
)

View File

@@ -0,0 +1,159 @@
package com.x8bit.bitwarden.data.vault.datasource.sdk
import com.bitwarden.core.Attachment
import com.bitwarden.core.Card
import com.bitwarden.core.Cipher
import com.bitwarden.core.CipherRepromptType
import com.bitwarden.core.CipherType
import com.bitwarden.core.Field
import com.bitwarden.core.FieldType
import com.bitwarden.core.Identity
import com.bitwarden.core.Login
import com.bitwarden.core.LoginUri
import com.bitwarden.core.PasswordHistory
import com.bitwarden.core.SecureNote
import com.bitwarden.core.SecureNoteType
import com.bitwarden.core.UriMatchType
import java.time.LocalDateTime
import java.time.ZoneOffset
/**
* Create a mock [Cipher] with a given [number].
*/
fun createMockSdkCipher(number: Int): Cipher =
Cipher(
id = "mockId-$number",
organizationId = "mockOrganizationId-$number",
folderId = "mockFolderId-$number",
collectionIds = listOf("mockCollectionId-$number"),
name = "mockName-$number",
notes = "mockNotes-$number",
type = CipherType.LOGIN,
login = createMockSdkLogin(number = number),
creationDate = LocalDateTime
.parse("2023-10-27T12:00:00")
.toInstant(ZoneOffset.UTC),
deletedDate = LocalDateTime
.parse("2023-10-27T12:00:00")
.toInstant(ZoneOffset.UTC),
revisionDate = LocalDateTime
.parse("2023-10-27T12:00:00")
.toInstant(ZoneOffset.UTC),
attachments = listOf(createMockSdkAttachment(number = number)),
card = createMockSdkCard(number = number),
fields = listOf(createMockSdkField(number = number)),
identity = createMockSdkIdentity(number = number),
favorite = false,
passwordHistory = listOf(createMockSdkPasswordHistory(number = number)),
reprompt = CipherRepromptType.NONE,
secureNote = createMockSdkSecureNote(),
edit = false,
organizationUseTotp = false,
viewPassword = false,
localData = null,
)
/**
* Create a mock [SecureNote] with a given [number].
*/
fun createMockSdkSecureNote(): SecureNote =
SecureNote(
type = SecureNoteType.GENERIC,
)
/**
* Create a mock [PasswordHistory] with a given [number].
*/
fun createMockSdkPasswordHistory(number: Int): PasswordHistory =
PasswordHistory(
password = "mockPassword-$number",
lastUsedDate = LocalDateTime
.parse("2023-10-27T12:00:00")
.toInstant(ZoneOffset.UTC),
)
/**
* Create a mock [Identity] with a given [number].
*/
fun createMockSdkIdentity(number: Int): Identity =
Identity(
firstName = "mockFirstName-$number",
middleName = "mockMiddleName-$number",
lastName = "mockLastName-$number",
passportNumber = "mockPassportNumber-$number",
country = "mockCountry-$number",
address1 = "mockAddress1-$number",
address2 = "mockAddress2-$number",
address3 = "mockAddress3-$number",
city = "mockCity-$number",
postalCode = "mockPostalCode-$number",
title = "mockTitle-$number",
ssn = "mockSsn-$number",
phone = "mockPhone-$number",
company = "mockCompany-$number",
licenseNumber = "mockLicenseNumber-$number",
state = "mockState-$number",
email = "mockEmail-$number",
username = "mockUsername-$number",
)
/**
* Create a mock [Field] with a given [number].
*/
fun createMockSdkField(number: Int): Field =
Field(
linkedId = 100U,
name = "mockName-$number",
type = FieldType.HIDDEN,
value = "mockValue-$number",
)
/**
* Create a mock [Card] with a given [number].
*/
fun createMockSdkCard(number: Int): Card =
Card(
number = "mockNumber-$number",
expMonth = "mockExpMonth-$number",
code = "mockCode-$number",
expYear = "mockExpirationYear-$number",
cardholderName = "mockCardholderName-$number",
brand = "mockBrand-$number",
)
/**
* Create a mock [Attachment] with a given [number].
*/
fun createMockSdkAttachment(number: Int): Attachment =
Attachment(
fileName = "mockFileName-$number",
size = "1",
sizeName = "mockSizeName-$number",
id = "mockId-$number",
url = "mockUrl-$number",
key = "mockKey-$number",
)
/**
* Create a mock [Login] with a given [number].
*/
fun createMockSdkLogin(number: Int): Login =
Login(
username = "mockUsername-$number",
password = "mockPassword-$number",
passwordRevisionDate = LocalDateTime
.parse("2023-10-27T12:00:00")
.toInstant(ZoneOffset.UTC),
autofillOnPageLoad = false,
uris = listOf(createMockSdkUri(number = number)),
totp = "mockTotp-$number",
)
/**
* Create a mock [LoginUri] with a given [number].
*/
fun createMockSdkUri(number: Int): LoginUri =
LoginUri(
uri = "mockUri-$number",
match = UriMatchType.HOST,
)

View File

@@ -0,0 +1,17 @@
package com.x8bit.bitwarden.data.vault.datasource.sdk
import com.bitwarden.core.Folder
import java.time.LocalDateTime
import java.time.ZoneOffset
/**
* Create a mock [Folder] with a given [number].
*/
fun createMockSdkFolder(number: Int): Folder =
Folder(
id = "mockId-$number",
name = "mockName-$number",
revisionDate = LocalDateTime
.parse("2023-10-27T12:00:00")
.toInstant(ZoneOffset.UTC),
)

View File

@@ -0,0 +1,115 @@
package com.x8bit.bitwarden.data.vault.datasource.sdk
import com.bitwarden.core.Cipher
import com.bitwarden.core.CipherListView
import com.bitwarden.core.CipherView
import com.bitwarden.core.Folder
import com.bitwarden.core.FolderView
import com.bitwarden.sdk.ClientVault
import com.x8bit.bitwarden.data.platform.util.asSuccess
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.mockk
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.jupiter.api.Test
class VaultSdkSourceTest {
private val clientVault = mockk<ClientVault>()
private val vaultSdkSource: VaultSdkSource = VaultSdkSourceImpl(
clientVault = clientVault,
)
@Test
fun `Cipher decrypt should call SDK and return a Result with correct data`() = runBlocking {
val mockCipher = mockk<Cipher>()
val expectedResult = mockk<CipherView>()
coEvery {
clientVault.ciphers().decrypt(
cipher = mockCipher,
)
} returns expectedResult
val result = vaultSdkSource.decryptCipher(
cipher = mockCipher,
)
assertEquals(
expectedResult.asSuccess(),
result,
)
coVerify {
clientVault.ciphers().decrypt(
cipher = mockCipher,
)
}
}
@Test
fun `Cipher decryptList should call SDK and return a Result with correct data`() = runBlocking {
val mockCiphers = mockk<List<Cipher>>()
val expectedResult = mockk<List<CipherListView>>()
coEvery {
clientVault.ciphers().decryptList(
ciphers = mockCiphers,
)
} returns expectedResult
val result = vaultSdkSource.decryptCipherList(
cipherList = mockCiphers,
)
assertEquals(
expectedResult.asSuccess(),
result,
)
coVerify {
clientVault.ciphers().decryptList(
ciphers = mockCiphers,
)
}
}
@Test
fun `Folder decrypt should call SDK and return a Result with correct data`() = runBlocking {
val mockFolder = mockk<Folder>()
val expectedResult = mockk<FolderView>()
coEvery {
clientVault.folders().decrypt(
folder = mockFolder,
)
} returns expectedResult
val result = vaultSdkSource.decryptFolder(
folder = mockFolder,
)
assertEquals(
expectedResult.asSuccess(),
result,
)
coVerify {
clientVault.folders().decrypt(
folder = mockFolder,
)
}
}
@Test
fun `Folder decryptList should call SDK and return a Result with correct data`() = runBlocking {
val mockFolders = mockk<List<Folder>>()
val expectedResult = mockk<List<FolderView>>()
coEvery {
clientVault.folders().decryptList(
folders = mockFolders,
)
} returns expectedResult
val result = vaultSdkSource.decryptFolderList(
folderList = mockFolders,
)
assertEquals(
expectedResult.asSuccess(),
result,
)
coVerify {
clientVault.folders().decryptList(
folders = mockFolders,
)
}
}
}

View File

@@ -0,0 +1,244 @@
package com.x8bit.bitwarden.data.vault.repository.util
import com.bitwarden.core.CipherRepromptType
import com.bitwarden.core.CipherType
import com.bitwarden.core.FieldType
import com.bitwarden.core.UriMatchType
import com.x8bit.bitwarden.data.vault.datasource.network.model.CipherRepromptTypeJson
import com.x8bit.bitwarden.data.vault.datasource.network.model.CipherTypeJson
import com.x8bit.bitwarden.data.vault.datasource.network.model.FieldTypeJson
import com.x8bit.bitwarden.data.vault.datasource.network.model.UriMatchTypeJson
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockAttachment
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockCard
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockCipher
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockField
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockIdentity
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockLogin
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockPasswordHistory
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockSecureNote
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockUri
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkAttachment
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkCard
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkCipher
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkField
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkIdentity
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkLogin
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkPasswordHistory
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkSecureNote
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkUri
import org.junit.Assert.assertEquals
import org.junit.Test
class VaultSdkCipherExtensionsTest {
@Test
fun `toEncryptedSdkCipherList should convert list of Network Cipher to List of Sdk Cipher`() {
val syncCiphers = listOf(
createMockCipher(number = 1),
createMockCipher(number = 2),
)
val sdkCiphers = syncCiphers.toEncryptedSdkCipherList()
assertEquals(
listOf(
createMockSdkCipher(number = 1),
createMockSdkCipher(number = 2),
),
sdkCiphers,
)
}
@Test
fun `toEncryptedSdkCipher should convert a SyncResponseJson Cipher to a Cipher`() {
val syncCipher = createMockCipher(number = 1)
val sdkCipher = syncCipher.toEncryptedSdkCipher()
assertEquals(
createMockSdkCipher(number = 1),
sdkCipher,
)
}
@Test
fun `toSdkLogin should convert a SyncResponseJson Cipher Login to a Login`() {
val syncLogin = createMockLogin(number = 1)
val sdkLogin = syncLogin.toSdkLogin()
assertEquals(
createMockSdkLogin(number = 1),
sdkLogin,
)
}
@Test
fun `toSdkIdentity should convert a SyncResponseJson Cipher Identity to a Identity`() {
val syncIdentity = createMockIdentity(number = 1)
val sdkIdentity = syncIdentity.toSdkIdentity()
assertEquals(
createMockSdkIdentity(number = 1),
sdkIdentity,
)
}
@Test
fun `toSdkCard should convert a SyncResponseJson Cipher Card to a Card`() {
val syncCard = createMockCard(number = 1)
val sdkCard = syncCard.toSdkCard()
assertEquals(
createMockSdkCard(number = 1),
sdkCard,
)
}
@Test
fun `toSdkSecureNote should convert a SyncResponseJson Cipher SecureNote to a SecureNote`() {
val syncSecureNote = createMockSecureNote()
val sdkSecureNote = syncSecureNote.toSdkSecureNote()
assertEquals(
createMockSdkSecureNote(),
sdkSecureNote,
)
}
@Test
fun `toSdkLoginUriList should convert list of LoginUri to List of Sdk LoginUri`() {
val syncLoginUris = listOf(
createMockUri(number = 1),
createMockUri(number = 2),
)
val sdkLoginUris = syncLoginUris.toSdkLoginUriList()
assertEquals(
listOf(
createMockSdkUri(number = 1),
createMockSdkUri(number = 2),
),
sdkLoginUris,
)
}
@Test
fun `toSdkLoginUri should convert Network Cipher LoginUri to Sdk LoginUri`() {
val syncLoginUri = createMockUri(number = 1)
val sdkLoginUri = syncLoginUri.toSdkLoginUri()
assertEquals(
createMockSdkUri(number = 1),
sdkLoginUri,
)
}
@Test
fun `toSdkAttachmentList should convert list of Attachment to List of Sdk Attachment`() {
val syncAttachments = listOf(
createMockAttachment(number = 1),
createMockAttachment(number = 2),
)
val sdkAttachments = syncAttachments.toSdkAttachmentList()
assertEquals(
listOf(
createMockSdkAttachment(number = 1),
createMockSdkAttachment(number = 2),
),
sdkAttachments,
)
}
@Test
fun `toSdkAttachment should convert Network Cipher Attachment to Sdk Attachment`() {
val syncAttachment = createMockAttachment(number = 1)
val sdkAttachment = syncAttachment.toSdkAttachment()
assertEquals(
createMockSdkAttachment(number = 1),
sdkAttachment,
)
}
@Test
fun `toSdkFieldList should convert list of Network Cipher Field to List of Sdk Field`() {
val syncFields = listOf(
createMockField(number = 1),
createMockField(number = 2),
)
val sdkFields = syncFields.toSdkFieldList()
assertEquals(
listOf(
createMockSdkField(number = 1),
createMockSdkField(number = 2),
),
sdkFields,
)
}
@Test
fun `toSdkField should convert Network Cipher Attachment to Sdk Attachment`() {
val syncField = createMockField(number = 1)
val sdkField = syncField.toSdkField()
assertEquals(
createMockSdkField(number = 1),
sdkField,
)
}
@Test
@Suppress("MaxLineLength")
fun `toSdkPasswordHistoryList should convert PasswordHistory list to Sdk PasswordHistory List`() {
val syncPasswordHistories = listOf(
createMockPasswordHistory(number = 1),
createMockPasswordHistory(number = 2),
)
val sdkPasswordHistories = syncPasswordHistories.toSdkPasswordHistoryList()
assertEquals(
listOf(
createMockSdkPasswordHistory(number = 1),
createMockSdkPasswordHistory(number = 2),
),
sdkPasswordHistories,
)
}
@Test
fun `toSdkPasswordHistory should convert PasswordHistory to Sdk PasswordHistory`() {
val syncPasswordHistory = createMockPasswordHistory(number = 1)
val sdkPasswordHistory = syncPasswordHistory.toSdkPasswordHistory()
assertEquals(
createMockSdkPasswordHistory(number = 1),
sdkPasswordHistory,
)
}
@Test
fun `toSdkCipherType should convert CipherTypeJson to CipherType`() {
val cipherType = CipherTypeJson.IDENTITY
val sdkCipherType = cipherType.toSdkCipherType()
assertEquals(
CipherType.IDENTITY,
sdkCipherType,
)
}
@Test
fun `toSdkMatchType should convert UriMatchTypeJson to UriMatchType`() {
val uriMatchType = UriMatchTypeJson.DOMAIN
val sdkUriMatchType = uriMatchType.toSdkMatchType()
assertEquals(
UriMatchType.DOMAIN,
sdkUriMatchType,
)
}
@Test
fun `toSdkRepromptType should convert CipherRepromptTypeJson to CipherRepromptType`() {
val repromptType = CipherRepromptTypeJson.NONE
val sdkRepromptType = repromptType.toSdkRepromptType()
assertEquals(
CipherRepromptType.NONE,
sdkRepromptType,
)
}
@Test
fun `toSdkFieldType should convert FieldTypeJson to FieldType`() {
val fieldType = FieldTypeJson.HIDDEN
val sdkFieldType = fieldType.toSdkFieldType()
assertEquals(
FieldType.HIDDEN,
sdkFieldType,
)
}
}

View File

@@ -0,0 +1,35 @@
package com.x8bit.bitwarden.data.vault.repository.util
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockFolder
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkFolder
import org.junit.Assert.assertEquals
import org.junit.Test
class VaultSdkFolderExtensionsTest {
@Test
fun `toEncryptedSdkFolderList should convert list of NetworkFolder to List of SdkFolder`() {
val syncFolders = listOf(
createMockFolder(number = 1),
createMockFolder(number = 2),
)
val sdkFolders = syncFolders.toEncryptedSdkFolderList()
assertEquals(
listOf(
createMockSdkFolder(number = 1),
createMockSdkFolder(number = 2),
),
sdkFolders,
)
}
@Test
fun `toEncryptedSdkFolder should convert a NetworkFolder to a SdkFolder`() {
val syncFolder = createMockFolder(number = 1)
val sdkFolder = syncFolder.toEncryptedSdkFolder()
assertEquals(
createMockSdkFolder(number = 1),
sdkFolder,
)
}
}