nodejs-quickstart-structure 1.12.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -3
- package/README.md +5 -3
- package/lib/generator.js +17 -3
- package/lib/modules/app-setup.js +167 -47
- package/lib/modules/caching-setup.js +13 -0
- package/lib/modules/config-files.js +25 -62
- package/lib/modules/database-setup.js +35 -30
- package/lib/modules/kafka-setup.js +79 -13
- package/package.json +1 -2
- package/templates/clean-architecture/js/src/errors/BadRequestError.js +1 -1
- package/templates/clean-architecture/js/src/errors/BadRequestError.spec.js.ejs +21 -0
- package/templates/clean-architecture/js/src/errors/NotFoundError.js +1 -1
- package/templates/clean-architecture/js/src/errors/NotFoundError.spec.js.ejs +21 -0
- package/templates/clean-architecture/js/src/infrastructure/log/logger.spec.js.ejs +63 -0
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +2 -3
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs +81 -0
- package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +20 -9
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.js.ejs +8 -4
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.spec.js.ejs +102 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/context.spec.js.ejs +31 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.spec.js.ejs +49 -0
- package/templates/clean-architecture/js/src/interfaces/routes/api.spec.js.ejs +38 -0
- package/templates/clean-architecture/js/src/usecases/CreateUser.spec.js.ejs +51 -0
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.spec.js.ejs +61 -0
- package/templates/clean-architecture/ts/src/errors/BadRequestError.spec.ts.ejs +21 -0
- package/templates/clean-architecture/ts/src/errors/BadRequestError.ts +1 -1
- package/templates/clean-architecture/ts/src/errors/NotFoundError.spec.ts.ejs +21 -0
- package/templates/clean-architecture/ts/src/errors/NotFoundError.ts +1 -1
- package/templates/clean-architecture/ts/src/index.ts.ejs +15 -11
- package/templates/clean-architecture/ts/src/infrastructure/log/logger.spec.ts.ejs +64 -0
- package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +85 -0
- package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.ts.ejs +2 -3
- package/templates/clean-architecture/ts/src/interfaces/controllers/userController.spec.ts.ejs +166 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/context.spec.ts.ejs +32 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.spec.ts.ejs +51 -0
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.spec.ts.ejs +40 -0
- package/templates/clean-architecture/ts/src/usecases/createUser.spec.ts.ejs +51 -0
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.spec.ts.ejs +63 -0
- package/templates/clean-architecture/ts/src/utils/{error.middleware.ts.ejs → errorMiddleware.ts.ejs} +1 -2
- package/templates/common/caching/js/memoryCache.spec.js.ejs +101 -0
- package/templates/common/caching/js/redisClient.js.ejs +4 -0
- package/templates/common/caching/js/redisClient.spec.js.ejs +149 -0
- package/templates/common/caching/ts/memoryCache.spec.ts.ejs +102 -0
- package/templates/common/caching/ts/redisClient.spec.ts.ejs +157 -0
- package/templates/common/caching/ts/redisClient.ts.ejs +4 -0
- package/templates/common/database/js/database.spec.js.ejs +56 -0
- package/templates/common/database/js/models/User.js.ejs +22 -0
- package/templates/common/database/js/models/User.spec.js.ejs +84 -0
- package/templates/common/database/js/mongoose.spec.js.ejs +43 -0
- package/templates/common/database/ts/database.spec.ts.ejs +56 -0
- package/templates/common/database/ts/models/User.spec.ts.ejs +84 -0
- package/templates/common/database/ts/models/User.ts.ejs +26 -0
- package/templates/common/database/ts/mongoose.spec.ts.ejs +42 -0
- package/templates/common/eslint.config.mjs.ejs +11 -2
- package/templates/common/health/js/healthRoute.js.ejs +44 -0
- package/templates/common/health/js/healthRoute.spec.js.ejs +70 -0
- package/templates/common/health/ts/healthRoute.spec.ts.ejs +76 -0
- package/templates/common/health/ts/healthRoute.ts.ejs +43 -0
- package/templates/common/jest.config.js.ejs +19 -5
- package/templates/common/kafka/js/config/kafka.spec.js.ejs +21 -0
- package/templates/common/kafka/js/services/kafkaService.js.ejs +13 -4
- package/templates/common/kafka/js/services/kafkaService.spec.js.ejs +60 -0
- package/templates/common/kafka/ts/config/kafka.spec.ts.ejs +21 -0
- package/templates/common/kafka/ts/services/kafkaService.spec.ts.ejs +61 -0
- package/templates/common/kafka/ts/services/kafkaService.ts.ejs +6 -1
- package/templates/common/package.json.ejs +0 -3
- package/templates/common/shutdown/js/gracefulShutdown.js.ejs +61 -0
- package/templates/common/shutdown/js/gracefulShutdown.spec.js.ejs +160 -0
- package/templates/common/shutdown/ts/gracefulShutdown.spec.ts.ejs +158 -0
- package/templates/common/shutdown/ts/gracefulShutdown.ts.ejs +58 -0
- package/templates/common/src/utils/errorMiddleware.spec.js.ejs +79 -0
- package/templates/common/src/utils/errorMiddleware.spec.ts.ejs +94 -0
- package/templates/common/tsconfig.json +1 -1
- package/templates/mvc/js/src/controllers/userController.js.ejs +4 -31
- package/templates/mvc/js/src/controllers/userController.spec.js.ejs +170 -0
- package/templates/mvc/js/src/errors/BadRequestError.js +1 -1
- package/templates/mvc/js/src/errors/BadRequestError.spec.js.ejs +21 -0
- package/templates/mvc/js/src/errors/NotFoundError.js +1 -1
- package/templates/mvc/js/src/errors/NotFoundError.spec.js.ejs +21 -0
- package/templates/mvc/js/src/graphql/context.spec.js.ejs +29 -0
- package/templates/mvc/js/src/graphql/resolvers/user.resolvers.spec.js.ejs +47 -0
- package/templates/mvc/js/src/index.js.ejs +11 -9
- package/templates/mvc/js/src/routes/api.spec.js.ejs +36 -0
- package/templates/mvc/js/src/utils/logger.spec.js.ejs +63 -0
- package/templates/mvc/ts/src/controllers/userController.spec.ts.ejs +185 -0
- package/templates/mvc/ts/src/controllers/userController.ts.ejs +4 -31
- package/templates/mvc/ts/src/errors/BadRequestError.spec.ts.ejs +21 -0
- package/templates/mvc/ts/src/errors/BadRequestError.ts +1 -1
- package/templates/mvc/ts/src/errors/NotFoundError.spec.ts.ejs +21 -0
- package/templates/mvc/ts/src/errors/NotFoundError.ts +1 -1
- package/templates/mvc/ts/src/graphql/context.spec.ts.ejs +30 -0
- package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.spec.ts.ejs +51 -0
- package/templates/mvc/ts/src/index.ts.ejs +13 -9
- package/templates/mvc/ts/src/routes/api.spec.ts.ejs +40 -0
- package/templates/mvc/ts/src/utils/{error.middleware.ts.ejs → errorMiddleware.ts.ejs} +1 -2
- package/templates/mvc/ts/src/utils/logger.spec.ts.ejs +64 -0
- package/docs/demo.gif +0 -0
- package/docs/generateCase.md +0 -265
- package/docs/generatorFlow.md +0 -233
- package/docs/releaseNoteRule.md +0 -42
- package/docs/ruleDevelop.md +0 -30
- package/templates/common/tests/health.test.ts.ejs +0 -14
- /package/templates/clean-architecture/js/src/infrastructure/webserver/{middlewares/error.middleware.js → middleware/errorMiddleware.js} +0 -0
- /package/templates/mvc/js/src/utils/{error.middleware.js → errorMiddleware.js} +0 -0
|
@@ -16,19 +16,15 @@ export class UserController {
|
|
|
16
16
|
try {
|
|
17
17
|
<%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
|
|
18
18
|
const users = await cacheService.getOrSet('users:all', async () => {
|
|
19
|
-
<%_ if (database === 'MongoDB') { -%>
|
|
19
|
+
<%_ if (database === 'MongoDB' || database === 'None') { -%>
|
|
20
20
|
return await User.find();
|
|
21
|
-
<%_ } else if (database === 'None') { -%>
|
|
22
|
-
return User.mockData;
|
|
23
21
|
<%_ } else { -%>
|
|
24
22
|
return await User.findAll();
|
|
25
23
|
<%_ } -%>
|
|
26
24
|
}, 60);
|
|
27
25
|
<%_ } else { -%>
|
|
28
|
-
<%_ if (database === 'MongoDB') { -%>
|
|
26
|
+
<%_ if (database === 'MongoDB' || database === 'None') { -%>
|
|
29
27
|
const users = await User.find();
|
|
30
|
-
<%_ } else if (database === 'None') { -%>
|
|
31
|
-
const users = User.mockData;
|
|
32
28
|
<%_ } else { -%>
|
|
33
29
|
const users = await User.findAll();
|
|
34
30
|
<%_ } -%>
|
|
@@ -43,20 +39,11 @@ export class UserController {
|
|
|
43
39
|
async createUser(data: { name: string, email: string }) {
|
|
44
40
|
try {
|
|
45
41
|
const { name, email } = data;
|
|
46
|
-
<%_ if (database === 'None') { -%>
|
|
47
|
-
const newUser = { id: String(User.mockData.length + 1), name, email };
|
|
48
|
-
User.mockData.push(newUser);
|
|
49
|
-
<%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
|
|
50
|
-
await cacheService.del('users:all');
|
|
51
|
-
<%_ } -%>
|
|
52
|
-
return newUser;
|
|
53
|
-
<%_ } else { -%>
|
|
54
42
|
const user = await User.create({ name, email });
|
|
55
43
|
<%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
|
|
56
44
|
await cacheService.del('users:all');
|
|
57
45
|
<%_ } -%>
|
|
58
46
|
return user;
|
|
59
|
-
<%_ } -%>
|
|
60
47
|
} catch (error) {
|
|
61
48
|
logger.error('Error creating user:', error);
|
|
62
49
|
throw error;
|
|
@@ -67,19 +54,15 @@ export class UserController {
|
|
|
67
54
|
try {
|
|
68
55
|
<%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
|
|
69
56
|
const users = await cacheService.getOrSet('users:all', async () => {
|
|
70
|
-
<%_ if (database === 'MongoDB') { -%>
|
|
57
|
+
<%_ if (database === 'MongoDB' || database === 'None') { -%>
|
|
71
58
|
return await User.find();
|
|
72
|
-
<%_ } else if (database === 'None') { -%>
|
|
73
|
-
return User.mockData;
|
|
74
59
|
<%_ } else { -%>
|
|
75
60
|
return await User.findAll();
|
|
76
61
|
<%_ } -%>
|
|
77
62
|
}, 60);
|
|
78
63
|
<%_ } else { -%>
|
|
79
|
-
<%_ if (database === 'MongoDB') { -%>
|
|
64
|
+
<%_ if (database === 'MongoDB' || database === 'None') { -%>
|
|
80
65
|
const users = await User.find();
|
|
81
|
-
<%_ } else if (database === 'None') { -%>
|
|
82
|
-
const users = User.mockData;
|
|
83
66
|
<%_ } else { -%>
|
|
84
67
|
const users = await User.findAll();
|
|
85
68
|
<%_ } -%>
|
|
@@ -94,20 +77,11 @@ export class UserController {
|
|
|
94
77
|
async createUser(req: Request, res: Response, next: NextFunction) {
|
|
95
78
|
try {
|
|
96
79
|
const { name, email } = req.body;
|
|
97
|
-
<%_ if (database === 'None') { -%>
|
|
98
|
-
const newUser = { id: String(User.mockData.length + 1), name, email };
|
|
99
|
-
User.mockData.push(newUser);
|
|
100
|
-
<%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
|
|
101
|
-
await cacheService.del('users:all');
|
|
102
|
-
<%_ } -%>
|
|
103
|
-
res.status(HTTP_STATUS.CREATED).json(newUser);
|
|
104
|
-
<%_ } else { -%>
|
|
105
80
|
const user = await User.create({ name, email });
|
|
106
81
|
<%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
|
|
107
82
|
await cacheService.del('users:all');
|
|
108
83
|
<%_ } -%>
|
|
109
84
|
res.status(HTTP_STATUS.CREATED).json(user);
|
|
110
|
-
<%_ } -%>
|
|
111
85
|
} catch (error) {
|
|
112
86
|
logger.error('Error creating user:', error);
|
|
113
87
|
next(error);
|
|
@@ -115,4 +89,3 @@ export class UserController {
|
|
|
115
89
|
}
|
|
116
90
|
<% } -%>
|
|
117
91
|
}
|
|
118
|
-
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { BadRequestError } from '@/errors/BadRequestError';
|
|
2
|
+
import { ApiError } from '@/errors/ApiError';
|
|
3
|
+
import { HTTP_STATUS } from '@/utils/httpCodes';
|
|
4
|
+
|
|
5
|
+
describe('BadRequestError', () => {
|
|
6
|
+
it('should extend ApiError', () => {
|
|
7
|
+
const error = new BadRequestError();
|
|
8
|
+
expect(error).toBeInstanceOf(ApiError);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('should have default message "Bad request"', () => {
|
|
12
|
+
const error = new BadRequestError();
|
|
13
|
+
expect(error.message).toBe('Bad request');
|
|
14
|
+
expect(error.statusCode).toBe(HTTP_STATUS.BAD_REQUEST);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should accept a custom message', () => {
|
|
18
|
+
const error = new BadRequestError('Custom bad request');
|
|
19
|
+
expect(error.message).toBe('Custom bad request');
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { NotFoundError } from '@/errors/NotFoundError';
|
|
2
|
+
import { ApiError } from '@/errors/ApiError';
|
|
3
|
+
import { HTTP_STATUS } from '@/utils/httpCodes';
|
|
4
|
+
|
|
5
|
+
describe('NotFoundError', () => {
|
|
6
|
+
it('should extend ApiError', () => {
|
|
7
|
+
const error = new NotFoundError();
|
|
8
|
+
expect(error).toBeInstanceOf(ApiError);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('should have default message "Resource not found"', () => {
|
|
12
|
+
const error = new NotFoundError();
|
|
13
|
+
expect(error.message).toBe('Resource not found');
|
|
14
|
+
expect(error.statusCode).toBe(HTTP_STATUS.NOT_FOUND);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should accept a custom message', () => {
|
|
18
|
+
const error = new NotFoundError('User not found');
|
|
19
|
+
expect(error.message).toBe('User not found');
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { gqlContext } from '@/graphql/context';
|
|
2
|
+
import { Request } from 'express';
|
|
3
|
+
import { resolvers } from '@/graphql/resolvers';
|
|
4
|
+
import { typeDefs } from '@/graphql/typeDefs';
|
|
5
|
+
|
|
6
|
+
describe('GraphQL Context', () => {
|
|
7
|
+
it('should exercise GraphQL index entry points', () => {
|
|
8
|
+
expect(resolvers).toBeDefined();
|
|
9
|
+
expect(typeDefs).toBeDefined();
|
|
10
|
+
});
|
|
11
|
+
it('should return context with token when authorization header is present', async () => {
|
|
12
|
+
const mockRequest = {
|
|
13
|
+
headers: {
|
|
14
|
+
authorization: 'Bearer token123',
|
|
15
|
+
},
|
|
16
|
+
} as Request;
|
|
17
|
+
|
|
18
|
+
const context = await gqlContext({ req: mockRequest });
|
|
19
|
+
expect(context).toEqual({ token: 'Bearer token123' });
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should return context with empty token when authorization header is missing', async () => {
|
|
23
|
+
const mockRequest = {
|
|
24
|
+
headers: {},
|
|
25
|
+
} as Request;
|
|
26
|
+
|
|
27
|
+
const context = await gqlContext({ req: mockRequest });
|
|
28
|
+
expect(context).toEqual({ token: '' });
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { userResolvers } from '@/graphql/resolvers/user.resolvers';
|
|
2
|
+
|
|
3
|
+
const mockGetUsers = jest.fn().mockResolvedValue([{ id: '1', name: 'John Doe', email: 'john@example.com' }]);
|
|
4
|
+
const mockCreateUser = jest.fn().mockResolvedValue({ id: '1', name: 'Jane', email: 'jane@example.com' });
|
|
5
|
+
|
|
6
|
+
jest.mock('@/controllers/userController', () => {
|
|
7
|
+
return {
|
|
8
|
+
UserController: jest.fn().mockImplementation(() => ({
|
|
9
|
+
getUsers: (...args: unknown[]) => mockGetUsers(...args),
|
|
10
|
+
createUser: (...args: unknown[]) => mockCreateUser(...args)
|
|
11
|
+
}))
|
|
12
|
+
};
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe('User Resolvers', () => {
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
jest.clearAllMocks();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('Query.getAllUsers', () => {
|
|
21
|
+
it('should return all users', async () => {
|
|
22
|
+
const result = await userResolvers.Query.getAllUsers();
|
|
23
|
+
expect(result).toEqual([{ id: '1', name: 'John Doe', email: 'john@example.com' }]);
|
|
24
|
+
expect(mockGetUsers).toHaveBeenCalledTimes(1);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('Mutation.createUser', () => {
|
|
29
|
+
it('should create and return a new user', async () => {
|
|
30
|
+
const result = await userResolvers.Mutation.createUser(null, { name: 'Jane', email: 'jane@example.com' });
|
|
31
|
+
expect(result).toEqual({ id: '1', name: 'Jane', email: 'jane@example.com' });
|
|
32
|
+
expect(mockCreateUser).toHaveBeenCalledWith({ name: 'Jane', email: 'jane@example.com' });
|
|
33
|
+
expect(mockCreateUser).toHaveBeenCalledTimes(1);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
37
|
+
describe('User.id', () => {
|
|
38
|
+
it('should return parent.id if available', () => {
|
|
39
|
+
const parent = { id: '123' };
|
|
40
|
+
const result = userResolvers.User.id(parent as { id?: string; _id?: unknown });
|
|
41
|
+
expect(result).toBe('123');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should fallback to parent._id if id is not available', () => {
|
|
45
|
+
const parent = { _id: '456' };
|
|
46
|
+
const result = userResolvers.User.id(parent as { id?: string; _id?: unknown });
|
|
47
|
+
expect(result).toBe('456');
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
<%_ } -%>
|
|
51
|
+
});
|
|
@@ -6,7 +6,9 @@ import hpp from 'hpp';
|
|
|
6
6
|
import rateLimit from 'express-rate-limit';
|
|
7
7
|
import logger from '@/utils/logger';
|
|
8
8
|
import morgan from 'morgan';
|
|
9
|
-
import { errorMiddleware } from '@/utils/
|
|
9
|
+
import { errorMiddleware } from '@/utils/errorMiddleware';
|
|
10
|
+
import { setupGracefulShutdown } from '@/utils/gracefulShutdown';
|
|
11
|
+
import healthRoutes from '@/routes/healthRoute';
|
|
10
12
|
<%_ if (communication === 'REST APIs') { -%>
|
|
11
13
|
import apiRoutes from '@/routes/api';<%_ } -%>
|
|
12
14
|
<% if (communication === 'REST APIs') { %>
|
|
@@ -70,15 +72,13 @@ app.get('/', (req: Request, res: Response) => {
|
|
|
70
72
|
});
|
|
71
73
|
});
|
|
72
74
|
<% } -%>
|
|
73
|
-
app.
|
|
74
|
-
res.json({ status: 'UP' });
|
|
75
|
-
});
|
|
75
|
+
app.use('/health', healthRoutes);
|
|
76
76
|
|
|
77
77
|
// Start Server Logic
|
|
78
78
|
const startServer = async () => {
|
|
79
79
|
<%_ if (communication === 'GraphQL') { -%>
|
|
80
80
|
// GraphQL Setup
|
|
81
|
-
const
|
|
81
|
+
const apolloServer = new ApolloServer<MyContext>({
|
|
82
82
|
typeDefs,
|
|
83
83
|
resolvers,
|
|
84
84
|
plugins: [ApolloServerPluginLandingPageLocalDefault({ embed: true })],
|
|
@@ -102,14 +102,16 @@ const startServer = async () => {
|
|
|
102
102
|
return formattedError;
|
|
103
103
|
},
|
|
104
104
|
});
|
|
105
|
-
await
|
|
106
|
-
app.use('/graphql', expressMiddleware(
|
|
105
|
+
await apolloServer.start();
|
|
106
|
+
app.use('/graphql', expressMiddleware(apolloServer, { context: gqlContext }));
|
|
107
107
|
<%_ } -%>
|
|
108
108
|
app.use(errorMiddleware);
|
|
109
|
-
|
|
109
|
+
<%_ if (communication === 'Kafka') { -%>
|
|
110
|
+
const kafkaService = new KafkaService();
|
|
111
|
+
<%_ } -%>
|
|
112
|
+
const server = app.listen(port, () => {
|
|
110
113
|
logger.info(`Server running on port ${port}`);
|
|
111
114
|
<%_ if (communication === 'Kafka') { -%>
|
|
112
|
-
const kafkaService = new KafkaService();
|
|
113
115
|
kafkaService.connect().then(() => {
|
|
114
116
|
logger.info('Kafka connected');
|
|
115
117
|
kafkaService.sendMessage('test-topic', 'Hello Kafka from MVC TS!');
|
|
@@ -118,6 +120,8 @@ const startServer = async () => {
|
|
|
118
120
|
});
|
|
119
121
|
<%_ } -%>
|
|
120
122
|
});
|
|
123
|
+
|
|
124
|
+
setupGracefulShutdown(server<% if(communication === 'Kafka') { %>, kafkaService<% } %>);
|
|
121
125
|
};
|
|
122
126
|
|
|
123
127
|
<%_ if (database !== 'None') { -%>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import request from 'supertest';
|
|
2
|
+
import express, { Express } from 'express';
|
|
3
|
+
import router from '@/routes/api';
|
|
4
|
+
|
|
5
|
+
const mockGetUsers = jest.fn().mockImplementation((req, res) => res.status(200).json([{ id: '1', name: 'John Doe' }]));
|
|
6
|
+
const mockCreateUser = jest.fn().mockImplementation((req, res) => res.status(201).json({ id: '1', name: 'Test' }));
|
|
7
|
+
|
|
8
|
+
jest.mock('@/controllers/userController', () => {
|
|
9
|
+
return {
|
|
10
|
+
UserController: jest.fn().mockImplementation(() => ({
|
|
11
|
+
getUsers: (...args: unknown[]) => mockGetUsers(...args),
|
|
12
|
+
createUser: (...args: unknown[]) => mockCreateUser(...args)
|
|
13
|
+
}))
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe('ApiRoutes', () => {
|
|
18
|
+
let app: Express;
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
app = express();
|
|
22
|
+
app.use(express.json());
|
|
23
|
+
app.use('/api', router);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('GET /users should call controller.getUsers', async () => {
|
|
27
|
+
await request(app)
|
|
28
|
+
.get('/api/users');
|
|
29
|
+
|
|
30
|
+
expect(mockGetUsers).toHaveBeenCalledTimes(1);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('POST /api/users should call controller.createUser', async () => {
|
|
34
|
+
await request(app)
|
|
35
|
+
.post('/api/users')
|
|
36
|
+
.send({ name: 'Test', email: 'test@example.com' });
|
|
37
|
+
|
|
38
|
+
expect(mockCreateUser).toHaveBeenCalledTimes(1);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -3,8 +3,7 @@ import logger from '@/utils/logger';
|
|
|
3
3
|
import { ApiError } from '@/errors/ApiError';
|
|
4
4
|
import { HTTP_STATUS } from '@/utils/httpCodes';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
export const errorMiddleware = (err: Error, req: Request, res: Response, next: unknown) => {
|
|
6
|
+
export const errorMiddleware = (err: Error, req: Request, res: Response, _next: unknown) => {
|
|
8
7
|
let error = err;
|
|
9
8
|
|
|
10
9
|
if (!(error instanceof ApiError)) {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
jest.mock('winston-daily-rotate-file');
|
|
2
|
+
jest.mock('winston', () => {
|
|
3
|
+
const mockLogger = {
|
|
4
|
+
add: jest.fn(),
|
|
5
|
+
info: jest.fn(),
|
|
6
|
+
error: jest.fn(),
|
|
7
|
+
warn: jest.fn()
|
|
8
|
+
};
|
|
9
|
+
const format = {
|
|
10
|
+
combine: jest.fn(),
|
|
11
|
+
timestamp: jest.fn(),
|
|
12
|
+
json: jest.fn(),
|
|
13
|
+
simple: jest.fn()
|
|
14
|
+
};
|
|
15
|
+
const transports = {
|
|
16
|
+
Console: jest.fn(),
|
|
17
|
+
DailyRotateFile: jest.fn()
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
format,
|
|
21
|
+
transports,
|
|
22
|
+
createLogger: jest.fn().mockReturnValue(mockLogger)
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
<% if (architecture === 'MVC') { -%>
|
|
27
|
+
import logger from '@/utils/logger';
|
|
28
|
+
<% } else { -%>
|
|
29
|
+
import logger from '@/infrastructure/log/logger';
|
|
30
|
+
<% } -%>
|
|
31
|
+
|
|
32
|
+
describe('Logger', () => {
|
|
33
|
+
it('should export a logger instance', () => {
|
|
34
|
+
expect(logger).toBeDefined();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should have info method', () => {
|
|
38
|
+
expect(typeof logger.info).toBe('function');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should have error method', () => {
|
|
42
|
+
expect(typeof logger.error).toBe('function');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should call info', () => {
|
|
46
|
+
logger.info('test message');
|
|
47
|
+
expect(logger.info).toHaveBeenCalledWith('test message');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should call error', () => {
|
|
51
|
+
logger.error('test error');
|
|
52
|
+
expect(logger.error).toHaveBeenCalledWith('test error');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should use JSON format in production environment', () => {
|
|
56
|
+
const winston = require('winston');
|
|
57
|
+
jest.resetModules();
|
|
58
|
+
process.env.NODE_ENV = 'production';
|
|
59
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
60
|
+
require('<% if (architecture === "MVC") { %>@/utils/logger<% } else { %>@/infrastructure/log/logger<% } %>');
|
|
61
|
+
expect(winston.format.json).toHaveBeenCalled();
|
|
62
|
+
process.env.NODE_ENV = 'test';
|
|
63
|
+
});
|
|
64
|
+
});
|
package/docs/demo.gif
DELETED
|
Binary file
|