diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/mocks/member-cipher-details-response.mock.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/mocks/member-cipher-details-response.mock.ts index 92d87175974..0f14642dc6b 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/mocks/member-cipher-details-response.mock.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/mocks/member-cipher-details-response.mock.ts @@ -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({ + 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({ + 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({ + 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({ + 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({ + 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({ + 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({ + userGuid: "user-1", + userName: "Mister Secure", + email: "mister.secure@secureco.com", + useKeyConnector: true, + cipherIds: ["cbea34a8-bde4-46ad-9d19-b05001227tt1"], + }), +]; diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/mocks/mock-data.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/mocks/mock-data.ts index 29016968a89..1a30ad754c3 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/mocks/mock-data.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/mocks/mock-data.ts @@ -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, + }, + }; +} diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/api/member-cipher-details-api.service.spec.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/api/member-cipher-details-api.service.spec.ts index d6474c2c9c4..beb19c91c13 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/api/member-cipher-details-api.service.spec.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/api/member-cipher-details-api.service.spec.ts @@ -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); diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/api/risk-insights-api.service.spec.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/api/risk-insights-api.service.spec.ts index 28520dc4619..879563af526 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/api/risk-insights-api.service.spec.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/api/risk-insights-api.service.spec.ts @@ -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(); }); }); diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.spec.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.spec.ts index 1e6fad1b021..f7d7da52a35 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.spec.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.spec.ts @@ -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({ activeAccount$: of(mock({ id: mockUserId })), @@ -42,40 +52,56 @@ describe("RiskInsightsOrchestratorService", () => { const mockOrganizationService = mock(); const mockCipherService = mock(); const mockMemberCipherDetailsApiService = mock(); - const mockPasswordHealthService = mock(); + let mockPasswordHealthService: PasswordHealthService; const mockReportApiService = mock(); - const mockReportService = mock(); + let mockReportService: RiskInsightsReportService; const mockRiskInsightsEncryptionService = mock(); const mockLogService = mock(); beforeEach(() => { + // Mock pipes from constructor + mockReportService = mock({ + 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({ + 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(); }); }); }); diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.ts index 5c45c3dfd39..9a68d4020c3 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.ts @@ -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), diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-report.service.spec.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-report.service.spec.ts index d53c8f08f28..f0e88f4e434 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-report.service.spec.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-report.service.spec.ts @@ -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, + }); }); }); }); diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/access-intelligence.module.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/access-intelligence.module.ts index 326a221d4c7..ab193e70130 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/access-intelligence.module.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/access-intelligence.module.ts @@ -57,13 +57,13 @@ import { RiskInsightsComponent } from "./risk-insights.component"; AccountServiceAbstraction, CipherService, CriticalAppsService, + LogService, MemberCipherDetailsApiService, OrganizationService, PasswordHealthService, RiskInsightsApiService, RiskInsightsReportService, RiskInsightsEncryptionService, - LogService, ], }), safeProvider({