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