nodejs-structure-cli 1.0.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 (243) hide show
  1. package/README.md +32 -0
  2. package/bin/index.js +143 -0
  3. package/lib/generator.js +145 -0
  4. package/lib/modules/app-setup.js +479 -0
  5. package/lib/modules/caching-setup.js +76 -0
  6. package/lib/modules/config-files.js +151 -0
  7. package/lib/modules/database-setup.js +116 -0
  8. package/lib/modules/kafka-setup.js +249 -0
  9. package/lib/modules/project-setup.js +32 -0
  10. package/lib/prompts.js +128 -0
  11. package/package.json +66 -0
  12. package/templates/clean-architecture/js/src/domain/models/User.js.ejs +11 -0
  13. package/templates/clean-architecture/js/src/errors/ApiError.js +14 -0
  14. package/templates/clean-architecture/js/src/errors/BadRequestError.js +11 -0
  15. package/templates/clean-architecture/js/src/errors/BadRequestError.spec.js.ejs +22 -0
  16. package/templates/clean-architecture/js/src/errors/NotFoundError.js +11 -0
  17. package/templates/clean-architecture/js/src/errors/NotFoundError.spec.js.ejs +22 -0
  18. package/templates/clean-architecture/js/src/index.js.ejs +56 -0
  19. package/templates/clean-architecture/js/src/infrastructure/config/env.js.ejs +47 -0
  20. package/templates/clean-architecture/js/src/infrastructure/log/logger.js +36 -0
  21. package/templates/clean-architecture/js/src/infrastructure/log/logger.spec.js.ejs +63 -0
  22. package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +88 -0
  23. package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs +142 -0
  24. package/templates/clean-architecture/js/src/infrastructure/webserver/middleware/errorMiddleware.js +30 -0
  25. package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +93 -0
  26. package/templates/clean-architecture/js/src/infrastructure/webserver/swagger.js.ejs +6 -0
  27. package/templates/clean-architecture/js/src/interfaces/controllers/userController.js.ejs +190 -0
  28. package/templates/clean-architecture/js/src/interfaces/controllers/userController.spec.js.ejs +234 -0
  29. package/templates/clean-architecture/js/src/interfaces/graphql/context.js.ejs +13 -0
  30. package/templates/clean-architecture/js/src/interfaces/graphql/context.spec.js.ejs +31 -0
  31. package/templates/clean-architecture/js/src/interfaces/graphql/index.js.ejs +5 -0
  32. package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/index.js.ejs +6 -0
  33. package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.js.ejs +27 -0
  34. package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.spec.js.ejs +66 -0
  35. package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/index.js.ejs +6 -0
  36. package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/user.types.js.ejs +19 -0
  37. package/templates/clean-architecture/js/src/interfaces/routes/api.js.ejs +17 -0
  38. package/templates/clean-architecture/js/src/interfaces/routes/api.spec.js.ejs +38 -0
  39. package/templates/clean-architecture/js/src/usecases/CreateUser.js.ejs +14 -0
  40. package/templates/clean-architecture/js/src/usecases/CreateUser.spec.js.ejs +51 -0
  41. package/templates/clean-architecture/js/src/usecases/DeleteUser.js +11 -0
  42. package/templates/clean-architecture/js/src/usecases/DeleteUser.spec.js.ejs +47 -0
  43. package/templates/clean-architecture/js/src/usecases/GetAllUsers.js +12 -0
  44. package/templates/clean-architecture/js/src/usecases/GetAllUsers.spec.js.ejs +61 -0
  45. package/templates/clean-architecture/js/src/usecases/UpdateUser.js.ejs +11 -0
  46. package/templates/clean-architecture/js/src/usecases/UpdateUser.spec.js.ejs +48 -0
  47. package/templates/clean-architecture/js/src/utils/errorMessages.js +14 -0
  48. package/templates/clean-architecture/js/src/utils/httpCodes.js +9 -0
  49. package/templates/clean-architecture/ts/src/config/env.ts.ejs +46 -0
  50. package/templates/clean-architecture/ts/src/config/swagger.ts.ejs +6 -0
  51. package/templates/clean-architecture/ts/src/domain/user.ts.ejs +9 -0
  52. package/templates/clean-architecture/ts/src/errors/ApiError.ts +15 -0
  53. package/templates/clean-architecture/ts/src/errors/BadRequestError.spec.ts.ejs +22 -0
  54. package/templates/clean-architecture/ts/src/errors/BadRequestError.ts +9 -0
  55. package/templates/clean-architecture/ts/src/errors/NotFoundError.spec.ts.ejs +22 -0
  56. package/templates/clean-architecture/ts/src/errors/NotFoundError.ts +9 -0
  57. package/templates/clean-architecture/ts/src/index.ts.ejs +144 -0
  58. package/templates/clean-architecture/ts/src/infrastructure/log/logger.spec.ts.ejs +63 -0
  59. package/templates/clean-architecture/ts/src/infrastructure/log/logger.ts +36 -0
  60. package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +175 -0
  61. package/templates/clean-architecture/ts/src/infrastructure/repositories/userRepository.ts.ejs +125 -0
  62. package/templates/clean-architecture/ts/src/interfaces/controllers/userController.spec.ts.ejs +331 -0
  63. package/templates/clean-architecture/ts/src/interfaces/controllers/userController.ts.ejs +208 -0
  64. package/templates/clean-architecture/ts/src/interfaces/graphql/context.spec.ts.ejs +32 -0
  65. package/templates/clean-architecture/ts/src/interfaces/graphql/context.ts.ejs +17 -0
  66. package/templates/clean-architecture/ts/src/interfaces/graphql/index.ts.ejs +3 -0
  67. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/index.ts.ejs +4 -0
  68. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.spec.ts.ejs +68 -0
  69. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.ts.ejs +29 -0
  70. package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/index.ts.ejs +4 -0
  71. package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/user.types.ts.ejs +17 -0
  72. package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.spec.ts.ejs +40 -0
  73. package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts.ejs +18 -0
  74. package/templates/clean-architecture/ts/src/usecases/createUser.spec.ts.ejs +51 -0
  75. package/templates/clean-architecture/ts/src/usecases/createUser.ts.ejs +11 -0
  76. package/templates/clean-architecture/ts/src/usecases/deleteUser.spec.ts.ejs +47 -0
  77. package/templates/clean-architecture/ts/src/usecases/deleteUser.ts +9 -0
  78. package/templates/clean-architecture/ts/src/usecases/getAllUsers.spec.ts.ejs +63 -0
  79. package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts +10 -0
  80. package/templates/clean-architecture/ts/src/usecases/updateUser.spec.ts.ejs +48 -0
  81. package/templates/clean-architecture/ts/src/usecases/updateUser.ts.ejs +10 -0
  82. package/templates/clean-architecture/ts/src/utils/errorMessages.ts +12 -0
  83. package/templates/clean-architecture/ts/src/utils/errorMiddleware.ts.ejs +27 -0
  84. package/templates/clean-architecture/ts/src/utils/httpCodes.ts +7 -0
  85. package/templates/common/.cursorrules.ejs +60 -0
  86. package/templates/common/.dockerignore +12 -0
  87. package/templates/common/.env.example.ejs +60 -0
  88. package/templates/common/.gitattributes +46 -0
  89. package/templates/common/.gitlab-ci.yml.ejs +86 -0
  90. package/templates/common/.lintstagedrc +6 -0
  91. package/templates/common/.prettierrc +7 -0
  92. package/templates/common/.snyk.ejs +45 -0
  93. package/templates/common/Dockerfile +73 -0
  94. package/templates/common/Jenkinsfile.ejs +87 -0
  95. package/templates/common/README.md.ejs +148 -0
  96. package/templates/common/_github/workflows/ci.yml.ejs +46 -0
  97. package/templates/common/_github/workflows/security.yml.ejs +36 -0
  98. package/templates/common/_gitignore +5 -0
  99. package/templates/common/_husky/pre-commit +4 -0
  100. package/templates/common/caching/clean/js/CreateUser.js.ejs +29 -0
  101. package/templates/common/caching/clean/js/DeleteUser.js.ejs +27 -0
  102. package/templates/common/caching/clean/js/GetAllUsers.js.ejs +37 -0
  103. package/templates/common/caching/clean/js/UpdateUser.js.ejs +27 -0
  104. package/templates/common/caching/clean/ts/createUser.ts.ejs +27 -0
  105. package/templates/common/caching/clean/ts/deleteUser.ts.ejs +24 -0
  106. package/templates/common/caching/clean/ts/getAllUsers.ts.ejs +34 -0
  107. package/templates/common/caching/clean/ts/updateUser.ts.ejs +25 -0
  108. package/templates/common/caching/js/memoryCache.js.ejs +60 -0
  109. package/templates/common/caching/js/memoryCache.spec.js.ejs +101 -0
  110. package/templates/common/caching/js/redisClient.js.ejs +75 -0
  111. package/templates/common/caching/js/redisClient.spec.js.ejs +147 -0
  112. package/templates/common/caching/ts/memoryCache.spec.ts.ejs +102 -0
  113. package/templates/common/caching/ts/memoryCache.ts.ejs +73 -0
  114. package/templates/common/caching/ts/redisClient.spec.ts.ejs +157 -0
  115. package/templates/common/caching/ts/redisClient.ts.ejs +89 -0
  116. package/templates/common/database/js/database.js.ejs +19 -0
  117. package/templates/common/database/js/database.spec.js.ejs +56 -0
  118. package/templates/common/database/js/models/User.js.ejs +91 -0
  119. package/templates/common/database/js/models/User.js.mongoose.ejs +35 -0
  120. package/templates/common/database/js/models/User.spec.js.ejs +94 -0
  121. package/templates/common/database/js/mongoose.js.ejs +33 -0
  122. package/templates/common/database/js/mongoose.spec.js.ejs +43 -0
  123. package/templates/common/database/ts/database.spec.ts.ejs +56 -0
  124. package/templates/common/database/ts/database.ts.ejs +21 -0
  125. package/templates/common/database/ts/models/User.spec.ts.ejs +100 -0
  126. package/templates/common/database/ts/models/User.ts.ejs +102 -0
  127. package/templates/common/database/ts/models/User.ts.mongoose.ejs +34 -0
  128. package/templates/common/database/ts/mongoose.spec.ts.ejs +42 -0
  129. package/templates/common/database/ts/mongoose.ts.ejs +28 -0
  130. package/templates/common/docker-compose.yml.ejs +159 -0
  131. package/templates/common/ecosystem.config.js.ejs +40 -0
  132. package/templates/common/eslint.config.mjs.ejs +77 -0
  133. package/templates/common/health/js/healthRoute.js.ejs +50 -0
  134. package/templates/common/health/js/healthRoute.spec.js.ejs +70 -0
  135. package/templates/common/health/ts/healthRoute.spec.ts.ejs +76 -0
  136. package/templates/common/health/ts/healthRoute.ts.ejs +49 -0
  137. package/templates/common/jest.config.js.ejs +32 -0
  138. package/templates/common/jest.e2e.config.js.ejs +8 -0
  139. package/templates/common/kafka/js/config/kafka.js +9 -0
  140. package/templates/common/kafka/js/config/kafka.spec.js.ejs +27 -0
  141. package/templates/common/kafka/js/messaging/baseConsumer.js.ejs +30 -0
  142. package/templates/common/kafka/js/messaging/baseConsumer.spec.js.ejs +58 -0
  143. package/templates/common/kafka/js/messaging/userEventSchema.js.ejs +12 -0
  144. package/templates/common/kafka/js/messaging/userEventSchema.spec.js.ejs +27 -0
  145. package/templates/common/kafka/js/messaging/welcomeEmailConsumer.js.ejs +44 -0
  146. package/templates/common/kafka/js/messaging/welcomeEmailConsumer.spec.js.ejs +86 -0
  147. package/templates/common/kafka/js/services/kafkaService.js.ejs +93 -0
  148. package/templates/common/kafka/js/services/kafkaService.spec.js.ejs +106 -0
  149. package/templates/common/kafka/js/utils/kafkaEvents.js.ejs +7 -0
  150. package/templates/common/kafka/ts/config/kafka.spec.ts.ejs +27 -0
  151. package/templates/common/kafka/ts/config/kafka.ts +7 -0
  152. package/templates/common/kafka/ts/messaging/baseConsumer.spec.ts.ejs +50 -0
  153. package/templates/common/kafka/ts/messaging/baseConsumer.ts.ejs +27 -0
  154. package/templates/common/kafka/ts/messaging/userEventSchema.spec.ts.ejs +51 -0
  155. package/templates/common/kafka/ts/messaging/userEventSchema.ts.ejs +12 -0
  156. package/templates/common/kafka/ts/messaging/welcomeEmailConsumer.spec.ts.ejs +86 -0
  157. package/templates/common/kafka/ts/messaging/welcomeEmailConsumer.ts.ejs +38 -0
  158. package/templates/common/kafka/ts/services/kafkaService.spec.ts.ejs +81 -0
  159. package/templates/common/kafka/ts/services/kafkaService.ts.ejs +95 -0
  160. package/templates/common/kafka/ts/utils/kafkaEvents.ts.ejs +5 -0
  161. package/templates/common/migrate-mongo-config.js.ejs +31 -0
  162. package/templates/common/migrations/init.js.ejs +23 -0
  163. package/templates/common/package.json.ejs +137 -0
  164. package/templates/common/prompts/add-feature.md.ejs +26 -0
  165. package/templates/common/prompts/project-context.md.ejs +43 -0
  166. package/templates/common/prompts/troubleshoot.md.ejs +28 -0
  167. package/templates/common/public/css/style.css +147 -0
  168. package/templates/common/scripts/run-e2e.js.ejs +63 -0
  169. package/templates/common/shutdown/js/gracefulShutdown.js.ejs +65 -0
  170. package/templates/common/shutdown/js/gracefulShutdown.spec.js.ejs +149 -0
  171. package/templates/common/shutdown/ts/gracefulShutdown.spec.ts.ejs +179 -0
  172. package/templates/common/shutdown/ts/gracefulShutdown.ts.ejs +59 -0
  173. package/templates/common/sonar-project.properties.ejs +27 -0
  174. package/templates/common/src/config/auth.js.ejs +19 -0
  175. package/templates/common/src/config/auth.ts.ejs +19 -0
  176. package/templates/common/src/controllers/authController.js.ejs +101 -0
  177. package/templates/common/src/controllers/authController.ts.ejs +101 -0
  178. package/templates/common/src/middleware/auth.js.ejs +20 -0
  179. package/templates/common/src/middleware/auth.ts.ejs +25 -0
  180. package/templates/common/src/middleware/upload.js.ejs +31 -0
  181. package/templates/common/src/middleware/upload.ts.ejs +32 -0
  182. package/templates/common/src/routes/authRoutes.js.ejs +20 -0
  183. package/templates/common/src/routes/authRoutes.ts.ejs +20 -0
  184. package/templates/common/src/tests/e2e/e2e.users.test.js.ejs +120 -0
  185. package/templates/common/src/tests/e2e/e2e.users.test.ts.ejs +120 -0
  186. package/templates/common/src/utils/errorMiddleware.spec.js.ejs +79 -0
  187. package/templates/common/src/utils/errorMiddleware.spec.ts.ejs +94 -0
  188. package/templates/common/swagger.yml.ejs +118 -0
  189. package/templates/common/tsconfig.json +23 -0
  190. package/templates/common/views/ejs/index.ejs +55 -0
  191. package/templates/common/views/pug/index.pug +40 -0
  192. package/templates/db/mysql/V1__Initial_Setup.sql.ejs +10 -0
  193. package/templates/db/postgres/V1__Initial_Setup.sql.ejs +10 -0
  194. package/templates/mvc/js/src/config/env.js.ejs +46 -0
  195. package/templates/mvc/js/src/config/swagger.js.ejs +6 -0
  196. package/templates/mvc/js/src/controllers/userController.js.ejs +288 -0
  197. package/templates/mvc/js/src/controllers/userController.spec.js.ejs +481 -0
  198. package/templates/mvc/js/src/errors/ApiError.js +14 -0
  199. package/templates/mvc/js/src/errors/BadRequestError.js +11 -0
  200. package/templates/mvc/js/src/errors/BadRequestError.spec.js.ejs +22 -0
  201. package/templates/mvc/js/src/errors/NotFoundError.js +11 -0
  202. package/templates/mvc/js/src/errors/NotFoundError.spec.js.ejs +22 -0
  203. package/templates/mvc/js/src/graphql/context.js.ejs +7 -0
  204. package/templates/mvc/js/src/graphql/context.spec.js.ejs +29 -0
  205. package/templates/mvc/js/src/graphql/index.js.ejs +5 -0
  206. package/templates/mvc/js/src/graphql/resolvers/index.js.ejs +6 -0
  207. package/templates/mvc/js/src/graphql/resolvers/user.resolvers.js.ejs +25 -0
  208. package/templates/mvc/js/src/graphql/resolvers/user.resolvers.spec.js.ejs +64 -0
  209. package/templates/mvc/js/src/graphql/typeDefs/index.js.ejs +6 -0
  210. package/templates/mvc/js/src/graphql/typeDefs/user.types.js.ejs +19 -0
  211. package/templates/mvc/js/src/index.js.ejs +141 -0
  212. package/templates/mvc/js/src/routes/api.js.ejs +15 -0
  213. package/templates/mvc/js/src/routes/api.spec.js.ejs +41 -0
  214. package/templates/mvc/js/src/utils/errorMessages.js +14 -0
  215. package/templates/mvc/js/src/utils/errorMiddleware.js +29 -0
  216. package/templates/mvc/js/src/utils/httpCodes.js +9 -0
  217. package/templates/mvc/js/src/utils/logger.js +40 -0
  218. package/templates/mvc/js/src/utils/logger.spec.js.ejs +63 -0
  219. package/templates/mvc/ts/src/config/env.ts.ejs +45 -0
  220. package/templates/mvc/ts/src/config/swagger.ts.ejs +6 -0
  221. package/templates/mvc/ts/src/controllers/userController.spec.ts.ejs +481 -0
  222. package/templates/mvc/ts/src/controllers/userController.ts.ejs +292 -0
  223. package/templates/mvc/ts/src/errors/ApiError.ts +15 -0
  224. package/templates/mvc/ts/src/errors/BadRequestError.spec.ts.ejs +22 -0
  225. package/templates/mvc/ts/src/errors/BadRequestError.ts +9 -0
  226. package/templates/mvc/ts/src/errors/NotFoundError.spec.ts.ejs +27 -0
  227. package/templates/mvc/ts/src/errors/NotFoundError.ts +9 -0
  228. package/templates/mvc/ts/src/graphql/context.spec.ts.ejs +30 -0
  229. package/templates/mvc/ts/src/graphql/context.ts.ejs +12 -0
  230. package/templates/mvc/ts/src/graphql/index.ts.ejs +3 -0
  231. package/templates/mvc/ts/src/graphql/resolvers/index.ts.ejs +4 -0
  232. package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.spec.ts.ejs +68 -0
  233. package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.ts.ejs +29 -0
  234. package/templates/mvc/ts/src/graphql/typeDefs/index.ts.ejs +4 -0
  235. package/templates/mvc/ts/src/graphql/typeDefs/user.types.ts.ejs +17 -0
  236. package/templates/mvc/ts/src/index.ts.ejs +157 -0
  237. package/templates/mvc/ts/src/routes/api.spec.ts.ejs +59 -0
  238. package/templates/mvc/ts/src/routes/api.ts.ejs +17 -0
  239. package/templates/mvc/ts/src/utils/errorMessages.ts +12 -0
  240. package/templates/mvc/ts/src/utils/errorMiddleware.ts.ejs +27 -0
  241. package/templates/mvc/ts/src/utils/httpCodes.ts +7 -0
  242. package/templates/mvc/ts/src/utils/logger.spec.ts.ejs +63 -0
  243. package/templates/mvc/ts/src/utils/logger.ts +36 -0
@@ -0,0 +1,481 @@
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
+ });
@@ -0,0 +1,14 @@
1
+ class ApiError extends Error {
2
+ constructor(statusCode, message, isOperational = true, stack = '') {
3
+ super(message);
4
+ this.statusCode = statusCode;
5
+ this.isOperational = isOperational;
6
+ if (stack) {
7
+ this.stack = stack;
8
+ } else {
9
+ Error.captureStackTrace(this, this.constructor);
10
+ }
11
+ }
12
+ }
13
+
14
+ module.exports = { ApiError };
@@ -0,0 +1,11 @@
1
+ const { ApiError } = require('./ApiError');
2
+ const HTTP_STATUS = require('../utils/httpCodes');
3
+ const ERROR_MESSAGES = require('../utils/errorMessages');
4
+
5
+ class BadRequestError extends ApiError {
6
+ constructor(message = ERROR_MESSAGES.BAD_REQUEST) {
7
+ super(HTTP_STATUS.BAD_REQUEST, message);
8
+ }
9
+ }
10
+
11
+ module.exports = { BadRequestError };
@@ -0,0 +1,22 @@
1
+ const { BadRequestError } = require('@/errors/BadRequestError');
2
+ const { ApiError } = require('@/errors/ApiError');
3
+ const HTTP_STATUS = require('@/utils/httpCodes');
4
+ const ERROR_MESSAGES = require('@/utils/errorMessages');
5
+
6
+ describe('BadRequestError', () => {
7
+ it('should extend ApiError', () => {
8
+ const error = new BadRequestError();
9
+ expect(error).toBeInstanceOf(ApiError);
10
+ });
11
+
12
+ it('should have default message "Bad Request"', () => {
13
+ const error = new BadRequestError();
14
+ expect(error.message).toBe(ERROR_MESSAGES.BAD_REQUEST);
15
+ expect(error.statusCode).toBe(HTTP_STATUS.BAD_REQUEST);
16
+ });
17
+
18
+ it('should accept a custom message', () => {
19
+ const error = new BadRequestError('Custom bad request');
20
+ expect(error.message).toBe('Custom bad request');
21
+ });
22
+ });
@@ -0,0 +1,11 @@
1
+ const { ApiError } = require('./ApiError');
2
+ const HTTP_STATUS = require('../utils/httpCodes');
3
+ const ERROR_MESSAGES = require('../utils/errorMessages');
4
+
5
+ class NotFoundError extends ApiError {
6
+ constructor(message = ERROR_MESSAGES.RESOURCE_NOT_FOUND) {
7
+ super(HTTP_STATUS.NOT_FOUND, message);
8
+ }
9
+ }
10
+
11
+ module.exports = { NotFoundError };
@@ -0,0 +1,22 @@
1
+ const { NotFoundError } = require('@/errors/NotFoundError');
2
+ const { ApiError } = require('@/errors/ApiError');
3
+ const HTTP_STATUS = require('@/utils/httpCodes');
4
+ const ERROR_MESSAGES = require('@/utils/errorMessages');
5
+
6
+ describe('NotFoundError', () => {
7
+ it('should extend ApiError', () => {
8
+ const error = new NotFoundError();
9
+ expect(error).toBeInstanceOf(ApiError);
10
+ });
11
+
12
+ it('should have default message "Resource not found"', () => {
13
+ const error = new NotFoundError();
14
+ expect(error.message).toBe(ERROR_MESSAGES.RESOURCE_NOT_FOUND);
15
+ expect(error.statusCode).toBe(HTTP_STATUS.NOT_FOUND);
16
+ });
17
+
18
+ it('should accept a custom message', () => {
19
+ const error = new NotFoundError(ERROR_MESSAGES.USER_NOT_FOUND);
20
+ expect(error.message).toBe(ERROR_MESSAGES.USER_NOT_FOUND);
21
+ });
22
+ });
@@ -0,0 +1,7 @@
1
+ const gqlContext = async ({ req }) => {
2
+ // Setup authorization or context here
3
+ const token = req.headers.authorization || '';
4
+ return { token };
5
+ };
6
+
7
+ module.exports = { gqlContext };
@@ -0,0 +1,29 @@
1
+ const { gqlContext } = require('@/graphql/context');
2
+ const { resolvers } = require('@/graphql/resolvers');
3
+ const { typeDefs } = require('@/graphql/typeDefs');
4
+
5
+ describe('GraphQL Context', () => {
6
+ it('should exercise GraphQL index entry points', () => {
7
+ expect(resolvers).toBeDefined();
8
+ expect(typeDefs).toBeDefined();
9
+ });
10
+ it('should return context with token when authorization header is present', async () => {
11
+ const mockRequest = {
12
+ headers: {
13
+ authorization: 'Bearer token123',
14
+ },
15
+ };
16
+
17
+ const context = await gqlContext({ req: mockRequest });
18
+ expect(context).toEqual({ token: 'Bearer token123' });
19
+ });
20
+
21
+ it('should return context with empty token when authorization header is missing', async () => {
22
+ const mockRequest = {
23
+ headers: {},
24
+ };
25
+
26
+ const context = await gqlContext({ req: mockRequest });
27
+ expect(context).toEqual({ token: '' });
28
+ });
29
+ });
@@ -0,0 +1,5 @@
1
+ const { typeDefs } = require('./typeDefs');
2
+ const { resolvers } = require('./resolvers');
3
+ const { gqlContext } = require('./context');
4
+
5
+ module.exports = { typeDefs, resolvers, gqlContext };
@@ -0,0 +1,6 @@
1
+ const { mergeResolvers } = require('@graphql-tools/merge');
2
+ const { userResolvers } = require('./user.resolvers');
3
+
4
+ const resolvers = mergeResolvers([userResolvers]);
5
+
6
+ module.exports = { resolvers };
@@ -0,0 +1,25 @@
1
+ const userController = require('../../controllers/userController');
2
+
3
+ const userResolvers = {
4
+ Query: {
5
+ getAllUsers: async () => {
6
+ return await userController.getUsers();
7
+ }
8
+ },
9
+ Mutation: {
10
+ createUser: async (_, { name, email }) => {
11
+ return await userController.createUser({ name, email });
12
+ },
13
+ updateUser: async (_, { id, name, email }) => {
14
+ return await userController.updateUser(id, { name, email });
15
+ },
16
+ deleteUser: async (_, { id }) => {
17
+ return await userController.deleteUser(id);
18
+ }
19
+ }<%_ if (database === 'MongoDB') { -%>,
20
+ User: {
21
+ id: (parent) => parent.id || parent._id
22
+ }<%_ } %>
23
+ };
24
+
25
+ module.exports = { userResolvers };