-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Initial CI, with just format checking * added name to action * typo in folder name * Updated lock * Simplified package.json and added testing to CI * Tests for validation pipe * Added test for authorization guard, modifying the user service to make it testable * Used automocking in existing tests, added tests for users service * Added mock of users service for e2e testing Data is chatGPT generated * Added tests on Joi schema createUser and some to checkAbilities * Tests on createUserSchema * Doubled speed of running tests with swc * Run tests in separate steps for each module * Chaged node version on CI * Added swcrc which should fix the problems with the action * Removed @swc/cli from api * Changed logic on role change, added first tests on abilities on person * Refactored role change check for unit testing predisposition * added rimraf as dev dependency * Added test on users controller
- Loading branch information
1 parent
b811390
commit 48874cc
Showing
24 changed files
with
2,793 additions
and
1,349 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
name: Fullstack | ||
|
||
on: | ||
- push | ||
- pull_request | ||
|
||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v3 | ||
|
||
- name: Install Node.js | ||
uses: actions/setup-node@v3 | ||
with: | ||
node-version: 20 | ||
|
||
- uses: pnpm/action-setup@v2 | ||
name: Install pnpm | ||
id: pnpm-install | ||
with: | ||
version: 7 | ||
run_install: false | ||
|
||
- name: Get pnpm store directory | ||
id: pnpm-cache | ||
shell: bash | ||
run: | | ||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT | ||
- uses: actions/cache@v3 | ||
name: Setup pnpm cache | ||
with: | ||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} | ||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} | ||
restore-keys: | | ||
${{ runner.os }}-pnpm-store- | ||
- name: Install dependencies | ||
run: pnpm install | ||
|
||
- name: Check fomatting | ||
run: pnpm run format-check | ||
|
||
- name: Test building | ||
run: pnpm run build && pnpm run clean | ||
- name: Run shared module tests | ||
run: pnpm --filter shared test | ||
- name: Run api tests | ||
run: pnpm --filter api test |
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
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,26 @@ | ||
{ | ||
"$schema": "https://json.schemastore.org/swcrc", | ||
"sourceMaps": true, | ||
"module": { | ||
"type": "commonjs" | ||
}, | ||
"jsc": { | ||
"target": "es2017", | ||
"parser": { | ||
"syntax": "typescript", | ||
"decorators": true, | ||
"dynamicImport": true | ||
}, | ||
"transform": { | ||
"legacyDecorator": true, | ||
"decoratorMetadata": true | ||
}, | ||
"keepClassNames": true, | ||
"baseUrl": "./", | ||
"paths": { | ||
"@hkrecruitment/shared": ["../shared/src"], | ||
"@hkrecruitment/shared/*": ["../shared/src/*"] | ||
} | ||
}, | ||
"minify": false | ||
} |
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
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
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 |
---|---|---|
@@ -1,6 +1,7 @@ | ||
import { AppAbility, UserAuth } from '@hkrecruitment/shared'; | ||
import { AppAbility, RoleChangeChecker, UserAuth } from '@hkrecruitment/shared'; | ||
|
||
export type AuthenticatedRequest = Request & { | ||
user: UserAuth; | ||
ability: AppAbility; | ||
roleChangeChecker: RoleChangeChecker; | ||
}; |
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 |
---|---|---|
@@ -1,7 +1,68 @@ | ||
import { AuthorizationGuard } from './authorization.guard'; | ||
import { Reflector } from '@nestjs/core'; | ||
import { UsersService } from 'src/users/users.service'; | ||
import { Role, abilityForUser } from '@hkrecruitment/shared'; | ||
import { ExecutionContext } from '@nestjs/common'; | ||
import { TestBed } from '@automock/jest'; | ||
import { createMock } from '@golevelup/ts-jest'; | ||
|
||
describe('AuthorizationGuard', () => { | ||
// it('should be defined', () => { | ||
// expect(new AuthorizationGuard()).toBeDefined(); | ||
// }); | ||
let reflector: Reflector; | ||
let usersService: UsersService; | ||
let authorizationGuard: AuthorizationGuard; | ||
|
||
beforeEach(async () => { | ||
const { unit, unitRef } = TestBed.create(AuthorizationGuard).compile(); | ||
|
||
reflector = unitRef.get(Reflector); | ||
usersService = unitRef.get(UsersService); | ||
authorizationGuard = unit; | ||
}); | ||
|
||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('should allow access if no policies are defined', async () => { | ||
const context = createMock<ExecutionContext>({ | ||
switchToHttp: () => ({ | ||
getRequest: () => ({ user: {} }), | ||
}), | ||
}); | ||
jest | ||
.spyOn(usersService, 'getRoleAndAbilityForOauthId') | ||
.mockResolvedValue([ | ||
Role.None, | ||
abilityForUser({ sub: 'test', role: Role.None }), | ||
]); | ||
const canActivate = await authorizationGuard.canActivate(context); | ||
expect(canActivate).toBe(true); | ||
}); | ||
|
||
it('should allow access if all policies are fulfilled', async () => { | ||
const mockUser = {}; | ||
const context = createMock<ExecutionContext>({ | ||
switchToHttp: () => ({ | ||
getRequest: () => ({ user: mockUser }), | ||
}), | ||
}); | ||
const handler = jest.fn(() => true); | ||
jest.spyOn(reflector, 'get').mockReturnValue([handler]); | ||
const mockAbility = abilityForUser({ sub: 'test', role: Role.None }); | ||
jest | ||
.spyOn(usersService, 'getRoleAndAbilityForOauthId') | ||
.mockResolvedValue([Role.None, mockAbility]); | ||
|
||
const canActivate = await authorizationGuard.canActivate(context); | ||
expect(canActivate).toBe(true); | ||
expect(mockUser).toHaveProperty('role', Role.None); | ||
|
||
expect(usersService.getRoleAndAbilityForOauthId).toHaveBeenCalledTimes(1); | ||
expect(usersService.getRoleAndAbilityForOauthId).toHaveBeenCalledWith( | ||
undefined, | ||
); | ||
|
||
expect(handler).toHaveBeenCalledTimes(1); | ||
expect(handler).toHaveBeenCalledWith(mockAbility); | ||
}); | ||
}); |
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
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 |
---|---|---|
@@ -1,3 +1,85 @@ | ||
import { BadRequestException } from '@nestjs/common'; | ||
import { JoiValidationPipe } from './joi-validation.pipe'; | ||
import * as Joi from 'joi'; | ||
|
||
describe('JoiValidationPipe', () => {}); | ||
describe('JoiValidationPipe', () => { | ||
it('should be defined', () => { | ||
expect(new JoiValidationPipe({})).toBeDefined(); | ||
}); | ||
|
||
it('should work with empty schema', () => { | ||
const pipe = new JoiValidationPipe({}); | ||
|
||
expect(() => pipe.transform({}, { type: 'body' })).toThrow( | ||
BadRequestException, | ||
); | ||
expect(() => | ||
pipe.transform({}, { type: 'query', data: 'anything' }), | ||
).toThrow(BadRequestException); | ||
expect(() => pipe.transform({}, { type: 'param' })).toThrow( | ||
BadRequestException, | ||
); | ||
expect(() => | ||
pipe.transform({ internal: 'stuff' }, { type: 'custom' }), | ||
).not.toThrow(); | ||
}); | ||
|
||
it('should work with query schema', () => { | ||
const pipe = new JoiValidationPipe({ | ||
query: { | ||
id: Joi.number(), | ||
}, | ||
}); | ||
|
||
expect(() => | ||
pipe.transform('Not a number', { type: 'query', data: 'id' }), | ||
).toThrow(/Validation of query.id failed: /); | ||
expect(() => | ||
pipe.transform("Doesn't matter", { type: 'query', data: 'notId' }), | ||
).toThrow(/Validation of query.notId failed: no schema defined/); | ||
|
||
expect(pipe.transform(1, { type: 'query', data: 'id' })).toBe(1); | ||
|
||
expect(() => pipe.transform({}, { type: 'query' })).toThrow( | ||
/Validation of query\..+ failed/, | ||
); | ||
expect(() => pipe.transform({}, { type: 'param' })).toThrow( | ||
/Validation of param failed: no schema defined/, | ||
); | ||
expect(() => pipe.transform({ key: 'val' }, { type: 'body' })).toThrow( | ||
/Validation of body failed: no schema defined/, | ||
); | ||
expect(() => | ||
pipe.transform({ internal: 'stuff' }, { type: 'custom' }), | ||
).not.toThrow(); | ||
}); | ||
|
||
it('should work with body schema', () => { | ||
const pipe = new JoiValidationPipe({ | ||
body: Joi.object({ | ||
id: Joi.number(), | ||
}), | ||
}); | ||
|
||
expect(() => pipe.transform('Not an object', { type: 'body' })).toThrow( | ||
/Validation of body failed: /, | ||
); | ||
expect(() => pipe.transform({ key: 'val' }, { type: 'body' })).toThrow( | ||
/Validation of body failed: /, | ||
); | ||
expect(() => | ||
pipe.transform({ id: 'Not a number' }, { type: 'body' }), | ||
).toThrow(/Validation of body failed: /); | ||
expect(pipe.transform({ id: 1 }, { type: 'body' })).toEqual({ id: 1 }); | ||
|
||
expect(() => pipe.transform({}, { type: 'query', data: 'thing' })).toThrow( | ||
/Validation of query.thing failed: no schema defined/, | ||
); | ||
expect(() => pipe.transform({}, { type: 'param' })).toThrow( | ||
/Validation of param failed: no schema defined/, | ||
); | ||
expect(() => | ||
pipe.transform({ internal: 'stuff' }, { type: 'custom' }), | ||
).not.toThrow(); | ||
}); | ||
}); |
Oops, something went wrong.