2023-01-28 00:12:11 -05:00
|
|
|
import { BadRequestException } from '@nestjs/common';
|
2024-08-16 09:48:43 -04:00
|
|
|
import { Permission } from 'src/enum';
|
2025-03-10 12:04:35 -04:00
|
|
|
import { ApiKeyService } from 'src/services/api-key.service';
|
|
|
|
|
import { factory, newUuid } from 'test/small.factory';
|
2025-02-10 18:47:42 -05:00
|
|
|
import { newTestService, ServiceMocks } from 'test/utils';
|
2023-01-18 09:40:15 -05:00
|
|
|
|
2025-03-10 12:04:35 -04:00
|
|
|
describe(ApiKeyService.name, () => {
|
|
|
|
|
let sut: ApiKeyService;
|
2025-02-10 18:47:42 -05:00
|
|
|
let mocks: ServiceMocks;
|
2023-01-18 09:40:15 -05:00
|
|
|
|
2024-03-05 23:23:06 +01:00
|
|
|
beforeEach(() => {
|
2025-03-10 12:04:35 -04:00
|
|
|
({ sut, mocks } = newTestService(ApiKeyService));
|
2023-01-18 09:40:15 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('create', () => {
|
|
|
|
|
it('should create a new key', async () => {
|
2025-03-10 12:04:35 -04:00
|
|
|
const auth = factory.auth();
|
|
|
|
|
const apiKey = factory.apiKey({ userId: auth.user.id, permissions: [Permission.ALL] });
|
|
|
|
|
const key = 'super-secret';
|
|
|
|
|
|
|
|
|
|
mocks.crypto.newPassword.mockReturnValue(key);
|
|
|
|
|
mocks.apiKey.create.mockResolvedValue(apiKey);
|
|
|
|
|
|
|
|
|
|
await sut.create(auth, { name: apiKey.name, permissions: apiKey.permissions });
|
|
|
|
|
|
2025-02-10 18:47:42 -05:00
|
|
|
expect(mocks.apiKey.create).toHaveBeenCalledWith({
|
2025-03-10 12:04:35 -04:00
|
|
|
key: 'super-secret (hashed)',
|
|
|
|
|
name: apiKey.name,
|
|
|
|
|
permissions: apiKey.permissions,
|
|
|
|
|
userId: apiKey.userId,
|
2023-01-18 09:40:15 -05:00
|
|
|
});
|
2025-02-10 18:47:42 -05:00
|
|
|
expect(mocks.crypto.newPassword).toHaveBeenCalled();
|
|
|
|
|
expect(mocks.crypto.hashSha256).toHaveBeenCalled();
|
2023-01-18 09:40:15 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should not require a name', async () => {
|
2025-03-10 12:04:35 -04:00
|
|
|
const auth = factory.auth();
|
|
|
|
|
const apiKey = factory.apiKey({ userId: auth.user.id });
|
|
|
|
|
const key = 'super-secret';
|
|
|
|
|
|
|
|
|
|
mocks.crypto.newPassword.mockReturnValue(key);
|
|
|
|
|
mocks.apiKey.create.mockResolvedValue(apiKey);
|
2023-01-18 09:40:15 -05:00
|
|
|
|
2025-03-10 12:04:35 -04:00
|
|
|
await sut.create(auth, { permissions: [Permission.ALL] });
|
2023-01-18 09:40:15 -05:00
|
|
|
|
2025-02-10 18:47:42 -05:00
|
|
|
expect(mocks.apiKey.create).toHaveBeenCalledWith({
|
2025-03-10 12:04:35 -04:00
|
|
|
key: 'super-secret (hashed)',
|
2023-01-18 09:40:15 -05:00
|
|
|
name: 'API Key',
|
2024-08-16 09:48:43 -04:00
|
|
|
permissions: [Permission.ALL],
|
2025-03-10 12:04:35 -04:00
|
|
|
userId: auth.user.id,
|
2023-01-18 09:40:15 -05:00
|
|
|
});
|
2025-02-10 18:47:42 -05:00
|
|
|
expect(mocks.crypto.newPassword).toHaveBeenCalled();
|
|
|
|
|
expect(mocks.crypto.hashSha256).toHaveBeenCalled();
|
2023-01-18 09:40:15 -05:00
|
|
|
});
|
2024-10-08 23:08:49 +02:00
|
|
|
|
|
|
|
|
it('should throw an error if the api key does not have sufficient permissions', async () => {
|
2025-03-10 12:04:35 -04:00
|
|
|
const auth = factory.auth({ apiKey: factory.authApiKey({ permissions: [Permission.ASSET_READ] }) });
|
|
|
|
|
|
|
|
|
|
await expect(sut.create(auth, { permissions: [Permission.ASSET_UPDATE] })).rejects.toBeInstanceOf(
|
|
|
|
|
BadRequestException,
|
|
|
|
|
);
|
2024-10-08 23:08:49 +02:00
|
|
|
});
|
2023-01-18 09:40:15 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('update', () => {
|
|
|
|
|
it('should throw an error if the key is not found', async () => {
|
2025-03-10 12:04:35 -04:00
|
|
|
const id = newUuid();
|
|
|
|
|
const auth = factory.auth();
|
|
|
|
|
|
2025-03-10 16:52:44 -04:00
|
|
|
mocks.apiKey.getById.mockResolvedValue(void 0);
|
|
|
|
|
|
2025-03-10 12:04:35 -04:00
|
|
|
await expect(sut.update(auth, id, { name: 'New Name' })).rejects.toBeInstanceOf(BadRequestException);
|
2023-01-18 09:40:15 -05:00
|
|
|
|
2025-03-10 12:04:35 -04:00
|
|
|
expect(mocks.apiKey.update).not.toHaveBeenCalledWith(id);
|
2023-01-18 09:40:15 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should update a key', async () => {
|
2025-03-10 12:04:35 -04:00
|
|
|
const auth = factory.auth();
|
|
|
|
|
const apiKey = factory.apiKey({ userId: auth.user.id });
|
|
|
|
|
const newName = 'New name';
|
2023-01-18 09:40:15 -05:00
|
|
|
|
2025-03-10 12:04:35 -04:00
|
|
|
mocks.apiKey.getById.mockResolvedValue(apiKey);
|
|
|
|
|
mocks.apiKey.update.mockResolvedValue(apiKey);
|
2023-01-18 09:40:15 -05:00
|
|
|
|
2025-03-10 12:04:35 -04:00
|
|
|
await sut.update(auth, apiKey.id, { name: newName });
|
|
|
|
|
|
|
|
|
|
expect(mocks.apiKey.update).toHaveBeenCalledWith(auth.user.id, apiKey.id, { name: newName });
|
2023-01-18 09:40:15 -05:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('delete', () => {
|
|
|
|
|
it('should throw an error if the key is not found', async () => {
|
2025-03-10 12:04:35 -04:00
|
|
|
const auth = factory.auth();
|
|
|
|
|
const id = newUuid();
|
|
|
|
|
|
2025-03-10 16:52:44 -04:00
|
|
|
mocks.apiKey.getById.mockResolvedValue(void 0);
|
|
|
|
|
|
2025-03-10 12:04:35 -04:00
|
|
|
await expect(sut.delete(auth, id)).rejects.toBeInstanceOf(BadRequestException);
|
2023-01-18 09:40:15 -05:00
|
|
|
|
2025-03-10 12:04:35 -04:00
|
|
|
expect(mocks.apiKey.delete).not.toHaveBeenCalledWith(id);
|
2023-01-18 09:40:15 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should delete a key', async () => {
|
2025-03-10 12:04:35 -04:00
|
|
|
const auth = factory.auth();
|
|
|
|
|
const apiKey = factory.apiKey({ userId: auth.user.id });
|
2023-01-18 09:40:15 -05:00
|
|
|
|
2025-03-10 12:04:35 -04:00
|
|
|
mocks.apiKey.getById.mockResolvedValue(apiKey);
|
2025-03-10 16:52:44 -04:00
|
|
|
mocks.apiKey.delete.mockResolvedValue();
|
2023-01-18 09:40:15 -05:00
|
|
|
|
2025-03-10 12:04:35 -04:00
|
|
|
await sut.delete(auth, apiKey.id);
|
|
|
|
|
|
|
|
|
|
expect(mocks.apiKey.delete).toHaveBeenCalledWith(auth.user.id, apiKey.id);
|
2023-01-18 09:40:15 -05:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('getById', () => {
|
|
|
|
|
it('should throw an error if the key is not found', async () => {
|
2025-03-10 12:04:35 -04:00
|
|
|
const auth = factory.auth();
|
|
|
|
|
const id = newUuid();
|
|
|
|
|
|
2025-03-10 16:52:44 -04:00
|
|
|
mocks.apiKey.getById.mockResolvedValue(void 0);
|
|
|
|
|
|
2025-03-10 12:04:35 -04:00
|
|
|
await expect(sut.getById(auth, id)).rejects.toBeInstanceOf(BadRequestException);
|
2023-01-18 09:40:15 -05:00
|
|
|
|
2025-03-10 12:04:35 -04:00
|
|
|
expect(mocks.apiKey.getById).toHaveBeenCalledWith(auth.user.id, id);
|
2023-01-18 09:40:15 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should get a key by id', async () => {
|
2025-03-10 12:04:35 -04:00
|
|
|
const auth = factory.auth();
|
|
|
|
|
const apiKey = factory.apiKey({ userId: auth.user.id });
|
2023-01-18 09:40:15 -05:00
|
|
|
|
2025-03-10 12:04:35 -04:00
|
|
|
mocks.apiKey.getById.mockResolvedValue(apiKey);
|
2023-01-18 09:40:15 -05:00
|
|
|
|
2025-03-10 12:04:35 -04:00
|
|
|
await sut.getById(auth, apiKey.id);
|
|
|
|
|
|
|
|
|
|
expect(mocks.apiKey.getById).toHaveBeenCalledWith(auth.user.id, apiKey.id);
|
2023-01-18 09:40:15 -05:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('getAll', () => {
|
|
|
|
|
it('should return all the keys for a user', async () => {
|
2025-03-10 12:04:35 -04:00
|
|
|
const auth = factory.auth();
|
|
|
|
|
const apiKey = factory.apiKey({ userId: auth.user.id });
|
|
|
|
|
|
|
|
|
|
mocks.apiKey.getByUserId.mockResolvedValue([apiKey]);
|
2023-01-18 09:40:15 -05:00
|
|
|
|
2025-03-10 12:04:35 -04:00
|
|
|
await expect(sut.getAll(auth)).resolves.toHaveLength(1);
|
2023-01-18 09:40:15 -05:00
|
|
|
|
2025-03-10 12:04:35 -04:00
|
|
|
expect(mocks.apiKey.getByUserId).toHaveBeenCalledWith(auth.user.id);
|
2023-01-18 09:40:15 -05:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|