1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-08 04:33:38 +00:00

cli convert to key connector unit tests

This commit is contained in:
Maciej Zieniuk
2025-03-11 08:36:43 +00:00
parent d496863e8d
commit d7c85331ae
4 changed files with 145 additions and 7 deletions

View File

@@ -78,7 +78,6 @@ export class UnlockCommand {
userId,
this.keyConnectorService,
this.environmentService,
this.syncService,
this.organizationApiService,
this.logout,
);

View File

@@ -0,0 +1,143 @@
import { createPromptModule } from "inquirer";
import { mock } from "jest-mock-extended";
import { of } from "rxjs";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
import {
Environment,
EnvironmentService,
Region,
Urls,
} from "@bitwarden/common/platform/abstractions/environment.service";
import { UserId } from "@bitwarden/common/types/guid";
import { Response } from "../models/response";
import { MessageResponse } from "../models/response/message.response";
import { ConvertToKeyConnectorCommand } from "./convert-to-key-connector.command";
jest.mock("inquirer", () => {
return {
createPromptModule: jest.fn(() => jest.fn(() => Promise.resolve({ convert: "" }))),
};
});
describe("ConvertToKeyConnectorCommand", () => {
let command: ConvertToKeyConnectorCommand;
const userId = "test-user-id" as UserId;
const organization = {
id: "test-organization-id",
name: "Test Organization",
keyConnectorUrl: "https://keyconnector.example.com",
} as Organization;
const keyConnectorService = mock<KeyConnectorService>();
const environmentService = mock<EnvironmentService>();
const organizationApiService = mock<OrganizationApiServiceAbstraction>();
const logout = jest.fn();
beforeEach(async () => {
command = new ConvertToKeyConnectorCommand(
userId,
keyConnectorService,
environmentService,
organizationApiService,
logout,
);
});
describe("run", () => {
it("should logout and return error response if no interaction available", async () => {
process.env.BW_NOINTERACTION = "true";
const response = await command.run();
expect(response).not.toBeNull();
expect(response.success).toEqual(false);
expect(response).toEqual(
Response.error(
new MessageResponse(
"An organization you are a member of is using Key Connector. In order to access the vault, you must opt-in to Key Connector now via the web vault. You have been logged out.",
null,
),
),
);
expect(logout).toHaveBeenCalled();
});
it("should logout and return error response if interaction answer is exit", async () => {
process.env.BW_NOINTERACTION = "false";
keyConnectorService.getManagingOrganization.mockResolvedValue(organization);
(createPromptModule as jest.Mock).mockImplementation(() =>
jest.fn(() => Promise.resolve({ convert: "exit" })),
);
const response = await command.run();
expect(response).not.toBeNull();
expect(response.success).toEqual(false);
expect(response).toEqual(Response.error("You have been logged out."));
expect(logout).toHaveBeenCalled();
});
it("should key connector migrate user and return success response if answer is remove", async () => {
process.env.BW_NOINTERACTION = "false";
keyConnectorService.getManagingOrganization.mockResolvedValue(organization);
environmentService.environment$ = of({
getUrls: () =>
({
keyConnector: "old-key-connector-url",
}) as Urls,
} as Environment);
(createPromptModule as jest.Mock).mockImplementation(() =>
jest.fn(() => Promise.resolve({ convert: "remove" })),
);
const response = await command.run();
expect(response).not.toBeNull();
expect(response.success).toEqual(true);
expect(keyConnectorService.migrateUser).toHaveBeenCalledWith(userId);
expect(keyConnectorService.removeConvertAccountRequired).toHaveBeenCalledWith(userId);
expect(keyConnectorService.setUsesKeyConnector).toHaveBeenCalledWith(true, userId);
expect(environmentService.setEnvironment).toHaveBeenCalledWith(Region.SelfHosted, {
keyConnector: organization.keyConnectorUrl,
} as Urls);
});
it("should logout and throw error if key connector migrate user fails", async () => {
process.env.BW_NOINTERACTION = "false";
keyConnectorService.getManagingOrganization.mockResolvedValue(organization);
(createPromptModule as jest.Mock).mockImplementation(() =>
jest.fn(() => Promise.resolve({ convert: "remove" })),
);
keyConnectorService.migrateUser.mockRejectedValue(new Error("Migration failed"));
await expect(command.run()).rejects.toThrow("Migration failed");
expect(logout).toHaveBeenCalled();
});
it("should leave organization and return success response if answer is leave", async () => {
process.env.BW_NOINTERACTION = "false";
keyConnectorService.getManagingOrganization.mockResolvedValue(organization);
(createPromptModule as jest.Mock).mockImplementation(() =>
jest.fn(() => Promise.resolve({ convert: "leave" })),
);
const response = await command.run();
expect(response).not.toBeNull();
expect(response.success).toEqual(true);
expect(organizationApiService.leave).toHaveBeenCalledWith(organization.id);
expect(keyConnectorService.removeConvertAccountRequired).toHaveBeenCalledWith(userId);
});
});
});

View File

@@ -1,5 +1,3 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import * as inquirer from "inquirer";
import { firstValueFrom } from "rxjs";
@@ -10,7 +8,6 @@ import {
Region,
} from "@bitwarden/common/platform/abstractions/environment.service";
import { UserId } from "@bitwarden/common/types/guid";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { Response } from "../models/response";
import { MessageResponse } from "../models/response/message.response";
@@ -20,7 +17,6 @@ export class ConvertToKeyConnectorCommand {
private readonly userId: UserId,
private keyConnectorService: KeyConnectorService,
private environmentService: EnvironmentService,
private syncService: SyncService,
private organizationApiService: OrganizationApiServiceAbstraction,
private logout: () => Promise<void>,
) {}

View File

@@ -5,11 +5,11 @@ import { BaseResponse } from "./base.response";
export class MessageResponse implements BaseResponse {
object: string;
title: string;
message: string;
message: string | null;
raw: string;
noColor = false;
constructor(title: string, message: string) {
constructor(title: string, message: string | null) {
this.object = "message";
this.title = title;
this.message = message;