-
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.
Merge pull request #27 from w3bdesign/backend
Backend tests and coverage
- Loading branch information
Showing
17 changed files
with
1,636 additions
and
150 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
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,108 @@ | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { Reflector } from '@nestjs/core'; | ||
import { ExecutionContext } from '@nestjs/common'; | ||
import { RolesGuard } from './roles.guard'; | ||
import { UserRole } from '../../users/entities/user.entity'; | ||
|
||
describe('RolesGuard', () => { | ||
let guard: RolesGuard; | ||
let reflector: Reflector; | ||
|
||
const mockReflector = { | ||
getAllAndOverride: jest.fn(), | ||
}; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
providers: [ | ||
RolesGuard, | ||
{ | ||
provide: Reflector, | ||
useValue: mockReflector, | ||
}, | ||
], | ||
}).compile(); | ||
|
||
guard = module.get<RolesGuard>(RolesGuard); | ||
reflector = module.get<Reflector>(Reflector); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(guard).toBeDefined(); | ||
}); | ||
|
||
describe('canActivate', () => { | ||
let mockExecutionContext: ExecutionContext; | ||
|
||
beforeEach(() => { | ||
mockExecutionContext = { | ||
getHandler: jest.fn(), | ||
getClass: jest.fn(), | ||
switchToHttp: jest.fn().mockReturnValue({ | ||
getRequest: jest.fn().mockReturnValue({ | ||
user: { | ||
role: UserRole.CUSTOMER, | ||
}, | ||
}), | ||
}), | ||
} as unknown as ExecutionContext; | ||
}); | ||
|
||
it('should allow access when no roles are required', () => { | ||
mockReflector.getAllAndOverride.mockReturnValue(null); | ||
|
||
const result = guard.canActivate(mockExecutionContext); | ||
|
||
expect(result).toBe(true); | ||
expect(reflector.getAllAndOverride).toHaveBeenCalledWith('roles', [ | ||
mockExecutionContext.getHandler(), | ||
mockExecutionContext.getClass(), | ||
]); | ||
}); | ||
|
||
it('should allow access when user has required role', () => { | ||
mockReflector.getAllAndOverride.mockReturnValue([UserRole.CUSTOMER]); | ||
|
||
const result = guard.canActivate(mockExecutionContext); | ||
|
||
expect(result).toBe(true); | ||
}); | ||
|
||
it('should deny access when user does not have required role', () => { | ||
mockReflector.getAllAndOverride.mockReturnValue([UserRole.ADMIN]); | ||
|
||
const result = guard.canActivate(mockExecutionContext); | ||
|
||
expect(result).toBe(false); | ||
}); | ||
|
||
it('should allow access when user has one of multiple required roles', () => { | ||
mockReflector.getAllAndOverride.mockReturnValue([ | ||
UserRole.ADMIN, | ||
UserRole.CUSTOMER, | ||
]); | ||
|
||
const result = guard.canActivate(mockExecutionContext); | ||
|
||
expect(result).toBe(true); | ||
}); | ||
|
||
it('should handle roles defined at both handler and class level', () => { | ||
mockReflector.getAllAndOverride.mockReturnValue([UserRole.CUSTOMER]); | ||
|
||
const mockContext = { | ||
...mockExecutionContext, | ||
getHandler: jest.fn(), | ||
getClass: jest.fn(), | ||
}; | ||
|
||
const result = guard.canActivate(mockContext as ExecutionContext); | ||
|
||
expect(result).toBe(true); | ||
expect(reflector.getAllAndOverride).toHaveBeenCalledWith('roles', [ | ||
mockContext.getHandler(), | ||
mockContext.getClass(), | ||
]); | ||
}); | ||
}); | ||
}); |
154 changes: 154 additions & 0 deletions
154
backend/src/database/migrations/1731981975581-InitialMigration.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,154 @@ | ||
import { QueryRunner } from 'typeorm'; | ||
import { InitialMigration1731981975581 } from './1731981975581-InitialMigration'; | ||
|
||
describe('InitialMigration1731981975581', () => { | ||
let migration: InitialMigration1731981975581; | ||
let queryRunner: QueryRunner; | ||
|
||
beforeEach(() => { | ||
migration = new InitialMigration1731981975581(); | ||
queryRunner = { | ||
query: jest.fn(), | ||
} as unknown as QueryRunner; | ||
}); | ||
|
||
it('should have correct name', () => { | ||
expect(migration.name).toBe('InitialMigration1731981975581'); | ||
}); | ||
|
||
describe('up', () => { | ||
it('should create user role enum', async () => { | ||
await migration.up(queryRunner); | ||
|
||
expect(queryRunner.query).toHaveBeenCalledWith( | ||
expect.stringContaining('CREATE TYPE "public"."users_role_enum"'), | ||
); | ||
}); | ||
|
||
it('should create users table', async () => { | ||
await migration.up(queryRunner); | ||
|
||
expect(queryRunner.query).toHaveBeenCalledWith( | ||
expect.stringContaining('CREATE TABLE "users"'), | ||
); | ||
}); | ||
|
||
it('should create employees table', async () => { | ||
await migration.up(queryRunner); | ||
|
||
expect(queryRunner.query).toHaveBeenCalledWith( | ||
expect.stringContaining('CREATE TABLE "employees"'), | ||
); | ||
}); | ||
|
||
it('should create services table', async () => { | ||
await migration.up(queryRunner); | ||
|
||
expect(queryRunner.query).toHaveBeenCalledWith( | ||
expect.stringContaining('CREATE TABLE "services"'), | ||
); | ||
}); | ||
|
||
it('should create booking status enum', async () => { | ||
await migration.up(queryRunner); | ||
|
||
expect(queryRunner.query).toHaveBeenCalledWith( | ||
expect.stringContaining('CREATE TYPE "public"."bookings_status_enum"'), | ||
); | ||
}); | ||
|
||
it('should create bookings table', async () => { | ||
await migration.up(queryRunner); | ||
|
||
expect(queryRunner.query).toHaveBeenCalledWith( | ||
expect.stringContaining('CREATE TABLE "bookings"'), | ||
); | ||
}); | ||
|
||
it('should create employee_services table', async () => { | ||
await migration.up(queryRunner); | ||
|
||
expect(queryRunner.query).toHaveBeenCalledWith( | ||
expect.stringContaining('CREATE TABLE "employee_services"'), | ||
); | ||
}); | ||
|
||
it('should create indexes', async () => { | ||
await migration.up(queryRunner); | ||
|
||
expect(queryRunner.query).toHaveBeenCalledWith( | ||
expect.stringContaining('CREATE INDEX'), | ||
); | ||
}); | ||
|
||
it('should create foreign key constraints', async () => { | ||
await migration.up(queryRunner); | ||
|
||
expect(queryRunner.query).toHaveBeenCalledWith( | ||
expect.stringContaining('ALTER TABLE'), | ||
); | ||
expect(queryRunner.query).toHaveBeenCalledWith( | ||
expect.stringContaining('FOREIGN KEY'), | ||
); | ||
}); | ||
}); | ||
|
||
describe('down', () => { | ||
it('should drop foreign key constraints', async () => { | ||
await migration.down(queryRunner); | ||
|
||
expect(queryRunner.query).toHaveBeenCalledWith( | ||
expect.stringContaining('DROP CONSTRAINT'), | ||
); | ||
}); | ||
|
||
it('should drop indexes', async () => { | ||
await migration.down(queryRunner); | ||
|
||
expect(queryRunner.query).toHaveBeenCalledWith( | ||
expect.stringContaining('DROP INDEX'), | ||
); | ||
}); | ||
|
||
it('should drop tables in correct order', async () => { | ||
await migration.down(queryRunner); | ||
|
||
const calls = (queryRunner.query as jest.Mock).mock.calls.map( | ||
call => call[0], | ||
); | ||
|
||
// Verify drop order: employee_services -> bookings -> services -> employees -> users | ||
const employeeServicesIndex = calls.findIndex(call => | ||
call.includes('DROP TABLE "employee_services"'), | ||
); | ||
const bookingsIndex = calls.findIndex(call => | ||
call.includes('DROP TABLE "bookings"'), | ||
); | ||
const servicesIndex = calls.findIndex(call => | ||
call.includes('DROP TABLE "services"'), | ||
); | ||
const employeesIndex = calls.findIndex(call => | ||
call.includes('DROP TABLE "employees"'), | ||
); | ||
const usersIndex = calls.findIndex(call => | ||
call.includes('DROP TABLE "users"'), | ||
); | ||
|
||
expect(employeeServicesIndex).toBeLessThan(bookingsIndex); | ||
expect(bookingsIndex).toBeLessThan(servicesIndex); | ||
expect(servicesIndex).toBeLessThan(employeesIndex); | ||
expect(employeesIndex).toBeLessThan(usersIndex); | ||
}); | ||
|
||
it('should drop enums', async () => { | ||
await migration.down(queryRunner); | ||
|
||
expect(queryRunner.query).toHaveBeenCalledWith( | ||
expect.stringContaining('DROP TYPE "public"."users_role_enum"'), | ||
); | ||
expect(queryRunner.query).toHaveBeenCalledWith( | ||
expect.stringContaining('DROP TYPE "public"."bookings_status_enum"'), | ||
); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.