nodejs-quickstart-structure 2.0.1 → 2.1.1

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 (165) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +64 -66
  3. package/bin/index.js +5 -2
  4. package/lib/generator.js +10 -4
  5. package/lib/modules/app-setup.js +76 -6
  6. package/lib/modules/auth-setup.js +143 -0
  7. package/lib/modules/caching-setup.js +8 -1
  8. package/lib/modules/config-files.js +6 -0
  9. package/lib/modules/database-setup.js +2 -1
  10. package/lib/modules/project-setup.js +1 -0
  11. package/lib/prompts.js +39 -0
  12. package/package.json +5 -4
  13. package/templates/clean-architecture/js/src/domain/models/User.js +3 -1
  14. package/templates/clean-architecture/js/src/index.js.ejs +2 -0
  15. package/templates/clean-architecture/js/src/infrastructure/config/env.js.ejs +12 -3
  16. package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +25 -2
  17. package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs +38 -1
  18. package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +3 -0
  19. package/templates/clean-architecture/js/src/infrastructure/webserver/server.spec.js.ejs +51 -0
  20. package/templates/clean-architecture/js/src/infrastructure/webserver/swagger.spec.js.ejs +14 -0
  21. package/templates/clean-architecture/js/src/interfaces/controllers/userController.js.ejs +41 -4
  22. package/templates/clean-architecture/js/src/interfaces/controllers/userController.spec.js.ejs +70 -5
  23. package/templates/clean-architecture/js/src/interfaces/graphql/context.js.ejs +13 -6
  24. package/templates/clean-architecture/js/src/interfaces/graphql/context.spec.js.ejs +55 -22
  25. package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.js.ejs +10 -5
  26. package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.spec.js.ejs +32 -10
  27. package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/user.types.js.ejs +1 -1
  28. package/templates/clean-architecture/js/src/interfaces/routes/api.js.ejs +15 -0
  29. package/templates/clean-architecture/js/src/interfaces/routes/api.spec.js.ejs +4 -0
  30. package/templates/clean-architecture/js/src/usecases/CreateUser.js.ejs +34 -0
  31. package/templates/clean-architecture/js/src/usecases/CreateUser.spec.js.ejs +12 -3
  32. package/templates/clean-architecture/js/src/usecases/DeleteUser.js.ejs +27 -0
  33. package/templates/clean-architecture/js/src/usecases/DeleteUser.spec.js.ejs +9 -1
  34. package/templates/clean-architecture/js/src/usecases/GetAllUsers.js.ejs +36 -0
  35. package/templates/clean-architecture/js/src/usecases/GetAllUsers.spec.js.ejs +23 -1
  36. package/templates/clean-architecture/js/src/usecases/GetUserById.js.ejs +36 -0
  37. package/templates/clean-architecture/js/src/usecases/GetUserById.spec.js.ejs +48 -0
  38. package/templates/clean-architecture/js/src/usecases/UpdateUser.js.ejs +28 -0
  39. package/templates/clean-architecture/js/src/usecases/UpdateUser.spec.js.ejs +9 -1
  40. package/templates/clean-architecture/js/src/utils/errorMessages.js +1 -0
  41. package/templates/clean-architecture/js/src/utils/httpCodes.js +2 -0
  42. package/templates/clean-architecture/ts/src/config/env.ts.ejs +12 -3
  43. package/templates/clean-architecture/ts/src/domain/user.ts +3 -1
  44. package/templates/clean-architecture/ts/src/index.ts.ejs +4 -0
  45. package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +71 -10
  46. package/templates/clean-architecture/ts/src/infrastructure/repositories/userRepository.ts.ejs +32 -3
  47. package/templates/clean-architecture/ts/src/interfaces/controllers/userController.spec.ts.ejs +43 -9
  48. package/templates/clean-architecture/ts/src/interfaces/controllers/userController.ts.ejs +57 -15
  49. package/templates/clean-architecture/ts/src/interfaces/graphql/context.spec.ts.ejs +57 -24
  50. package/templates/clean-architecture/ts/src/interfaces/graphql/context.ts.ejs +14 -8
  51. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.spec.ts.ejs +33 -10
  52. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.ts.ejs +15 -5
  53. package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/user.types.ts.ejs +1 -1
  54. package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.spec.ts.ejs +9 -1
  55. package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts.ejs +16 -0
  56. package/templates/clean-architecture/ts/src/usecases/createUser.spec.ts.ejs +12 -3
  57. package/templates/clean-architecture/ts/src/usecases/createUser.ts.ejs +35 -0
  58. package/templates/clean-architecture/ts/src/usecases/deleteUser.spec.ts.ejs +10 -1
  59. package/templates/clean-architecture/ts/src/usecases/deleteUser.ts.ejs +24 -0
  60. package/templates/clean-architecture/ts/src/usecases/getAllUsers.spec.ts.ejs +9 -1
  61. package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts.ejs +21 -0
  62. package/templates/clean-architecture/ts/src/usecases/getUserById.spec.ts.ejs +55 -0
  63. package/templates/clean-architecture/ts/src/usecases/getUserById.ts.ejs +23 -0
  64. package/templates/clean-architecture/ts/src/usecases/updateUser.spec.ts.ejs +10 -1
  65. package/templates/clean-architecture/ts/src/usecases/updateUser.ts.ejs +25 -0
  66. package/templates/clean-architecture/ts/src/utils/errorMessages.ts +1 -0
  67. package/templates/clean-architecture/ts/src/utils/httpCodes.ts +1 -0
  68. package/templates/common/.cursorrules.ejs +9 -0
  69. package/templates/common/.env.example.ejs +17 -10
  70. package/templates/common/README.md.ejs +63 -18
  71. package/templates/common/auth/js/controllers/authController.js.ejs +170 -0
  72. package/templates/common/auth/js/controllers/authController.spec.js.ejs +148 -0
  73. package/templates/common/auth/js/middleware/authMiddleware.js.ejs +58 -0
  74. package/templates/common/auth/js/middleware/authMiddleware.spec.js.ejs +108 -0
  75. package/templates/common/auth/js/routes/authRoutes.js.ejs +16 -0
  76. package/templates/common/auth/js/services/jwtService.js.ejs +54 -0
  77. package/templates/common/auth/js/services/jwtService.spec.js.ejs +84 -0
  78. package/templates/common/auth/ts/controllers/authController.spec.ts.ejs +161 -0
  79. package/templates/common/auth/ts/controllers/authController.ts.ejs +167 -0
  80. package/templates/common/auth/ts/middleware/authMiddleware.spec.ts.ejs +128 -0
  81. package/templates/common/auth/ts/middleware/authMiddleware.ts.ejs +59 -0
  82. package/templates/common/auth/ts/routes/authRoutes.ts.ejs +20 -0
  83. package/templates/common/auth/ts/services/jwtService.spec.ts.ejs +89 -0
  84. package/templates/common/auth/ts/services/jwtService.ts.ejs +60 -0
  85. package/templates/common/babel.config.js.ejs +5 -0
  86. package/templates/common/caching/clean/js/CreateUser.js.ejs +14 -5
  87. package/templates/common/caching/clean/js/DeleteUser.js.ejs +2 -1
  88. package/templates/common/caching/clean/js/GetUserById.js.ejs +39 -0
  89. package/templates/common/caching/clean/js/UpdateUser.js.ejs +2 -1
  90. package/templates/common/caching/clean/ts/createUser.ts.ejs +14 -6
  91. package/templates/common/caching/clean/ts/deleteUser.ts.ejs +2 -1
  92. package/templates/common/caching/clean/ts/getUserById.ts.ejs +32 -0
  93. package/templates/common/caching/clean/ts/updateUser.ts.ejs +2 -1
  94. package/templates/common/caching/js/memoryCache.spec.js.ejs +2 -0
  95. package/templates/common/caching/js/redisClient.spec.js.ejs +2 -0
  96. package/templates/common/caching/ts/memoryCache.spec.ts.ejs +2 -0
  97. package/templates/common/caching/ts/redisClient.spec.ts.ejs +2 -0
  98. package/templates/common/database/js/models/User.js.ejs +14 -1
  99. package/templates/common/database/js/models/User.js.mongoose.ejs +7 -0
  100. package/templates/common/database/js/models/User.spec.js.ejs +12 -0
  101. package/templates/common/database/js/mongoose.spec.js.ejs +2 -0
  102. package/templates/common/database/ts/models/User.spec.ts.ejs +10 -0
  103. package/templates/common/database/ts/models/User.ts.ejs +17 -0
  104. package/templates/common/database/ts/models/User.ts.mongoose.ejs +8 -0
  105. package/templates/common/database/ts/mongoose.spec.ts.ejs +2 -0
  106. package/templates/common/docker-compose.yml.ejs +12 -0
  107. package/templates/common/ecosystem.config.js.ejs +9 -3
  108. package/templates/common/eslint.config.mjs.ejs +3 -0
  109. package/templates/common/jest.config.js.ejs +13 -9
  110. package/templates/common/kafka/js/messaging/baseConsumer.js.ejs +1 -1
  111. package/templates/common/kafka/js/services/kafkaService.js.ejs +1 -1
  112. package/templates/common/migrations/init.js.ejs +5 -4
  113. package/templates/common/package.json.ejs +11 -2
  114. package/templates/common/prompts/project-context.md.ejs +8 -1
  115. package/templates/common/src/tests/e2e/e2e.users.test.js.ejs +149 -107
  116. package/templates/common/src/tests/e2e/e2e.users.test.ts.ejs +88 -47
  117. package/templates/common/swagger.yml.ejs +148 -0
  118. package/templates/common/tsconfig.eslint.json +15 -0
  119. package/templates/common/tsconfig.json +3 -1
  120. package/templates/common/views/ejs/index.ejs +264 -30
  121. package/templates/common/views/ejs/login.ejs.ejs +244 -0
  122. package/templates/common/views/ejs/signup.ejs.ejs +282 -0
  123. package/templates/common/views/pug/index.pug +269 -38
  124. package/templates/common/views/pug/login.pug.ejs +195 -0
  125. package/templates/common/views/pug/signup.pug.ejs +241 -0
  126. package/templates/db/mysql/V1__Initial_Setup.sql.ejs +6 -0
  127. package/templates/db/postgres/V1__Initial_Setup.sql.ejs +6 -0
  128. package/templates/mvc/js/src/config/env.js.ejs +12 -3
  129. package/templates/mvc/js/src/controllers/userController.js.ejs +29 -5
  130. package/templates/mvc/js/src/controllers/userController.spec.js.ejs +27 -12
  131. package/templates/mvc/js/src/graphql/context.js.ejs +14 -3
  132. package/templates/mvc/js/src/graphql/context.spec.js.ejs +36 -21
  133. package/templates/mvc/js/src/graphql/resolvers/user.resolvers.js.ejs +10 -5
  134. package/templates/mvc/js/src/graphql/resolvers/user.resolvers.spec.js.ejs +32 -10
  135. package/templates/mvc/js/src/graphql/typeDefs/user.types.js.ejs +1 -1
  136. package/templates/mvc/js/src/index.js.ejs +16 -3
  137. package/templates/mvc/js/src/routes/api.js.ejs +14 -0
  138. package/templates/mvc/js/src/routes/api.spec.js.ejs +3 -0
  139. package/templates/mvc/js/src/utils/errorMessages.js +1 -0
  140. package/templates/mvc/js/src/utils/httpCodes.js +1 -0
  141. package/templates/mvc/ts/src/config/env.ts.ejs +12 -3
  142. package/templates/mvc/ts/src/controllers/userController.spec.ts.ejs +95 -7
  143. package/templates/mvc/ts/src/controllers/userController.ts.ejs +68 -11
  144. package/templates/mvc/ts/src/graphql/context.spec.ts.ejs +36 -23
  145. package/templates/mvc/ts/src/graphql/context.ts.ejs +15 -6
  146. package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.spec.ts.ejs +32 -10
  147. package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.ts.ejs +15 -5
  148. package/templates/mvc/ts/src/graphql/typeDefs/user.types.ts.ejs +1 -1
  149. package/templates/mvc/ts/src/index.ts.ejs +15 -3
  150. package/templates/mvc/ts/src/routes/api.spec.ts.ejs +6 -0
  151. package/templates/mvc/ts/src/routes/api.ts.ejs +15 -0
  152. package/templates/mvc/ts/src/utils/errorMessages.ts +1 -0
  153. package/templates/mvc/ts/src/utils/httpCodes.ts +1 -0
  154. package/templates/clean-architecture/js/src/interfaces/routes/api.js +0 -12
  155. package/templates/clean-architecture/js/src/usecases/CreateUser.js +0 -14
  156. package/templates/clean-architecture/js/src/usecases/DeleteUser.js +0 -11
  157. package/templates/clean-architecture/js/src/usecases/GetAllUsers.js +0 -12
  158. package/templates/clean-architecture/js/src/usecases/UpdateUser.js +0 -11
  159. package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts +0 -13
  160. package/templates/clean-architecture/ts/src/usecases/createUser.ts +0 -13
  161. package/templates/clean-architecture/ts/src/usecases/deleteUser.ts +0 -9
  162. package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts +0 -10
  163. package/templates/clean-architecture/ts/src/usecases/updateUser.ts +0 -9
  164. package/templates/mvc/js/src/routes/api.js +0 -10
  165. package/templates/mvc/ts/src/routes/api.ts +0 -12
@@ -29,7 +29,8 @@
29
29
  "braces": "^3.0.3",
30
30
  "picomatch": "^4.0.4",
31
31
  "lodash": "^4.17.23",
32
- "debounce": "^1.2.1"
32
+ "debounce": "^1.2.1",
33
+ "uuid": "^14.0.0"
33
34
  },
34
35
  "dependencies": {
35
36
  "express": "^4.18.2",
@@ -53,6 +54,9 @@
53
54
  <% if (viewEngine === 'EJS') { %> "ejs": "^3.1.10",
54
55
  <% } -%>
55
56
  <% if (viewEngine === 'Pug') { %> "pug": "^3.0.2",
57
+ <% } -%>
58
+ <% if (auth.includes('JWT')) { %> "jsonwebtoken": "^9.0.2",
59
+ "bcryptjs": "^2.4.3",
56
60
  <% } -%>
57
61
  "cors": "^2.8.5",
58
62
  "helmet": "^7.1.0",
@@ -85,7 +89,9 @@
85
89
  <% } -%>
86
90
  "@types/morgan": "^1.9.9",
87
91
  "rimraf": "^6.0.1"<% if ((viewEngine && viewEngine !== 'None') || communication === 'REST APIs' || communication === 'Kafka') { %>,
88
- "cpx2": "^8.0.0"<% } %><% } %>,
92
+ "cpx2": "^8.0.0"<% } %><% if (auth.includes('JWT')) { %>,
93
+ "@types/jsonwebtoken": "^9.0.6",
94
+ "@types/bcryptjs": "^2.4.6"<% } %><% } %>,
89
95
  "eslint": "^10.1.0",
90
96
  "@eslint/js": "^9.20.0",
91
97
  "globals": "^15.14.0",
@@ -108,6 +114,9 @@
108
114
  "tsc-alias": "^1.8.10",
109
115
  "@types/supertest": "^6.0.2"<% } else { %>,
110
116
  "jest": "^29.7.0",
117
+ "babel-jest": "^29.7.0",
118
+ "@babel/core": "^7.24.0",
119
+ "@babel/preset-env": "^7.24.0",
111
120
  "wait-on": "^7.2.0",
112
121
  "supertest": "^7.1.3"<% } %>
113
122
  },
@@ -16,6 +16,9 @@ You are an expert working on **<%= projectName %>**.
16
16
  <% if (caching !== 'None') { -%>
17
17
  - **Caching**: <%= caching %>
18
18
  <% } -%>
19
+ <% if (auth.includes('JWT')) { -%>
20
+ - **Authentication**: JWT (Access & Refresh Tokens)
21
+ <% } -%>
19
22
 
20
23
  ## High-Level Architecture
21
24
  <% if (architecture === 'Clean Architecture') { -%>
@@ -34,7 +37,11 @@ We use the MVC (Model-View-Controller) pattern.
34
37
  ## Core Standards
35
38
  1. **Testing**: We enforce > 80% coverage. Tests use Jest and the AAA (Arrange, Act, Assert) pattern.
36
39
  2. **Error Handling**: We use centralized custom errors (e.g., `ApiError`) and global error middleware. Status codes come from standard constants, not hardcoded numbers.
37
- 3. **Paths & Naming**:
40
+ 3. **Security**:
41
+ <% if (auth.includes('JWT')) { %>- Use `authMiddleware` for protected routes.<% } %>
42
+ - Validate and sanitize all inputs to prevent injection and XSS.
43
+ - Never expose sensitive data (passwords, inner keys) in API responses.
44
+ 4. **Paths & Naming**:
38
45
  <% if (language === 'TypeScript') { -%>
39
46
  - We use `@/` path aliases for internal imports.
40
47
  <% } -%>
@@ -3,118 +3,160 @@ const request = require('supertest');
3
3
  const SERVER_URL = process.env.TEST_URL || `http://127.0.0.1:${process.env.PORT || 3001}`;
4
4
 
5
5
  describe('E2E User Tests', () => {
6
- // Global setup and teardown hooks can be added here
7
- // typically for database seeding or external authentication checks prior to E2E.
8
- let userId;
9
- const uniqueEmail = `test_${Date.now()}@example.com`;
6
+ let userId;
7
+ <%_ if (auth.includes('JWT')) { _%>
8
+ let authToken;
9
+ <%_ } _%>
10
+ const uniqueEmail = `test_${Date.now()}@example.com`;
11
+ <%_ if (auth.includes('JWT')) { _%>
12
+ const testPassword = 'password123';
13
+ <%_ } _%>
14
+
15
+ <%_ if (auth.includes('JWT')) { _%>
16
+ it('should fail to fetch users without token (Protected)', async () => {
17
+ <%_ if (communication === 'GraphQL') { _%>
18
+ const query = `{ getAllUsers { id name } }`;
19
+ const response = await request(SERVER_URL).post('/graphql').send({ query });
20
+ // In GraphQL errors are usually in the body with 200/400 status
21
+ if (response.statusCode === 200 && response.body.errors) {
22
+ expect(response.body.errors[0].message.toLowerCase()).toContain('unauthorized');
23
+ } else {
24
+ expect([401, 400]).toContain(response.statusCode);
25
+ }
26
+ <%_ } else { _%>
27
+ const response = await request(SERVER_URL).get('/api/users');
28
+ expect(response.statusCode).toBe(401);
29
+ <%_ } _%>
30
+ });
31
+ <%_ } _%>
10
32
 
11
33
  <%_ if (communication === 'GraphQL') { -%>
12
- it('should create a user via GraphQL', async () => {
13
- const query = `
14
- mutation {
15
- createUser(name: "Test User", email: "${uniqueEmail}") {
16
- id
17
- name
18
- email
19
- }
20
- }
21
- `;
22
- const response = await request(SERVER_URL)
23
- .post('/graphql')
24
- .send({ query });
25
-
26
- expect(response.statusCode).toBe(200);
27
- userId = response.body.data.createUser.id;
28
- expect(userId).toBeDefined();
29
- });
30
-
31
- it('should update a user via GraphQL', async () => {
32
- const query = `
33
- mutation {
34
- updateUser(id: "${userId}", name: "Updated User") {
35
- id
36
- name
37
- }
38
- }
39
- `;
40
- const response = await request(SERVER_URL)
41
- .post('/graphql')
42
- .send({ query });
43
-
44
- expect(response.statusCode).toBe(200);
45
- expect(response.body.data.updateUser.name).toBe("Updated User");
46
- });
47
-
48
- it('should delete a user via GraphQL', async () => {
49
- const query = `
50
- mutation {
51
- deleteUser(id: "${userId}")
52
- }
53
- `;
54
- const response = await request(SERVER_URL)
55
- .post('/graphql')
56
- .send({ query });
57
-
58
- expect(response.statusCode).toBe(200);
59
- expect(response.body.data.deleteUser).toBe(true);
60
- });
61
- <%_ } else if (communication === 'Kafka') { -%>
62
- it('should trigger Kafka event for user creation', async () => {
63
- const response = await request(SERVER_URL)
64
- .post('/api/users')
65
- .send({ name: 'Test User', email: uniqueEmail });
66
-
67
- expect([201, 202]).toContain(response.statusCode);
68
- userId = response.body.id || response.body._id;
69
- expect(userId).toBeDefined();
70
-
71
- // Wait for Kafka to process...
72
- await new Promise(resolve => setTimeout(resolve, 500));
73
- });
74
-
75
- it('should trigger Kafka event for user update', async () => {
76
- const response = await request(SERVER_URL)
77
- .patch(`/api/users/${userId}`)
78
- .send({ name: 'Updated User' });
79
-
80
- expect([200, 202, 204]).toContain(response.statusCode);
81
-
82
- // Wait for Kafka to process...
83
- await new Promise(resolve => setTimeout(resolve, 500));
84
- });
85
-
86
- it('should trigger Kafka event for user deletion', async () => {
87
- const response = await request(SERVER_URL)
88
- .delete(`/api/users/${userId}`);
89
-
90
- expect([200, 202, 204]).toContain(response.statusCode);
91
-
92
- // Wait for Kafka to process...
93
- await new Promise(resolve => setTimeout(resolve, 500));
94
- });
34
+ it('should create a user via GraphQL', async () => {
35
+ const query = `
36
+ mutation {
37
+ createUser(name: "Test User", email: "${uniqueEmail}"<%_ if (auth.includes('JWT')) { _%>, password: "${testPassword}"<%_ } _%>) {
38
+ id
39
+ name
40
+ email
41
+ }
42
+ }
43
+ `;
44
+ const response = await request(SERVER_URL)
45
+ .post('/graphql')
46
+ .send({ query });
47
+
48
+ expect(response.statusCode).toBe(200);
49
+ expect(response.body.errors).toBeUndefined();
50
+ userId = response.body.data.createUser.id;
51
+ expect(userId).toBeDefined();
52
+ });
95
53
  <%_ } else { -%>
96
- it('should create a user successfully via REST', async () => {
97
- const response = await request(SERVER_URL)
98
- .post('/api/users')
99
- .send({ name: 'Test User', email: uniqueEmail });
100
-
101
- expect(response.statusCode).toBe(201);
102
- userId = response.body.id || response.body._id;
103
- });
104
-
105
- it('should update a user successfully via REST', async () => {
106
- const response = await request(SERVER_URL)
107
- .patch(`/api/users/${userId}`)
108
- .send({ name: 'Updated User' });
54
+ it('should create a user successfully (Signup)', async () => {
55
+ const response = await request(SERVER_URL)
56
+ .post('/api/users')
57
+ .send({ name: 'Test User', email: uniqueEmail<%_ if (auth.includes('JWT')) { _%>, password: testPassword <%_ } _%>});
58
+
59
+ expect([201, 202]).toContain(response.statusCode);
60
+ userId = response.body.id || response.body._id;
61
+ });
62
+ <%_ } -%>
109
63
 
110
- expect(response.statusCode).toBe(200);
111
- });
64
+ <%_ if (auth.includes('JWT')) { _%>
65
+ it('should login and obtain a JWT token', async () => {
66
+ const response = await request(SERVER_URL)
67
+ .post('/api/auth/login')
68
+ .send({ email: uniqueEmail, password: testPassword });
112
69
 
113
- it('should delete a user successfully via REST', async () => {
114
- const response = await request(SERVER_URL)
115
- .delete(`/api/users/${userId}`);
70
+ expect(response.statusCode).toBe(200);
71
+ expect(response.body.accessToken || response.body.token).toBeDefined();
72
+ authToken = response.body.accessToken || response.body.token;
73
+ });
74
+ <%_ } _%>
116
75
 
117
- expect(response.statusCode).toBe(200);
118
- });
76
+ <%_ if (communication === 'GraphQL') { -%>
77
+ it('should fetch all users via GraphQL', async () => {
78
+ const query = `{ getAllUsers { id name email } }`;
79
+ const response = await request(SERVER_URL)
80
+ .post('/graphql')
81
+ <%_ if (auth.includes('JWT')) { _%>
82
+ .set('Authorization', `Bearer ${authToken}`)
83
+ <%_ } _%>
84
+ .send({ query });
85
+
86
+ expect(response.statusCode).toBe(200);
87
+ expect(Array.isArray(response.body.data.getAllUsers)).toBe(true);
88
+ const user = response.body.data.getAllUsers.find(u => u.id === userId);
89
+ expect(user).toBeDefined();
90
+ });
91
+
92
+ it('should update a user via GraphQL', async () => {
93
+ const query = `
94
+ mutation {
95
+ updateUser(id: "${userId}", name: "Updated User") {
96
+ id
97
+ name
98
+ }
99
+ }
100
+ `;
101
+ const response = await request(SERVER_URL)
102
+ .post('/graphql')
103
+ <%_ if (auth.includes('JWT')) { _%>
104
+ .set('Authorization', `Bearer ${authToken}`)
105
+ <%_ } _%>
106
+ .set('Content-Type', 'application/json')
107
+ .send({ query });
108
+
109
+ expect(response.statusCode).toBe(200);
110
+ expect(response.body.data.updateUser.name).toBe("Updated User");
111
+ });
112
+
113
+ it('should delete a user via GraphQL', async () => {
114
+ const query = `
115
+ mutation {
116
+ deleteUser(id: "${userId}")
117
+ }
118
+ `;
119
+ const response = await request(SERVER_URL)
120
+ .post('/graphql')
121
+ <%_ if (auth.includes('JWT')) { _%>
122
+ .set('Authorization', `Bearer ${authToken}`)
123
+ <%_ } _%>
124
+ .set('Content-Type', 'application/json')
125
+ .send({ query });
126
+
127
+ expect(response.statusCode).toBe(200);
128
+ expect(response.body.data.deleteUser).toBe(true);
129
+ });
130
+ <%_ } else { -%>
131
+ it('should fetch users successfully', async () => {
132
+ const response = await request(SERVER_URL)
133
+ .get('/api/users')
134
+ <%_ if (auth.includes('JWT')) { _%>
135
+ .set('Authorization', `Bearer ${authToken}`);
136
+ <%_ } _%>
137
+ expect(response.statusCode).toBe(200);
138
+ expect(Array.isArray(response.body)).toBe(true);
139
+ });
140
+
141
+ it('should update a user successfully', async () => {
142
+ const response = await request(SERVER_URL)
143
+ .patch(`/api/users/${userId}`)
144
+ <%_ if (auth.includes('JWT')) { _%>
145
+ .set('Authorization', `Bearer ${authToken}`)
146
+ <%_ } _%>
147
+ .send({ name: 'Updated User' });
148
+
149
+ expect([200, 202, 204]).toContain(response.statusCode);
150
+ });
151
+
152
+ it('should delete a user successfully', async () => {
153
+ const response = await request(SERVER_URL)
154
+ .delete(`/api/users/${userId}`)
155
+ <%_ if (auth.includes('JWT')) { _%>
156
+ .set('Authorization', `Bearer ${authToken}`)
157
+ <%_ } _%>;
158
+
159
+ expect([200, 202, 204]).toContain(response.statusCode);
160
+ });
119
161
  <%_ } -%>
120
162
  });
@@ -3,16 +3,37 @@ import request from 'supertest';
3
3
  const SERVER_URL = process.env.TEST_URL || `http://127.0.0.1:${process.env.PORT || 3001}`;
4
4
 
5
5
  describe('E2E User Tests', () => {
6
- // Global setup and teardown hooks can be added here
7
- // typically for database seeding or external authentication checks prior to E2E.
8
6
  let userId: string;
7
+ <%_ if (auth.includes('JWT')) { _%>
8
+ let authToken: string;
9
+ <%_ } _%>
9
10
  const uniqueEmail = `test_${Date.now()}@example.com`;
11
+ <%_ if (auth.includes('JWT')) { _%>
12
+ const testPassword = 'password123';
13
+ <%_ } _%>
14
+
15
+ <%_ if (auth.includes('JWT')) { _%>
16
+ it('should fail to fetch users without token (Protected)', async () => {
17
+ <%_ if (communication === 'GraphQL') { _%>
18
+ const query = `{ getAllUsers { id name } }`;
19
+ const response = await request(SERVER_URL).post('/graphql').send({ query });
20
+ if (response.statusCode === 200 && response.body.errors) {
21
+ expect(response.body.errors[0].message.toLowerCase()).toContain('unauthorized');
22
+ } else {
23
+ expect([401, 400]).toContain(response.statusCode);
24
+ }
25
+ <%_ } else { _%>
26
+ const response = await request(SERVER_URL).get('/api/users');
27
+ expect(response.statusCode).toBe(401);
28
+ <%_ } _%>
29
+ });
30
+ <%_ } _%>
10
31
 
11
32
  <%_ if (communication === 'GraphQL') { -%>
12
33
  it('should create a user via GraphQL', async () => {
13
34
  const query = `
14
35
  mutation {
15
- createUser(name: "Test User", email: "${uniqueEmail}") {
36
+ createUser(name: "Test User", email: "${uniqueEmail}"<%_ if (auth.includes('JWT')) { _%>, password: "${testPassword}"<%_ } _%>) {
16
37
  id
17
38
  name
18
39
  email
@@ -24,9 +45,48 @@ describe('E2E User Tests', () => {
24
45
  .send({ query });
25
46
 
26
47
  expect(response.statusCode).toBe(200);
48
+ expect(response.body.errors).toBeUndefined();
27
49
  userId = response.body.data.createUser.id;
28
50
  expect(userId).toBeDefined();
29
51
  });
52
+ <%_ } else { -%>
53
+ it('should create a user successfully (Signup)', async () => {
54
+ const response = await request(SERVER_URL)
55
+ .post('/api/users')
56
+ .send({ name: 'Test User', email: uniqueEmail<%_ if (auth.includes('JWT')) { _%>, password: testPassword <%_ } _%>});
57
+
58
+ expect([201, 202]).toContain(response.statusCode);
59
+ userId = response.body.id || response.body._id;
60
+ });
61
+ <%_ } -%>
62
+
63
+ <%_ if (auth.includes('JWT')) { _%>
64
+ it('should login and obtain a JWT token', async () => {
65
+ const response = await request(SERVER_URL)
66
+ .post('/api/auth/login')
67
+ .send({ email: uniqueEmail, password: testPassword });
68
+
69
+ expect(response.statusCode).toBe(200);
70
+ expect(response.body.accessToken || response.body.token).toBeDefined();
71
+ authToken = response.body.accessToken || response.body.token;
72
+ });
73
+ <%_ } _%>
74
+
75
+ <%_ if (communication === 'GraphQL') { -%>
76
+ it('should fetch all users via GraphQL', async () => {
77
+ const query = `{ getAllUsers { id name email } }`;
78
+ const response = await request(SERVER_URL)
79
+ .post('/graphql')
80
+ <%_ if (auth.includes('JWT')) { _%>
81
+ .set('Authorization', `Bearer ${authToken}`)
82
+ <%_ } _%>
83
+ .send({ query });
84
+
85
+ expect(response.statusCode).toBe(200);
86
+ expect(Array.isArray(response.body.data.getAllUsers)).toBe(true);
87
+ const user = response.body.data.getAllUsers.find((u: any) => u.id === userId);
88
+ expect(user).toBeDefined();
89
+ });
30
90
 
31
91
  it('should update a user via GraphQL', async () => {
32
92
  const query = `
@@ -39,6 +99,10 @@ describe('E2E User Tests', () => {
39
99
  `;
40
100
  const response = await request(SERVER_URL)
41
101
  .post('/graphql')
102
+ <%_ if (auth.includes('JWT')) { _%>
103
+ .set('Authorization', `Bearer ${authToken}`)
104
+ <%_ } _%>
105
+ .set('Content-Type', 'application/json')
42
106
  .send({ query });
43
107
 
44
108
  expect(response.statusCode).toBe(200);
@@ -53,68 +117,45 @@ describe('E2E User Tests', () => {
53
117
  `;
54
118
  const response = await request(SERVER_URL)
55
119
  .post('/graphql')
120
+ <%_ if (auth.includes('JWT')) { _%>
121
+ .set('Authorization', `Bearer ${authToken}`)
122
+ <%_ } _%>
123
+ .set('Content-Type', 'application/json')
56
124
  .send({ query });
57
125
 
58
126
  expect(response.statusCode).toBe(200);
59
127
  expect(response.body.data.deleteUser).toBe(true);
60
128
  });
61
- <%_ } else if (communication === 'Kafka') { -%>
62
- it('should trigger Kafka event for user creation', async () => {
129
+ <%_ } else { -%>
130
+ it('should fetch users successfully', async () => {
63
131
  const response = await request(SERVER_URL)
64
- .post('/api/users')
65
- .send({ name: 'Test User', email: uniqueEmail });
66
-
67
- expect([201, 202]).toContain(response.statusCode);
68
- userId = response.body.id || response.body._id;
69
- expect(userId).toBeDefined();
70
-
71
- // Wait for Kafka to process...
72
- await new Promise(resolve => setTimeout(resolve, 500));
132
+ .get('/api/users')
133
+ <%_ if (auth.includes('JWT')) { _%>
134
+ .set('Authorization', `Bearer ${authToken}`)
135
+ <%_ } _%>;
136
+ expect(response.statusCode).toBe(200);
137
+ expect(Array.isArray(response.body)).toBe(true);
73
138
  });
74
139
 
75
- it('should trigger Kafka event for user update', async () => {
140
+ it('should update a user successfully', async () => {
76
141
  const response = await request(SERVER_URL)
77
142
  .patch(`/api/users/${userId}`)
143
+ <%_ if (auth.includes('JWT')) { _%>
144
+ .set('Authorization', `Bearer ${authToken}`)
145
+ <%_ } _%>
78
146
  .send({ name: 'Updated User' });
79
147
 
80
148
  expect([200, 202, 204]).toContain(response.statusCode);
81
-
82
- // Wait for Kafka to process...
83
- await new Promise(resolve => setTimeout(resolve, 500));
84
149
  });
85
150
 
86
- it('should trigger Kafka event for user deletion', async () => {
151
+ it('should delete a user successfully', async () => {
87
152
  const response = await request(SERVER_URL)
88
- .delete(`/api/users/${userId}`);
153
+ .delete(`/api/users/${userId}`)
154
+ <%_ if (auth.includes('JWT')) { _%>
155
+ .set('Authorization', `Bearer ${authToken}`)
156
+ <%_ } _%>;
89
157
 
90
158
  expect([200, 202, 204]).toContain(response.statusCode);
91
-
92
- // Wait for Kafka to process...
93
- await new Promise(resolve => setTimeout(resolve, 500));
94
- });
95
- <%_ } else { -%>
96
- it('should create a user successfully via REST', async () => {
97
- const response = await request(SERVER_URL)
98
- .post('/api/users')
99
- .send({ name: 'Test User', email: uniqueEmail });
100
-
101
- expect(response.statusCode).toBe(201);
102
- userId = response.body.id || response.body._id;
103
- });
104
-
105
- it('should update a user successfully via REST', async () => {
106
- const response = await request(SERVER_URL)
107
- .patch(`/api/users/${userId}`)
108
- .send({ name: 'Updated User' });
109
-
110
- expect(response.statusCode).toBe(200);
111
- });
112
-
113
- it('should delete a user successfully via REST', async () => {
114
- const response = await request(SERVER_URL)
115
- .delete(`/api/users/${userId}`);
116
-
117
- expect(response.statusCode).toBe(200);
118
159
  });
119
160
  <%_ } -%>
120
161
  });