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

Test event upload logic

This commit is contained in:
Matt Gibson
2025-06-16 08:54:30 -07:00
parent 063929350e
commit f811fc3346
2 changed files with 136 additions and 6 deletions

View File

@@ -0,0 +1,126 @@
import { mock, MockProxy } from "jest-mock-extended";
import { of } from "rxjs";
import { FakeAccountService, FakeStateProvider, mockAccountServiceWith } from "../../../spec";
import { AuthService } from "../../auth/abstractions/auth.service";
import { AuthenticationStatus } from "../../auth/enums/authentication-status";
import { EventType } from "../../enums";
import { EventData } from "../../models/data/event.data";
import { LogService } from "../../platform/abstractions/log.service";
import { TaskSchedulerService } from "../../platform/scheduling";
import { UserId } from "../../types/guid";
import { ApiService } from "../api.service";
import { EventUploadService } from "./event-upload.service";
import { EVENT_COLLECTION } from "./key-definitions";
describe("event upload service", () => {
let eventUploadService: EventUploadService;
let apiService: MockProxy<ApiService>;
let stateProvider: FakeStateProvider;
let logService: MockProxy<LogService>;
let authService: MockProxy<AuthService>;
let taskSchedulerService: MockProxy<TaskSchedulerService>;
const userId = "00000000-0000-0000-0000-000000000001" as UserId;
let accountService: FakeAccountService;
beforeEach(() => {
apiService = mock();
accountService = mockAccountServiceWith(userId);
stateProvider = new FakeStateProvider(accountService);
logService = mock();
authService = mock();
taskSchedulerService = mock();
eventUploadService = new EventUploadService(
apiService,
stateProvider,
logService,
authService,
taskSchedulerService,
);
});
afterEach(() => {
jest.resetAllMocks();
});
describe("uploadEvents", () => {
describe("unlocked user", () => {
beforeEach(() => {
authService.authStatusFor$.mockReturnValue(of(AuthenticationStatus.Unlocked));
});
it("attempts upload of all existing events", async () => {
const events = makeEventData(3);
await stateProvider.setUserState(EVENT_COLLECTION, events, userId);
await eventUploadService.uploadEvents(userId);
expect(apiService.postEventsCollect).toHaveBeenCalledWith(
events.map((e) => eventUploadService.eventDataToEventRequest(e)),
userId,
);
});
it("re-stores events returned from api upload", async () => {
const events = makeEventData(3);
await stateProvider.setUserState(EVENT_COLLECTION, events, userId);
const response = events.slice(1);
apiService.postEventsCollect.mockResolvedValueOnce(response);
await eventUploadService.uploadEvents(userId);
expect(stateProvider.mock.setUserState).toHaveBeenCalledWith(
EVENT_COLLECTION,
response,
userId,
);
});
it("re-stores all events if api upload throws", async () => {
const events = makeEventData(3);
await stateProvider.setUserState(EVENT_COLLECTION, events, userId);
apiService.postEventsCollect.mockRejectedValueOnce(new Error("API error"));
await eventUploadService.uploadEvents(userId);
expect(stateProvider.mock.setUserState).toHaveBeenCalledWith(
EVENT_COLLECTION,
events,
userId,
);
});
it("does not store events if api upload is successful and no events are returned", async () => {
const events = makeEventData(3);
await stateProvider.setUserState(EVENT_COLLECTION, events, userId);
// remove the above call to the mock
stateProvider.mock.setUserState.mockClear();
apiService.postEventsCollect.mockResolvedValueOnce([]);
await eventUploadService.uploadEvents(userId);
expect(stateProvider.mock.setUserState).not.toHaveBeenCalled();
});
});
});
});
function makeEventData(count: number): EventData[] {
const events: EventData[] = [];
for (let i = 0; i < count; i++) {
events.push(
EventData.fromJSON({
type: EventType.Cipher_ClientViewed,
date: new Date().toISOString(),
cipherId: "00000000-0000-0000-0000-000000000001",
organizationId: null!,
}),
);
}
return events;
}

View File

@@ -72,12 +72,7 @@ export class EventUploadService implements EventUploadServiceAbstraction {
return;
}
const eventRequests = eventCollection.map((e) => {
const req = new EventRequest();
req.type = e.type;
req.cipherId = e.cipherId;
req.date = e.date;
req.organizationId = e.organizationId;
return req;
return this.eventDataToEventRequest(e);
});
let failedEvents: EventRequest[];
@@ -102,6 +97,15 @@ export class EventUploadService implements EventUploadServiceAbstraction {
}
}
eventDataToEventRequest(event: EventData): EventRequest {
const req = new EventRequest();
req.type = event.type;
req.cipherId = event.cipherId;
req.date = event.date;
req.organizationId = event.organizationId;
return req;
}
/** Return user's events and then clear them from state
* @param userId the user to grab and clear events for
*/