From 86b213aa8e4489a149f4541e8f8826d00cb253e1 Mon Sep 17 00:00:00 2001 From: Mick Letofsky Date: Tue, 4 Nov 2025 10:08:18 +0100 Subject: [PATCH] [PM-25820] - Migrate cipher data model to adhere to ts-strict (#17073) * Migrate cipher data model to adhere to ts-strict & added unit tests where applicable --- .../src/vault/models/data/attachment.data.ts | 14 ++- .../common/src/vault/models/data/card.data.ts | 14 ++- .../src/vault/models/data/cipher.data.ts | 37 ++++---- .../models/data/fido2-credential.data.ts | 28 +++--- .../src/vault/models/data/field.data.ts | 10 +- .../src/vault/models/data/identity.data.ts | 38 ++++---- .../src/vault/models/data/login-uri.data.ts | 8 +- .../src/vault/models/data/login.data.ts | 14 ++- .../models/data/password-history.data.ts | 6 +- .../src/vault/models/data/secure-note.data.ts | 4 +- .../src/vault/models/data/ssh-key.data.ts | 8 +- .../vault/models/domain/attachment.spec.ts | 6 ++ .../src/vault/models/domain/card.spec.ts | 7 ++ .../src/vault/models/domain/cipher.spec.ts | 46 ++++++++-- .../src/vault/models/domain/field.spec.ts | 2 +- .../src/vault/models/domain/identity.spec.ts | 21 +++++ .../src/vault/models/domain/login-uri.spec.ts | 23 ++++- .../src/vault/models/domain/login.spec.ts | 8 ++ libs/common/src/vault/models/domain/login.ts | 5 +- .../src/vault/models/domain/password.spec.ts | 46 ++++++++++ .../vault/models/domain/secure-note.spec.ts | 92 +++++++++++++++++-- .../src/vault/models/domain/ssh-key.spec.ts | 73 +++++++++++++++ 22 files changed, 391 insertions(+), 119 deletions(-) diff --git a/libs/common/src/vault/models/data/attachment.data.ts b/libs/common/src/vault/models/data/attachment.data.ts index dfc9f9d1afa..dde5db24b57 100644 --- a/libs/common/src/vault/models/data/attachment.data.ts +++ b/libs/common/src/vault/models/data/attachment.data.ts @@ -1,14 +1,12 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { AttachmentResponse } from "../response/attachment.response"; export class AttachmentData { - id: string; - url: string; - fileName: string; - key: string; - size: string; - sizeName: string; + id?: string; + url?: string; + fileName?: string; + key?: string; + size?: string; + sizeName?: string; constructor(response?: AttachmentResponse) { if (response == null) { diff --git a/libs/common/src/vault/models/data/card.data.ts b/libs/common/src/vault/models/data/card.data.ts index 677c33f4886..8345c345fd7 100644 --- a/libs/common/src/vault/models/data/card.data.ts +++ b/libs/common/src/vault/models/data/card.data.ts @@ -1,14 +1,12 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { CardApi } from "../api/card.api"; export class CardData { - cardholderName: string; - brand: string; - number: string; - expMonth: string; - expYear: string; - code: string; + cardholderName?: string; + brand?: string; + number?: string; + expMonth?: string; + expYear?: string; + code?: string; constructor(data?: CardApi) { if (data == null) { diff --git a/libs/common/src/vault/models/data/cipher.data.ts b/libs/common/src/vault/models/data/cipher.data.ts index 4921cce8df2..743ea941b0e 100644 --- a/libs/common/src/vault/models/data/cipher.data.ts +++ b/libs/common/src/vault/models/data/cipher.data.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { Jsonify } from "type-fest"; import { CipherRepromptType } from "../../enums/cipher-reprompt-type"; @@ -17,18 +15,18 @@ import { SecureNoteData } from "./secure-note.data"; import { SshKeyData } from "./ssh-key.data"; export class CipherData { - id: string; - organizationId: string; - folderId: string; - edit: boolean; - viewPassword: boolean; - permissions: CipherPermissionsApi; - organizationUseTotp: boolean; - favorite: boolean; + id: string = ""; + organizationId?: string; + folderId?: string; + edit: boolean = false; + viewPassword: boolean = true; + permissions?: CipherPermissionsApi; + organizationUseTotp: boolean = false; + favorite: boolean = false; revisionDate: string; - type: CipherType; - name: string; - notes: string; + type: CipherType = CipherType.Login; + name: string = ""; + notes?: string; login?: LoginData; secureNote?: SecureNoteData; card?: CardData; @@ -39,13 +37,14 @@ export class CipherData { passwordHistory?: PasswordHistoryData[]; collectionIds?: string[]; creationDate: string; - deletedDate: string | undefined; - archivedDate: string | undefined; - reprompt: CipherRepromptType; - key: string; + deletedDate?: string; + archivedDate?: string; + reprompt: CipherRepromptType = CipherRepromptType.None; + key?: string; constructor(response?: CipherResponse, collectionIds?: string[]) { if (response == null) { + this.creationDate = this.revisionDate = new Date().toISOString(); return; } @@ -101,7 +100,9 @@ export class CipherData { static fromJSON(obj: Jsonify) { const result = Object.assign(new CipherData(), obj); - result.permissions = CipherPermissionsApi.fromJSON(obj.permissions); + if (obj.permissions != null) { + result.permissions = CipherPermissionsApi.fromJSON(obj.permissions); + } return result; } } diff --git a/libs/common/src/vault/models/data/fido2-credential.data.ts b/libs/common/src/vault/models/data/fido2-credential.data.ts index 94716e8d86c..602b74f9805 100644 --- a/libs/common/src/vault/models/data/fido2-credential.data.ts +++ b/libs/common/src/vault/models/data/fido2-credential.data.ts @@ -1,21 +1,19 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { Fido2CredentialApi } from "../api/fido2-credential.api"; export class Fido2CredentialData { - credentialId: string; - keyType: "public-key"; - keyAlgorithm: "ECDSA"; - keyCurve: "P-256"; - keyValue: string; - rpId: string; - userHandle: string; - userName: string; - counter: string; - rpName: string; - userDisplayName: string; - discoverable: string; - creationDate: string; + credentialId!: string; + keyType!: string; + keyAlgorithm!: string; + keyCurve!: string; + keyValue!: string; + rpId!: string; + userHandle?: string; + userName?: string; + counter!: string; + rpName?: string; + userDisplayName?: string; + discoverable!: string; + creationDate!: string; constructor(data?: Fido2CredentialApi) { if (data == null) { diff --git a/libs/common/src/vault/models/data/field.data.ts b/libs/common/src/vault/models/data/field.data.ts index cf9df69a6b0..a63e903e665 100644 --- a/libs/common/src/vault/models/data/field.data.ts +++ b/libs/common/src/vault/models/data/field.data.ts @@ -1,13 +1,11 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { FieldType, LinkedIdType } from "../../enums"; import { FieldApi } from "../api/field.api"; export class FieldData { - type: FieldType; - name: string; - value: string; - linkedId: LinkedIdType | null; + type: FieldType = FieldType.Text; + name?: string; + value?: string; + linkedId?: LinkedIdType; constructor(response?: FieldApi) { if (response == null) { diff --git a/libs/common/src/vault/models/data/identity.data.ts b/libs/common/src/vault/models/data/identity.data.ts index 158daace371..de854df1ec6 100644 --- a/libs/common/src/vault/models/data/identity.data.ts +++ b/libs/common/src/vault/models/data/identity.data.ts @@ -1,26 +1,24 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { IdentityApi } from "../api/identity.api"; export class IdentityData { - title: string; - firstName: string; - middleName: string; - lastName: string; - address1: string; - address2: string; - address3: string; - city: string; - state: string; - postalCode: string; - country: string; - company: string; - email: string; - phone: string; - ssn: string; - username: string; - passportNumber: string; - licenseNumber: string; + title?: string; + firstName?: string; + middleName?: string; + lastName?: string; + address1?: string; + address2?: string; + address3?: string; + city?: string; + state?: string; + postalCode?: string; + country?: string; + company?: string; + email?: string; + phone?: string; + ssn?: string; + username?: string; + passportNumber?: string; + licenseNumber?: string; constructor(data?: IdentityApi) { if (data == null) { diff --git a/libs/common/src/vault/models/data/login-uri.data.ts b/libs/common/src/vault/models/data/login-uri.data.ts index 852dad4e112..ea3a1d9adce 100644 --- a/libs/common/src/vault/models/data/login-uri.data.ts +++ b/libs/common/src/vault/models/data/login-uri.data.ts @@ -1,12 +1,10 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { UriMatchStrategySetting } from "../../../models/domain/domain-service"; import { LoginUriApi } from "../api/login-uri.api"; export class LoginUriData { - uri: string; - uriChecksum: string; - match: UriMatchStrategySetting = null; + uri?: string; + uriChecksum?: string; + match?: UriMatchStrategySetting; constructor(data?: LoginUriApi) { if (data == null) { diff --git a/libs/common/src/vault/models/data/login.data.ts b/libs/common/src/vault/models/data/login.data.ts index 0fe021d923c..8c0aba0fdaa 100644 --- a/libs/common/src/vault/models/data/login.data.ts +++ b/libs/common/src/vault/models/data/login.data.ts @@ -1,17 +1,15 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { LoginApi } from "../api/login.api"; import { Fido2CredentialData } from "./fido2-credential.data"; import { LoginUriData } from "./login-uri.data"; export class LoginData { - uris: LoginUriData[]; - username: string; - password: string; - passwordRevisionDate: string; - totp: string; - autofillOnPageLoad: boolean; + uris?: LoginUriData[]; + username?: string; + password?: string; + passwordRevisionDate?: string; + totp?: string; + autofillOnPageLoad?: boolean; fido2Credentials?: Fido2CredentialData[]; constructor(data?: LoginApi) { diff --git a/libs/common/src/vault/models/data/password-history.data.ts b/libs/common/src/vault/models/data/password-history.data.ts index 75a51ed3728..465b5e59b8d 100644 --- a/libs/common/src/vault/models/data/password-history.data.ts +++ b/libs/common/src/vault/models/data/password-history.data.ts @@ -1,10 +1,8 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { PasswordHistoryResponse } from "../response/password-history.response"; export class PasswordHistoryData { - password: string; - lastUsedDate: string; + password!: string; + lastUsedDate!: string; constructor(response?: PasswordHistoryResponse) { if (response == null) { diff --git a/libs/common/src/vault/models/data/secure-note.data.ts b/libs/common/src/vault/models/data/secure-note.data.ts index 7d109398ab7..5556417ef9b 100644 --- a/libs/common/src/vault/models/data/secure-note.data.ts +++ b/libs/common/src/vault/models/data/secure-note.data.ts @@ -1,10 +1,8 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { SecureNoteType } from "../../enums"; import { SecureNoteApi } from "../api/secure-note.api"; export class SecureNoteData { - type: SecureNoteType; + type: SecureNoteType = SecureNoteType.Generic; constructor(data?: SecureNoteApi) { if (data == null) { diff --git a/libs/common/src/vault/models/data/ssh-key.data.ts b/libs/common/src/vault/models/data/ssh-key.data.ts index 2b93c93d931..1e06a1a7df5 100644 --- a/libs/common/src/vault/models/data/ssh-key.data.ts +++ b/libs/common/src/vault/models/data/ssh-key.data.ts @@ -1,11 +1,9 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { SshKeyApi } from "../api/ssh-key.api"; export class SshKeyData { - privateKey: string; - publicKey: string; - keyFingerprint: string; + privateKey!: string; + publicKey!: string; + keyFingerprint!: string; constructor(data?: SshKeyApi) { if (data == null) { diff --git a/libs/common/src/vault/models/domain/attachment.spec.ts b/libs/common/src/vault/models/domain/attachment.spec.ts index 972c77537ff..77bb3eda38d 100644 --- a/libs/common/src/vault/models/domain/attachment.spec.ts +++ b/libs/common/src/vault/models/domain/attachment.spec.ts @@ -39,6 +39,12 @@ describe("Attachment", () => { key: undefined, fileName: undefined, }); + expect(data.id).toBeUndefined(); + expect(data.url).toBeUndefined(); + expect(data.fileName).toBeUndefined(); + expect(data.key).toBeUndefined(); + expect(data.size).toBeUndefined(); + expect(data.sizeName).toBeUndefined(); }); it("Convert", () => { diff --git a/libs/common/src/vault/models/domain/card.spec.ts b/libs/common/src/vault/models/domain/card.spec.ts index a4d242329a4..185c2fa4b8f 100644 --- a/libs/common/src/vault/models/domain/card.spec.ts +++ b/libs/common/src/vault/models/domain/card.spec.ts @@ -29,6 +29,13 @@ describe("Card", () => { expYear: undefined, code: undefined, }); + + expect(data.cardholderName).toBeUndefined(); + expect(data.brand).toBeUndefined(); + expect(data.number).toBeUndefined(); + expect(data.expMonth).toBeUndefined(); + expect(data.expYear).toBeUndefined(); + expect(data.code).toBeUndefined(); }); it("Convert", () => { diff --git a/libs/common/src/vault/models/domain/cipher.spec.ts b/libs/common/src/vault/models/domain/cipher.spec.ts index 4052c9e5338..7503d71573f 100644 --- a/libs/common/src/vault/models/domain/cipher.spec.ts +++ b/libs/common/src/vault/models/domain/cipher.spec.ts @@ -44,22 +44,22 @@ describe("Cipher DTO", () => { const data = new CipherData(); const cipher = new Cipher(data); - expect(cipher.id).toBeUndefined(); + expect(cipher.id).toEqual(""); expect(cipher.organizationId).toBeUndefined(); expect(cipher.folderId).toBeUndefined(); expect(cipher.name).toBeInstanceOf(EncString); expect(cipher.notes).toBeUndefined(); - expect(cipher.type).toBeUndefined(); - expect(cipher.favorite).toBeUndefined(); - expect(cipher.organizationUseTotp).toBeUndefined(); - expect(cipher.edit).toBeUndefined(); - expect(cipher.viewPassword).toBeUndefined(); + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.favorite).toEqual(false); + expect(cipher.organizationUseTotp).toEqual(false); + expect(cipher.edit).toEqual(false); + expect(cipher.viewPassword).toEqual(true); expect(cipher.revisionDate).toBeInstanceOf(Date); expect(cipher.collectionIds).toEqual([]); expect(cipher.localData).toBeUndefined(); expect(cipher.creationDate).toBeInstanceOf(Date); expect(cipher.deletedDate).toBeUndefined(); - expect(cipher.reprompt).toBeUndefined(); + expect(cipher.reprompt).toEqual(CipherRepromptType.None); expect(cipher.attachments).toBeUndefined(); expect(cipher.fields).toBeUndefined(); expect(cipher.passwordHistory).toBeUndefined(); @@ -836,6 +836,38 @@ describe("Cipher DTO", () => { expect(actual).toBeInstanceOf(Cipher); }); + it("handles null permissions correctly without calling CipherPermissionsApi constructor", () => { + const spy = jest.spyOn(CipherPermissionsApi.prototype, "constructor" as any); + const revisionDate = new Date("2022-08-04T01:06:40.441Z"); + const actual = Cipher.fromJSON({ + name: "myName", + revisionDate: revisionDate.toISOString(), + permissions: null, + } as Jsonify); + + expect(actual.permissions).toBeUndefined(); + expect(actual).toBeInstanceOf(Cipher); + // Verify that CipherPermissionsApi constructor was not called for null permissions + expect(spy).not.toHaveBeenCalledWith(null); + spy.mockRestore(); + }); + + it("calls CipherPermissionsApi constructor when permissions are provided", () => { + const spy = jest.spyOn(CipherPermissionsApi.prototype, "constructor" as any); + const revisionDate = new Date("2022-08-04T01:06:40.441Z"); + const permissionsObj = { delete: true, restore: false }; + const actual = Cipher.fromJSON({ + name: "myName", + revisionDate: revisionDate.toISOString(), + permissions: permissionsObj, + } as Jsonify); + + expect(actual.permissions).toBeInstanceOf(CipherPermissionsApi); + expect(actual.permissions.delete).toBe(true); + expect(actual.permissions.restore).toBe(false); + spy.mockRestore(); + }); + test.each([ // Test description, CipherType, expected output ["LoginView", CipherType.Login, { login: "myLogin_fromJSON" }], diff --git a/libs/common/src/vault/models/domain/field.spec.ts b/libs/common/src/vault/models/domain/field.spec.ts index d99336adad0..0a4bc8e3c29 100644 --- a/libs/common/src/vault/models/domain/field.spec.ts +++ b/libs/common/src/vault/models/domain/field.spec.ts @@ -29,7 +29,7 @@ describe("Field", () => { const field = new Field(data); expect(field).toEqual({ - type: undefined, + type: FieldType.Text, name: undefined, value: undefined, linkedId: undefined, diff --git a/libs/common/src/vault/models/domain/identity.spec.ts b/libs/common/src/vault/models/domain/identity.spec.ts index c2c2363fa0d..411f6d1c9ea 100644 --- a/libs/common/src/vault/models/domain/identity.spec.ts +++ b/libs/common/src/vault/models/domain/identity.spec.ts @@ -53,6 +53,27 @@ describe("Identity", () => { title: undefined, username: undefined, }); + + expect(data).toEqual({ + title: undefined, + firstName: undefined, + middleName: undefined, + lastName: undefined, + address1: undefined, + address2: undefined, + address3: undefined, + city: undefined, + state: undefined, + postalCode: undefined, + country: undefined, + company: undefined, + email: undefined, + phone: undefined, + ssn: undefined, + username: undefined, + passportNumber: undefined, + licenseNumber: undefined, + }); }); it("Convert", () => { diff --git a/libs/common/src/vault/models/domain/login-uri.spec.ts b/libs/common/src/vault/models/domain/login-uri.spec.ts index 982b435384b..2effd1bb9fe 100644 --- a/libs/common/src/vault/models/domain/login-uri.spec.ts +++ b/libs/common/src/vault/models/domain/login-uri.spec.ts @@ -7,6 +7,7 @@ import { mockEnc, mockFromJson } from "../../../../spec"; import { EncryptService } from "../../../key-management/crypto/abstractions/encrypt.service"; import { EncString } from "../../../key-management/crypto/models/enc-string"; import { UriMatchStrategy } from "../../../models/domain/domain-service"; +import { LoginUriApi } from "../api/login-uri.api"; import { LoginUriData } from "../data/login-uri.data"; import { LoginUri } from "./login-uri"; @@ -31,6 +32,9 @@ describe("LoginUri", () => { uri: undefined, uriChecksum: undefined, }); + expect(data.uri).toBeUndefined(); + expect(data.uriChecksum).toBeUndefined(); + expect(data.match).toBeUndefined(); }); it("Convert", () => { @@ -61,6 +65,23 @@ describe("LoginUri", () => { }); }); + it("handle null match", () => { + const apiData = Object.assign(new LoginUriApi(), { + uri: "testUri", + uriChecksum: "testChecksum", + match: null, + }); + + const loginUriData = new LoginUriData(apiData); + + // The data model stores it as-is (null or undefined) + expect(loginUriData.match).toBeNull(); + + // But the domain model converts null to undefined + const loginUri = new LoginUri(loginUriData); + expect(loginUri.match).toBeUndefined(); + }); + describe("validateChecksum", () => { let encryptService: MockProxy; @@ -118,7 +139,7 @@ describe("LoginUri", () => { }); describe("SDK Login Uri Mapping", () => { - it("should map to SDK login uri", () => { + it("maps to SDK login uri", () => { const loginUri = new LoginUri(data); const sdkLoginUri = loginUri.toSdkLoginUri(); diff --git a/libs/common/src/vault/models/domain/login.spec.ts b/libs/common/src/vault/models/domain/login.spec.ts index 9f03e225b7f..6ebcfea057a 100644 --- a/libs/common/src/vault/models/domain/login.spec.ts +++ b/libs/common/src/vault/models/domain/login.spec.ts @@ -25,6 +25,14 @@ describe("Login DTO", () => { password: undefined, totp: undefined, }); + + expect(data.username).toBeUndefined(); + expect(data.password).toBeUndefined(); + expect(data.passwordRevisionDate).toBeUndefined(); + expect(data.totp).toBeUndefined(); + expect(data.autofillOnPageLoad).toBeUndefined(); + expect(data.uris).toBeUndefined(); + expect(data.fido2Credentials).toBeUndefined(); }); it("Convert from full LoginData", () => { diff --git a/libs/common/src/vault/models/domain/login.ts b/libs/common/src/vault/models/domain/login.ts index 13342c69014..a9cec13fc7c 100644 --- a/libs/common/src/vault/models/domain/login.ts +++ b/libs/common/src/vault/models/domain/login.ts @@ -111,10 +111,7 @@ export class Login extends Domain { }); if (this.uris != null && this.uris.length > 0) { - l.uris = []; - this.uris.forEach((u) => { - l.uris.push(u.toLoginUriData()); - }); + l.uris = this.uris.map((u) => u.toLoginUriData()); } if (this.fido2Credentials != null && this.fido2Credentials.length > 0) { diff --git a/libs/common/src/vault/models/domain/password.spec.ts b/libs/common/src/vault/models/domain/password.spec.ts index 2e37c5e8375..4b2de34beca 100644 --- a/libs/common/src/vault/models/domain/password.spec.ts +++ b/libs/common/src/vault/models/domain/password.spec.ts @@ -20,6 +20,9 @@ describe("Password", () => { expect(password).toBeInstanceOf(Password); expect(password.password).toBeInstanceOf(EncString); expect(password.lastUsedDate).toBeInstanceOf(Date); + + expect(data.password).toBeUndefined(); + expect(data.lastUsedDate).toBeUndefined(); }); it("Convert", () => { @@ -83,4 +86,47 @@ describe("Password", () => { }); }); }); + + describe("fromSdkPasswordHistory", () => { + beforeEach(() => { + jest.restoreAllMocks(); + }); + + it("creates Password from SDK object", () => { + const sdkPasswordHistory = { + password: "2.encPassword|encryptedData" as EncryptedString, + lastUsedDate: "2022-01-31T12:00:00.000Z", + }; + + const password = Password.fromSdkPasswordHistory(sdkPasswordHistory); + + expect(password).toBeInstanceOf(Password); + expect(password?.password).toBeInstanceOf(EncString); + expect(password?.password.encryptedString).toBe("2.encPassword|encryptedData"); + expect(password?.lastUsedDate).toEqual(new Date("2022-01-31T12:00:00.000Z")); + }); + + it("returns undefined for null input", () => { + const result = Password.fromSdkPasswordHistory(null as any); + expect(result).toBeUndefined(); + }); + + it("returns undefined for undefined input", () => { + const result = Password.fromSdkPasswordHistory(undefined); + expect(result).toBeUndefined(); + }); + + it("handles empty SDK object", () => { + const sdkPasswordHistory = { + password: "" as EncryptedString, + lastUsedDate: "", + }; + + const password = Password.fromSdkPasswordHistory(sdkPasswordHistory); + + expect(password).toBeInstanceOf(Password); + expect(password?.password).toBeInstanceOf(EncString); + expect(password?.lastUsedDate).toBeInstanceOf(Date); + }); + }); }); diff --git a/libs/common/src/vault/models/domain/secure-note.spec.ts b/libs/common/src/vault/models/domain/secure-note.spec.ts index 4c8e8d470ca..e445e9ea035 100644 --- a/libs/common/src/vault/models/domain/secure-note.spec.ts +++ b/libs/common/src/vault/models/domain/secure-note.spec.ts @@ -16,22 +16,27 @@ describe("SecureNote", () => { const data = new SecureNoteData(); const secureNote = new SecureNote(data); - expect(secureNote).toEqual({ - type: undefined, - }); + expect(data).toBeDefined(); + expect(secureNote).toEqual({ type: SecureNoteType.Generic }); + expect(data.type).toBe(SecureNoteType.Generic); + }); + + it("Convert from undefined", () => { + const data = new SecureNoteData(undefined); + expect(data.type).toBe(SecureNoteType.Generic); }); it("Convert", () => { const secureNote = new SecureNote(data); - expect(secureNote).toEqual({ - type: 0, - }); + expect(secureNote).toEqual({ type: 0 }); + expect(data.type).toBe(SecureNoteType.Generic); }); it("toSecureNoteData", () => { const secureNote = new SecureNote(data); expect(secureNote.toSecureNoteData()).toEqual(data); + expect(secureNote.toSecureNoteData().type).toBe(SecureNoteType.Generic); }); it("Decrypt", async () => { @@ -49,6 +54,14 @@ describe("SecureNote", () => { it("returns undefined if object is null", () => { expect(SecureNote.fromJSON(null)).toBeUndefined(); }); + + it("creates SecureNote instance from JSON object", () => { + const jsonObj = { type: SecureNoteType.Generic }; + const result = SecureNote.fromJSON(jsonObj); + + expect(result).toBeInstanceOf(SecureNote); + expect(result.type).toBe(SecureNoteType.Generic); + }); }); describe("toSdkSecureNote", () => { @@ -63,4 +76,71 @@ describe("SecureNote", () => { }); }); }); + + describe("fromSdkSecureNote", () => { + it("returns undefined when null is provided", () => { + const result = SecureNote.fromSdkSecureNote(null); + + expect(result).toBeUndefined(); + }); + + it("returns undefined when undefined is provided", () => { + const result = SecureNote.fromSdkSecureNote(undefined); + + expect(result).toBeUndefined(); + }); + + it("creates SecureNote with Generic type from SDK object", () => { + const sdkSecureNote = { + type: SecureNoteType.Generic, + }; + + const result = SecureNote.fromSdkSecureNote(sdkSecureNote); + + expect(result).toBeInstanceOf(SecureNote); + expect(result.type).toBe(SecureNoteType.Generic); + }); + + it("preserves the type value from SDK object", () => { + const sdkSecureNote = { + type: SecureNoteType.Generic, + }; + + const result = SecureNote.fromSdkSecureNote(sdkSecureNote); + + expect(result.type).toBe(0); + }); + + it("creates a new SecureNote instance", () => { + const sdkSecureNote = { + type: SecureNoteType.Generic, + }; + + const result = SecureNote.fromSdkSecureNote(sdkSecureNote); + + expect(result).not.toBe(sdkSecureNote); + expect(result).toBeInstanceOf(SecureNote); + }); + + it("handles SDK object with undefined type", () => { + const sdkSecureNote = { + type: undefined as SecureNoteType, + }; + + const result = SecureNote.fromSdkSecureNote(sdkSecureNote); + + expect(result).toBeInstanceOf(SecureNote); + expect(result.type).toBeUndefined(); + }); + + it("returns symmetric with toSdkSecureNote", () => { + const original = new SecureNote(); + original.type = SecureNoteType.Generic; + + const sdkFormat = original.toSdkSecureNote(); + const reconstructed = SecureNote.fromSdkSecureNote(sdkFormat); + + expect(reconstructed.type).toBe(original.type); + }); + }); }); diff --git a/libs/common/src/vault/models/domain/ssh-key.spec.ts b/libs/common/src/vault/models/domain/ssh-key.spec.ts index 38228e54a4a..10149ebc82d 100644 --- a/libs/common/src/vault/models/domain/ssh-key.spec.ts +++ b/libs/common/src/vault/models/domain/ssh-key.spec.ts @@ -1,4 +1,5 @@ import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string"; +import { EncString as SdkEncString, SshKey as SdkSshKey } from "@bitwarden/sdk-internal"; import { mockEnc } from "../../../../spec"; import { SshKeyApi } from "../api/ssh-key.api"; @@ -37,6 +38,9 @@ describe("Sshkey", () => { expect(sshKey.privateKey).toBeInstanceOf(EncString); expect(sshKey.publicKey).toBeInstanceOf(EncString); expect(sshKey.keyFingerprint).toBeInstanceOf(EncString); + expect(data.privateKey).toBeUndefined(); + expect(data.publicKey).toBeUndefined(); + expect(data.keyFingerprint).toBeUndefined(); }); it("toSshKeyData", () => { @@ -64,6 +68,21 @@ describe("Sshkey", () => { it("returns undefined if object is null", () => { expect(SshKey.fromJSON(null)).toBeUndefined(); }); + + it("creates SshKey instance from JSON object", () => { + const jsonObj = { + privateKey: "2.privateKey|encryptedData", + publicKey: "2.publicKey|encryptedData", + keyFingerprint: "2.keyFingerprint|encryptedData", + }; + + const result = SshKey.fromJSON(jsonObj); + + expect(result).toBeInstanceOf(SshKey); + expect(result.privateKey).toBeDefined(); + expect(result.publicKey).toBeDefined(); + expect(result.keyFingerprint).toBeDefined(); + }); }); describe("toSdkSshKey", () => { @@ -78,4 +97,58 @@ describe("Sshkey", () => { }); }); }); + + describe("fromSdkSshKey", () => { + it("returns undefined when null is provided", () => { + const result = SshKey.fromSdkSshKey(null); + + expect(result).toBeUndefined(); + }); + + it("returns undefined when undefined is provided", () => { + const result = SshKey.fromSdkSshKey(undefined); + + expect(result).toBeUndefined(); + }); + + it("creates SshKey from SDK object", () => { + const sdkSshKey: SdkSshKey = { + privateKey: "2.privateKey|encryptedData" as SdkEncString, + publicKey: "2.publicKey|encryptedData" as SdkEncString, + fingerprint: "2.keyFingerprint|encryptedData" as SdkEncString, + }; + + const result = SshKey.fromSdkSshKey(sdkSshKey); + + expect(result).toBeInstanceOf(SshKey); + expect(result.privateKey).toBeDefined(); + expect(result.publicKey).toBeDefined(); + expect(result.keyFingerprint).toBeDefined(); + }); + + it("creates a new SshKey instance", () => { + const sdkSshKey: SdkSshKey = { + privateKey: "2.privateKey|encryptedData" as SdkEncString, + publicKey: "2.publicKey|encryptedData" as SdkEncString, + fingerprint: "2.keyFingerprint|encryptedData" as SdkEncString, + }; + + const result = SshKey.fromSdkSshKey(sdkSshKey); + + expect(result).not.toBe(sdkSshKey); + expect(result).toBeInstanceOf(SshKey); + }); + + it("is symmetric with toSdkSshKey", () => { + const original = new SshKey(data); + const sdkFormat = original.toSdkSshKey(); + const reconstructed = SshKey.fromSdkSshKey(sdkFormat); + + expect(reconstructed.privateKey.encryptedString).toBe(original.privateKey.encryptedString); + expect(reconstructed.publicKey.encryptedString).toBe(original.publicKey.encryptedString); + expect(reconstructed.keyFingerprint.encryptedString).toBe( + original.keyFingerprint.encryptedString, + ); + }); + }); });