Skip to content

Commit

Permalink
add test factory for ICurrentUser
Browse files Browse the repository at this point in the history
  • Loading branch information
MajedAlaitwniCap committed Jul 25, 2024
1 parent d1200fc commit 3d949c8
Show file tree
Hide file tree
Showing 10 changed files with 316 additions and 189 deletions.
128 changes: 70 additions & 58 deletions apps/server/src/modules/account/api/account.uc.spec.ts

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { UserDO } from '@shared/domain/domainobject/user.do';
import { Permission, RoleName } from '@shared/domain/interface';
import { roleFactory, schoolEntityFactory, setupEntities, userDoFactory, userFactory } from '@shared/testing';
import { ICurrentUser, OauthCurrentUser } from '../interface';
import { CreateJwtPayload, JwtPayload } from '../interface/jwt-payload';
import { CreateJwtPayload } from '../interface/jwt-payload';
import { iCurrentUserFactory, jwtPayloadFactory } from '../testing';
import { CurrentUserMapper } from './current-user.mapper';

describe('CurrentUserMapper', () => {
Expand Down Expand Up @@ -221,111 +222,77 @@ describe('CurrentUserMapper', () => {
describe('jwtToICurrentUser', () => {
describe('when JWT is provided with all claims', () => {
const setup = () => {
const jwtPayload: JwtPayload = {
accountId: 'dummyAccountId',
systemId: 'dummySystemId',
roles: ['mockRoleId'],
schoolId: 'dummySchoolId',
userId: 'dummyUserId',
support: true,
isExternalUser: true,
sub: 'dummyAccountId',
jti: 'random string',
aud: 'some audience',
iss: 'feathers',
iat: Math.floor(new Date().getTime() / 1000),
exp: Math.floor(new Date().getTime() / 1000) + 3600,
};
const mockJwtPayload = jwtPayloadFactory.build();

return {
jwtPayload,
mockJwtPayload,
};
};

it('should return current user', () => {
const { jwtPayload } = setup();
const { mockJwtPayload } = setup();

const currentUser = CurrentUserMapper.jwtToICurrentUser(jwtPayload);
const currentUser = CurrentUserMapper.jwtToICurrentUser(mockJwtPayload);

expect(currentUser).toMatchObject({
accountId: jwtPayload.accountId,
systemId: jwtPayload.systemId,
roles: [jwtPayload.roles[0]],
schoolId: jwtPayload.schoolId,
userId: jwtPayload.userId,
impersonated: jwtPayload.support,
accountId: mockJwtPayload.accountId,
systemId: mockJwtPayload.systemId,
roles: [mockJwtPayload.roles[0]],
schoolId: mockJwtPayload.schoolId,
userId: mockJwtPayload.userId,
impersonated: mockJwtPayload.support,
});
});

it('should return current user with default for isExternalUser', () => {
const { jwtPayload } = setup();
const { mockJwtPayload } = setup();

const currentUser = CurrentUserMapper.jwtToICurrentUser(jwtPayload);
const currentUser = CurrentUserMapper.jwtToICurrentUser(mockJwtPayload);

expect(currentUser).toMatchObject({
isExternalUser: jwtPayload.isExternalUser,
isExternalUser: mockJwtPayload.isExternalUser,
});
});
});

describe('when JWT is provided without optional claims', () => {
const setup = () => {
const jwtPayload: JwtPayload = {
accountId: 'dummyAccountId',
roles: ['mockRoleId'],
schoolId: 'dummySchoolId',
userId: 'dummyUserId',
isExternalUser: false,
sub: 'dummyAccountId',
jti: 'random string',
aud: 'some audience',
iss: 'feathers',
iat: Math.floor(new Date().getTime() / 1000),
exp: Math.floor(new Date().getTime() / 1000) + 3600,
};
const mockJwtPayload = jwtPayloadFactory.build();

return {
jwtPayload,
mockJwtPayload,
};
};

it('should return current user', () => {
const { jwtPayload } = setup();
const { mockJwtPayload } = setup();

const currentUser = CurrentUserMapper.jwtToICurrentUser(jwtPayload);
const currentUser = CurrentUserMapper.jwtToICurrentUser(mockJwtPayload);

expect(currentUser).toMatchObject({
accountId: jwtPayload.accountId,
roles: [jwtPayload.roles[0]],
schoolId: jwtPayload.schoolId,
userId: jwtPayload.userId,
isExternalUser: false,
accountId: mockJwtPayload.accountId,
roles: [mockJwtPayload.roles[0]],
schoolId: mockJwtPayload.schoolId,
userId: mockJwtPayload.userId,
isExternalUser: true,
});
});

it('should return current user with default for isExternalUser', () => {
const { jwtPayload } = setup();
const { mockJwtPayload } = setup();

const currentUser = CurrentUserMapper.jwtToICurrentUser(jwtPayload);
const currentUser = CurrentUserMapper.jwtToICurrentUser(mockJwtPayload);

expect(currentUser).toMatchObject({
isExternalUser: false,
isExternalUser: true,
});
});
});
});

describe('mapCurrentUserToCreateJwtPayload', () => {
it('should map current user to create jwt payload', () => {
const currentUser: ICurrentUser = {
accountId: 'dummyAccountId',
systemId: 'dummySystemId',
roles: ['mockRoleId'],
schoolId: 'dummySchoolId',
userId: 'dummyUserId',
impersonated: true,
isExternalUser: false,
};
const currentUser = iCurrentUserFactory.build();

const createJwtPayload: CreateJwtPayload = CurrentUserMapper.mapCurrentUserToCreateJwtPayload(currentUser);

Expand All @@ -335,7 +302,6 @@ describe('CurrentUserMapper', () => {
roles: currentUser.roles,
schoolId: currentUser.schoolId,
userId: currentUser.userId,
support: currentUser.impersonated,
isExternalUser: false,
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { createMock, DeepMocked } from '@golevelup/ts-jest';
import { AccountService, Account } from '@modules/account';
import { ICurrentUser } from '@modules/authentication';
import { Account, AccountService } from '@modules/account';
import { UnauthorizedException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { JwtService } from '@nestjs/jwt';
import { Test, TestingModule } from '@nestjs/testing';
import jwt from 'jsonwebtoken';
import { BruteForceError } from '../errors/brute-force.error';
import { JwtValidationAdapter } from '../helper/jwt-validation.adapter';
import { AuthenticationService } from './authentication.service';
import { UserAccountDeactivatedLoggableException } from '../loggable/user-account-deactivated-exception';
import { iCurrentUserFactory } from '../testing';
import { AuthenticationService } from './authentication.service';

jest.mock('jsonwebtoken');

Expand Down Expand Up @@ -114,18 +114,13 @@ describe('AuthenticationService', () => {
describe('generateJwt', () => {
describe('when generating new jwt', () => {
it('should pass the correct parameters', async () => {
const mockCurrentUser: ICurrentUser = {
accountId: 'mockAccountId',
roles: ['student'],
schoolId: 'mockSchoolId',
userId: 'mockUserId',
isExternalUser: false,
};
await authenticationService.generateJwt(mockCurrentUser);
const mockCurrentStudentUser = iCurrentUserFactory.buildStudentICurrentUser();

await authenticationService.generateJwt(mockCurrentStudentUser);
expect(jwtService.sign).toBeCalledWith(
mockCurrentUser,
mockCurrentStudentUser,
expect.objectContaining({
subject: mockCurrentUser.accountId,
subject: mockCurrentStudentUser.accountId,
})
);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { ObjectId } from '@mikro-orm/mongodb';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { Test, TestingModule } from '@nestjs/testing';
Expand All @@ -7,24 +6,15 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest';
import { UnauthorizedException } from '@nestjs/common';
import { setupEntities } from '@shared/testing';
import { jwtConstants } from '../constants';
import { JwtPayload } from '../interface/jwt-payload';

import { JwtValidationAdapter } from '../helper/jwt-validation.adapter';
import { jwtPayloadFactory } from '../testing';
import { JwtStrategy } from './jwt.strategy';

describe('jwt strategy', () => {
let validationAdapter: DeepMocked<JwtValidationAdapter>;
let strategy: JwtStrategy;
let module: TestingModule;
const jwtPayload: JwtPayload = {
accountId: new ObjectId().toHexString(),
userId: new ObjectId().toHexString(),
schoolId: new ObjectId().toHexString(),
roles: [new ObjectId().toHexString()],
jti: 'someRandomString',
systemId: new ObjectId().toHexString(),
support: true,
} as JwtPayload;

beforeAll(async () => {
await setupEntities();
Expand Down Expand Up @@ -55,39 +45,48 @@ describe('jwt strategy', () => {

describe('when authenticate a user with jwt', () => {
const setup = () => {
const mockJwtPayload = jwtPayloadFactory.build();

validationAdapter.isWhitelisted.mockResolvedValueOnce();
validationAdapter.isWhitelisted.mockClear();
return {
mockJwtPayload,
};
};

it('should check jwt for being whitelisted', async () => {
setup();
await strategy.validate(jwtPayload);
expect(validationAdapter.isWhitelisted).toHaveBeenCalledWith(jwtPayload.accountId, jwtPayload.jti);
const { mockJwtPayload } = setup();
await strategy.validate(mockJwtPayload);
expect(validationAdapter.isWhitelisted).toHaveBeenCalledWith(mockJwtPayload.accountId, mockJwtPayload.jti);
});

it('should return user', async () => {
setup();
const user = await strategy.validate(jwtPayload);
const { mockJwtPayload } = setup();
const user = await strategy.validate(mockJwtPayload);
expect(user).toMatchObject({
userId: jwtPayload.userId,
roles: [jwtPayload.roles[0]],
schoolId: jwtPayload.schoolId,
accountId: jwtPayload.accountId,
systemId: jwtPayload.systemId,
impersonated: jwtPayload.support,
userId: mockJwtPayload.userId,
roles: [mockJwtPayload.roles[0]],
schoolId: mockJwtPayload.schoolId,
accountId: mockJwtPayload.accountId,
systemId: mockJwtPayload.systemId,
impersonated: mockJwtPayload.support,
});
});
});

describe('when jwt is not whitelisted', () => {
const setup = () => {
const mockJwtPayload = jwtPayloadFactory.build();
validationAdapter.isWhitelisted.mockRejectedValueOnce(null);
validationAdapter.isWhitelisted.mockClear();
return {
mockJwtPayload,
};
};

it('should throw an UnauthorizedException', async () => {
setup();
await expect(() => strategy.validate(jwtPayload)).rejects.toThrow(UnauthorizedException);
const { mockJwtPayload } = setup();
await expect(() => strategy.validate(mockJwtPayload)).rejects.toThrow(UnauthorizedException);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { UserDO } from '@shared/domain/domainobject/user.do';
import { RoleName } from '@shared/domain/interface';
import { EntityId } from '@shared/domain/types';
import { userDoFactory } from '@shared/testing';
import { accountDoFactory } from '@src/modules/account/testing';

import { ICurrentUser, OauthCurrentUser } from '../interface';

Expand Down Expand Up @@ -56,12 +57,7 @@ describe('Oauth2Strategy', () => {
const setup = () => {
const systemId: EntityId = 'systemId';
const user: UserDO = userDoFactory.withRoles([{ id: 'roleId', name: RoleName.USER }]).buildWithId();
const account: Account = new Account({
id: 'accountId',
createdAt: new Date(),
updatedAt: new Date(),
username: 'username',
});
const account = accountDoFactory.build();

const idToken = 'idToken';
oauthService.authenticateUser.mockResolvedValue(
Expand Down
Loading

0 comments on commit 3d949c8

Please sign in to comment.