Skip to content

Commit

Permalink
Merge pull request #50 from w3bdesign/backend
Browse files Browse the repository at this point in the history
Upgrade packages
  • Loading branch information
w3bdesign authored Nov 21, 2024
2 parents 38d4cf3 + 077ec8c commit e71467b
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 20 deletions.
6 changes: 3 additions & 3 deletions backend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "backend",
"version": "0.0.2",
"version": "0.0.3",
"description": "Hair Salon Booking System Backend",
"author": "",
"private": true,
Expand Down Expand Up @@ -55,14 +55,14 @@
},
"devDependencies": {
"@faker-js/faker": "^9.2.0",
"@nestjs/cli": "^10.4.7",
"@nestjs/cli": "^10.4.8",
"@nestjs/schematics": "^10.2.3",
"@nestjs/testing": "^10.4.8",
"@types/bcrypt": "^5.0.2",
"@types/cache-manager": "^4.0.6",
"@types/express": "^5.0.0",
"@types/jest": "^29.5.14",
"@types/node": "^22.9.0",
"@types/node": "^22.9.1",
"@types/passport-jwt": "^4.0.1",
"@types/supertest": "^6.0.2",
"@typescript-eslint/eslint-plugin": "^8.15.0",
Expand Down
4 changes: 2 additions & 2 deletions backend/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 4 additions & 8 deletions backend/src/bookings/bookings.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,15 +189,13 @@ describe("BookingsController", () => {
startTime: "2024-01-01T10:00:00.000Z",
status: BookingStatus.CANCELLED,
cancelledAt: new Date(),
cancellationReason: "Customer request",
cancellationReason: "Cancelled by administrator",
};

it("should cancel a booking successfully", async () => {
mockBookingsService.cancel.mockResolvedValue(mockBooking);

const result = await controller.cancel("booking-id", {
reason: "Customer request",
});
const result = await controller.cancel("booking-id");

expect(result).toBeDefined();
expect(result.id).toBe(mockBooking.id);
Expand All @@ -206,7 +204,7 @@ describe("BookingsController", () => {
expect(result.serviceName).toBe(mockService.name);
expect(service.cancel).toHaveBeenCalledWith(
"booking-id",
"Customer request",
"Cancelled by administrator"
);
});

Expand All @@ -215,9 +213,7 @@ describe("BookingsController", () => {
new NotFoundException("Booking not found"),
);

await expect(
controller.cancel("non-existent-id", { reason: "Customer request" }),
).rejects.toThrow(NotFoundException);
await expect(controller.cancel("non-existent-id")).rejects.toThrow(NotFoundException);
});
});

Expand Down
6 changes: 3 additions & 3 deletions backend/src/bookings/bookings.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Test, TestingModule } from '@nestjs/testing';
import { BookingsService } from './bookings.service';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Repository, MoreThan, In } from 'typeorm';
import { Booking, BookingStatus } from './entities/booking.entity';
import { UsersService } from '../users/users.service';
import { EmployeesService } from '../employees/employees.service';
Expand Down Expand Up @@ -235,7 +235,7 @@ describe('BookingsService', () => {
});

describe('findUpcoming', () => {
it('should return upcoming confirmed bookings', async () => {
it('should return upcoming pending and confirmed bookings', async () => {
const mockBookings = [{ id: 'booking-1' }, { id: 'booking-2' }];
mockBookingRepository.find.mockResolvedValue(mockBookings);

Expand All @@ -244,7 +244,7 @@ describe('BookingsService', () => {
expect(mockBookingRepository.find).toHaveBeenCalledWith({
where: {
startTime: expect.any(Object),
status: BookingStatus.CONFIRMED,
status: In([BookingStatus.PENDING, BookingStatus.CONFIRMED]),
},
relations: ['customer', 'employee', 'employee.user', 'service'],
order: { startTime: 'ASC' },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { QueryRunner } from 'typeorm';
import { ClearServices1732141970009 } from './1732141970009-ClearServices';

describe('ClearServices1732141970009', () => {
let migration: ClearServices1732141970009;
let queryRunner: QueryRunner;

beforeEach(() => {
migration = new ClearServices1732141970009();
queryRunner = {
query: jest.fn(),
} as unknown as QueryRunner;
});

describe('up', () => {
it('should clear existing services and insert new Norwegian services', async () => {
await migration.up(queryRunner);

// Verify deletion queries
expect(queryRunner.query).toHaveBeenCalledWith(`DELETE FROM "employee_services"`);
expect(queryRunner.query).toHaveBeenCalledWith(`DELETE FROM "services"`);

// Verify insertion of new services
expect(queryRunner.query).toHaveBeenCalledWith(expect.stringContaining('INSERT INTO "services"'));
expect(queryRunner.query).toHaveBeenCalledWith(expect.stringContaining('Standard Klipp'));
expect(queryRunner.query).toHaveBeenCalledWith(expect.stringContaining('Styling Klipp'));
expect(queryRunner.query).toHaveBeenCalledWith(expect.stringContaining('Skjegg Trim'));
expect(queryRunner.query).toHaveBeenCalledWith(expect.stringContaining('Full Service'));
});
});

describe('down', () => {
it('should delete Norwegian services', async () => {
await migration.down(queryRunner);

expect(queryRunner.query).toHaveBeenCalledWith(
`DELETE FROM "services" WHERE "name" IN ('Standard Klipp', 'Styling Klipp', 'Skjegg Trim', 'Full Service')`
);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { QueryRunner } from 'typeorm';
import { UpdateServices1732142680425 } from './1732142680425-UpdateServices';

describe('UpdateServices1732142680425', () => {
let migration: UpdateServices1732142680425;
let queryRunner: QueryRunner;
let queryMock: jest.Mock;

beforeEach(() => {
queryMock = jest.fn();
queryRunner = {
query: queryMock,
} as unknown as QueryRunner;
migration = new UpdateServices1732142680425();
});

describe('up', () => {
it('should clear existing services and insert updated Norwegian services', async () => {
await migration.up(queryRunner);

// Verify deletion queries
expect(queryRunner.query).toHaveBeenCalledWith(`DELETE FROM "employee_services"`);
expect(queryRunner.query).toHaveBeenCalledWith(`DELETE FROM "services"`);

// Verify insertion of updated services
const insertQuery = queryMock.mock.calls[2][0];
expect(insertQuery).toContain('INSERT INTO "services"');
expect(insertQuery).toContain('Standard Klipp');
expect(insertQuery).toContain('299.00');
expect(insertQuery).toContain('Styling Klipp');
expect(insertQuery).toContain('399.00');
expect(insertQuery).toContain('Skjegg Trim');
expect(insertQuery).toContain('199.00');
expect(insertQuery).toContain('Full Service');
expect(insertQuery).toContain('549.00');
});
});

describe('down', () => {
it('should delete updated Norwegian services', async () => {
await migration.down(queryRunner);

expect(queryRunner.query).toHaveBeenCalledWith(
`DELETE FROM "services" WHERE "name" IN ('Standard Klipp', 'Styling Klipp', 'Skjegg Trim', 'Full Service')`
);
});
});
});
12 changes: 8 additions & 4 deletions backend/src/database/seeds/create-sample-bookings.seed.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ describe('createSampleBookings', () => {
let mockServiceRepository: Partial<Repository<Service>>;
let mockBookingRepository: Partial<Repository<Booking>>;
const originalEnv = process.env;
const originalRandom = Math.random;

beforeEach(() => {
// Mock repositories
Expand Down Expand Up @@ -95,6 +96,7 @@ describe('createSampleBookings', () => {

afterEach(() => {
process.env = originalEnv;
Math.random = originalRandom;
jest.clearAllMocks();
});

Expand Down Expand Up @@ -190,11 +192,13 @@ describe('createSampleBookings', () => {
(mockEmployeeRepository.findOne as jest.Mock).mockResolvedValue(mockEmployee);
(mockServiceRepository.find as jest.Mock).mockResolvedValue(mockServices);

// Mock faker to create a cancelled booking
// Mock faker to return service and customer
(faker.helpers.arrayElement as jest.Mock)
.mockReturnValueOnce(mockServices[0]) // Service
.mockReturnValueOnce({ id: 'customer-1' }) // Customer
.mockReturnValue(BookingStatus.CANCELLED); // Status
.mockReturnValue({ id: 'customer-1' }); // Customer

// Mock Math.random to force a cancelled booking
Math.random = jest.fn().mockReturnValue(0.95); // This will select CANCELLED status (> 0.9)

const mockStartDate = new Date('2024-01-01T10:00:00Z');
const mockCancelDate = new Date('2024-01-01T09:00:00Z');
Expand All @@ -211,7 +215,7 @@ describe('createSampleBookings', () => {
expect(cancelledBooking).toEqual(expect.objectContaining({
status: BookingStatus.CANCELLED,
cancelledAt: mockCancelDate,
cancellationReason: expect.any(String),
cancellationReason: 'Sample note',
}));
});

Expand Down
56 changes: 56 additions & 0 deletions backend/src/users/entities/user.entity.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { User } from './user.entity';
import * as bcrypt from 'bcrypt';

jest.mock('bcrypt');

describe('User Entity', () => {
let user: User;

beforeEach(() => {
user = new User();
user.password = 'password123';
(bcrypt.genSalt as jest.Mock).mockResolvedValue('salt');
(bcrypt.hash as jest.Mock).mockResolvedValue('hashed_password');
(bcrypt.compare as jest.Mock).mockResolvedValue(true);
});

afterEach(() => {
jest.clearAllMocks();
});

describe('hashPassword', () => {
it('should hash the password before insert', async () => {
await user.hashPassword();

expect(bcrypt.genSalt).toHaveBeenCalled();
expect(bcrypt.hash).toHaveBeenCalledWith('password123', 'salt');
expect(user.password).toBe('hashed_password');
});

it('should not hash the password if it has not been modified', async () => {
user.password = undefined;
await user.hashPassword();

expect(bcrypt.genSalt).not.toHaveBeenCalled();
expect(bcrypt.hash).not.toHaveBeenCalled();
});
});

describe('validatePassword', () => {
it('should return true for valid password', async () => {
const isValid = await user.validatePassword('password123');

expect(bcrypt.compare).toHaveBeenCalledWith('password123', 'password123');
expect(isValid).toBe(true);
});

it('should return false for invalid password', async () => {
(bcrypt.compare as jest.Mock).mockResolvedValue(false);

const isValid = await user.validatePassword('wrongpassword');

expect(bcrypt.compare).toHaveBeenCalledWith('wrongpassword', 'password123');
expect(isValid).toBe(false);
});
});
});

0 comments on commit e71467b

Please sign in to comment.