1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-05 19:23:19 +00:00

Add tests for delete ops

This commit is contained in:
Nik Gilmore
2026-01-08 14:59:53 -08:00
parent 46e486c05f
commit 4f07425d1e

View File

@@ -1114,6 +1114,382 @@ describe("Cipher Service", () => {
});
});
describe("deleteWithServer()", () => {
let mockSdkClient: any;
let mockCiphersSdk: any;
let mockAdminSdk: any;
let mockVaultSdk: any;
const testCipherId = "5ff8c0b2-1d3e-4f8c-9b2d-1d3e4f8c0b22" as CipherId;
beforeEach(() => {
// Mock the SDK client chain for delete operations
mockAdminSdk = {
delete: jest.fn().mockResolvedValue(undefined),
};
mockCiphersSdk = {
delete: jest.fn().mockResolvedValue(undefined),
admin: jest.fn().mockReturnValue(mockAdminSdk),
};
mockVaultSdk = {
ciphers: jest.fn().mockReturnValue(mockCiphersSdk),
};
const mockSdkValue = {
vault: jest.fn().mockReturnValue(mockVaultSdk),
};
mockSdkClient = {
take: jest.fn().mockReturnValue({
value: mockSdkValue,
[Symbol.dispose]: jest.fn(),
}),
};
// Mock sdkService to return the mock client
sdkService.userClient$.mockReturnValue(of(mockSdkClient));
});
it("should call apiService.deleteCipher when feature flag is disabled", async () => {
configService.getFeatureFlag
.calledWith(FeatureFlag.PM27632_SdkCipherCrudOperations)
.mockResolvedValue(false);
const apiSpy = jest.spyOn(apiService, "deleteCipher").mockResolvedValue(undefined);
await cipherService.deleteWithServer(testCipherId, userId);
expect(apiSpy).toHaveBeenCalledWith(testCipherId);
expect(mockSdkClient.take).not.toHaveBeenCalled();
});
it("should call apiService.deleteCipherAdmin when feature flag is disabled and asAdmin is true", async () => {
configService.getFeatureFlag
.calledWith(FeatureFlag.PM27632_SdkCipherCrudOperations)
.mockResolvedValue(false);
const apiSpy = jest.spyOn(apiService, "deleteCipherAdmin").mockResolvedValue(undefined);
await cipherService.deleteWithServer(testCipherId, userId, true);
expect(apiSpy).toHaveBeenCalledWith(testCipherId);
expect(mockSdkClient.take).not.toHaveBeenCalled();
});
it("should use SDK to delete cipher when feature flag is enabled", async () => {
configService.getFeatureFlag
.calledWith(FeatureFlag.PM27632_SdkCipherCrudOperations)
.mockResolvedValue(true);
const apiSpy = jest.spyOn(apiService, "deleteCipher");
const clearCacheSpy = jest.spyOn(cipherService as any, "clearCache");
await cipherService.deleteWithServer(testCipherId, userId);
expect(mockSdkClient.take).toHaveBeenCalled();
expect(mockCiphersSdk.delete).toHaveBeenCalledWith(testCipherId);
expect(apiSpy).not.toHaveBeenCalled();
expect(clearCacheSpy).toHaveBeenCalledWith(userId);
});
it("should use SDK admin delete when feature flag is enabled and asAdmin is true", async () => {
configService.getFeatureFlag
.calledWith(FeatureFlag.PM27632_SdkCipherCrudOperations)
.mockResolvedValue(true);
const apiSpy = jest.spyOn(apiService, "deleteCipherAdmin");
const clearCacheSpy = jest.spyOn(cipherService as any, "clearCache");
await cipherService.deleteWithServer(testCipherId, userId, true);
expect(mockSdkClient.take).toHaveBeenCalled();
expect(mockAdminSdk.delete).toHaveBeenCalledWith(testCipherId);
expect(apiSpy).not.toHaveBeenCalled();
expect(clearCacheSpy).toHaveBeenCalledWith(userId);
});
});
describe("deleteManyWithServer()", () => {
let mockSdkClient: any;
let mockCiphersSdk: any;
let mockAdminSdk: any;
let mockVaultSdk: any;
const testCipherIds = [
"5ff8c0b2-1d3e-4f8c-9b2d-1d3e4f8c0b22" as CipherId,
"6ff8c0b2-1d3e-4f8c-9b2d-1d3e4f8c0b23" as CipherId,
];
beforeEach(() => {
// Mock the SDK client chain for delete many operations
mockAdminSdk = {
delete_many: jest.fn().mockResolvedValue(undefined),
};
mockCiphersSdk = {
delete_many: jest.fn().mockResolvedValue(undefined),
admin: jest.fn().mockReturnValue(mockAdminSdk),
};
mockVaultSdk = {
ciphers: jest.fn().mockReturnValue(mockCiphersSdk),
};
const mockSdkValue = {
vault: jest.fn().mockReturnValue(mockVaultSdk),
};
mockSdkClient = {
take: jest.fn().mockReturnValue({
value: mockSdkValue,
[Symbol.dispose]: jest.fn(),
}),
};
// Mock sdkService to return the mock client
sdkService.userClient$.mockReturnValue(of(mockSdkClient));
});
it("should call apiService.deleteManyCiphers when feature flag is disabled", async () => {
configService.getFeatureFlag
.calledWith(FeatureFlag.PM27632_SdkCipherCrudOperations)
.mockResolvedValue(false);
const apiSpy = jest.spyOn(apiService, "deleteManyCiphers").mockResolvedValue(undefined);
await cipherService.deleteManyWithServer(testCipherIds, userId);
expect(apiSpy).toHaveBeenCalled();
expect(mockSdkClient.take).not.toHaveBeenCalled();
});
it("should call apiService.deleteManyCiphersAdmin when feature flag is disabled and asAdmin is true", async () => {
configService.getFeatureFlag
.calledWith(FeatureFlag.PM27632_SdkCipherCrudOperations)
.mockResolvedValue(false);
const apiSpy = jest.spyOn(apiService, "deleteManyCiphersAdmin").mockResolvedValue(undefined);
await cipherService.deleteManyWithServer(testCipherIds, userId, true, orgId);
expect(apiSpy).toHaveBeenCalled();
expect(mockSdkClient.take).not.toHaveBeenCalled();
});
it("should use SDK to delete multiple ciphers when feature flag is enabled", async () => {
configService.getFeatureFlag
.calledWith(FeatureFlag.PM27632_SdkCipherCrudOperations)
.mockResolvedValue(true);
const apiSpy = jest.spyOn(apiService, "deleteManyCiphers");
const clearCacheSpy = jest.spyOn(cipherService as any, "clearCache");
await cipherService.deleteManyWithServer(testCipherIds, userId);
expect(mockSdkClient.take).toHaveBeenCalled();
expect(mockCiphersSdk.delete_many).toHaveBeenCalledWith(testCipherIds);
expect(apiSpy).not.toHaveBeenCalled();
expect(clearCacheSpy).toHaveBeenCalledWith(userId);
});
it("should use SDK admin delete many when feature flag is enabled and asAdmin is true", async () => {
configService.getFeatureFlag
.calledWith(FeatureFlag.PM27632_SdkCipherCrudOperations)
.mockResolvedValue(true);
const apiSpy = jest.spyOn(apiService, "deleteManyCiphersAdmin");
const clearCacheSpy = jest.spyOn(cipherService as any, "clearCache");
await cipherService.deleteManyWithServer(testCipherIds, userId, true, orgId);
expect(mockSdkClient.take).toHaveBeenCalled();
expect(mockAdminSdk.delete_many).toHaveBeenCalledWith(testCipherIds, orgId);
expect(apiSpy).not.toHaveBeenCalled();
expect(clearCacheSpy).toHaveBeenCalledWith(userId);
});
});
describe("softDeleteWithServer()", () => {
let mockSdkClient: any;
let mockCiphersSdk: any;
let mockAdminSdk: any;
let mockVaultSdk: any;
const testCipherId = "5ff8c0b2-1d3e-4f8c-9b2d-1d3e4f8c0b22" as CipherId;
beforeEach(() => {
// Mock the SDK client chain for soft delete operations
mockAdminSdk = {
soft_delete: jest.fn().mockResolvedValue(undefined),
};
mockCiphersSdk = {
soft_delete: jest.fn().mockResolvedValue(undefined),
admin: jest.fn().mockReturnValue(mockAdminSdk),
};
mockVaultSdk = {
ciphers: jest.fn().mockReturnValue(mockCiphersSdk),
};
const mockSdkValue = {
vault: jest.fn().mockReturnValue(mockVaultSdk),
};
mockSdkClient = {
take: jest.fn().mockReturnValue({
value: mockSdkValue,
[Symbol.dispose]: jest.fn(),
}),
};
// Mock sdkService to return the mock client
sdkService.userClient$.mockReturnValue(of(mockSdkClient));
});
it("should call apiService.putDeleteCipher when feature flag is disabled", async () => {
configService.getFeatureFlag
.calledWith(FeatureFlag.PM27632_SdkCipherCrudOperations)
.mockResolvedValue(false);
const apiSpy = jest.spyOn(apiService, "putDeleteCipher").mockResolvedValue(undefined);
await cipherService.softDeleteWithServer(testCipherId, userId);
expect(apiSpy).toHaveBeenCalledWith(testCipherId);
expect(mockSdkClient.take).not.toHaveBeenCalled();
});
it("should call apiService.putDeleteCipherAdmin when feature flag is disabled and asAdmin is true", async () => {
configService.getFeatureFlag
.calledWith(FeatureFlag.PM27632_SdkCipherCrudOperations)
.mockResolvedValue(false);
const apiSpy = jest.spyOn(apiService, "putDeleteCipherAdmin").mockResolvedValue(undefined);
await cipherService.softDeleteWithServer(testCipherId, userId, true);
expect(apiSpy).toHaveBeenCalledWith(testCipherId);
expect(mockSdkClient.take).not.toHaveBeenCalled();
});
it("should use SDK to soft delete cipher when feature flag is enabled", async () => {
configService.getFeatureFlag
.calledWith(FeatureFlag.PM27632_SdkCipherCrudOperations)
.mockResolvedValue(true);
const apiSpy = jest.spyOn(apiService, "putDeleteCipher");
const clearCacheSpy = jest.spyOn(cipherService as any, "clearCache");
await cipherService.softDeleteWithServer(testCipherId, userId);
expect(mockSdkClient.take).toHaveBeenCalled();
expect(mockCiphersSdk.soft_delete).toHaveBeenCalledWith(testCipherId);
expect(apiSpy).not.toHaveBeenCalled();
expect(clearCacheSpy).toHaveBeenCalledWith(userId);
});
it("should use SDK admin soft delete when feature flag is enabled and asAdmin is true", async () => {
configService.getFeatureFlag
.calledWith(FeatureFlag.PM27632_SdkCipherCrudOperations)
.mockResolvedValue(true);
const apiSpy = jest.spyOn(apiService, "putDeleteCipherAdmin");
const clearCacheSpy = jest.spyOn(cipherService as any, "clearCache");
await cipherService.softDeleteWithServer(testCipherId, userId, true);
expect(mockSdkClient.take).toHaveBeenCalled();
expect(mockAdminSdk.soft_delete).toHaveBeenCalledWith(testCipherId);
expect(apiSpy).not.toHaveBeenCalled();
expect(clearCacheSpy).toHaveBeenCalledWith(userId);
});
});
describe("softDeleteManyWithServer()", () => {
let mockSdkClient: any;
let mockCiphersSdk: any;
let mockAdminSdk: any;
let mockVaultSdk: any;
const testCipherIds = [
"5ff8c0b2-1d3e-4f8c-9b2d-1d3e4f8c0b22" as CipherId,
"6ff8c0b2-1d3e-4f8c-9b2d-1d3e4f8c0b23" as CipherId,
];
beforeEach(() => {
// Mock the SDK client chain for soft delete many operations
mockAdminSdk = {
soft_delete_many: jest.fn().mockResolvedValue(undefined),
};
mockCiphersSdk = {
soft_delete_many: jest.fn().mockResolvedValue(undefined),
admin: jest.fn().mockReturnValue(mockAdminSdk),
};
mockVaultSdk = {
ciphers: jest.fn().mockReturnValue(mockCiphersSdk),
};
const mockSdkValue = {
vault: jest.fn().mockReturnValue(mockVaultSdk),
};
mockSdkClient = {
take: jest.fn().mockReturnValue({
value: mockSdkValue,
[Symbol.dispose]: jest.fn(),
}),
};
// Mock sdkService to return the mock client
sdkService.userClient$.mockReturnValue(of(mockSdkClient));
});
it("should call apiService.putDeleteManyCiphers when feature flag is disabled", async () => {
configService.getFeatureFlag
.calledWith(FeatureFlag.PM27632_SdkCipherCrudOperations)
.mockResolvedValue(false);
const apiSpy = jest.spyOn(apiService, "putDeleteManyCiphers").mockResolvedValue(undefined);
await cipherService.softDeleteManyWithServer(testCipherIds, userId);
expect(apiSpy).toHaveBeenCalled();
expect(mockSdkClient.take).not.toHaveBeenCalled();
});
it("should call apiService.putDeleteManyCiphersAdmin when feature flag is disabled and asAdmin is true", async () => {
configService.getFeatureFlag
.calledWith(FeatureFlag.PM27632_SdkCipherCrudOperations)
.mockResolvedValue(false);
const apiSpy = jest
.spyOn(apiService, "putDeleteManyCiphersAdmin")
.mockResolvedValue(undefined);
await cipherService.softDeleteManyWithServer(testCipherIds, userId, true, orgId);
expect(apiSpy).toHaveBeenCalled();
expect(mockSdkClient.take).not.toHaveBeenCalled();
});
it("should use SDK to soft delete multiple ciphers when feature flag is enabled", async () => {
configService.getFeatureFlag
.calledWith(FeatureFlag.PM27632_SdkCipherCrudOperations)
.mockResolvedValue(true);
const apiSpy = jest.spyOn(apiService, "putDeleteManyCiphers");
const clearCacheSpy = jest.spyOn(cipherService as any, "clearCache");
await cipherService.softDeleteManyWithServer(testCipherIds, userId);
expect(mockSdkClient.take).toHaveBeenCalled();
expect(mockCiphersSdk.soft_delete_many).toHaveBeenCalledWith(testCipherIds);
expect(apiSpy).not.toHaveBeenCalled();
expect(clearCacheSpy).toHaveBeenCalledWith(userId);
});
it("should use SDK admin soft delete many when feature flag is enabled and asAdmin is true", async () => {
configService.getFeatureFlag
.calledWith(FeatureFlag.PM27632_SdkCipherCrudOperations)
.mockResolvedValue(true);
const apiSpy = jest.spyOn(apiService, "putDeleteManyCiphersAdmin");
const clearCacheSpy = jest.spyOn(cipherService as any, "clearCache");
await cipherService.softDeleteManyWithServer(testCipherIds, userId, true, orgId);
expect(mockSdkClient.take).toHaveBeenCalled();
expect(mockAdminSdk.soft_delete_many).toHaveBeenCalledWith(testCipherIds, orgId);
expect(apiSpy).not.toHaveBeenCalled();
expect(clearCacheSpy).toHaveBeenCalledWith(userId);
});
});
describe("replace (no upsert)", () => {
// In order to set up initial state we need to manually update the encrypted state
// which will result in an emission. All tests will have this baseline emission.