UNPKG

mongodb-dynamic-api

Version:

Auto generated CRUD API for MongoDB using NestJS

914 lines 91.8 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Object.defineProperty(exports, "__esModule", { value: true }); const common_1 = require("@nestjs/common"); const jwt_1 = require("@nestjs/jwt"); const mongoose_1 = require("@nestjs/mongoose"); const testing_1 = require("@nestjs/testing"); const class_validator_1 = require("class-validator"); const mongoose_2 = require("mongoose"); const src_1 = require("../src"); const socket_adapter_1 = require("../src/adapters/socket-adapter"); const e2e_setup_1 = require("./e2e.setup"); require("dotenv/config"); const utils_1 = require("./utils"); describe('DynamicApiModule forRoot (e2e)', () => { const uri = process.env.MONGO_DB_URL; const initModule = async (dynamicApiForRootOptions, initFixtures, initMainCb, testGateway) => { const moduleRef = await testing_1.Test.createTestingModule({ imports: [src_1.DynamicApiModule.forRoot(uri, dynamicApiForRootOptions)], providers: testGateway ? [e2e_setup_1.TestGateway] : [], }).compile(); return (0, e2e_setup_1.createTestingApp)(moduleRef, initFixtures, initMainCb); }; beforeEach(() => { src_1.DynamicApiModule.state['resetState'](); }); afterEach(async () => { await (0, e2e_setup_1.closeTestingApp)(mongoose_2.default.connections); }); it('should initialize dynamic api module state with default options', async () => { const app = await initModule({}); expect(app).toBeDefined(); expect(src_1.DynamicApiModule.state.get()).toStrictEqual({ uri, initialized: true, isGlobalCacheEnabled: true, connectionName: 'dynamic-api-connection', cacheExcludedPaths: [], credentials: null, isAuthEnabled: false, jwtExpirationTime: undefined, jwtSecret: undefined, routesConfig: { defaults: [ 'GetMany', 'GetOne', 'CreateMany', 'CreateOne', 'UpdateMany', 'UpdateOne', 'ReplaceOne', 'DuplicateMany', 'DuplicateOne', 'DeleteMany', 'DeleteOne', ], excluded: [], }, gatewayOptions: undefined, }); }); it('should initialize dynamic api module state with custom options', async () => { const app = await initModule({ useGlobalCache: false, cacheOptions: { excludePaths: ['/fake-path'], }, routesConfig: { defaults: ['GetMany', 'GetOne', 'CreateOne', 'UpdateOne', 'DeleteOne'], excluded: ['CreateMany', 'UpdateMany', 'DeleteMany'], }, }); expect(app).toBeDefined(); expect(src_1.DynamicApiModule.state.get()).toStrictEqual({ uri, initialized: true, isGlobalCacheEnabled: false, connectionName: 'dynamic-api-connection', cacheExcludedPaths: ['/fake-path'], credentials: null, isAuthEnabled: false, jwtExpirationTime: undefined, jwtSecret: undefined, routesConfig: { defaults: ['GetMany', 'GetOne', 'CreateOne', 'UpdateOne', 'DeleteOne'], excluded: ['CreateMany', 'UpdateMany', 'DeleteMany'], }, gatewayOptions: undefined, }); }); describe('Authentication API', () => { describe('useAuth when only userEntity is provided', () => { let UserEntity = class UserEntity extends src_1.BaseEntity { }; __decorate([ (0, mongoose_1.Prop)({ type: String, required: true }), __metadata("design:type", String) ], UserEntity.prototype, "email", void 0); __decorate([ (0, mongoose_1.Prop)({ type: String, required: true }), __metadata("design:type", String) ], UserEntity.prototype, "password", void 0); UserEntity = __decorate([ (0, mongoose_1.Schema)({ collection: 'users' }) ], UserEntity); let app; beforeEach(async () => { app = await initModule({ useAuth: { userEntity: UserEntity } }); }); it('should initialize dynamic api module state and authentication API with default options', async () => { expect(app).toBeDefined(); expect(src_1.DynamicApiModule.state.get()).toStrictEqual({ uri, initialized: true, isGlobalCacheEnabled: true, connectionName: 'dynamic-api-connection', cacheExcludedPaths: [], credentials: { loginField: 'email', passwordField: 'password', }, isAuthEnabled: true, jwtExpirationTime: '1d', jwtSecret: 'dynamic-api-jwt-secret', routesConfig: { defaults: [ 'GetMany', 'GetOne', 'CreateMany', 'CreateOne', 'UpdateMany', 'UpdateOne', 'ReplaceOne', 'DuplicateMany', 'DuplicateOne', 'DeleteMany', 'DeleteOne', ], excluded: [], }, gatewayOptions: undefined, }); }); describe('POST /auth/register', () => { it('should throw a bad request exception if email is missing', async () => { const { body, status } = await e2e_setup_1.server.post('/auth/register', { username: 'unit-test', password: 'test-2' }); expect(status).toBe(400); expect(body).toEqual({ error: 'Bad Request', message: ['email property is required'], statusCode: 400, }); }); it('should throw a bad request exception if password is missing', async () => { const { body, status } = await e2e_setup_1.server.post('/auth/register', { email: 'unit@test.co', pass: 'test-2' }); expect(status).toBe(400); expect(body).toEqual({ error: 'Bad Request', message: ['password property is required'], statusCode: 400, }); }); it('should create a new user and return access token', async () => { const { body, status } = await e2e_setup_1.server.post('/auth/register', { email: 'unit@test.co', password: 'test' }); expect(status).toBe(201); expect(body).toEqual({ accessToken: expect.any(String) }); }); }); describe('POST /auth/login', () => { it('should throw an unauthorized exception if email is missing', async () => { const { body, status } = await e2e_setup_1.server.post('/auth/login', { pass: 'test-2' }); expect(status).toBe(401); expect(body).toEqual({ message: 'Unauthorized', statusCode: 401, }); }); it('should throw an unauthorized exception if password is missing', async () => { const { body, status } = await e2e_setup_1.server.post('/auth/login', { email: 'unit@test.co' }); expect(status).toBe(401); expect(body).toEqual({ message: 'Unauthorized', statusCode: 401, }); }); it('should return access token', async () => { await e2e_setup_1.server.post('/auth/register', { email: 'unit@test.co', password: 'test' }); const { body, status } = await e2e_setup_1.server.post('/auth/login', { email: 'unit@test.co', password: 'test' }); expect(status).toBe(200); expect(body).toEqual({ accessToken: expect.any(String) }); }); }); describe('GET /auth/account', () => { it('should throw an unauthorized exception if access token is missing', async () => { const { body, status } = await e2e_setup_1.server.get('/auth/account'); expect(status).toBe(401); expect(body).toEqual({ message: 'Unauthorized', statusCode: 401, }); }); it('should return user account', async () => { await e2e_setup_1.server.post('/auth/register', { email: 'unit@test.co', password: 'test' }); const { body: { accessToken } } = await e2e_setup_1.server.post('/auth/login', { email: 'unit@test.co', password: 'test' }); const headers = { Authorization: `Bearer ${accessToken}` }; const { body: account, status: accountStatus } = await e2e_setup_1.server.get('/auth/account', { headers }); expect(accountStatus).toBe(200); expect(account).toEqual({ id: expect.any(String), email: 'unit@test.co' }); }); }); it('should throw a Service Unavailable exception when requesting reset password endpoint if reset password options are not configured', async () => { const { body, status } = await e2e_setup_1.server.post('/auth/reset-password', { email: 'toto@test.co' }); expect(status).toBe(503); expect(body).toEqual({ error: 'Service Unavailable', message: 'This feature is not available', statusCode: 503, }); }); it('should throw a Service Unavailable exception when requesting change password endpoint if reset password options are not configured', async () => { const { body, status } = await e2e_setup_1.server.patch('/auth/change-password', { newPassword: 'test' }); expect(status).toBe(503); expect(body).toEqual({ error: 'Service Unavailable', message: 'This feature is not available', statusCode: 503, }); }); }); describe('useAuth with jwt options', () => { let jwtService; let token; let app; let UserEntity = class UserEntity extends src_1.BaseEntity { }; __decorate([ (0, mongoose_1.Prop)({ type: String, required: true }), __metadata("design:type", String) ], UserEntity.prototype, "email", void 0); __decorate([ (0, mongoose_1.Prop)({ type: String, required: true }), __metadata("design:type", String) ], UserEntity.prototype, "password", void 0); UserEntity = __decorate([ (0, mongoose_1.Schema)({ collection: 'users' }) ], UserEntity); beforeEach(async () => { app = await initModule({ useAuth: { userEntity: UserEntity, jwt: { secret: 'test-secret', expiresIn: '4s', }, }, }); jwtService = app.get(jwt_1.JwtService); const { body: { accessToken } } = await e2e_setup_1.server.post('/auth/register', { email: 'unit@test.co', password: 'test' }); token = accessToken; }); it('should initialize dynamic api module state and authentication API with jwt options', async () => { expect(app).toBeDefined(); expect(src_1.DynamicApiModule.state.get()).toStrictEqual({ uri, initialized: true, isGlobalCacheEnabled: true, connectionName: 'dynamic-api-connection', cacheExcludedPaths: [], credentials: { loginField: 'email', passwordField: 'password', }, isAuthEnabled: true, jwtExpirationTime: '4s', jwtSecret: 'test-secret', routesConfig: { defaults: [ 'GetMany', 'GetOne', 'CreateMany', 'CreateOne', 'UpdateMany', 'UpdateOne', 'ReplaceOne', 'DuplicateMany', 'DuplicateOne', 'DeleteMany', 'DeleteOne', ], excluded: [], }, gatewayOptions: undefined, }); }); it('should throw an unauthorized exception if access token is expired', async () => { await (0, utils_1.wait)(5000); const headers = { Authorization: `Bearer ${token}` }; const { body, status } = await e2e_setup_1.server.get('/auth/account', { headers }); expect(status).toBe(401); expect(body).toEqual({ message: 'Unauthorized', statusCode: 401, }); }, 6000); it('should throw an unauthorized exception if secret is invalid', async () => { const invalidToken = jwtService.sign({ email: 'u', password: 'p' }, { secret: 'invalid-secret' }); const headers = { Authorization: `Bearer ${invalidToken}` }; const { body, status } = await e2e_setup_1.server.get('/auth/account', { headers }); expect(status).toBe(401); expect(body).toEqual({ message: 'Unauthorized', statusCode: 401, }); }); }); describe('useAuth with validation options', () => { let app; let UserEntity = class UserEntity extends src_1.BaseEntity { }; __decorate([ (0, mongoose_1.Prop)({ type: String, required: true }), (0, class_validator_1.IsEmail)(), __metadata("design:type", String) ], UserEntity.prototype, "email", void 0); __decorate([ (0, mongoose_1.Prop)({ type: String, required: true }), (0, class_validator_1.IsStrongPassword)({ minLength: 6, minLowercase: 1, minUppercase: 1, minNumbers: 1, minSymbols: 1, }), __metadata("design:type", String) ], UserEntity.prototype, "password", void 0); UserEntity = __decorate([ (0, mongoose_1.Schema)({ collection: 'users' }) ], UserEntity); beforeEach(async () => { app = await initModule({ useAuth: { userEntity: UserEntity, validationPipeOptions: { whitelist: true, forbidNonWhitelisted: true, transform: true, }, }, }); }); describe('POST /auth/register', () => { it('should throw a bad request exception if payload contains non whitelisted property', async () => { const { body, status } = await e2e_setup_1.server.post('/auth/register', { email: 'unit@test.co', password: 'Test-2', role: 'ADMIN' }); expect(status).toBe(400); expect(body).toEqual({ error: 'Bad Request', message: ['property role should not exist'], statusCode: 400, }); }); it('should throw a bad request exception if validation fails', async () => { const { body, status } = await e2e_setup_1.server.post('/auth/register', { email: 'unit.test.co', password: 'test-2' }); expect(status).toBe(400); expect(body).toEqual({ error: 'Bad Request', message: [ 'email must be an email', 'password is not strong enough', ], statusCode: 400, }); }); it('should create a new user and return access token if the validation was successful', async () => { const { body, status } = await e2e_setup_1.server.post('/auth/register', { email: 'unit@test.co', password: 'Test-2' }); expect(status).toBe(201); expect(body).toEqual({ accessToken: expect.any(String) }); }); }); describe('POST /auth/login', () => { beforeEach(async () => { await e2e_setup_1.server.post('/auth/register', { email: 'unit@test.co', password: 'Test-2' }); }); it('should throw an unauthorized exception if payload contains non whitelisted property', async () => { const { body, status } = await e2e_setup_1.server.post('/auth/login', { email: 'unit@test.co', password: 'Test-2', role: 'ADMIN' }); expect(status).toBe(400); expect(body).toEqual({ error: 'Bad Request', message: ['property role should not exist'], statusCode: 400, }); }); it('should throw an unauthorized exception if email is missing', async () => { const { body, status } = await e2e_setup_1.server.post('/auth/login', { password: 'Test-2' }); expect(status).toBe(401); expect(body).toEqual({ message: 'Unauthorized', statusCode: 401, }); }); it('should throw an unauthorized exception if password is missing', async () => { const { body, status } = await e2e_setup_1.server.post('/auth/login', { email: 'unit@test.co' }); expect(status).toBe(401); expect(body).toEqual({ message: 'Unauthorized', statusCode: 401, }); }); it('should return access token if the validation was successful', async () => { await e2e_setup_1.server.post('/auth/register', { email: 'unit@test.co', password: 'test' }); const { body, status } = await e2e_setup_1.server.post('/auth/login', { email: 'unit@test.co', password: 'Test-2' }); expect(status).toBe(200); expect(body).toEqual({ accessToken: expect.any(String) }); }); }); }); describe('POST /auth/register with register options', () => { let User = class User extends src_1.BaseEntity { constructor() { super(...arguments); this.role = 'user'; } }; __decorate([ (0, mongoose_1.Prop)({ type: String, required: true }), __metadata("design:type", String) ], User.prototype, "email", void 0); __decorate([ (0, mongoose_1.Prop)({ type: String, required: true }), __metadata("design:type", String) ], User.prototype, "password", void 0); __decorate([ (0, mongoose_1.Prop)({ type: String, default: 'user' }), __metadata("design:type", String) ], User.prototype, "role", void 0); __decorate([ (0, mongoose_1.Prop)({ type: Boolean, default: false }), __metadata("design:type", Boolean) ], User.prototype, "isVerified", void 0); User = __decorate([ (0, mongoose_1.Schema)({ collection: 'users' }) ], User); const admin = { email: 'admin@test.co', password: 'admin', role: 'admin', isVerified: true }; const user = { email: 'user@test.co', password: 'user' }; beforeEach(async () => { const bcryptService = new src_1.BcryptService(); const fixtures = async (_) => { const model = await (0, utils_1.getModelFromEntity)(User); await model.insertMany([ { ...admin, password: await bcryptService.hashPassword(admin.password) }, { ...user, password: await bcryptService.hashPassword(user.password) }, ]); }; await initModule({ useAuth: { userEntity: User, register: { protected: true, abilityPredicate: (user) => user.isVerified, additionalFields: ['role'], callback: async (user, { updateOneDocument }) => { if (user.role !== 'admin') { return; } await updateOneDocument(User, { _id: user.id }, { $set: { isVerified: true } }); }, }, login: { additionalFields: ['role', 'isVerified'], }, }, }, fixtures); }); describe('protected', () => { it('should throw an unauthorized exception if user is not logged in and protected is true', async () => { const { body, status } = await e2e_setup_1.server.post('/auth/register', { email: 'unit@test.co', password: 'test' }); expect(status).toBe(401); expect(body).toEqual({ message: 'Unauthorized', statusCode: 401, }); }); }); describe('abilityPredicate', () => { it('should not create a new user if user is not verified', async () => { const { email, password } = user; const { body: { accessToken } } = await e2e_setup_1.server.post('/auth/login', { email, password }); const { body, status } = await e2e_setup_1.server.post('/auth/register', { email: 'unit@test.co', password: 'test' }, { headers: { Authorization: `Bearer ${accessToken}` }, }); expect(status).toBe(403); expect(body).toEqual({ error: 'Forbidden', message: 'Access denied', statusCode: 403, }); }); it('should create a new user and return access token if user is verified', async () => { const { email, password } = admin; const { body: { accessToken } } = await e2e_setup_1.server.post('/auth/login', { email, password }); const { body, status } = await e2e_setup_1.server.post('/auth/register', { email: 'unit@test.co', password: 'test' }, { headers: { Authorization: `Bearer ${accessToken}` }, }); expect(status).toBe(201); expect(body).toEqual({ accessToken: expect.any(String) }); }); }); describe('additionalFields', () => { it('should allow to register a new user with additional fields', async () => { const { email, password } = admin; const { body: { accessToken } } = await e2e_setup_1.server.post('/auth/login', { email, password }); const { body, status } = await e2e_setup_1.server.post('/auth/register', { email: 'client@test.co', password: 'client', role: 'client' }, { headers: { Authorization: `Bearer ${accessToken}` }, }); expect(status).toBe(201); expect(body).toEqual({ accessToken: expect.any(String) }); }); }); describe('callback', () => { it('should not set isVerified to true if role is not admin', async () => { const { email, password } = admin; const { body: loginBody } = await e2e_setup_1.server.post('/auth/login', { email, password }); const { body: { accessToken } } = await e2e_setup_1.server.post('/auth/register', { email: 'client@test.co', password: 'client', role: 'client' }, { headers: { Authorization: `Bearer ${loginBody.accessToken}` }, }); const { body, status } = await e2e_setup_1.server.get('/auth/account', { headers: { Authorization: `Bearer ${accessToken}` } }); expect(status).toBe(200); expect(body).toHaveProperty('isVerified', false); }); it('should set isVerified to true if role is admin', async () => { const { email, password } = admin; const { body: loginBody } = await e2e_setup_1.server.post('/auth/login', { email, password }); const { body: { accessToken } } = await e2e_setup_1.server.post('/auth/register', { email: 'admin2@test.co', password: 'admin2', role: 'admin' }, { headers: { Authorization: `Bearer ${loginBody.accessToken}` }, }); const { body, status } = await e2e_setup_1.server.get('/auth/account', { headers: { Authorization: `Bearer ${accessToken}` } }); expect(status).toBe(200); expect(body).toHaveProperty('isVerified', true); }); }); }); describe('POST /auth/login with login options', () => { let User = class User extends src_1.BaseEntity { constructor() { super(...arguments); this.role = 'user'; } }; __decorate([ (0, mongoose_1.Prop)({ type: String, required: true }), __metadata("design:type", String) ], User.prototype, "username", void 0); __decorate([ (0, mongoose_1.Prop)({ type: String, required: true }), __metadata("design:type", String) ], User.prototype, "pass", void 0); __decorate([ (0, mongoose_1.Prop)({ type: String, default: 'user' }), __metadata("design:type", String) ], User.prototype, "role", void 0); __decorate([ (0, mongoose_1.Prop)({ type: Boolean, default: false }), __metadata("design:type", Boolean) ], User.prototype, "isVerified", void 0); User = __decorate([ (0, mongoose_1.Schema)({ collection: 'users' }) ], User); const admin = { username: 'admin', pass: 'admin', role: 'admin', isVerified: true }; const user = { username: 'user', pass: 'user' }; const client = { username: 'client', pass: 'client', role: 'client', isVerified: true }; beforeEach(async () => { const bcryptService = new src_1.BcryptService(); const fixtures = async (_) => { const model = await (0, utils_1.getModelFromEntity)(User); await model.insertMany([ { ...admin, pass: await bcryptService.hashPassword(admin.pass) }, { ...user, pass: await bcryptService.hashPassword(user.pass) }, { ...client, pass: await bcryptService.hashPassword(client.pass) }, ]); }; await initModule({ useAuth: { userEntity: User, login: { loginField: 'username', passwordField: 'pass', additionalFields: ['role', 'isVerified'], abilityPredicate: (user) => user.role === 'admin' || user.role === 'user', callback: async (user) => { if (user.isVerified) { return; } throw new common_1.UnauthorizedException(`Hello ${user.username}, you must verify your account first!`); }, }, }, }, fixtures); }); describe('loginField', () => { it('should throw an unauthorized exception if loginField is missing', async () => { const { body, status } = await e2e_setup_1.server.post('/auth/login', { pass: 'test' }); expect(status).toBe(401); expect(body).toEqual({ message: 'Unauthorized', statusCode: 401, }); }); }); describe('passwordField', () => { it('should throw an unauthorized exception if passwordField is missing', async () => { const { body, status } = await e2e_setup_1.server.post('/auth/login', { username: 'unit' }); expect(status).toBe(401); expect(body).toEqual({ message: 'Unauthorized', statusCode: 401, }); }); }); describe('abilityPredicate', () => { it('should throw an forbidden exception if user role is not admin or user', async () => { const { username, pass } = client; const { body, status } = await e2e_setup_1.server.post('/auth/login', { username, pass }); expect(status).toBe(403); expect(body).toEqual({ error: 'Forbidden', message: 'Access denied', statusCode: 403, }); }); }); describe('callback', () => { it('should throw an unauthorized exception if user is not verified', async () => { const { username, pass } = user; const { body, status } = await e2e_setup_1.server.post('/auth/login', { username, pass }); expect(status).toBe(401); expect(body).toEqual({ error: 'Unauthorized', message: `Hello ${username}, you must verify your account first!`, statusCode: 401, }); }); }); describe('additionalFields', () => { it('should return additional fields', async () => { const { username, pass } = admin; const { body: { accessToken } } = await e2e_setup_1.server.post('/auth/login', { username, pass }); const { body, status } = await e2e_setup_1.server.get('/auth/account', { headers: { Authorization: `Bearer ${accessToken}` } }); expect(status).toBe(200); expect(body).toEqual({ id: expect.any(String), username: 'admin', role: 'admin', isVerified: true }); }); }); }); describe('useAuth with resetPassword options', () => { let User = class User extends src_1.BaseEntity { }; __decorate([ (0, mongoose_1.Prop)({ type: String, required: true }), __metadata("design:type", String) ], User.prototype, "email", void 0); __decorate([ (0, mongoose_1.Prop)({ type: String, required: true }), __metadata("design:type", String) ], User.prototype, "password", void 0); __decorate([ (0, mongoose_1.Prop)({ type: Boolean, default: false }), __metadata("design:type", Boolean) ], User.prototype, "isVerified", void 0); __decorate([ (0, mongoose_1.Prop)({ type: String }), __metadata("design:type", String) ], User.prototype, "resetPasswordToken", void 0); User = __decorate([ (0, mongoose_1.Schema)({ collection: 'users' }) ], User); let model; let user; let client; let app; beforeEach(async () => { user = { email: 'user@test.co', password: 'user', isVerified: true }; client = { email: 'client@test.co', password: 'client' }; const bcryptService = new src_1.BcryptService(); const fixtures = async (_) => { model = await (0, utils_1.getModelFromEntity)(User); await model.insertMany([ { ...user, password: await bcryptService.hashPassword(user.password) }, { ...client, password: await bcryptService.hashPassword(client.password) }, ]); }; app = await initModule({ useAuth: { userEntity: User, resetPassword: { emailField: 'email', expirationInMinutes: 1, resetPasswordCallback: async ({ resetPasswordToken }, { updateUserByEmail }) => { await updateUserByEmail({ $set: { resetPasswordToken } }); }, changePasswordAbilityPredicate: (user) => user.isVerified && !!user.resetPasswordToken, changePasswordCallback: async (user, { updateOneDocument }) => { await updateOneDocument(User, { _id: user.id }, { $unset: { resetPasswordToken: 1 } }); }, }, }, }, fixtures); }); describe('POST /auth/reset-password', () => { it('should throw a bad request exception if email is missing if no validation options are provided', async () => { const { body, status } = await e2e_setup_1.server.post('/auth/reset-password', {}); expect(status).toBe(400); expect(body).toEqual({ error: 'Bad Request', message: 'Invalid or missing argument', statusCode: 400, }); }); it('should not throw a bad request exception if email is invalid if no validation options are provided', async () => { const { body, status } = await e2e_setup_1.server.post('/auth/reset-password', { email: 'unit.test.co' }); expect(status).toBe(204); expect(body).toEqual({}); }); it('should not throw an exception if email is not found', async () => { const { body, status } = await e2e_setup_1.server.post('/auth/reset-password', { email: 'invalid@test.co' }); expect(status).toBe(204); expect(body).toEqual({}); }); describe('resetPasswordCallback', () => { it('should set resetPasswordToken if email is valid', async () => { const { email } = user; const { resetPasswordToken: resetPasswordTokenBeforeUpdate } = (await model.findOne({ email }).lean().exec()); const { status } = await e2e_setup_1.server.post('/auth/reset-password', { email }); const { resetPasswordToken: resetPasswordTokenAfterUpdate } = (await model.findOne({ email }).lean().exec()); expect(status).toBe(204); expect(resetPasswordTokenBeforeUpdate).toStrictEqual(undefined); expect(resetPasswordTokenAfterUpdate).toStrictEqual(expect.any(String)); }); }); }); describe('PATCH /auth/change-password', () => { it('should throw a bad request exception if resetPasswordToken is missing', async () => { const { body, status } = await e2e_setup_1.server.patch('/auth/change-password', { newPassword: 'test' }); expect(status).toBe(400); expect(body).toEqual({ error: 'Bad Request', message: 'Invalid or missing argument', statusCode: 400, }); }); it('should throw a bad request exception if newPassword is missing', async () => { const { email } = user; await e2e_setup_1.server.post('/auth/reset-password', { email }); const { resetPasswordToken: resetPasswordTokenAfterUpdate } = (await model.findOne({ email }).lean().exec()); const resetPasswordToken = resetPasswordTokenAfterUpdate; const { body, status } = await e2e_setup_1.server.patch('/auth/change-password', { resetPasswordToken }); expect(status).toBe(400); expect(body).toEqual({ error: 'Bad Request', message: 'Invalid or missing argument', statusCode: 400, }); }); it('should throw an unauthorized exception if resetPasswordToken is invalid', async () => { const { body, status } = await e2e_setup_1.server.patch('/auth/change-password', { resetPasswordToken: 'test', newPassword: 'newPassword' }); expect(status).toBe(400); expect(body).toEqual({ error: 'Bad Request', message: 'Invalid reset password token. Please redo the reset password process.', statusCode: 400, }); }); it('should throw an unauthorized exception if resetPasswordToken is expired', async () => { const jwtService = app.get(jwt_1.JwtService); const expiredResetPasswordToken = jwtService.sign({ email: user.email }, { expiresIn: 1 }); await (0, utils_1.wait)(500); const { body, status } = await e2e_setup_1.server.patch('/auth/change-password', { resetPasswordToken: expiredResetPasswordToken, newPassword: 'newPassword' }); expect(status).toBe(401); expect(body).toEqual({ error: 'Unauthorized', message: 'Time to reset password has expired. Please redo the reset password process.', statusCode: 401, }); }); describe('changePasswordAbilityPredicate', () => { let resetPasswordToken; beforeEach(async () => { await e2e_setup_1.server.post('/auth/reset-password', { email: client.email }); const { resetPasswordToken: token } = (await model.findOne({ email: client.email }).lean().exec()); resetPasswordToken = token; }); it('should throw a forbidden exception if user is not allowed to change password', async () => { expect(resetPasswordToken).toStrictEqual(expect.any(String)); const { body, status } = await e2e_setup_1.server.patch('/auth/change-password', { resetPasswordToken, newPassword: 'newPassword' }); expect(status).toBe(403); expect(body).toEqual({ error: 'Forbidden', message: 'You are not allowed to change your password.', statusCode: 403, }); }); }); describe('changePasswordCallback', () => { let resetPasswordToken; beforeEach(async () => { await e2e_setup_1.server.post('/auth/reset-password', { email: user.email }); const { resetPasswordToken: token } = (await model.findOne({ email: user.email }).lean().exec()); resetPasswordToken = token; }); it('should change password and unset resetPasswordToken if resetPasswordToken is valid', async () => { expect(resetPasswordToken).toStrictEqual(expect.any(String)); const newPassword = 'newPassword'; const bcryptService = app.get(src_1.BcryptService); const { password: passwordBeforeUpdate } = (await model.findOne({ email: user.email }).lean().exec()); const { status } = await e2e_setup_1.server.patch('/auth/change-password', { resetPasswordToken, newPassword }); const { password: passwordAfterUpdate, resetPasswordToken: tokenAfterUpdate } = (await model.findOne({ email: user.email }).lean().exec()); const isPreviousPassword = await bcryptService.comparePassword(user.password, passwordBeforeUpdate); expect(isPreviousPassword).toBe(true); const isNewPassword = await bcryptService.comparePassword(newPassword, passwordAfterUpdate); expect(isNewPassword).toBe(true); expect(status).toBe(204); expect(tokenAfterUpdate).toStrictEqual(undefined); }); }); }); }); }); describe('Websockets', () => { it('should enable websockets globally', async () => { await initModule({ webSocket: true, }, undefined, async (app) => { app.useWebSocketAdapter(new socket_adapter_1.SocketAdapter(app)); }, true); expect(src_1.DynamicApiModule.state.get('gatewayOptions')).toStrictEqual({}); await e2e_setup_1.server.emit('test', { message: 'Hello' }); expect(e2e_setup_1.handleSocketResponse).toHaveBeenCalledTimes(1); expect(e2e_setup_1.handleSocketResponse).toHaveBeenCalledWith({ message: 'Hello' }); expect(e2e_setup_1.handleSocketException).not.toHaveBeenCalled(); }); describe('Authentication EVENTS', () => { describe('useAuth when only userEntity is provided', () => { let UserEntity = class UserEntity extends src_1.BaseEntity { }; __decorate([ (0, mongoose_1.Prop)({ type: String, required: true }), __metadata("design:type", String) ], UserEntity.prototype, "email", void 0); __decorate([ (0, mongoose_1.Prop)({ type: String, required: true }), __metadata("design:type", String) ], UserEntity.prototype, "password", void 0); UserEntity = __decorate([ (0, mongoose_1.Schema)({ collection: 'users' }) ], UserEntity); beforeEach(async () => { await initModule({ useAuth: { userEntity: UserEntity, webSocket: true, }, }, undefined, async (app) => { app.useWebSocketAdapter(new socket_adapter_1.SocketAdapter(app)); }); }); describe('EVENT auth-register', () => { it('should throw a ws exception if email is missing', async () => { await e2e_setup_1.server.emit('auth-register', { username: 'unit-test', password: 'test-2' }); expect(e2e_setup_1.handleSocketException).toHaveBeenCalledTimes(1); expect(e2e_setup_1.handleSocketException).toHaveBeenCalledWith({ message: ['email property is required'], }); expect(e2e_setup_1.handleSocketResponse).not.toHaveBeenCalled(); }); it('should throw a ws exception if password is missing', async () => { await e2e_setup_1.server.emit('auth-register', { email: 'unit@test.co', pass: 'test-2' }); expect(e2e_setup_1.handleSocketException).toHaveBeenCalledTimes(1); expect(e2e_setup_1.handleSocketException).toHaveBeenCalledWith({ message: ['password property is required'], }); expect(e2e_setup_1.handleSocketResponse).not.toHaveBeenCalled(); }); it('should create a new user and return access token', async () => { await e2e_setup_1.server.emit('auth-register', { email: 'unit@test.co', password: 'test' }); expect(e2e_setup_1.handleSocketException).not.toHaveBeenCalled(); expect(e2e_setup_1.handleSocketResponse).toHaveBeenCalledTimes(1); expect(e2e_setup_1.handleSocketResponse).toHaveBeenCalledWith({ accessToken: expect.any(String) }); }); }); describe('EVENT auth-login', () => { it('should throw a ws exception if email is missing', async () => { await e2e_setup_1.server.emit('auth-login', { password: 'test-2' }); expect(e2e_setup_1.handleSocketResponse).not.toHaveBeenCalled(); expect(e2e_setup_1.handleSocketException).toHaveBeenCalledTimes(1); expect(e2e_setup_1.handleSocketException).toHaveBeenCalledWith({ message: 'Unauthorized', }); }); it('should throw a ws exception if password is missing', async () => { await e2e_setup_1.server.emit('auth-login', { email: 'unit@test.co' }); expect(e2e_setup_1.handleSocketResponse).not.toHaveBeenCalled(); expect(e2e_setup_1.handleSocketException).toHaveBeenCalledTimes(1); expect(e2e_setup_1.handleSocketException).toHaveBeenCalledWith({ message: 'Unauthorized', }); }); it('should return access token', async () => { await e2e_setup_1.server.emit('auth-register', { email: 'unit@test.co', password: 'test' }); await e2e_setup_1.server.emit('auth-login', { email