1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-03 02:03:53 +00:00

Update test cases

This commit is contained in:
Leslie Tilton
2025-10-20 08:58:38 -05:00
parent b0c81f461e
commit cb42c1d351
8 changed files with 223 additions and 230 deletions

View File

@@ -1,79 +1,83 @@
export const mockMemberCipherDetailsResponse: { data: any[] } = {
data: [
{
UserName: "David Brent",
Email: "david.brent@wernhamhogg.uk",
UsesKeyConnector: true,
cipherIds: [
"cbea34a8-bde4-46ad-9d19-b05001228ab1",
"cbea34a8-bde4-46ad-9d19-b05001228ab2",
"cbea34a8-bde4-46ad-9d19-b05001228xy4",
"cbea34a8-bde4-46ad-9d19-b05001227nm5",
],
},
{
UserName: "Tim Canterbury",
Email: "tim.canterbury@wernhamhogg.uk",
UsesKeyConnector: false,
cipherIds: [
"cbea34a8-bde4-46ad-9d19-b05001228ab2",
"cbea34a8-bde4-46ad-9d19-b05001228cd3",
"cbea34a8-bde4-46ad-9d19-b05001228xy4",
"cbea34a8-bde4-46ad-9d19-b05001227nm5",
],
},
{
UserName: "Gareth Keenan",
Email: "gareth.keenan@wernhamhogg.uk",
UsesKeyConnector: true,
cipherIds: [
"cbea34a8-bde4-46ad-9d19-b05001228cd3",
"cbea34a8-bde4-46ad-9d19-b05001228xy4",
"cbea34a8-bde4-46ad-9d19-b05001227nm5",
"cbea34a8-bde4-46ad-9d19-b05001227nm7",
],
},
{
UserName: "Dawn Tinsley",
Email: "dawn.tinsley@wernhamhogg.uk",
UsesKeyConnector: true,
cipherIds: [
"cbea34a8-bde4-46ad-9d19-b05001228ab2",
"cbea34a8-bde4-46ad-9d19-b05001228cd3",
"cbea34a8-bde4-46ad-9d19-b05001228xy4",
],
},
{
UserName: "Keith Bishop",
Email: "keith.bishop@wernhamhogg.uk",
UsesKeyConnector: false,
cipherIds: [
"cbea34a8-bde4-46ad-9d19-b05001228ab1",
"cbea34a8-bde4-46ad-9d19-b05001228cd3",
"cbea34a8-bde4-46ad-9d19-b05001228xy4",
"cbea34a8-bde4-46ad-9d19-b05001227nm5",
],
},
{
UserName: "Chris Finch",
Email: "chris.finch@wernhamhogg.uk",
UsesKeyConnector: true,
cipherIds: [
"cbea34a8-bde4-46ad-9d19-b05001228ab2",
"cbea34a8-bde4-46ad-9d19-b05001228cd3",
"cbea34a8-bde4-46ad-9d19-b05001228xy4",
],
},
{
UserName: "Chris Finch Tester",
Email: "chris.finch@wernhamhogg.uk",
UsesKeyConnector: true,
cipherIds: [
"cbea34a8-bde4-46ad-9d19-b05001228ab2",
"cbea34a8-bde4-46ad-9d19-b05001228cd3",
"cbea34a8-bde4-46ad-9d19-b05001228xy4",
"cbea34a8-bde4-46ad-9d19-b05001228ab1",
],
},
],
};
import { mock } from "jest-mock-extended";
import { MemberCipherDetailsResponse } from "..";
export const mockMemberCipherDetailsResponse: MemberCipherDetailsResponse[] = [
mock<MemberCipherDetailsResponse>({
userGuid: "user-1",
userName: "David Brent",
email: "david.brent@wernhamhogg.uk",
useKeyConnector: true,
cipherIds: [
"cbea34a8-bde4-46ad-9d19-b05001228ab1",
"cbea34a8-bde4-46ad-9d19-b05001228ab2",
"cbea34a8-bde4-46ad-9d19-b05001228xy4",
"cbea34a8-bde4-46ad-9d19-b05001227nm5",
],
}),
mock<MemberCipherDetailsResponse>({
userGuid: "user-2",
userName: "Tim Canterbury",
email: "tim.canterbury@wernhamhogg.uk",
useKeyConnector: false,
cipherIds: [
"cbea34a8-bde4-46ad-9d19-b05001228ab2",
"cbea34a8-bde4-46ad-9d19-b05001228cd3",
"cbea34a8-bde4-46ad-9d19-b05001228xy4",
"cbea34a8-bde4-46ad-9d19-b05001227nm5",
],
}),
mock<MemberCipherDetailsResponse>({
userGuid: "user-3",
userName: "Gareth Keenan",
email: "gareth.keenan@wernhamhogg.uk",
useKeyConnector: true,
cipherIds: [
"cbea34a8-bde4-46ad-9d19-b05001228cd3",
"cbea34a8-bde4-46ad-9d19-b05001228xy4",
"cbea34a8-bde4-46ad-9d19-b05001227nm5",
"cbea34a8-bde4-46ad-9d19-b05001227nm7",
],
}),
mock<MemberCipherDetailsResponse>({
userGuid: "user-4",
userName: "Dawn Tinsley",
email: "dawn.tinsley@wernhamhogg.uk",
useKeyConnector: true,
cipherIds: [
"cbea34a8-bde4-46ad-9d19-b05001228ab2",
"cbea34a8-bde4-46ad-9d19-b05001228cd3",
"cbea34a8-bde4-46ad-9d19-b05001228xy4",
],
}),
mock<MemberCipherDetailsResponse>({
userGuid: "user-5",
userName: "Keith Bishop",
email: "keith.bishop@wernhamhogg.uk",
useKeyConnector: false,
cipherIds: [
"cbea34a8-bde4-46ad-9d19-b05001228ab1",
"cbea34a8-bde4-46ad-9d19-b05001228cd3",
"cbea34a8-bde4-46ad-9d19-b05001228xy4",
"cbea34a8-bde4-46ad-9d19-b05001227nm5",
],
}),
mock<MemberCipherDetailsResponse>({
userGuid: "user-1",
userName: "Chris Finch",
email: "chris.finch@wernhamhogg.uk",
useKeyConnector: true,
cipherIds: [
"cbea34a8-bde4-46ad-9d19-b05001228ab2",
"cbea34a8-bde4-46ad-9d19-b05001228cd3",
"cbea34a8-bde4-46ad-9d19-b05001228xy4",
],
}),
mock<MemberCipherDetailsResponse>({
userGuid: "user-1",
userName: "Mister Secure",
email: "mister.secure@secureco.com",
useKeyConnector: true,
cipherIds: ["cbea34a8-bde4-46ad-9d19-b05001227tt1"],
}),
];

View File

@@ -7,8 +7,10 @@ import { MemberCipherDetailsResponse } from "..";
import { ApplicationHealthReportDetailEnriched } from "../report-data-service.types";
import {
ApplicationHealthReportDetail,
CipherHealthReport,
OrganizationReportApplication,
OrganizationReportSummary,
PasswordHealthData,
} from "../report-models";
const mockApplication1: ApplicationHealthReportDetail = {
@@ -139,3 +141,41 @@ export const mockMemberDetails = [
email: "user3@other.com",
}),
];
export const mockCipherHealthReports: CipherHealthReport[] = [
{
applications: ["app.com"],
cipherMembers: [],
healthData: createPasswordHealthData(0),
cipher: mockCipherViews[0],
},
{
applications: ["app.com"],
cipherMembers: [],
healthData: createPasswordHealthData(1),
cipher: mockCipherViews[1],
},
{
applications: ["other.com"],
cipherMembers: [],
healthData: createPasswordHealthData(2),
cipher: mockCipherViews[2],
},
];
function createPasswordHealthData(reusedPasswordCount: number | null): PasswordHealthData {
return {
reusedPasswordCount: reusedPasswordCount ?? 0,
weakPasswordDetail: {
score: 0,
detailValue: {
label: "",
badgeVariant: "info",
},
},
exposedPasswordDetail: {
cipherId: "",
exposedXTimes: 0,
},
};
}

View File

@@ -2,80 +2,9 @@ import { mock } from "jest-mock-extended";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { MemberCipherDetailsApiService } from "./member-cipher-details-api.service";
import { mockMemberCipherDetailsResponse } from "../../models/mocks/member-cipher-details-response.mock";
export const mockMemberCipherDetails: any = [
{
userName: "David Brent",
email: "david.brent@wernhamhogg.uk",
usesKeyConnector: true,
cipherIds: [
"cbea34a8-bde4-46ad-9d19-b05001228ab1",
"cbea34a8-bde4-46ad-9d19-b05001228ab2",
"cbea34a8-bde4-46ad-9d19-b05001228xy4",
"cbea34a8-bde4-46ad-9d19-b05001227nm5",
],
},
{
userName: "Tim Canterbury",
email: "tim.canterbury@wernhamhogg.uk",
usesKeyConnector: false,
cipherIds: [
"cbea34a8-bde4-46ad-9d19-b05001228ab2",
"cbea34a8-bde4-46ad-9d19-b05001228cd3",
"cbea34a8-bde4-46ad-9d19-b05001228xy4",
"cbea34a8-bde4-46ad-9d19-b05001227nm5",
],
},
{
userName: "Gareth Keenan",
email: "gareth.keenan@wernhamhogg.uk",
usesKeyConnector: true,
cipherIds: [
"cbea34a8-bde4-46ad-9d19-b05001228cd3",
"cbea34a8-bde4-46ad-9d19-b05001228xy4",
"cbea34a8-bde4-46ad-9d19-b05001227nm5",
"cbea34a8-bde4-46ad-9d19-b05001227nm7",
],
},
{
userName: "Dawn Tinsley",
email: "dawn.tinsley@wernhamhogg.uk",
usesKeyConnector: true,
cipherIds: [
"cbea34a8-bde4-46ad-9d19-b05001228ab2",
"cbea34a8-bde4-46ad-9d19-b05001228cd3",
"cbea34a8-bde4-46ad-9d19-b05001228xy4",
],
},
{
userName: "Keith Bishop",
email: "keith.bishop@wernhamhogg.uk",
usesKeyConnector: false,
cipherIds: [
"cbea34a8-bde4-46ad-9d19-b05001228ab1",
"cbea34a8-bde4-46ad-9d19-b05001228cd3",
"cbea34a8-bde4-46ad-9d19-b05001228xy4",
"cbea34a8-bde4-46ad-9d19-b05001227nm5",
],
},
{
userName: "Chris Finch",
email: "chris.finch@wernhamhogg.uk",
usesKeyConnector: true,
cipherIds: [
"cbea34a8-bde4-46ad-9d19-b05001228ab2",
"cbea34a8-bde4-46ad-9d19-b05001228cd3",
"cbea34a8-bde4-46ad-9d19-b05001228xy4",
],
},
{
userName: "Mister Secure",
email: "mister.secure@secureco.com",
usesKeyConnector: true,
cipherIds: ["cbea34a8-bde4-46ad-9d19-b05001227tt1"],
},
];
import { MemberCipherDetailsApiService } from "./member-cipher-details-api.service";
describe("Member Cipher Details API Service", () => {
let memberCipherDetailsApiService: MemberCipherDetailsApiService;
@@ -92,7 +21,7 @@ describe("Member Cipher Details API Service", () => {
});
it("getMemberCipherDetails retrieves data", async () => {
apiService.send.mockResolvedValue(mockMemberCipherDetails);
apiService.send.mockResolvedValue(mockMemberCipherDetailsResponse);
const orgId = "1234";
const result = await memberCipherDetailsApiService.getMemberCipherDetails(orgId);

View File

@@ -240,10 +240,10 @@ describe("RiskInsightsApiService", () => {
expect(mockApiService.send).toHaveBeenCalledWith(
"PATCH",
`/reports/organizations/${orgId.toString()}/data/application/${reportId.toString()}`,
mockApplication,
{ applicationData: mockApplication.encryptedString!, id: reportId, organizationId: orgId },
true,
true,
);
expect(result).toBeUndefined();
expect(result).toBeTruthy();
});
});

View File

@@ -9,6 +9,7 @@ import { LogService } from "@bitwarden/logging";
import { createNewSummaryData } from "../../helpers";
import { RiskInsightsData, SaveRiskInsightsReportResponse } from "../../models";
import { mockMemberCipherDetailsResponse } from "../../models/mocks/member-cipher-details-response.mock";
import {
mockApplicationData,
mockEnrichedReportData,
@@ -32,6 +33,15 @@ describe("RiskInsightsOrchestratorService", () => {
const mockUserId = "user-101" as UserId;
const mockReportId = "report-1" as OrganizationReportId;
const reportState: RiskInsightsData = {
id: mockReportId,
reportData: [],
summaryData: createNewSummaryData(),
applicationData: [],
creationDate: new Date(),
};
const mockCiphers = [{ id: "cipher-1" }] as any;
// Mock services
const mockAccountService = mock<AccountService>({
activeAccount$: of(mock<Account>({ id: mockUserId })),
@@ -42,40 +52,56 @@ describe("RiskInsightsOrchestratorService", () => {
const mockOrganizationService = mock<OrganizationService>();
const mockCipherService = mock<CipherService>();
const mockMemberCipherDetailsApiService = mock<MemberCipherDetailsApiService>();
const mockPasswordHealthService = mock<PasswordHealthService>();
let mockPasswordHealthService: PasswordHealthService;
const mockReportApiService = mock<RiskInsightsApiService>();
const mockReportService = mock<RiskInsightsReportService>();
let mockReportService: RiskInsightsReportService;
const mockRiskInsightsEncryptionService = mock<RiskInsightsEncryptionService>();
const mockLogService = mock<LogService>();
beforeEach(() => {
// Mock pipes from constructor
mockReportService = mock<RiskInsightsReportService>({
generateApplicationsReport: jest.fn().mockReturnValue(mockEnrichedReportData),
getApplicationsSummary: jest.fn().mockReturnValue(mockSummaryData),
getOrganizationApplications: jest.fn().mockReturnValue(mockApplicationData),
getRiskInsightsReport$: jest.fn().mockReturnValue(of(reportState)),
saveRiskInsightsReport$: jest
.fn()
.mockReturnValue(of({ id: mockReportId } as SaveRiskInsightsReportResponse)),
});
// Arrange mocks for new flow
mockMemberCipherDetailsApiService.getMemberCipherDetails.mockResolvedValue(
mockMemberCipherDetailsResponse,
);
mockPasswordHealthService = mock<PasswordHealthService>({
auditPasswordLeaks$: jest.fn(() => of([])),
isValidCipher: jest.fn().mockReturnValue(true),
findWeakPasswordDetails: jest.fn().mockReturnValue(null),
});
mockCipherService.getAllFromApiForOrganization.mockReturnValue(mockCiphers);
service = new RiskInsightsOrchestratorService(
mockAccountService,
mockCipherService,
mockCriticalAppsService,
mockLogService,
mockMemberCipherDetailsApiService,
mockOrganizationService,
mockPasswordHealthService,
mockReportApiService,
mockReportService,
mockRiskInsightsEncryptionService,
mockLogService,
);
});
describe("fetchReport", () => {
it("should call reportService.getRiskInsightsReport$ with correct org and user IDs and emit ReportState", (done) => {
it("should call with correct org and user IDs and emit ReportState", (done) => {
// Arrange
const privateOrganizationDetailsSubject = service["_organizationDetailsSubject"];
const privateUserIdSubject = service["_userIdSubject"];
// Arrange
const reportState: RiskInsightsData = {
id: mockReportId,
reportData: [],
summaryData: createNewSummaryData(),
applicationData: [],
creationDate: new Date(),
};
mockReportService.getRiskInsightsReport$.mockReturnValueOnce(of(reportState));
// Set up organization and user context
privateOrganizationDetailsSubject.next({
organizationId: mockOrgId,
@@ -100,18 +126,31 @@ describe("RiskInsightsOrchestratorService", () => {
});
it("should emit error ReportState when getRiskInsightsReport$ throws", (done) => {
const { _organizationDetailsSubject, _userIdSubject } = service as any;
mockReportService.getRiskInsightsReport$.mockReturnValueOnce(
// Simulate error
throwError(() => new Error("API error")),
// Setup error passed via constructor for this test case
mockReportService.getRiskInsightsReport$ = jest
.fn()
.mockReturnValue(throwError(() => new Error("API error")));
const testService = new RiskInsightsOrchestratorService(
mockAccountService,
mockCipherService,
mockCriticalAppsService,
mockLogService,
mockMemberCipherDetailsApiService,
mockOrganizationService,
mockPasswordHealthService,
mockReportApiService,
mockReportService,
mockRiskInsightsEncryptionService,
);
const { _organizationDetailsSubject, _userIdSubject } = testService as any;
_organizationDetailsSubject.next({
organizationId: mockOrgId,
organizationName: mockOrgName,
});
_userIdSubject.next(mockUserId);
service.fetchReport();
service.rawReportData$.subscribe((state) => {
testService.fetchReport();
testService.rawReportData$.subscribe((state) => {
if (!state.loading) {
expect(state.error).toBe("Failed to fetch report");
expect(state.data).toBeNull();
@@ -122,17 +161,11 @@ describe("RiskInsightsOrchestratorService", () => {
});
describe("generateReport", () => {
it("should call reportService.generateApplicationsReport and saveRiskInsightsReport$ and emit ReportState", (done) => {
it("should generate report using member ciphers and password health, then save and emit ReportState", (done) => {
const privateOrganizationDetailsSubject = service["_organizationDetailsSubject"];
const privateUserIdSubject = service["_userIdSubject"];
// Arrange
mockReportService.generateApplicationsReport.mockReturnValueOnce(mockEnrichedReportData);
mockReportService.getApplicationsSummary.mockReturnValueOnce(mockSummaryData);
mockReportService.getOrganizationApplications.mockReturnValueOnce(mockApplicationData);
mockReportService.saveRiskInsightsReport$.mockReturnValueOnce(
of({ id: mockReportId } as SaveRiskInsightsReportResponse),
);
// Set up ciphers in orchestrator
privateOrganizationDetailsSubject.next({
organizationId: mockOrgId,
organizationName: mockOrgName,
@@ -145,7 +178,10 @@ describe("RiskInsightsOrchestratorService", () => {
// Assert
service.rawReportData$.subscribe((state) => {
if (!state.loading && state.data) {
expect(mockReportService.generateApplicationsReport).toHaveBeenCalledWith(mockOrgId);
expect(mockMemberCipherDetailsApiService.getMemberCipherDetails).toHaveBeenCalledWith(
mockOrgId,
);
expect(mockReportService.generateApplicationsReport).toHaveBeenCalled();
expect(mockReportService.saveRiskInsightsReport$).toHaveBeenCalledWith(
mockEnrichedReportData,
mockSummaryData,
@@ -160,47 +196,22 @@ describe("RiskInsightsOrchestratorService", () => {
});
});
it("should emit error ReportState when saveRiskInsightsReport$ throws", (done) => {
const privateOrganizationDetailsSubject = service["_organizationDetailsSubject"];
const privateUserIdSubject = service["_userIdSubject"];
describe("destroy", () => {
it("should complete destroy$ subject and unsubscribe reportStateSubscription", () => {
const privateDestroy = (service as any)._destroy$;
const privateReportStateSubscription = (service as any)._reportStateSubscription;
mockReportService.generateApplicationsReport.mockReturnValueOnce(mockEnrichedReportData);
mockReportService.getApplicationsSummary.mockReturnValueOnce(mockSummaryData);
mockReportService.getOrganizationApplications.mockReturnValueOnce(mockApplicationData);
mockReportService.saveRiskInsightsReport$.mockReturnValueOnce(
throwError(() => new Error("Save error")),
);
privateOrganizationDetailsSubject.next({
organizationId: mockOrgId,
organizationName: mockOrgName,
// Spy on the methods you expect to be called.
const destroyCompleteSpy = jest.spyOn(privateDestroy, "complete");
const unsubscribeSpy = jest.spyOn(privateReportStateSubscription, "unsubscribe");
// Execute the destroy method.
service.destroy();
// Assert that the methods were called as expected.
expect(destroyCompleteSpy).toHaveBeenCalled();
expect(unsubscribeSpy).toHaveBeenCalled();
});
privateUserIdSubject.next(mockUserId);
service.generateReport();
service.rawReportData$.subscribe((state) => {
if (!state.loading) {
expect(state.error).toBe("Failed to generate or save report");
expect(state.data).toBeNull();
done();
}
});
});
});
describe("destroy", () => {
it("should complete destroy$ subject and unsubscribe reportStateSubscription", () => {
const privateDestroy = (service as any)._destroy$;
const privateReportStateSubscription = (service as any)._reportStateSubscription;
// Spy on the methods you expect to be called.
const destroyCompleteSpy = jest.spyOn(privateDestroy, "complete");
const unsubscribeSpy = jest.spyOn(privateReportStateSubscription, "unsubscribe");
// Execute the destroy method.
service.destroy();
// Assert that the methods were called as expected.
expect(destroyCompleteSpy).toHaveBeenCalled();
expect(unsubscribeSpy).toHaveBeenCalled();
});
});
});

View File

@@ -109,13 +109,13 @@ export class RiskInsightsOrchestratorService {
private accountService: AccountService,
private cipherService: CipherService,
private criticalAppsService: CriticalAppsService,
private logService: LogService,
private memberCipherDetailsApiService: MemberCipherDetailsApiService,
private organizationService: OrganizationService,
private passwordHealthService: PasswordHealthService,
private reportApiService: RiskInsightsApiService,
private reportService: RiskInsightsReportService,
private riskInsightsEncryptionService: RiskInsightsEncryptionService,
private logService: LogService,
) {
this.logService.debug("[RiskInsightsOrchestratorService] Setting up");
this._setupCriticalApplicationContext();
@@ -283,8 +283,10 @@ export class RiskInsightsOrchestratorService {
this.memberCipherDetailsApiService.getMemberCipherDetails(organizationId),
).pipe(map((memberCiphers) => flattenMemberDetails(memberCiphers)));
return forkJoin([this._ciphers$, memberCiphers$]).pipe(
tap(() => this.logService.debug("[RiskInsightsOrchestratorService] Generating new report")),
return forkJoin([this._ciphers$.pipe(take(1)), memberCiphers$]).pipe(
tap(() => {
this.logService.debug("[RiskInsightsOrchestratorService] Generating new report");
}),
switchMap(([ciphers, memberCiphers]) => this._getCipherHealth(ciphers ?? [], memberCiphers)),
map((cipherHealthReports) =>
this.reportService.generateApplicationsReport(cipherHealthReports),

View File

@@ -12,15 +12,16 @@ import {
SaveRiskInsightsReportResponse,
} from "../../models/api-models.types";
import { mockCiphers } from "../../models/mocks/ciphers.mock";
import { mockMemberCipherDetailsResponse } from "../../models/mocks/member-cipher-details-response.mock";
import {
mockApplicationData,
mockCipherHealthReports,
mockCipherViews,
mockMemberDetails,
mockReportData,
mockSummaryData,
} from "../../models/mocks/mock-data";
import { MemberCipherDetailsApiService } from "../api/member-cipher-details-api.service";
import { mockMemberCipherDetails } from "../api/member-cipher-details-api.service.spec";
import { RiskInsightsApiService } from "../api/risk-insights-api.service";
import { PasswordHealthService } from "./password-health.service";
@@ -54,7 +55,9 @@ describe("RiskInsightsReportService", () => {
beforeEach(() => {
cipherService.getAllFromApiForOrganization.mockResolvedValue(mockCiphers);
memberCipherDetailsService.getMemberCipherDetails.mockResolvedValue(mockMemberCipherDetails);
memberCipherDetailsService.getMemberCipherDetails.mockResolvedValue(
mockMemberCipherDetailsResponse,
);
// Mock PasswordHealthService methods
mockPasswordHealthService.isValidCipher.mockImplementation((cipher: any) => {
@@ -95,7 +98,7 @@ describe("RiskInsightsReportService", () => {
cipherService.getAllFromApiForOrganization.mockResolvedValue(mockCipherViews);
memberCipherDetailsService.getMemberCipherDetails.mockResolvedValue(mockMemberDetails);
const result = service.generateApplicationsReport("orgId" as any);
const result = service.generateApplicationsReport(mockCipherHealthReports);
expect(Array.isArray(result)).toBe(true);
// Should group by application name (trimmedUris)
@@ -229,7 +232,11 @@ describe("RiskInsightsReportService", () => {
expect.anything(),
expect.anything(),
);
expect(result).toEqual({ ...mockDecryptedData, creationDate: mockResponse.creationDate });
expect(result).toEqual({
...mockDecryptedData,
id: mockResponse.id,
creationDate: mockResponse.creationDate,
});
});
});
});

View File

@@ -57,13 +57,13 @@ import { RiskInsightsComponent } from "./risk-insights.component";
AccountServiceAbstraction,
CipherService,
CriticalAppsService,
LogService,
MemberCipherDetailsApiService,
OrganizationService,
PasswordHealthService,
RiskInsightsApiService,
RiskInsightsReportService,
RiskInsightsEncryptionService,
LogService,
],
}),
safeProvider({