1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-16 08:34:39 +00:00

Added tests, moved method.

This commit is contained in:
Todd Martin
2025-03-18 17:03:30 -04:00
parent 1b3a2ecf49
commit 9716a95c7a
6 changed files with 182 additions and 17 deletions

View File

@@ -16,7 +16,7 @@ import { filter, first, map, take } from "rxjs/operators";
import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model";
import { AuthRequestApiService } from "@bitwarden/auth/common";
import { AuthRequestService } from "@bitwarden/auth/common";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
@@ -110,7 +110,7 @@ export class VaultComponent implements OnInit, OnDestroy {
private totpService: TotpService,
private passwordRepromptService: PasswordRepromptService,
private searchBarService: SearchBarService,
private authRequestApiService: AuthRequestApiService,
private authRequestService: AuthRequestService,
private dialogService: DialogService,
private billingAccountProfileStateService: BillingAccountProfileStateService,
private toastService: ToastService,
@@ -231,7 +231,7 @@ export class VaultComponent implements OnInit, OnDestroy {
this.searchBarService.setEnabled(true);
this.searchBarService.setPlaceholderText(this.i18nService.t("searchVault"));
const authRequest = await this.authRequestApiService.getLastAuthRequest();
const authRequest = await this.authRequestService.getLastAuthRequest();
if (authRequest != null) {
this.messagingService.send("openLoginApproval", {
notificationId: authRequest.id,

View File

@@ -1,6 +1,7 @@
import { AuthRequestUpdateRequest } from "@bitwarden/common/auth/models/request/auth-request-update.request";
import { AuthRequest } from "@bitwarden/common/auth/models/request/auth.request";
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
import { ListResponse } from "@bitwarden/common/models/response/list.response";
export abstract class AuthRequestApiService {
/**
@@ -49,9 +50,9 @@ export abstract class AuthRequestApiService {
) => Promise<AuthRequestResponse>;
/**
* Gets the most-recently-created auth request for the logged-in user.
* Gets a list open auth requests for the logged-in user.
*
* @returns A promise that resolves to the auth request response.
* @returns A promise that resolves to a list response of auth request responses.
*/
abstract getLastAuthRequest: () => Promise<AuthRequestResponse>;
abstract getAuthRequests(): Promise<ListResponse<AuthRequestResponse>>;
}

View File

@@ -0,0 +1,146 @@
import { mock, MockProxy } from "jest-mock-extended";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuthRequestType } from "@bitwarden/common/auth/enums/auth-request-type";
import { AuthRequestUpdateRequest } from "@bitwarden/common/auth/models/request/auth-request-update.request";
import { AuthRequest } from "@bitwarden/common/auth/models/request/auth.request";
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
import { ListResponse } from "@bitwarden/common/models/response/list.response";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { DefaultAuthRequestApiService } from "./auth-request-api.service";
describe("DefaultAuthRequestApiService", () => {
let service: DefaultAuthRequestApiService;
let apiService: MockProxy<ApiService>;
let logService: MockProxy<LogService>;
beforeEach(() => {
apiService = mock<ApiService>();
logService = mock<LogService>();
service = new DefaultAuthRequestApiService(apiService, logService);
});
afterEach(() => {
jest.resetAllMocks();
});
describe("getAuthRequest", () => {
it("calls API with correct parameters", async () => {
const requestId = "test-request-id";
apiService.send.mockResolvedValue(true);
const result = await service.getAuthRequest(requestId);
expect(result).toBeInstanceOf(AuthRequestResponse);
expect(apiService.send).toHaveBeenCalledWith(
"GET",
"/auth-requests/" + requestId,
null,
true,
true,
);
});
});
describe("getAuthResponse", () => {
it("calls API with correct parameters", async () => {
const requestId = "test-request-id";
const accessCode = "test-access-code";
apiService.send.mockResolvedValue(true);
const result = await service.getAuthResponse(requestId, accessCode);
expect(result).toBeInstanceOf(AuthRequestResponse);
expect(apiService.send).toHaveBeenCalledWith(
"GET",
"/auth-requests/" + requestId + "/response?code=" + accessCode,
null,
false,
true,
);
});
});
describe("postAdminAuthRequest", () => {
it("calls API with correct parameters", async () => {
const mockRequest = new AuthRequest(
"test@test.com",
"test-identifier",
"test-public-key",
AuthRequestType.AdminApproval,
"test-access-code",
);
const result = await service.postAdminAuthRequest(mockRequest);
expect(result).toBeInstanceOf(AuthRequestResponse);
expect(apiService.send).toHaveBeenCalledWith(
"POST",
"/auth-requests/admin-request",
mockRequest,
true,
true,
);
});
});
describe("postAuthRequest", () => {
it("calls API with correct parameters", async () => {
const mockRequest = new AuthRequest(
"test@test.com",
"test-identifier",
"test-public-key",
AuthRequestType.AuthenticateAndUnlock,
"test-access-code",
);
const result = await service.postAuthRequest(mockRequest);
expect(result).toBeInstanceOf(AuthRequestResponse);
expect(apiService.send).toHaveBeenCalledWith(
"POST",
"/auth-requests/",
mockRequest,
false,
true,
null,
expect.any(Function),
);
});
});
describe("putAuthRequest", () => {
it("calls API with correct parameters", async () => {
const requestId = "test-request-id";
const mockRequest = new AuthRequestUpdateRequest(
"test-key",
"test-hash",
"test-identifier",
true,
);
const result = await service.putAuthRequest(requestId, mockRequest);
expect(apiService.send).toHaveBeenCalledWith(
"PUT",
"/auth-requests/" + requestId,
mockRequest,
true,
true,
);
expect(result).toBeInstanceOf(AuthRequestResponse);
});
});
describe("getAuthRequests", () => {
it("calls API with correct parameters and return list", async () => {
const mockResponse = { data: [{ id: "test-request-id-1" }, { id: "test-request-id-2" }] };
apiService.send.mockResolvedValue(mockResponse);
const result = await service.getAuthRequests();
expect(apiService.send).toHaveBeenCalledWith("GET", "/auth-requests/", null, true, true);
expect(result).toBeInstanceOf(ListResponse<AuthRequestResponse>);
});
});
});

View File

@@ -92,15 +92,4 @@ export class DefaultAuthRequestApiService implements AuthRequestApiService {
const r = await this.apiService.send("GET", path, null, true, true);
return new ListResponse(r, AuthRequestResponse);
}
async getLastAuthRequest(): Promise<AuthRequestResponse> {
const requests = await this.getAuthRequests();
const activeRequests = requests.data.filter(
(m: AuthRequestResponse) => !m.isAnswered && !m.isExpired,
);
const lastRequest = activeRequests.sort((a: AuthRequestResponse, b: AuthRequestResponse) =>
a.creationDate.localeCompare(b.creationDate),
)[activeRequests.length - 1];
return lastRequest;
}
}

View File

@@ -3,6 +3,7 @@ import { mock } from "jest-mock-extended";
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { FakeMasterPasswordService } from "@bitwarden/common/key-management/master-password/services/fake-master-password.service";
import { ListResponse } from "@bitwarden/common/models/response/list.response";
import { AuthRequestPushNotification } from "@bitwarden/common/models/response/notification.response";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
@@ -281,4 +282,21 @@ describe("AuthRequestService", () => {
expect(phrase).toEqual(phraseUpperCase);
});
});
describe("getLastAuthRequest", () => {
it("should return the most recent unanswered and unexpired request", async () => {
const mockData = {
data: [
{ creationDate: "2025-03-15", isAnswered: false, isExpired: false },
{ creationDate: "2025-03-16", isAnswered: false, isExpired: false },
],
} as ListResponse<AuthRequestResponse>;
apiService.getAuthRequests.mockResolvedValue(mockData);
const result = await sut.getLastAuthRequest();
expect(result.creationDate).toBe("2025-03-16");
});
});
});

View File

@@ -203,6 +203,17 @@ export class AuthRequestService implements AuthRequestServiceAbstraction {
};
}
async getLastAuthRequest(): Promise<AuthRequestResponse> {
const requests = await this.apiService.getAuthRequests();
const activeRequests = requests.data.filter(
(m: AuthRequestResponse) => !m.isAnswered && !m.isExpired,
);
const lastRequest = activeRequests.sort((a: AuthRequestResponse, b: AuthRequestResponse) =>
a.creationDate.localeCompare(b.creationDate),
)[activeRequests.length - 1];
return lastRequest;
}
sendAuthRequestPushNotification(notification: AuthRequestPushNotification): void {
if (notification.id != null) {
this.authRequestPushNotificationSubject.next(notification.id);