Skip to content

Commit 80fa9df

Browse files
WIP Crontabs
1 parent 907c6d6 commit 80fa9df

File tree

7 files changed

+444
-7
lines changed

7 files changed

+444
-7
lines changed

service/src/core/core.module.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ import { DynamicModule, Module } from '@nestjs/common'
22
import { RouterModule } from '@nestjs/core'
33
import { CoreService } from '~/core/core.service'
44
import { CoreController } from '~/core/core.controller'
5-
import { AuthModule } from './auth/auth.module';
6-
import { UsersModule } from './users/users.module';
7-
import { CategoriesModule } from './categories/categories.module';
5+
import { AuthModule } from './auth/auth.module'
6+
import { UsersModule } from './users/users.module'
7+
import { CategoriesModule } from './categories/categories.module'
8+
import { ProjectModule } from './projects/project.module'
9+
import { TriggersModule } from './triggers/triggers.module';
10+
import { CrontabsModule } from './crontabs/crontabs.module';
811

912
@Module({
10-
imports: [
11-
AuthModule,
12-
UsersModule,
13-
CategoriesModule],
13+
imports: [AuthModule, UsersModule, CategoriesModule, ProjectModule, TriggersModule, CrontabsModule],
1414
providers: [CoreService],
1515
controllers: [CoreController],
1616
})
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
import { DeleteResult } from 'mongodb'
2+
import { Test, TestingModule } from '@nestjs/testing'
3+
import { CrontabsController } from './crontabs.controller'
4+
import { CrontabsService } from './crontabs.service'
5+
import { CrontabsDto } from './dto/crontabs.dto'
6+
import { Crontabs } from './schemas/crontabs.schema'
7+
import { HttpException, HttpStatus } from '@nestjs/common'
8+
import { Types } from 'mongoose'
9+
import { Response, Request } from 'express'
10+
import { getMockReq, getMockRes } from '@jest-mock/express'
11+
12+
describe('CrontabsController', () => {
13+
let controller: CrontabsController
14+
let service: CrontabsService
15+
const date = new Date()
16+
const _id = new Types.ObjectId()
17+
const { res, mockClear } = getMockRes()
18+
const object: Crontabs = {
19+
_id,
20+
metadata: {
21+
createdAt: date,
22+
createdBy: 'console',
23+
lastUpdateAt: date,
24+
lastUpdateBy: 'console',
25+
},
26+
info: [],
27+
}
28+
29+
beforeEach(async () => {
30+
mockClear()
31+
const module: TestingModule = await Test.createTestingModule({
32+
controllers: [CrontabsController],
33+
providers: [
34+
CrontabsService,
35+
{
36+
provide: CrontabsService,
37+
useValue: {
38+
search: jest.fn().mockResolvedValue([[object], 1]),
39+
create: jest.fn().mockResolvedValue(object),
40+
read: jest.fn().mockResolvedValue(object),
41+
update: jest.fn().mockResolvedValue(object),
42+
remove: jest.fn().mockResolvedValue({
43+
acknowledged: true,
44+
deletedCount: 1,
45+
}),
46+
},
47+
},
48+
],
49+
}).compile()
50+
51+
controller = module.get<CrontabsController>(CrontabsController)
52+
service = module.get<CrontabsService>(CrontabsService)
53+
})
54+
55+
describe('search', () => {
56+
it('should return an array of Crontabs objects and the total count', async () => {
57+
const req = getMockReq()
58+
const query = { _id: _id.toString() }
59+
const limit = 10
60+
const skip = 1
61+
const expectedResult: [Crontabs[], number] = [[object], 1]
62+
jest.spyOn(service, 'search').mockImplementation(async () => await expectedResult)
63+
const response = await controller.search(req, res, query, limit.toString(), skip.toString(), null)
64+
expect(response.json).toHaveBeenCalledWith({ data: expectedResult[0], total: expectedResult[1] })
65+
expect(response.status).toHaveBeenCalledWith(HttpStatus.OK)
66+
})
67+
68+
it('should return an array of Crontabs objects with default pagination', async () => {
69+
const req = getMockReq()
70+
const query = { _id: _id.toString() }
71+
const limit = 0
72+
const skip = 0
73+
const expectedResult: [Crontabs[], number] = [[object], 1]
74+
jest.spyOn(service, 'search').mockImplementation(async () => await expectedResult)
75+
const response = await controller.search(req, res, query, limit.toString(), skip.toString(), null)
76+
expect(response.json).toHaveBeenCalledWith({ data: expectedResult[0], total: expectedResult[1] })
77+
expect(response.status).toHaveBeenCalledWith(HttpStatus.OK)
78+
})
79+
80+
it('should throw HttpException with BAD_REQUEST status when an error occurs', async () => {
81+
const req = {} as Request
82+
const res = {} as Response
83+
const query = { someParam: 'value' }
84+
const limit = 10
85+
const skip = 0
86+
jest.spyOn(service, 'search').mockRejectedValue(new Error('Something went wrong'))
87+
88+
try {
89+
await controller.search(req, res, query, limit.toString(), skip.toString(), null)
90+
} catch (error) {
91+
const status = error.getStatus()
92+
const response = error.getResponse()
93+
94+
expect(error).toBeInstanceOf(HttpException)
95+
expect(status).toBe(400)
96+
expect(response).toBe('Something went wrong')
97+
}
98+
})
99+
})
100+
101+
describe('read', () => {
102+
it('should return the data successfully', async () => {
103+
const expectedResult: Crontabs = object
104+
jest.spyOn(service, 'read').mockImplementation(async () => await expectedResult)
105+
const result = await controller.read(_id.toString(), res)
106+
expect(res.status).toHaveBeenCalledWith(HttpStatus.OK)
107+
expect(res.json).toHaveBeenCalledWith(expectedResult)
108+
expect(result).toEqual(res)
109+
})
110+
111+
it('should throw an HttpException with the error message', async () => {
112+
const errorMessage = 'Error message'
113+
jest.spyOn(service, 'read').mockRejectedValue(new Error(errorMessage))
114+
try {
115+
await controller.read(_id.toString(), res)
116+
} catch (error) {
117+
expect(error.getStatus()).toBe(HttpStatus.BAD_REQUEST)
118+
expect(error.getResponse()).toBe(errorMessage)
119+
}
120+
})
121+
})
122+
123+
describe('create', () => {
124+
it('should return a Crontabs object', async () => {
125+
const dto: CrontabsDto = { someProp: 'value' }
126+
const expectedResult = object
127+
jest.spyOn(service, 'create').mockImplementation(async () => await expectedResult)
128+
const response = await controller.create(dto, res)
129+
expect(response.status).toHaveBeenCalledWith(HttpStatus.CREATED)
130+
expect(response.json).toHaveBeenCalledWith(expectedResult)
131+
})
132+
133+
it('should throw HttpException with BAD_REQUEST status when an error occurs', async () => {
134+
const dto: CrontabsDto = { someProp: 'value' }
135+
jest.spyOn(service, 'create').mockRejectedValue(new Error('Something went wrong'))
136+
137+
try {
138+
await controller.create(dto, res)
139+
} catch (error) {
140+
expect(error).toBeInstanceOf(HttpException)
141+
expect(error.getStatus()).toBe(400)
142+
expect(error.getResponse()).toBe('Something went wrong')
143+
}
144+
})
145+
})
146+
147+
describe('update', () => {
148+
it('should return a Crontabs object', async () => {
149+
const id = _id.toString()
150+
const dto: CrontabsDto = { someProp: 'value' }
151+
const expectedResult: Crontabs = object
152+
jest.spyOn(service, 'update').mockImplementation(async () => await expectedResult)
153+
const response = await controller.update(id, dto, res)
154+
expect(response.status).toHaveBeenCalledWith(HttpStatus.OK)
155+
expect(response.json).toHaveBeenCalledWith(expectedResult)
156+
})
157+
158+
it('should throw HttpException with BAD_REQUEST status when an error occurs', async () => {
159+
const id = _id.toString()
160+
const dto: CrontabsDto = { someProp: 'value' }
161+
jest.spyOn(service, 'update').mockRejectedValue(new Error('Something went wrong'))
162+
163+
try {
164+
await controller.update(id, dto, res)
165+
} catch (error) {
166+
expect(error).toBeInstanceOf(HttpException)
167+
expect(error.getStatus()).toBe(400)
168+
expect(error.getResponse()).toBe('Something went wrong')
169+
}
170+
})
171+
})
172+
173+
describe('remove', () => {
174+
it('should return a Crontabs object', async () => {
175+
const id = _id.toString()
176+
const expectedResult: DeleteResult = {
177+
acknowledged: true,
178+
deletedCount: 1,
179+
}
180+
jest.spyOn(service, 'remove').mockImplementation(async () => await expectedResult)
181+
const response = await controller.remove(id, res)
182+
expect(response.status).toHaveBeenCalledWith(HttpStatus.OK)
183+
expect(response.json).toHaveBeenCalledWith(expectedResult)
184+
})
185+
186+
it('should throw HttpException with BAD_REQUEST status when an error occurs', async () => {
187+
const id = _id.toString()
188+
jest.spyOn(service, 'remove').mockRejectedValue(new Error('Something went wrong'))
189+
190+
try {
191+
await controller.remove(id, res)
192+
} catch (error) {
193+
expect(error).toBeInstanceOf(HttpException)
194+
expect(error.getStatus()).toBe(400)
195+
expect(error.getResponse()).toBe('Something went wrong')
196+
}
197+
})
198+
})
199+
})
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { Body, Controller, Delete, Get, HttpStatus, Param, Patch, Post, Req, Res } from '@nestjs/common'
2+
import { CrontabsCreateDto, CrontabsUpdateDto } from './dto/crontabs.dto'
3+
import { CrontabsService } from './crontabs.service'
4+
import { AbstractController } from '~/_common/abstracts/abstract.controller'
5+
import { ApiParam } from '@nestjs/swagger'
6+
import { SearchFilterSchema, FilterSchema, SearchFilterOptions, FilterOptions, ObjectIdValidationPipe } from '@streamkits/nestjs_module_scrud'
7+
import { Types } from 'mongoose'
8+
import { Request, Response } from 'express'
9+
@Controller('crontabs')
10+
export class CrontabsController extends AbstractController {
11+
protected readonly projection = {
12+
name: 1,
13+
description: 1,
14+
}
15+
16+
constructor(private readonly _service: CrontabsService) {
17+
super()
18+
}
19+
20+
@Post()
21+
public async create(@Req() req: Request, @Res() res: Response, @Body() body: CrontabsCreateDto) {
22+
const data = await this._service.create(body)
23+
return res.status(HttpStatus.CREATED).json({
24+
statusCode: HttpStatus.CREATED,
25+
data,
26+
})
27+
}
28+
29+
@Get()
30+
public async search(@Res() res: Response, @SearchFilterSchema() searchFilterSchema: FilterSchema, @SearchFilterOptions() searchFilterOptions: FilterOptions) {
31+
const [data, total] = await this._service.findAndCount(searchFilterSchema, this.projection, searchFilterOptions)
32+
return res.status(HttpStatus.OK).json({
33+
statusCode: HttpStatus.OK,
34+
total,
35+
data,
36+
})
37+
}
38+
39+
@Get(':_id([0-9a-fA-F]{24})')
40+
@ApiParam({ name: '_id', type: String })
41+
public async read(@Param('_id', ObjectIdValidationPipe) _id: Types.ObjectId, @Res() res: Response) {
42+
const data = await this._service.findById(_id)
43+
return res.status(HttpStatus.OK).json({
44+
statusCode: HttpStatus.OK,
45+
data,
46+
})
47+
}
48+
49+
@Patch(':_id([0-9a-fA-F]{24})')
50+
@ApiParam({ name: '_id', type: String })
51+
public async update(@Param('_id', ObjectIdValidationPipe) _id: Types.ObjectId, @Body() body: CrontabsUpdateDto, @Res() res: Response) {
52+
const data = await this._service.update(_id, body)
53+
return res.status(HttpStatus.OK).json({
54+
statusCode: HttpStatus.OK,
55+
data,
56+
})
57+
}
58+
59+
@Delete(':_id([0-9a-fA-F]{24})')
60+
@ApiParam({ name: '_id', type: String })
61+
public async remove(@Param('_id', ObjectIdValidationPipe) _id: Types.ObjectId, @Res() res: Response) {
62+
const data = await this._service.delete(_id)
63+
return res.status(HttpStatus.OK).json({
64+
statusCode: HttpStatus.OK,
65+
data,
66+
})
67+
}
68+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { DeleteResult } from 'mongodb'
2+
import { Test, TestingModule } from '@nestjs/testing'
3+
import { CrontabsService } from './crontabs.service'
4+
import { getModelToken } from '@nestjs/mongoose'
5+
import { Crontabs } from './schemas/crontabs.schema'
6+
import { Model, Types } from 'mongoose'
7+
8+
describe('CrontabsService', () => {
9+
let service: CrontabsService
10+
let model: Model<Crontabs>
11+
const _id = new Types.ObjectId()
12+
const date = new Date()
13+
const mockCrontabs: Crontabs = {
14+
_id,
15+
metadata: {
16+
createdAt: date,
17+
createdBy: 'console',
18+
lastUpdateAt: date,
19+
lastUpdateBy: 'console',
20+
},
21+
info: [],
22+
}
23+
24+
beforeEach(async () => {
25+
const module: TestingModule = await Test.createTestingModule({
26+
providers: [
27+
CrontabsService,
28+
{
29+
provide: getModelToken(Crontabs.name),
30+
useValue: Model,
31+
},
32+
],
33+
}).compile()
34+
service = module.get<CrontabsService>(CrontabsService)
35+
model = module.get<Model<Crontabs>>(getModelToken(Crontabs.name))
36+
})
37+
38+
describe('create', () => {
39+
it('should create a new record', async () => {
40+
const createTest = {}
41+
jest.spyOn(model, 'create').mockImplementationOnce(() => Promise.resolve(mockCrontabs))
42+
const result = await service.create(createTest)
43+
expect(result).toEqual(mockCrontabs)
44+
})
45+
})
46+
47+
describe('search', () => {
48+
it('should return an array of records and total count', async () => {
49+
const expected = [mockCrontabs]
50+
const total = 1
51+
jest.spyOn(model, 'countDocuments').mockResolvedValueOnce(total)
52+
jest.spyOn(model, 'find').mockResolvedValueOnce(expected)
53+
const [result, resultTotal] = await service.search()
54+
expect(result).toEqual(expected)
55+
expect(resultTotal).toEqual(total)
56+
})
57+
58+
it('should return an empty array if no records are found', async () => {
59+
const expected = []
60+
const total = 0
61+
jest.spyOn(model, 'countDocuments').mockResolvedValueOnce(total)
62+
jest.spyOn(model, 'find').mockResolvedValueOnce(expected)
63+
const [result, resultTotal] = await service.search()
64+
expect(result).toEqual(expected)
65+
expect(resultTotal).toEqual(total)
66+
})
67+
})
68+
69+
describe('read', () => {
70+
it('should return a Crontabs record by ID', async () => {
71+
const expected = mockCrontabs
72+
jest.spyOn(model, 'findById').mockResolvedValueOnce(expected)
73+
const result = await service.read(_id.toString())
74+
expect(result).toEqual(expected)
75+
})
76+
77+
it('should return null if Crontabs record is not found', async () => {
78+
const expected = null
79+
jest.spyOn(model, 'findById').mockResolvedValueOnce(expected)
80+
const result = await service.read('123')
81+
expect(result).toEqual(expected)
82+
})
83+
})
84+
85+
describe('update', () => {
86+
it('should update a Crontabs record by ID with metadata', async () => {
87+
const crontabsDto = { info: { key: 'updated value' } }
88+
const newObject = { ...mockCrontabs }
89+
const expected = newObject.info.push(crontabsDto.info)
90+
jest.spyOn(model, 'findByIdAndUpdate').mockResolvedValueOnce(expected)
91+
const result = await service.update(_id.toString(), crontabsDto)
92+
expect(result).toEqual(expected)
93+
})
94+
})
95+
96+
describe('remove', () => {
97+
it('should remove a Crontabs record by ID', async () => {
98+
const id = '123'
99+
const deleteResult: DeleteResult = { acknowledged: true, deletedCount: 1 }
100+
jest.spyOn(model, 'deleteOne').mockResolvedValueOnce(deleteResult)
101+
const result = await service.remove(id)
102+
expect(result).toEqual(deleteResult)
103+
})
104+
})
105+
})

0 commit comments

Comments
 (0)