Skip to content
This repository has been archived by the owner on Dec 20, 2024. It is now read-only.

Commit

Permalink
Merge pull request #84 from Deco-Team/test/garden-timesheet
Browse files Browse the repository at this point in the history
test: garden timesheet
  • Loading branch information
MinhhTien authored Dec 6, 2024
2 parents 3726f34 + 25f47a7 commit 9fa5976
Show file tree
Hide file tree
Showing 7 changed files with 1,235 additions and 29 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/nestjs-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:

- run: npm run build --if-present

- run: npm run test
- run: npm run test:cov

- name: Check Test Results
run: |
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Controller, Get, UseGuards, Inject, Query, Req, Param } from '@nestjs/common'
import { Controller, Get, UseGuards, Inject, Query, Req } from '@nestjs/common'
import { ApiBadRequestResponse, ApiBearerAuth, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'
import * as _ from 'lodash'
import { ErrorResponse } from '@common/contracts/dto'
Expand All @@ -7,18 +7,6 @@ import { UserRole } from '@common/contracts/constant'
import { JwtAuthGuard } from '@auth/guards/jwt-auth.guard'
import { RolesGuard } from '@auth/guards/roles.guard'
import { IGardenTimesheetService } from '@garden-timesheet/services/garden-timesheet.service'
import {
QueryTeachingTimesheetDto,
ViewTeachingTimesheetListDataResponse
} from '@garden-timesheet/dto/view-teaching-timesheet.dto'
import {
QueryAvailableTimeDto,
ViewAvailableTimeDataResponse
} from '@garden-timesheet/dto/view-available-timesheet.dto'
import { Errors } from '@common/contracts/error'
import { ApiErrorResponse } from '@common/decorators/api-response.decorator'
import { AppException } from '@common/exceptions/app.exception'
import { ViewSlotDto } from '@garden-timesheet/dto/slot.dto'
import { QueryMyTimesheetDto, ViewMyTimesheetListDataResponse } from '@garden-timesheet/dto/view-my-timesheet.dto'

@ApiTags('GardenTimesheet - Learner')
Expand All @@ -44,18 +32,4 @@ export class LearnerGardenTimesheetController {
const docs = await this.gardenTimesheetService.viewMyTimesheet(queryMyTimesheetDto)
return { docs }
}

// @ApiOperation({
// summary: `View Slot Detail`
// })
// @ApiOkResponse({ type: ViewSlotDto })
// @ApiErrorResponse([Errors.SLOT_NOT_FOUND])
// @Get('slots/:slotId([0-9a-f]{24})')
// async getSlotDetail(@Req() req, @Param('slotId') slotId: string) {
// const { _id: instructorId } = _.get(req, 'user')
// const slot = await this.gardenTimesheetService.findSlotBy({ slotId, instructorId })

// if (!slot) throw new AppException(Errors.SLOT_NOT_FOUND)
// return slot
// }
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export class ManagementGardenTimesheetController {
data: {
type: FCMNotificationDataType.GARDEN_TIMESHEET,
id: gardenTimesheet._id,
date: date.toISOString(),
date: date?.toString(),
gardenId: gardenId.toString()
}
})
Expand Down
258 changes: 258 additions & 0 deletions src/garden-timesheet/tests/garden-timesheet.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
import { Test, TestingModule } from '@nestjs/testing'
import { Types } from 'mongoose'
import * as moment from 'moment-timezone'
import { GardenTimesheetService, IGardenTimesheetService } from '../services/garden-timesheet.service'
import { IGardenTimesheetRepository } from '../repositories/garden-timesheet.repository'
import { IGardenRepository } from '@garden/repositories/garden.repository'
import { IClassService } from '@class/services/class.service'
import { ILearnerClassService } from '@class/services/learner-class.service'
import { HelperService } from '@common/services/helper.service'
import {
GardenStatus,
GardenTimesheetStatus,
SlotNumber,
SlotStatus,
TimesheetType,
Weekday
} from '@common/contracts/constant'
import { VN_TIMEZONE } from '@src/config'
import { GardenTimesheet, GardenTimesheetDocument } from '@garden-timesheet/schemas/garden-timesheet.schema'
import { GardenDocument } from '@garden/schemas/garden.schema'
import { Course } from '@course/schemas/course.schema'

describe('GardenTimesheetService', () => {
let service: IGardenTimesheetService
let gardenTimesheetRepository: IGardenTimesheetRepository
let gardenRepository: IGardenRepository
let classService: IClassService
let learnerClassService: ILearnerClassService
let helperService: HelperService

const mockGardenId = new Types.ObjectId()
const mockInstructorId = new Types.ObjectId()
const mockClassId = new Types.ObjectId()
const mockLearnerId = new Types.ObjectId()
const mockDate = moment().tz(VN_TIMEZONE).startOf('date').toDate()

const mockGardenTimesheet = {
_id: new Types.ObjectId(),
gardenId: mockGardenId,
date: mockDate,
status: GardenTimesheetStatus.ACTIVE,
gardenMaxClass: 2,
slots: [
{
_id: new Types.ObjectId(),
slotNumber: SlotNumber.ONE,
start: moment(mockDate).add(7, 'hour').toDate(),
end: moment(mockDate).add(9, 'hour').toDate(),
status: SlotStatus.NOT_AVAILABLE,
instructorId: mockInstructorId,
classId: mockClassId
}
]
} as unknown as GardenTimesheetDocument

const mockGarden = {
_id: mockGardenId,
status: GardenStatus.ACTIVE,
maxClass: 2
} as unknown as GardenDocument

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
GardenTimesheetService,
{
provide: IGardenTimesheetRepository,
useValue: {
findOne: jest.fn(),
findMany: jest.fn(),
findOneAndUpdate: jest.fn(),
updateMany: jest.fn(),
model: {
insertMany: jest.fn(),
aggregate: jest.fn()
}
}
},
{
provide: IGardenRepository,
useValue: {
findOne: jest.fn(),
findMany: jest.fn()
}
},
{
provide: IClassService,
useValue: {
findById: jest.fn()
}
},
{
provide: ILearnerClassService,
useValue: {
findMany: jest.fn()
}
},
{
provide: HelperService,
useValue: {
validateWeekdays: jest.fn()
}
}
]
}).compile()

service = module.get<IGardenTimesheetService>(GardenTimesheetService)
gardenTimesheetRepository = module.get(IGardenTimesheetRepository)
gardenRepository = module.get(IGardenRepository)
classService = module.get(IClassService)
learnerClassService = module.get(ILearnerClassService)
helperService = module.get(HelperService)
})

describe('viewGardenTimesheetList', () => {
const queryDto = {
gardenId: mockGardenId.toString(),
date: mockDate,
type: TimesheetType.DAY
}

it('should return garden timesheet list successfully', async () => {
jest.spyOn(gardenTimesheetRepository, 'findOne').mockResolvedValue(mockGardenTimesheet)
jest.spyOn(gardenTimesheetRepository, 'findMany').mockResolvedValue([mockGardenTimesheet])

const result = await service.viewGardenTimesheetList(queryDto, mockGarden)

expect(result).toBeDefined()
expect(result.length).toBe(1)
})

it('should generate timesheet if not exists', async () => {
jest.spyOn(gardenTimesheetRepository, 'findOne').mockResolvedValue(null)
jest.spyOn(gardenTimesheetRepository.model, 'insertMany').mockResolvedValue([])
jest.spyOn(gardenTimesheetRepository, 'findMany').mockResolvedValue([mockGardenTimesheet])

const result = await service.viewGardenTimesheetList(queryDto, mockGarden)

expect(gardenTimesheetRepository.model.insertMany).toHaveBeenCalled()
expect(result).toBeDefined()
})
})

describe('viewTeachingTimesheet', () => {
const queryDto = {
date: mockDate,
type: TimesheetType.DAY,
instructorId: mockInstructorId.toString()
}

it('should return teaching timesheet list successfully', async () => {
jest.spyOn(gardenTimesheetRepository, 'findMany').mockResolvedValue([mockGardenTimesheet])

const result = await service.viewTeachingTimesheet(queryDto)

expect(result).toBeDefined()
expect(result.length).toBe(1)
})
})

describe('viewAvailableTime', () => {
const queryDto = {
startDate: mockDate,
duration: 4,
weekdays: [Weekday.MONDAY, Weekday.THURSDAY],
instructorId: mockInstructorId
}

it('should return available time slots successfully', async () => {
jest.spyOn(gardenRepository, 'findMany').mockResolvedValue([mockGarden])
jest.spyOn(gardenTimesheetRepository, 'findMany').mockResolvedValue([])
jest.spyOn(gardenTimesheetRepository.model, 'aggregate').mockResolvedValue([
{
_id: mockGardenId,
timesheets: [mockGardenTimesheet],
count: 8
}
])
jest.spyOn(helperService, 'validateWeekdays').mockResolvedValue(true as never)

const result = await service.viewAvailableTime(queryDto)

expect(result).toBeDefined()
expect(result.slotNumbers).toBeDefined()
expect(result.availableTimeOfGardens).toBeDefined()
})

it('should throw error for invalid weekdays', async () => {
const invalidQuery = {
...queryDto,
weekdays: [Weekday.MONDAY, Weekday.MONDAY]
}

await expect(service.viewAvailableTime(invalidQuery)).rejects.toThrow()
})
})

describe('generateSlotsForClass', () => {
const params = {
startDate: mockDate,
duration: 4,
weekdays: [Weekday.MONDAY, Weekday.THURSDAY],
slotNumbers: [SlotNumber.ONE],
gardenId: mockGardenId,
instructorId: mockInstructorId,
classId: mockClassId,
metadata: {
code: 'TEST-001',
title: 'Test Class'
},
courseData: {
sessions: [
{
_id: new Types.ObjectId(),
sessionNumber: 1,
title: 'Session 1'
}
]
} as unknown as Course
}

it('should generate slots for class successfully', async () => {
jest.spyOn(gardenTimesheetRepository, 'findMany').mockResolvedValue([mockGardenTimesheet])
jest.spyOn(gardenTimesheetRepository, 'findOneAndUpdate').mockResolvedValue(mockGardenTimesheet)

const result = await service.generateSlotsForClass(params)

expect(result).toBe(true)
expect(gardenTimesheetRepository.findOneAndUpdate).toHaveBeenCalled()
})
})

describe('findSlotBy', () => {
const params = {
slotId: mockGardenTimesheet.slots[0]._id.toString(),
instructorId: mockInstructorId.toString()
}

it('should find slot successfully', async () => {
jest.spyOn(gardenTimesheetRepository, 'findOne').mockResolvedValue(mockGardenTimesheet)
jest.spyOn(gardenRepository, 'findOne').mockResolvedValue(mockGarden)
jest.spyOn(classService, 'findById').mockResolvedValue(null)

const result = await service.findSlotBy(params)

expect(result).toBeDefined()
expect(result._id.toString()).toBe(params.slotId)
})

it('should return null when slot not found', async () => {
jest.spyOn(gardenTimesheetRepository, 'findOne').mockResolvedValue(null)

const result = await service.findSlotBy(params)

expect(result).toBeNull()
})
})
})
Loading

0 comments on commit 9fa5976

Please sign in to comment.