nodejs-quickstart-structure 1.18.0 → 1.19.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 +17 -4
- package/README.md +2 -1
- package/bin/index.js +93 -92
- package/lib/generator.js +1 -1
- package/lib/modules/caching-setup.js +76 -73
- package/lib/modules/config-files.js +4 -0
- package/lib/modules/kafka-setup.js +249 -191
- package/lib/modules/project-setup.js +1 -0
- package/package.json +13 -2
- package/templates/clean-architecture/js/src/errors/BadRequestError.js +11 -10
- package/templates/clean-architecture/js/src/errors/BadRequestError.spec.js.ejs +22 -21
- package/templates/clean-architecture/js/src/errors/NotFoundError.js +11 -10
- package/templates/clean-architecture/js/src/errors/NotFoundError.spec.js.ejs +22 -21
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +69 -39
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs +142 -81
- package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +1 -1
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.js.ejs +156 -75
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.spec.js.ejs +234 -138
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.js.ejs +27 -21
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.spec.js.ejs +66 -49
- package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/user.types.js.ejs +19 -17
- package/templates/clean-architecture/js/src/interfaces/routes/api.js +12 -10
- package/templates/clean-architecture/js/src/usecases/DeleteUser.js +11 -0
- package/templates/clean-architecture/js/src/usecases/DeleteUser.spec.js.ejs +47 -0
- package/templates/clean-architecture/js/src/usecases/UpdateUser.js +11 -0
- package/templates/clean-architecture/js/src/usecases/UpdateUser.spec.js.ejs +48 -0
- package/templates/clean-architecture/js/src/utils/errorMessages.js +14 -0
- package/templates/clean-architecture/ts/src/errors/BadRequestError.spec.ts.ejs +22 -21
- package/templates/clean-architecture/ts/src/errors/BadRequestError.ts +9 -8
- package/templates/clean-architecture/ts/src/errors/NotFoundError.spec.ts.ejs +22 -21
- package/templates/clean-architecture/ts/src/errors/NotFoundError.ts +9 -8
- package/templates/clean-architecture/ts/src/index.ts.ejs +1 -1
- package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +175 -85
- package/templates/clean-architecture/ts/src/infrastructure/repositories/userRepository.ts.ejs +74 -0
- package/templates/clean-architecture/ts/src/interfaces/controllers/userController.spec.ts.ejs +331 -185
- package/templates/clean-architecture/ts/src/interfaces/controllers/userController.ts.ejs +173 -84
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.spec.ts.ejs +68 -51
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.ts.ejs +29 -21
- package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/user.types.ts.ejs +17 -15
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts +13 -11
- package/templates/clean-architecture/ts/src/usecases/deleteUser.spec.ts.ejs +47 -0
- package/templates/clean-architecture/ts/src/usecases/deleteUser.ts +9 -0
- package/templates/clean-architecture/ts/src/usecases/updateUser.spec.ts.ejs +48 -0
- package/templates/clean-architecture/ts/src/usecases/updateUser.ts +9 -0
- package/templates/clean-architecture/ts/src/utils/errorMessages.ts +12 -0
- package/templates/common/.gitattributes +46 -0
- package/templates/common/.snyk.ejs +45 -0
- package/templates/common/Dockerfile +17 -9
- package/templates/common/README.md.ejs +295 -263
- package/templates/common/caching/clean/js/DeleteUser.js.ejs +27 -0
- package/templates/common/caching/clean/js/UpdateUser.js.ejs +27 -0
- package/templates/common/caching/clean/ts/deleteUser.ts.ejs +24 -0
- package/templates/common/caching/clean/ts/updateUser.ts.ejs +25 -0
- package/templates/common/caching/ts/memoryCache.ts.ejs +73 -64
- package/templates/common/caching/ts/redisClient.ts.ejs +89 -80
- package/templates/common/database/js/models/User.js.ejs +79 -53
- package/templates/common/database/js/models/User.js.mongoose.ejs +23 -19
- package/templates/common/database/js/models/User.spec.js.ejs +94 -84
- package/templates/common/database/ts/models/User.spec.ts.ejs +100 -84
- package/templates/common/database/ts/models/User.ts.ejs +87 -61
- package/templates/common/database/ts/models/User.ts.mongoose.ejs +30 -25
- package/templates/common/health/js/healthRoute.js.ejs +50 -47
- package/templates/common/health/ts/healthRoute.ts.ejs +49 -46
- package/templates/common/jest.e2e.config.js.ejs +8 -8
- package/templates/common/kafka/js/messaging/baseConsumer.js.ejs +30 -30
- package/templates/common/kafka/js/messaging/userEventSchema.js.ejs +12 -11
- package/templates/common/kafka/js/messaging/welcomeEmailConsumer.js.ejs +44 -31
- package/templates/common/kafka/js/messaging/welcomeEmailConsumer.spec.js.ejs +86 -49
- package/templates/common/kafka/js/services/kafkaService.js.ejs +93 -93
- package/templates/common/kafka/js/utils/kafkaEvents.js.ejs +7 -0
- package/templates/common/kafka/ts/messaging/userEventSchema.spec.ts.ejs +51 -51
- package/templates/common/kafka/ts/messaging/userEventSchema.ts.ejs +12 -11
- package/templates/common/kafka/ts/messaging/welcomeEmailConsumer.spec.ts.ejs +86 -49
- package/templates/common/kafka/ts/messaging/welcomeEmailConsumer.ts.ejs +38 -25
- package/templates/common/kafka/ts/services/kafkaService.ts.ejs +95 -95
- package/templates/common/kafka/ts/utils/kafkaEvents.ts.ejs +5 -0
- package/templates/common/package.json.ejs +10 -2
- package/templates/common/shutdown/js/gracefulShutdown.js.ejs +65 -61
- package/templates/common/shutdown/js/gracefulShutdown.spec.js.ejs +149 -160
- package/templates/common/shutdown/ts/gracefulShutdown.spec.ts.ejs +179 -158
- package/templates/common/shutdown/ts/gracefulShutdown.ts.ejs +59 -55
- package/templates/common/src/tests/e2e/e2e.users.test.js.ejs +120 -49
- package/templates/common/src/tests/e2e/e2e.users.test.ts.ejs +120 -49
- package/templates/common/swagger.yml.ejs +118 -66
- package/templates/db/mysql/V1__Initial_Setup.sql.ejs +10 -9
- package/templates/db/postgres/V1__Initial_Setup.sql.ejs +10 -9
- package/templates/mvc/js/src/controllers/userController.js.ejs +246 -105
- package/templates/mvc/js/src/controllers/userController.spec.js.ejs +481 -209
- package/templates/mvc/js/src/errors/BadRequestError.js +11 -10
- package/templates/mvc/js/src/errors/BadRequestError.spec.js.ejs +22 -21
- package/templates/mvc/js/src/errors/NotFoundError.js +11 -10
- package/templates/mvc/js/src/errors/NotFoundError.spec.js.ejs +22 -21
- package/templates/mvc/js/src/graphql/resolvers/user.resolvers.js.ejs +25 -19
- package/templates/mvc/js/src/graphql/resolvers/user.resolvers.spec.js.ejs +64 -47
- package/templates/mvc/js/src/graphql/typeDefs/user.types.js.ejs +19 -17
- package/templates/mvc/js/src/index.js.ejs +1 -1
- package/templates/mvc/js/src/routes/api.js +10 -8
- package/templates/mvc/js/src/routes/api.spec.js.ejs +41 -36
- package/templates/mvc/js/src/utils/errorMessages.js +14 -0
- package/templates/mvc/ts/src/controllers/userController.spec.ts.ejs +481 -203
- package/templates/mvc/ts/src/controllers/userController.ts.ejs +248 -107
- package/templates/mvc/ts/src/errors/BadRequestError.spec.ts.ejs +22 -21
- package/templates/mvc/ts/src/errors/BadRequestError.ts +9 -8
- package/templates/mvc/ts/src/errors/NotFoundError.spec.ts.ejs +27 -21
- package/templates/mvc/ts/src/errors/NotFoundError.ts +9 -8
- package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.spec.ts.ejs +68 -51
- package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.ts.ejs +29 -21
- package/templates/mvc/ts/src/graphql/typeDefs/user.types.ts.ejs +17 -15
- package/templates/mvc/ts/src/index.ts.ejs +156 -153
- package/templates/mvc/ts/src/routes/api.spec.ts.ejs +59 -40
- package/templates/mvc/ts/src/routes/api.ts +12 -10
- package/templates/mvc/ts/src/utils/errorMessages.ts +12 -0
- package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.ts.ejs +0 -37
|
@@ -1,203 +1,481 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import
|
|
7
|
-
<%_ if (caching === 'Redis') { -%>
|
|
8
|
-
import cacheService from '@/config/redisClient';
|
|
9
|
-
<%_ } else if (caching === 'Memory Cache') { -%>
|
|
10
|
-
import cacheService from '@/config/memoryCache';
|
|
11
|
-
<%_ } -%>
|
|
12
|
-
|
|
13
|
-
// Mock dependencies
|
|
14
|
-
jest.mock('@/models/User')
|
|
15
|
-
|
|
16
|
-
jest.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
jest.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
<%
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
<%_
|
|
98
|
-
|
|
99
|
-
<%_ } else { -%>
|
|
100
|
-
|
|
101
|
-
<%_ } -%>
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
<%_
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
expect(
|
|
124
|
-
<% } -%>
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
<%
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
<%
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
<%_
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
<% }
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
<% } -%>
|
|
173
|
-
|
|
174
|
-
expect(
|
|
175
|
-
<%
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
<%
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
1
|
+
import { HTTP_STATUS } from '@/utils/httpCodes';
|
|
2
|
+
import { ERROR_MESSAGES } from '@/utils/errorMessages';
|
|
3
|
+
<% if (communication !== 'GraphQL') { -%>
|
|
4
|
+
import { Request, Response, NextFunction } from 'express';
|
|
5
|
+
<% } -%>
|
|
6
|
+
import { UserController } from '@/controllers/userController';
|
|
7
|
+
<%_ if (caching === 'Redis') { -%>
|
|
8
|
+
import cacheService from '@/config/redisClient';
|
|
9
|
+
<%_ } else if (caching === 'Memory Cache') { -%>
|
|
10
|
+
import cacheService from '@/config/memoryCache';
|
|
11
|
+
<%_ } -%>
|
|
12
|
+
|
|
13
|
+
// Mock dependencies
|
|
14
|
+
jest.mock('@/models/User', () => {
|
|
15
|
+
return {
|
|
16
|
+
create: jest.fn(),
|
|
17
|
+
find: jest.fn(),
|
|
18
|
+
findAll: jest.fn(),
|
|
19
|
+
findById: jest.fn(),
|
|
20
|
+
findByIdAndUpdate: jest.fn(),
|
|
21
|
+
findByIdAndDelete: jest.fn(),
|
|
22
|
+
findByPk: jest.fn(),
|
|
23
|
+
update: jest.fn(),
|
|
24
|
+
destroy: jest.fn(),
|
|
25
|
+
mockData: []
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
const User = require('@/models/User');
|
|
29
|
+
<%_ if (caching === 'Redis') { -%>
|
|
30
|
+
jest.mock('@/config/redisClient', () => ({
|
|
31
|
+
getOrSet: jest.fn((_key, fetcher) => fetcher()),
|
|
32
|
+
del: jest.fn(),
|
|
33
|
+
flush: jest.fn()
|
|
34
|
+
}));
|
|
35
|
+
<%_ } else if (caching === 'Memory Cache') { -%>
|
|
36
|
+
jest.mock('@/config/memoryCache', () => ({
|
|
37
|
+
getOrSet: jest.fn((_key, fetcher) => fetcher()),
|
|
38
|
+
del: jest.fn(),
|
|
39
|
+
flush: jest.fn()
|
|
40
|
+
}));
|
|
41
|
+
<%_ } -%>
|
|
42
|
+
jest.mock('@/utils/logger');
|
|
43
|
+
<%_ if (communication === 'Kafka') { -%>
|
|
44
|
+
jest.mock('@/services/kafkaService', () => {
|
|
45
|
+
const mockSendMessage = jest.fn().mockResolvedValue(undefined);
|
|
46
|
+
return {
|
|
47
|
+
sendMessage: mockSendMessage,
|
|
48
|
+
kafkaService: {
|
|
49
|
+
sendMessage: mockSendMessage
|
|
50
|
+
},
|
|
51
|
+
KafkaService: jest.fn().mockImplementation(() => ({
|
|
52
|
+
sendMessage: mockSendMessage
|
|
53
|
+
}))
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
import { kafkaService } from '@/services/kafkaService';
|
|
57
|
+
<%_ } -%>
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
describe('UserController', () => {
|
|
61
|
+
let userController: UserController;
|
|
62
|
+
<% if (communication !== 'GraphQL') { -%>
|
|
63
|
+
let mockRequest: Partial<Request>;
|
|
64
|
+
let mockResponse: Partial<Response>;
|
|
65
|
+
let mockNext: NextFunction;
|
|
66
|
+
<% } -%>
|
|
67
|
+
|
|
68
|
+
beforeEach(() => {
|
|
69
|
+
userController = new UserController();
|
|
70
|
+
<% if (communication !== 'GraphQL') { -%>
|
|
71
|
+
mockRequest = {
|
|
72
|
+
body: {}
|
|
73
|
+
};
|
|
74
|
+
mockResponse = {
|
|
75
|
+
json: jest.fn(),
|
|
76
|
+
status: jest.fn().mockReturnThis(),
|
|
77
|
+
};
|
|
78
|
+
mockNext = jest.fn();
|
|
79
|
+
<% } -%>
|
|
80
|
+
<%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
|
|
81
|
+
(cacheService.getOrSet as jest.Mock).mockImplementation((_key, fetcher) => fetcher());
|
|
82
|
+
(cacheService.flush as jest.Mock).mockClear();
|
|
83
|
+
<%_ } -%>
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
afterEach(() => {
|
|
87
|
+
jest.clearAllMocks();
|
|
88
|
+
<%_ if (database === 'None') { -%>
|
|
89
|
+
(User as any).mockData = [];
|
|
90
|
+
<%_ } -%>
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('getUsers', () => {
|
|
94
|
+
it('should return successfully (Happy Path)', async () => {
|
|
95
|
+
// Arrange
|
|
96
|
+
const usersMock = [{ id: '1', name: 'Test', email: 'test@example.com' }];
|
|
97
|
+
<%_ if (database === 'MongoDB' || database === 'None') { -%>
|
|
98
|
+
(User.find as jest.Mock).mockResolvedValue(usersMock);
|
|
99
|
+
<%_ } else { -%>
|
|
100
|
+
(User.findAll as jest.Mock).mockResolvedValue(usersMock);
|
|
101
|
+
<%_ } -%>
|
|
102
|
+
|
|
103
|
+
// Act
|
|
104
|
+
<% if (communication === 'GraphQL') { -%>
|
|
105
|
+
const result = await userController.getUsers();
|
|
106
|
+
|
|
107
|
+
// Assert
|
|
108
|
+
expect(result!).toEqual(usersMock);
|
|
109
|
+
<% } else { -%>
|
|
110
|
+
await userController.getUsers(mockRequest as Request, mockResponse as Response, mockNext);
|
|
111
|
+
|
|
112
|
+
// Assert
|
|
113
|
+
expect(mockResponse.json).toHaveBeenCalledWith(usersMock);
|
|
114
|
+
<% } -%>
|
|
115
|
+
<%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
|
|
116
|
+
expect(cacheService.getOrSet).toHaveBeenCalled();
|
|
117
|
+
<%_ } else { -%>
|
|
118
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
119
|
+
expect(User.find).toHaveBeenCalled();
|
|
120
|
+
<%_ } else if (database === 'None') { -%>
|
|
121
|
+
// No DB mock logic
|
|
122
|
+
<%_ } else { -%>
|
|
123
|
+
expect(User.findAll).toHaveBeenCalled();
|
|
124
|
+
<%_ } -%>
|
|
125
|
+
<%_ } -%>
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should return an empty array when no users found', async () => {
|
|
129
|
+
// Arrange
|
|
130
|
+
const usersMock: any[] = [];
|
|
131
|
+
<%_ if (database === 'MongoDB' || database === 'None') { -%>
|
|
132
|
+
(User.find as jest.Mock).mockResolvedValue(usersMock);
|
|
133
|
+
<%_ } else { -%>
|
|
134
|
+
(User.findAll as jest.Mock).mockResolvedValue(usersMock);
|
|
135
|
+
<%_ } -%>
|
|
136
|
+
|
|
137
|
+
// Act
|
|
138
|
+
<% if (communication === 'GraphQL') { -%>
|
|
139
|
+
const result = await userController.getUsers();
|
|
140
|
+
|
|
141
|
+
// Assert
|
|
142
|
+
expect(result!).toEqual(usersMock);
|
|
143
|
+
<% } else { -%>
|
|
144
|
+
await userController.getUsers(mockRequest as Request, mockResponse as Response, mockNext);
|
|
145
|
+
|
|
146
|
+
// Assert
|
|
147
|
+
expect(mockResponse.json).toHaveBeenCalledWith(usersMock);
|
|
148
|
+
<% } -%>
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('should handle errors correctly (Error Handling)', async () => {
|
|
152
|
+
// Arrange
|
|
153
|
+
const error = new Error('Database Error');
|
|
154
|
+
<%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
|
|
155
|
+
// Simulating error inside the fetcher by making User.findAll fail
|
|
156
|
+
<%_ if (database === 'MongoDB' || database === 'None') { -%>
|
|
157
|
+
(User.find as jest.Mock).mockRejectedValue(error);
|
|
158
|
+
<%_ } else { -%>
|
|
159
|
+
(User.findAll as jest.Mock).mockRejectedValue(error);
|
|
160
|
+
<%_ } -%>
|
|
161
|
+
<%_ } else { -%>
|
|
162
|
+
<%_ if (database === 'MongoDB' || database === 'None') { -%>
|
|
163
|
+
(User.find as jest.Mock).mockRejectedValue(error);
|
|
164
|
+
<%_ } else { -%>
|
|
165
|
+
(User.findAll as jest.Mock).mockRejectedValue(error);
|
|
166
|
+
<%_ } -%>
|
|
167
|
+
<%_ } -%>
|
|
168
|
+
|
|
169
|
+
// Act & Assert
|
|
170
|
+
<% if (communication === 'GraphQL') { -%>
|
|
171
|
+
await expect(userController.getUsers()).rejects.toThrow(error);
|
|
172
|
+
<% } else { -%>
|
|
173
|
+
await userController.getUsers(mockRequest as Request, mockResponse as Response, mockNext);
|
|
174
|
+
expect(mockNext).toHaveBeenCalledWith(error);
|
|
175
|
+
<% } -%>
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe('createUser', () => {
|
|
180
|
+
it('should successfully create a new user (Happy Path)', async () => {
|
|
181
|
+
// Arrange
|
|
182
|
+
const payload = { name: 'Alice', email: 'alice@example.com' };
|
|
183
|
+
<% if (communication === 'GraphQL') { -%>
|
|
184
|
+
const dataArg = payload;
|
|
185
|
+
<% } else { -%>
|
|
186
|
+
mockRequest.body = payload;
|
|
187
|
+
<% } -%>
|
|
188
|
+
|
|
189
|
+
const expectedUser = { id: '1', ...payload };
|
|
190
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
191
|
+
(User.create as jest.Mock).mockResolvedValue(expectedUser);
|
|
192
|
+
<%_ } else if (database === 'None') { -%>
|
|
193
|
+
(User.create as jest.Mock).mockResolvedValue(expectedUser);
|
|
194
|
+
<%_ } else { -%>
|
|
195
|
+
(User.create as jest.Mock).mockResolvedValue(expectedUser);
|
|
196
|
+
<%_ } -%>
|
|
197
|
+
|
|
198
|
+
// Act
|
|
199
|
+
<% if (communication === 'GraphQL') { -%>
|
|
200
|
+
const result = await userController.createUser(dataArg) as any;
|
|
201
|
+
|
|
202
|
+
// Assert
|
|
203
|
+
expect(result!).toEqual(expectedUser);
|
|
204
|
+
<% } else { -%>
|
|
205
|
+
await userController.createUser(mockRequest as Request, mockResponse as Response, mockNext);
|
|
206
|
+
|
|
207
|
+
// Assert
|
|
208
|
+
expect(mockResponse.status).toHaveBeenCalledWith(HTTP_STATUS.CREATED);
|
|
209
|
+
expect(mockResponse.json).toHaveBeenCalledWith(expectedUser);
|
|
210
|
+
expect(User.create).toHaveBeenCalledWith(payload);
|
|
211
|
+
<% } -%>
|
|
212
|
+
<%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
|
|
213
|
+
expect(cacheService.del).toHaveBeenCalledWith('users:all');
|
|
214
|
+
<%_ } -%>
|
|
215
|
+
<%_ if (communication === 'Kafka') { -%>
|
|
216
|
+
expect(kafkaService.sendMessage).toHaveBeenCalled();
|
|
217
|
+
<%_ } -%>
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('should handle errors when creation fails (Error Handling)', async () => {
|
|
221
|
+
// Arrange
|
|
222
|
+
const error = new Error('Creation Error');
|
|
223
|
+
const payload = { name: 'Bob', email: 'bob@example.com' };
|
|
224
|
+
<% if (communication === 'GraphQL') { -%>
|
|
225
|
+
const dataArg = payload;
|
|
226
|
+
<% } else { -%>
|
|
227
|
+
mockRequest.body = payload;
|
|
228
|
+
<% } -%>
|
|
229
|
+
|
|
230
|
+
(User.create as jest.Mock).mockRejectedValue(error);
|
|
231
|
+
|
|
232
|
+
// Act & Assert
|
|
233
|
+
<% if (communication === 'GraphQL') { -%>
|
|
234
|
+
await expect(userController.createUser(dataArg)).rejects.toThrow(error);
|
|
235
|
+
<% } else { -%>
|
|
236
|
+
await userController.createUser(mockRequest as Request, mockResponse as Response, mockNext);
|
|
237
|
+
expect(mockNext).toHaveBeenCalledWith(error);
|
|
238
|
+
<% } -%>
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
describe('updateUser', () => {
|
|
243
|
+
it('should successfully update a user (Happy Path)', async () => {
|
|
244
|
+
// Arrange
|
|
245
|
+
const id = '1';
|
|
246
|
+
const payload = { name: 'Alice Updated' };
|
|
247
|
+
<% if (communication === 'GraphQL') { -%>
|
|
248
|
+
const idArg = id;
|
|
249
|
+
const dataArg = payload;
|
|
250
|
+
<% } else { -%>
|
|
251
|
+
mockRequest.params = { id };
|
|
252
|
+
mockRequest.body = payload;
|
|
253
|
+
<% } -%>
|
|
254
|
+
|
|
255
|
+
const expectedUser = { id, ...payload, email: 'alice@example.com' };
|
|
256
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
257
|
+
(User.findByIdAndUpdate as jest.Mock).mockResolvedValue(expectedUser);
|
|
258
|
+
(User.findById as jest.Mock).mockResolvedValue(expectedUser);
|
|
259
|
+
<%_ } else if (database === 'None') { -%>
|
|
260
|
+
(User.update as jest.Mock).mockResolvedValue(expectedUser);
|
|
261
|
+
(User.findByPk as jest.Mock).mockResolvedValue(expectedUser);
|
|
262
|
+
<%_ } else { -%>
|
|
263
|
+
const userMock = { ...expectedUser, update: jest.fn().mockResolvedValue(true) };
|
|
264
|
+
(User.findByPk as jest.Mock).mockResolvedValue(userMock);
|
|
265
|
+
<%_ } -%>
|
|
266
|
+
|
|
267
|
+
// Act
|
|
268
|
+
<% if (communication === 'GraphQL') { -%>
|
|
269
|
+
const result = await userController.updateUser(idArg, dataArg);
|
|
270
|
+
|
|
271
|
+
// Assert
|
|
272
|
+
<%_ if (database === 'None' || database === 'MongoDB') { -%>
|
|
273
|
+
expect(result!).toEqual(expectedUser);
|
|
274
|
+
<%_ } else { -%>
|
|
275
|
+
expect(result!).toMatchObject(payload);
|
|
276
|
+
expect(User.findByPk).toHaveBeenCalledWith(id);
|
|
277
|
+
<%_ } -%>
|
|
278
|
+
<% } else { -%>
|
|
279
|
+
await userController.updateUser(mockRequest as Request, mockResponse as Response, mockNext);
|
|
280
|
+
|
|
281
|
+
// Assert
|
|
282
|
+
<%_ if (database === 'None' || database === 'MongoDB') { -%>
|
|
283
|
+
expect(mockResponse.json).toHaveBeenCalledWith(expectedUser);
|
|
284
|
+
<%_ } else { -%>
|
|
285
|
+
expect(mockResponse.json).toHaveBeenCalled();
|
|
286
|
+
expect(User.findByPk).toHaveBeenCalledWith(id);
|
|
287
|
+
<%_ } -%>
|
|
288
|
+
<% } -%>
|
|
289
|
+
<%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
|
|
290
|
+
expect(cacheService.del).toHaveBeenCalledWith('users:all');
|
|
291
|
+
<%_ } -%>
|
|
292
|
+
<%_ if (communication === 'Kafka') { -%>
|
|
293
|
+
expect(kafkaService.sendMessage).toHaveBeenCalled();
|
|
294
|
+
<%_ } -%>
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('should handle 404/errors when user not found or update fails', async () => {
|
|
298
|
+
// Arrange
|
|
299
|
+
const id = '999';
|
|
300
|
+
<%_ if (communication === 'GraphQL') { -%>
|
|
301
|
+
const idArg = id;
|
|
302
|
+
const dataArg = { name: 'Fail' };
|
|
303
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
304
|
+
(User.findByIdAndUpdate as jest.Mock).mockResolvedValue(null);
|
|
305
|
+
<%_ } else { -%>
|
|
306
|
+
(User.findByPk as jest.Mock).mockResolvedValue(null);
|
|
307
|
+
<%_ } -%>
|
|
308
|
+
await expect(userController.updateUser(idArg, dataArg)).rejects.toThrow(ERROR_MESSAGES.USER_NOT_FOUND);
|
|
309
|
+
<%_ } else { -%>
|
|
310
|
+
mockRequest.params = { id };
|
|
311
|
+
mockRequest.body = { name: 'Fail' };
|
|
312
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
313
|
+
(User.findByIdAndUpdate as jest.Mock).mockResolvedValue(null);
|
|
314
|
+
<%_ } else if (database === 'None') { -%>
|
|
315
|
+
(User.findByPk as jest.Mock).mockResolvedValue(null);
|
|
316
|
+
<%_ } else { -%>
|
|
317
|
+
(User.findByPk as jest.Mock).mockResolvedValue(null);
|
|
318
|
+
<%_ } -%>
|
|
319
|
+
|
|
320
|
+
// Act
|
|
321
|
+
await userController.updateUser(mockRequest as Request, mockResponse as Response, mockNext);
|
|
322
|
+
expect(mockResponse.status).toHaveBeenCalledWith(HTTP_STATUS.NOT_FOUND);
|
|
323
|
+
<%_ } -%>
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it('should handle database errors during update (Error Handling)', async () => {
|
|
327
|
+
// Arrange
|
|
328
|
+
const id = '1';
|
|
329
|
+
const error = new Error('Database Error');
|
|
330
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
331
|
+
(User.findByIdAndUpdate as jest.Mock).mockRejectedValue(error);
|
|
332
|
+
<%_ } else { -%>
|
|
333
|
+
(User.findByPk as jest.Mock).mockRejectedValue(error);
|
|
334
|
+
<%_ } -%>
|
|
335
|
+
<%_ if (communication === 'GraphQL') { -%>
|
|
336
|
+
await expect(userController.updateUser(id, { name: 'Fail' })).rejects.toThrow(error);
|
|
337
|
+
<%_ } else { -%>
|
|
338
|
+
mockRequest.params = { id };
|
|
339
|
+
await userController.updateUser(mockRequest as Request, mockResponse as Response, mockNext);
|
|
340
|
+
expect(mockNext).toHaveBeenCalledWith(error);
|
|
341
|
+
<%_ } -%>
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
describe('deleteUser', () => {
|
|
346
|
+
it('should successfully delete a user (Happy Path)', async () => {
|
|
347
|
+
// Arrange
|
|
348
|
+
const id = '1';
|
|
349
|
+
<% if (communication === 'GraphQL') { -%>
|
|
350
|
+
const idArg = id;
|
|
351
|
+
<% } else { -%>
|
|
352
|
+
mockRequest.params = { id };
|
|
353
|
+
<% } -%>
|
|
354
|
+
|
|
355
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
356
|
+
(User.findByIdAndDelete as jest.Mock).mockResolvedValue(true);
|
|
357
|
+
<%_ } else if (database === 'None') { -%>
|
|
358
|
+
(User.destroy as jest.Mock).mockResolvedValue(true);
|
|
359
|
+
<%_ } else { -%>
|
|
360
|
+
const userMock = { id, destroy: jest.fn().mockResolvedValue(true) };
|
|
361
|
+
(User.findByPk as jest.Mock).mockResolvedValue(userMock);
|
|
362
|
+
<%_ } -%>
|
|
363
|
+
|
|
364
|
+
// Act
|
|
365
|
+
<% if (communication === 'GraphQL') { -%>
|
|
366
|
+
const result = await userController.deleteUser(idArg);
|
|
367
|
+
expect(result).toBe(true);
|
|
368
|
+
<% } else { -%>
|
|
369
|
+
await userController.deleteUser(mockRequest as Request, mockResponse as Response, mockNext);
|
|
370
|
+
expect(mockResponse.status).toHaveBeenCalledWith(HTTP_STATUS.OK);
|
|
371
|
+
<% } -%>
|
|
372
|
+
|
|
373
|
+
<%_ if (database === 'None') { -%>
|
|
374
|
+
expect(User.destroy).toHaveBeenCalledWith(id);
|
|
375
|
+
<%_ } -%>
|
|
376
|
+
<%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
|
|
377
|
+
expect(cacheService.del).toHaveBeenCalledWith('users:all');
|
|
378
|
+
<%_ } -%>
|
|
379
|
+
<%_ if (communication === 'Kafka') { -%>
|
|
380
|
+
expect(kafkaService.sendMessage).toHaveBeenCalled();
|
|
381
|
+
<%_ } -%>
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
<% if (communication === 'GraphQL') { -%>
|
|
385
|
+
it('should handle user not found during deletion (Error Handling)', async () => {
|
|
386
|
+
const id = '999';
|
|
387
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
388
|
+
(User.findByIdAndDelete as jest.Mock).mockResolvedValue(null);
|
|
389
|
+
<%_ } else if (database === 'None') { -%>
|
|
390
|
+
(User.destroy as jest.Mock).mockResolvedValue(false);
|
|
391
|
+
<%_ } else { -%>
|
|
392
|
+
(User.findByPk as jest.Mock).mockResolvedValue(null);
|
|
393
|
+
<%_ } -%>
|
|
394
|
+
await expect(userController.deleteUser(id)).rejects.toThrow(ERROR_MESSAGES.USER_NOT_FOUND);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it('should handle database errors during deletion (Error Handling)', async () => {
|
|
398
|
+
const id = '1';
|
|
399
|
+
const error = new Error('Database Error');
|
|
400
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
401
|
+
(User.findByIdAndDelete as jest.Mock).mockRejectedValue(error);
|
|
402
|
+
<%_ } else if (database === 'None') { -%>
|
|
403
|
+
(User.destroy as jest.Mock).mockRejectedValue(error);
|
|
404
|
+
<%_ } else { -%>
|
|
405
|
+
(User.findByPk as jest.Mock).mockRejectedValue(error);
|
|
406
|
+
<%_ } -%>
|
|
407
|
+
await expect(userController.deleteUser(id)).rejects.toThrow(error);
|
|
408
|
+
});
|
|
409
|
+
<% } else { -%>
|
|
410
|
+
it('should handle user not found during deletion (Error Handling)', async () => {
|
|
411
|
+
// Arrange
|
|
412
|
+
const id = '999';
|
|
413
|
+
mockRequest.params = { id };
|
|
414
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
415
|
+
(User.findByIdAndDelete as jest.Mock).mockResolvedValue(null);
|
|
416
|
+
<%_ } else if (database === 'None') { -%>
|
|
417
|
+
(User.destroy as jest.Mock).mockResolvedValue(false);
|
|
418
|
+
<%_ } else { -%>
|
|
419
|
+
(User.findByPk as jest.Mock).mockResolvedValue(null);
|
|
420
|
+
<%_ } -%>
|
|
421
|
+
await userController.deleteUser(mockRequest as Request, mockResponse as Response, mockNext);
|
|
422
|
+
expect(mockResponse.status).toHaveBeenCalledWith(HTTP_STATUS.NOT_FOUND);
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it('should handle database errors during deletion (Error Handling)', async () => {
|
|
426
|
+
// Arrange
|
|
427
|
+
const id = '1';
|
|
428
|
+
mockRequest.params = { id };
|
|
429
|
+
const error = new Error('Database Error');
|
|
430
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
431
|
+
(User.findByIdAndDelete as jest.Mock).mockRejectedValue(error);
|
|
432
|
+
<%_ } else if (database === 'None') { -%>
|
|
433
|
+
(User.destroy as jest.Mock).mockRejectedValue(error);
|
|
434
|
+
<%_ } else { -%>
|
|
435
|
+
(User.findByPk as jest.Mock).mockRejectedValue(error);
|
|
436
|
+
<%_ } -%>
|
|
437
|
+
await userController.deleteUser(mockRequest as Request, mockResponse as Response, mockNext);
|
|
438
|
+
expect(mockNext).toHaveBeenCalledWith(error);
|
|
439
|
+
});
|
|
440
|
+
<% } -%>
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
describe('createUser Error Paths', () => {
|
|
444
|
+
it('should handle database errors during creation (Error Handling)', async () => {
|
|
445
|
+
const error = new Error('Database Error');
|
|
446
|
+
(User.create as jest.Mock).mockRejectedValue(error);
|
|
447
|
+
<% if (communication === 'GraphQL') { -%>
|
|
448
|
+
await expect(userController.createUser({ name: 'Alice', email: 'alice@example.com' })).rejects.toThrow(error);
|
|
449
|
+
<% } else { -%>
|
|
450
|
+
mockRequest.body = { name: 'Alice', email: 'alice@example.com' };
|
|
451
|
+
await userController.createUser(mockRequest as Request, mockResponse as Response, mockNext);
|
|
452
|
+
expect(mockNext).toHaveBeenCalledWith(error);
|
|
453
|
+
<% } -%>
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
describe('updateUser Error Paths', () => {
|
|
458
|
+
it('should handle database errors during update (Error Handling)', async () => {
|
|
459
|
+
const id = '1';
|
|
460
|
+
const error = new Error('Database Error');
|
|
461
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
462
|
+
(User.findById as jest.Mock).mockResolvedValue({ id });
|
|
463
|
+
(User.findByIdAndUpdate as jest.Mock).mockRejectedValue(error);
|
|
464
|
+
<%_ } else if (database === 'None') { -%>
|
|
465
|
+
(User.findByPk as jest.Mock).mockResolvedValue({ id });
|
|
466
|
+
(User.update as jest.Mock).mockRejectedValue(error);
|
|
467
|
+
<%_ } else { -%>
|
|
468
|
+
const userMock = { id, update: jest.fn().mockRejectedValue(error) };
|
|
469
|
+
(User.findByPk as jest.Mock).mockResolvedValue(userMock);
|
|
470
|
+
<%_ } -%>
|
|
471
|
+
<% if (communication === 'GraphQL') { -%>
|
|
472
|
+
await expect(userController.updateUser(id, { name: 'Bob' })).rejects.toThrow(error);
|
|
473
|
+
<% } else { -%>
|
|
474
|
+
mockRequest.params = { id };
|
|
475
|
+
mockRequest.body = { name: 'Bob' };
|
|
476
|
+
await userController.updateUser(mockRequest as Request, mockResponse as Response, mockNext);
|
|
477
|
+
expect(mockNext).toHaveBeenCalledWith(error);
|
|
478
|
+
<% } -%>
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
});
|