diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/member-cipher-details-api.service.spec.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/member-cipher-details-api.service.spec.ts index d6474c2c9c4..067b5562a6c 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/member-cipher-details-api.service.spec.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/member-cipher-details-api.service.spec.ts @@ -75,6 +75,24 @@ export const mockMemberCipherDetails: any = [ usesKeyConnector: true, cipherIds: ["cbea34a8-bde4-46ad-9d19-b05001227tt1"], }, + { + userName: "invited User 1", + email: "invited.user1@secureco.com", + usesKeyConnector: true, + cipherIds: [""], + }, + { + userName: "invited User 2", + email: "invited.user2@secureco.com", + usesKeyConnector: true, + cipherIds: [""], + }, + { + userName: "invited User 2", + email: "invited.user2@secureco.com", + usesKeyConnector: true, + cipherIds: [""], + }, ]; describe("Member Cipher Details API Service", () => { @@ -97,7 +115,7 @@ describe("Member Cipher Details API Service", () => { const orgId = "1234"; const result = await memberCipherDetailsApiService.getMemberCipherDetails(orgId); expect(result).not.toBeNull(); - expect(result).toHaveLength(7); + expect(result).toHaveLength(10); expect(apiService.send).toHaveBeenCalledWith( "GET", "/reports/member-cipher-details/" + orgId, diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-data.service.spec.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-data.service.spec.ts new file mode 100644 index 00000000000..1dc594ff1c2 --- /dev/null +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-data.service.spec.ts @@ -0,0 +1,78 @@ +import { mock } from "jest-mock-extended"; +import { of, throwError } from "rxjs"; + +import { + MemberDetailsFlat, + HealthReportUriDetailWithMemberDetails, +} from "../models/password-health"; + +import { RiskInsightsDataService } from "./risk-insights-data.service"; +import { RiskInsightsReportService } from "./risk-insights-report.service"; + +describe("RiskInsightsDataService - memberDetailsSubject", () => { + let service: RiskInsightsDataService; + const mockReportService = mock(); + + beforeEach(() => { + mockReportService.generateApplicationsReport$.mockReturnValue( + of({ + healthReport: [], + members: [], + } as HealthReportUriDetailWithMemberDetails), + ); + service = new RiskInsightsDataService(mockReportService); + }); + + it("should initialize memberDetailsSubject with an empty array", (done) => { + service.memberDetails$.subscribe((value) => { + expect(value).toEqual([]); + done(); + }); + }); + + it("should update memberDetailsSubject when fetchApplicationsReport succeeds", (done) => { + const mockMembers: MemberDetailsFlat[] = [ + { id: "1", name: "Alice" } as unknown as MemberDetailsFlat, + { id: "2", name: "Bob" } as unknown as MemberDetailsFlat, + ]; + const mockReport: HealthReportUriDetailWithMemberDetails = { + healthReport: [], + members: mockMembers, + }; + mockReportService.generateApplicationsReport$.mockReturnValue(of(mockReport)); + + service.memberDetails$.subscribe((value) => { + if (value.length > 0) { + expect(value).toEqual(mockMembers); + done(); + } + }); + + service.fetchApplicationsReport("org-123"); + }); + + it("should set memberDetailsSubject to empty array on fetchApplicationsReport error", (done) => { + // setup a failure when the method is called + mockReportService.generateApplicationsReport$.mockReturnValue( + throwError(() => new Error("Network error")), + ); + + // Set initial value to something else to verify it resets + (service as any).memberDetailsSubject.next([ + { id: "x", name: "Test" } as unknown as MemberDetailsFlat, + ]); + + // test to ensure that the subscribe result is an empty array + service.memberDetails$.pipe().subscribe({ + next: (value) => { + if (value.length === 0) { + expect(value).toEqual([]); + done(); + } + }, + }); + + // invoke the method that should trigger the error + service.fetchApplicationsReport("org-123"); + }); +}); diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-data.service.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-data.service.ts index f39e3cc1a60..21f5e29ec98 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-data.service.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-data.service.ts @@ -68,6 +68,7 @@ export class RiskInsightsDataService { }, error: () => { this.applicationsSubject.next([]); + this.memberDetailsSubject.next([]); }, }); } diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.spec.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.spec.ts index a64d59e2138..83ffe4ffde3 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.spec.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.spec.ts @@ -44,6 +44,7 @@ describe("RiskInsightsReportService", () => { const result = await firstValueFrom(service.generateRawDataReport$("orgId")); expect(result.ciphers).toHaveLength(6); + expect(result.members).toHaveLength(9); let testCaseResults = result.ciphers.filter( (x) => x.id === "cbea34a8-bde4-46ad-9d19-b05001228ab1", @@ -68,10 +69,23 @@ describe("RiskInsightsReportService", () => { expect(testCase.reusedPasswordCount).toEqual(1); }); + it("should generate the raw data for members correctly", async () => { + const result = await firstValueFrom(service.generateRawDataReport$("orgId")); + + expect(result.members).toHaveLength(9); + + let testCaseResults = result.members.filter((x) => x.email === "invited.user1@secureco.com"); + expect(testCaseResults).toHaveLength(1); + + testCaseResults = result.members.filter((x) => x.email === "invited.user2@secureco.com"); + expect(testCaseResults).toHaveLength(1); + }); + it("should generate the raw data + uri report correctly", async () => { const result = await firstValueFrom(service.generateRawDataUriReport$("orgId")); expect(result.ciphers).toHaveLength(11); + expect(result.members).toHaveLength(9); // Two ciphers that have google.com as their uri. There should be 2 results const googleResults = result.ciphers.filter((x) => x.trimmedUri === "google.com"); @@ -143,7 +157,7 @@ describe("RiskInsightsReportService", () => { reportResult.members, ); - expect(reportSummary.totalMemberCount).toEqual(7); + expect(reportSummary.totalMemberCount).toEqual(9); expect(reportSummary.totalAtRiskMemberCount).toEqual(6); expect(reportSummary.totalApplicationCount).toEqual(8); expect(reportSummary.totalAtRiskApplicationCount).toEqual(7);