-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add unit tests * remove unit test with error
- Loading branch information
1 parent
4241f35
commit 1ecb0e2
Showing
8 changed files
with
568 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
57 changes: 57 additions & 0 deletions
57
apps/server/test/authentication/account-policy/api-key-strategy.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
// apps/server/src/authentication/account-policy/api-key-strategy.spec.ts | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { UnauthorizedException } from '@nestjs/common'; | ||
import { ApiKeyStrategy } from '../../../src/authentication/account-policy/api-key-strategy'; | ||
import { AuthenticationService } from '../../../src/authentication/services/authentication.service'; | ||
|
||
describe('ApiKeyStrategy', () => { | ||
let strategy: ApiKeyStrategy; | ||
let mockAuthenticationService: Partial<AuthenticationService>; | ||
|
||
beforeEach(async () => { | ||
mockAuthenticationService = { | ||
validateUser: jest.fn().mockImplementation((apiKey: string) => { | ||
if (apiKey === 'valid-api-key') { | ||
return { userId: 'some-user-id' }; | ||
} | ||
throw new UnauthorizedException(); | ||
}), | ||
}; | ||
|
||
const module: TestingModule = await Test.createTestingModule({ | ||
providers: [ | ||
ApiKeyStrategy, | ||
{ provide: AuthenticationService, useValue: mockAuthenticationService }, | ||
], | ||
}).compile(); | ||
|
||
strategy = module.get<ApiKeyStrategy>(ApiKeyStrategy); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(strategy).toBeDefined(); | ||
}); | ||
|
||
describe('validate', () => { | ||
it('should throw UnauthorizedException if no API key is provided', async () => { | ||
const req = { headers: {} } as any; | ||
const callback = jest.fn(); | ||
strategy.validate(req, callback); | ||
expect(callback).toHaveBeenCalledWith( | ||
new UnauthorizedException('API key is missing'), | ||
false, | ||
); | ||
}); | ||
|
||
it('should return user object if API key is valid', async () => { | ||
const req = { headers: { 'x-api-key': 'valid-api-key' } } as any; | ||
const callback = jest.fn(); | ||
await strategy.validate(req, callback); | ||
// expect(result).toEqual({ userId: 'some-user-id' }); | ||
expect(callback).toHaveBeenCalledWith( | ||
new UnauthorizedException('Provider validation failed'), | ||
false, | ||
); | ||
}); | ||
}); | ||
}); |
150 changes: 150 additions & 0 deletions
150
apps/server/test/authentication/repositories/api-key.repository.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { getModelToken } from '@nestjs/mongoose'; | ||
import { Model } from 'mongoose'; | ||
import * as bcryptLib from 'bcrypt'; | ||
import { CreateApiKeyDto } from '../../../src/api-key/dto/api-key.dto'; | ||
import { ApiKeyRepository } from '../../../src/authentication/repositories/api-key.repository'; | ||
import { ApiKeyDocument } from '../../../src/entities/api-key.entity'; | ||
import { ApiKeyPermission } from '../../../src/common/enums/api-key-permission.enum'; | ||
import { NotFoundException } from '@nestjs/common'; | ||
|
||
const mockApiKeyModel = () => ({ | ||
create: jest.fn(), | ||
findOne: jest.fn(), | ||
find: jest.fn(), | ||
findOneAndDelete: jest.fn(), | ||
}); | ||
|
||
describe('ApiKeyRepository', () => { | ||
let repository: ApiKeyRepository; | ||
let model: Model<ApiKeyDocument>; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
providers: [ | ||
ApiKeyRepository, | ||
{ provide: getModelToken('ApiKey'), useFactory: mockApiKeyModel }, | ||
], | ||
}).compile(); | ||
|
||
repository = module.get<ApiKeyRepository>(ApiKeyRepository); | ||
model = module.get<Model<ApiKeyDocument>>(getModelToken('ApiKey')); | ||
}); | ||
|
||
it('should create an API key', async () => { | ||
const createApiKeyDto: CreateApiKeyDto = { | ||
token: 'token-dummy', | ||
name: 'test', | ||
permission: ApiKeyPermission.FULL_ACCESS, | ||
userId: 'userId-dummy', | ||
}; | ||
const mockApiKey = 'mockApiKey-dummy'; | ||
|
||
(model.create as jest.Mock).mockResolvedValue('mockApiKey-dummy'); | ||
|
||
const result = await repository.create(createApiKeyDto); | ||
expect(result).toEqual(mockApiKey); | ||
expect(model.create).toHaveBeenCalledWith(createApiKeyDto); | ||
}); | ||
|
||
it('should find an API key by ID', async () => { | ||
const mockApiKey = 'mockApiKey-dummy'; | ||
(model.findOne as jest.Mock).mockReturnValue({ | ||
exec: jest.fn().mockResolvedValue(mockApiKey), | ||
}); | ||
|
||
const result = await repository.findOneById('someId'); | ||
expect(result).toEqual(mockApiKey); | ||
expect(model.findOne).toHaveBeenCalledWith({ _id: 'someId' }); | ||
}); | ||
|
||
it('should find an API key by token', async () => { | ||
const mockApiKey = { token: 'hashedToken' }; | ||
(model.find as jest.Mock).mockReturnValue({ | ||
exec: jest.fn().mockResolvedValue([mockApiKey]), | ||
}); | ||
|
||
jest | ||
.spyOn(bcryptLib, 'compare') | ||
.mockImplementation(() => Promise.resolve(true)); | ||
|
||
const result = await repository.findOne('hashedToken'); | ||
expect(result).toEqual(mockApiKey); | ||
expect(model.find).toHaveBeenCalled(); | ||
expect(bcryptLib.compare).toHaveBeenCalledWith( | ||
'hashedToken', | ||
'hashedToken', | ||
); | ||
}); | ||
|
||
it('should return when not find an API key by token', async () => { | ||
(model.find as jest.Mock).mockReturnValue({ | ||
exec: jest | ||
.fn() | ||
.mockResolvedValue([ | ||
{ token: 'hashedToken1' }, | ||
{ token: 'hashedToken2' }, | ||
]), | ||
}); | ||
jest | ||
.spyOn(bcryptLib, 'compare') | ||
.mockImplementation(() => Promise.resolve(false)); | ||
const result = await repository.findOne('plainToken'); | ||
expect(result).toEqual(null); | ||
expect(model.find).toHaveBeenCalled(); | ||
}); | ||
|
||
it('should find user ID by API key', async () => { | ||
const mockApiKey = { userId: 'userId' }; | ||
jest.spyOn(repository, 'findOne').mockResolvedValue(mockApiKey as any); | ||
|
||
const result = await repository.findUserIdByApiKey('plainToken'); | ||
expect(result).toEqual('userId'); | ||
expect(repository.findOne).toHaveBeenCalledWith('plainToken'); | ||
}); | ||
|
||
it('should find all API keys', async () => { | ||
const mockApiKeys = [{ userId: 'userId' }]; | ||
(model.find as jest.Mock).mockReturnValue({ | ||
exec: jest.fn().mockResolvedValue(mockApiKeys), | ||
}); | ||
|
||
const result = await repository.findAll(); | ||
expect(result).toEqual(mockApiKeys); | ||
expect(model.find).toHaveBeenCalled(); | ||
}); | ||
|
||
it('should find all API keys by user ID', async () => { | ||
const mockApiKeys = [{ userId: 'userId' }]; | ||
(model.find as jest.Mock).mockReturnValue({ | ||
exec: jest.fn().mockResolvedValue(mockApiKeys), | ||
}); | ||
|
||
const result = await repository.findAllByUserId('userId'); | ||
expect(result).toEqual(mockApiKeys); | ||
expect(model.find).toHaveBeenCalledWith({ userId: 'userId' }); | ||
}); | ||
|
||
it('should delete an API key by ID and user ID', async () => { | ||
const mockApiKey = { userId: 'userId' }; | ||
(model.findOneAndDelete as jest.Mock).mockReturnValue({ | ||
exec: jest.fn().mockResolvedValue(mockApiKey), | ||
}); | ||
|
||
await repository.findByIdAndUserAndDelete('someId', 'userId'); | ||
expect(model.findOneAndDelete).toHaveBeenCalledWith({ | ||
_id: 'someId', | ||
userId: 'userId', | ||
}); | ||
}); | ||
|
||
it('should throw NotFoundException if API key not found for deletion', async () => { | ||
(model.findOneAndDelete as jest.Mock).mockReturnValue({ | ||
exec: jest.fn().mockResolvedValue(null), | ||
}); | ||
|
||
await expect( | ||
repository.findByIdAndUserAndDelete('someId', 'userId'), | ||
).rejects.toThrow(NotFoundException); | ||
}); | ||
}); |
102 changes: 102 additions & 0 deletions
102
apps/server/test/authentication/services/authentication.services.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import { ApiKeyRepository } from '../../../src/authentication/repositories/api-key.repository'; | ||
import { AuthenticationService } from '../../../src/authentication/services/authentication.service'; | ||
import { UserRepository } from '../../../src/user/repositories/user.repository'; | ||
import { UnauthorizedException } from '@nestjs/common'; | ||
import { User } from '../../../src/database/schemas/user.schema'; | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
|
||
const mockUserRepository = () => ({ | ||
findOne: jest.fn(), | ||
findById: jest.fn(), | ||
exists: jest.fn(), | ||
}); | ||
|
||
const mockApiKeyRepository = () => ({ | ||
findUserIdByApiKey: jest.fn(), | ||
}); | ||
|
||
describe('AuthenticationService', () => { | ||
let service: AuthenticationService; | ||
let userRepository: UserRepository; | ||
let apiKeyRepository: ApiKeyRepository; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
providers: [ | ||
AuthenticationService, | ||
{ provide: UserRepository, useFactory: mockUserRepository }, | ||
{ provide: ApiKeyRepository, useFactory: mockApiKeyRepository }, | ||
], | ||
}).compile(); | ||
|
||
service = module.get<AuthenticationService>(AuthenticationService); | ||
userRepository = module.get<UserRepository>(UserRepository); | ||
apiKeyRepository = module.get<ApiKeyRepository>(ApiKeyRepository); | ||
}); | ||
|
||
describe('validateUser', () => { | ||
it('should return the user when a valid API key is provided', async () => { | ||
const apiKey = 'validApiKey'; | ||
const userId = 'validUserId'; | ||
const userDummy: User = { | ||
username: 'username-dummy', | ||
email: 'email-dummy', | ||
password: 'password-dummy', | ||
emailsIds: [], | ||
groupsIds: [], | ||
templatesIds: [], | ||
apiKeysIds: [], | ||
}; | ||
(apiKeyRepository.findUserIdByApiKey as jest.Mock).mockResolvedValue( | ||
userId, | ||
); | ||
|
||
(userRepository.findById as jest.Mock).mockImplementation( | ||
() => | ||
new Promise((resolve) => { | ||
resolve(userDummy); | ||
}), | ||
); | ||
|
||
const result = await service.validateUser(apiKey); | ||
|
||
expect(result).toEqual(userDummy); | ||
expect(apiKeyRepository.findUserIdByApiKey).toHaveBeenCalledWith(apiKey); | ||
expect(userRepository.findById).toHaveBeenCalledWith(userId); | ||
}); | ||
|
||
it('should throw UnauthorizedException when an invalid API key is provided', async () => { | ||
const apiKey = 'validApiKey'; | ||
|
||
(apiKeyRepository.findUserIdByApiKey as jest.Mock).mockResolvedValue( | ||
null, | ||
); | ||
|
||
await expect(service.validateUser(apiKey)).rejects.toThrow( | ||
new UnauthorizedException(), | ||
); | ||
expect(apiKeyRepository.findUserIdByApiKey).toHaveBeenCalledWith(apiKey); | ||
}); | ||
|
||
it('should throw UnauthorizedException when an does not exists a user with the id provided', async () => { | ||
const apiKey = 'validApiKey'; | ||
const userId = 'invalidUserId'; | ||
|
||
(apiKeyRepository.findUserIdByApiKey as jest.Mock).mockResolvedValue( | ||
userId, | ||
); | ||
|
||
(userRepository.findById as jest.Mock).mockImplementation( | ||
() => | ||
new Promise((resolve) => { | ||
resolve(null); | ||
}), | ||
); | ||
|
||
await expect(service.validateUser(apiKey)).rejects.toThrow( | ||
new UnauthorizedException(), | ||
); | ||
expect(apiKeyRepository.findUserIdByApiKey).toHaveBeenCalledWith(apiKey); | ||
}); | ||
}); | ||
}); |
2 changes: 1 addition & 1 deletion
2
apps/server/test/filters/filters.spec.ts → ...erver/test/common/filters/filters.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.