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.
- package/CHANGELOG.md +25 -0
- package/README.md +64 -66
- package/bin/index.js +5 -2
- package/lib/generator.js +10 -4
- package/lib/modules/app-setup.js +76 -6
- package/lib/modules/auth-setup.js +143 -0
- package/lib/modules/caching-setup.js +8 -1
- package/lib/modules/config-files.js +6 -0
- package/lib/modules/database-setup.js +2 -1
- package/lib/modules/project-setup.js +1 -0
- package/lib/prompts.js +39 -0
- package/package.json +5 -4
- package/templates/clean-architecture/js/src/domain/models/User.js +3 -1
- package/templates/clean-architecture/js/src/index.js.ejs +2 -0
- package/templates/clean-architecture/js/src/infrastructure/config/env.js.ejs +12 -3
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +25 -2
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs +38 -1
- package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +3 -0
- package/templates/clean-architecture/js/src/infrastructure/webserver/server.spec.js.ejs +51 -0
- package/templates/clean-architecture/js/src/infrastructure/webserver/swagger.spec.js.ejs +14 -0
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.js.ejs +41 -4
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.spec.js.ejs +70 -5
- package/templates/clean-architecture/js/src/interfaces/graphql/context.js.ejs +13 -6
- package/templates/clean-architecture/js/src/interfaces/graphql/context.spec.js.ejs +55 -22
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.js.ejs +10 -5
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.spec.js.ejs +32 -10
- package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/user.types.js.ejs +1 -1
- package/templates/clean-architecture/js/src/interfaces/routes/api.js.ejs +15 -0
- package/templates/clean-architecture/js/src/interfaces/routes/api.spec.js.ejs +4 -0
- package/templates/clean-architecture/js/src/usecases/CreateUser.js.ejs +34 -0
- package/templates/clean-architecture/js/src/usecases/CreateUser.spec.js.ejs +12 -3
- package/templates/clean-architecture/js/src/usecases/DeleteUser.js.ejs +27 -0
- package/templates/clean-architecture/js/src/usecases/DeleteUser.spec.js.ejs +9 -1
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.js.ejs +36 -0
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.spec.js.ejs +23 -1
- package/templates/clean-architecture/js/src/usecases/GetUserById.js.ejs +36 -0
- package/templates/clean-architecture/js/src/usecases/GetUserById.spec.js.ejs +48 -0
- package/templates/clean-architecture/js/src/usecases/UpdateUser.js.ejs +28 -0
- package/templates/clean-architecture/js/src/usecases/UpdateUser.spec.js.ejs +9 -1
- package/templates/clean-architecture/js/src/utils/errorMessages.js +1 -0
- package/templates/clean-architecture/js/src/utils/httpCodes.js +2 -0
- package/templates/clean-architecture/ts/src/config/env.ts.ejs +12 -3
- package/templates/clean-architecture/ts/src/domain/user.ts +3 -1
- package/templates/clean-architecture/ts/src/index.ts.ejs +4 -0
- package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +71 -10
- package/templates/clean-architecture/ts/src/infrastructure/repositories/userRepository.ts.ejs +32 -3
- package/templates/clean-architecture/ts/src/interfaces/controllers/userController.spec.ts.ejs +43 -9
- package/templates/clean-architecture/ts/src/interfaces/controllers/userController.ts.ejs +57 -15
- package/templates/clean-architecture/ts/src/interfaces/graphql/context.spec.ts.ejs +57 -24
- package/templates/clean-architecture/ts/src/interfaces/graphql/context.ts.ejs +14 -8
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.spec.ts.ejs +33 -10
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.ts.ejs +15 -5
- package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/user.types.ts.ejs +1 -1
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.spec.ts.ejs +9 -1
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts.ejs +16 -0
- package/templates/clean-architecture/ts/src/usecases/createUser.spec.ts.ejs +12 -3
- package/templates/clean-architecture/ts/src/usecases/createUser.ts.ejs +35 -0
- package/templates/clean-architecture/ts/src/usecases/deleteUser.spec.ts.ejs +10 -1
- package/templates/clean-architecture/ts/src/usecases/deleteUser.ts.ejs +24 -0
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.spec.ts.ejs +9 -1
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts.ejs +21 -0
- package/templates/clean-architecture/ts/src/usecases/getUserById.spec.ts.ejs +55 -0
- package/templates/clean-architecture/ts/src/usecases/getUserById.ts.ejs +23 -0
- package/templates/clean-architecture/ts/src/usecases/updateUser.spec.ts.ejs +10 -1
- package/templates/clean-architecture/ts/src/usecases/updateUser.ts.ejs +25 -0
- package/templates/clean-architecture/ts/src/utils/errorMessages.ts +1 -0
- package/templates/clean-architecture/ts/src/utils/httpCodes.ts +1 -0
- package/templates/common/.cursorrules.ejs +9 -0
- package/templates/common/.env.example.ejs +17 -10
- package/templates/common/README.md.ejs +63 -18
- package/templates/common/auth/js/controllers/authController.js.ejs +170 -0
- package/templates/common/auth/js/controllers/authController.spec.js.ejs +148 -0
- package/templates/common/auth/js/middleware/authMiddleware.js.ejs +58 -0
- package/templates/common/auth/js/middleware/authMiddleware.spec.js.ejs +108 -0
- package/templates/common/auth/js/routes/authRoutes.js.ejs +16 -0
- package/templates/common/auth/js/services/jwtService.js.ejs +54 -0
- package/templates/common/auth/js/services/jwtService.spec.js.ejs +84 -0
- package/templates/common/auth/ts/controllers/authController.spec.ts.ejs +161 -0
- package/templates/common/auth/ts/controllers/authController.ts.ejs +167 -0
- package/templates/common/auth/ts/middleware/authMiddleware.spec.ts.ejs +128 -0
- package/templates/common/auth/ts/middleware/authMiddleware.ts.ejs +59 -0
- package/templates/common/auth/ts/routes/authRoutes.ts.ejs +20 -0
- package/templates/common/auth/ts/services/jwtService.spec.ts.ejs +89 -0
- package/templates/common/auth/ts/services/jwtService.ts.ejs +60 -0
- package/templates/common/babel.config.js.ejs +5 -0
- package/templates/common/caching/clean/js/CreateUser.js.ejs +14 -5
- package/templates/common/caching/clean/js/DeleteUser.js.ejs +2 -1
- package/templates/common/caching/clean/js/GetUserById.js.ejs +39 -0
- package/templates/common/caching/clean/js/UpdateUser.js.ejs +2 -1
- package/templates/common/caching/clean/ts/createUser.ts.ejs +14 -6
- package/templates/common/caching/clean/ts/deleteUser.ts.ejs +2 -1
- package/templates/common/caching/clean/ts/getUserById.ts.ejs +32 -0
- package/templates/common/caching/clean/ts/updateUser.ts.ejs +2 -1
- package/templates/common/caching/js/memoryCache.spec.js.ejs +2 -0
- package/templates/common/caching/js/redisClient.spec.js.ejs +2 -0
- package/templates/common/caching/ts/memoryCache.spec.ts.ejs +2 -0
- package/templates/common/caching/ts/redisClient.spec.ts.ejs +2 -0
- package/templates/common/database/js/models/User.js.ejs +14 -1
- package/templates/common/database/js/models/User.js.mongoose.ejs +7 -0
- package/templates/common/database/js/models/User.spec.js.ejs +12 -0
- package/templates/common/database/js/mongoose.spec.js.ejs +2 -0
- package/templates/common/database/ts/models/User.spec.ts.ejs +10 -0
- package/templates/common/database/ts/models/User.ts.ejs +17 -0
- package/templates/common/database/ts/models/User.ts.mongoose.ejs +8 -0
- package/templates/common/database/ts/mongoose.spec.ts.ejs +2 -0
- package/templates/common/docker-compose.yml.ejs +12 -0
- package/templates/common/ecosystem.config.js.ejs +9 -3
- package/templates/common/eslint.config.mjs.ejs +3 -0
- package/templates/common/jest.config.js.ejs +13 -9
- package/templates/common/kafka/js/messaging/baseConsumer.js.ejs +1 -1
- package/templates/common/kafka/js/services/kafkaService.js.ejs +1 -1
- package/templates/common/migrations/init.js.ejs +5 -4
- package/templates/common/package.json.ejs +11 -2
- package/templates/common/prompts/project-context.md.ejs +8 -1
- package/templates/common/src/tests/e2e/e2e.users.test.js.ejs +149 -107
- package/templates/common/src/tests/e2e/e2e.users.test.ts.ejs +88 -47
- package/templates/common/swagger.yml.ejs +148 -0
- package/templates/common/tsconfig.eslint.json +15 -0
- package/templates/common/tsconfig.json +3 -1
- package/templates/common/views/ejs/index.ejs +264 -30
- package/templates/common/views/ejs/login.ejs.ejs +244 -0
- package/templates/common/views/ejs/signup.ejs.ejs +282 -0
- package/templates/common/views/pug/index.pug +269 -38
- package/templates/common/views/pug/login.pug.ejs +195 -0
- package/templates/common/views/pug/signup.pug.ejs +241 -0
- package/templates/db/mysql/V1__Initial_Setup.sql.ejs +6 -0
- package/templates/db/postgres/V1__Initial_Setup.sql.ejs +6 -0
- package/templates/mvc/js/src/config/env.js.ejs +12 -3
- package/templates/mvc/js/src/controllers/userController.js.ejs +29 -5
- package/templates/mvc/js/src/controllers/userController.spec.js.ejs +27 -12
- package/templates/mvc/js/src/graphql/context.js.ejs +14 -3
- package/templates/mvc/js/src/graphql/context.spec.js.ejs +36 -21
- package/templates/mvc/js/src/graphql/resolvers/user.resolvers.js.ejs +10 -5
- package/templates/mvc/js/src/graphql/resolvers/user.resolvers.spec.js.ejs +32 -10
- package/templates/mvc/js/src/graphql/typeDefs/user.types.js.ejs +1 -1
- package/templates/mvc/js/src/index.js.ejs +16 -3
- package/templates/mvc/js/src/routes/api.js.ejs +14 -0
- package/templates/mvc/js/src/routes/api.spec.js.ejs +3 -0
- package/templates/mvc/js/src/utils/errorMessages.js +1 -0
- package/templates/mvc/js/src/utils/httpCodes.js +1 -0
- package/templates/mvc/ts/src/config/env.ts.ejs +12 -3
- package/templates/mvc/ts/src/controllers/userController.spec.ts.ejs +95 -7
- package/templates/mvc/ts/src/controllers/userController.ts.ejs +68 -11
- package/templates/mvc/ts/src/graphql/context.spec.ts.ejs +36 -23
- package/templates/mvc/ts/src/graphql/context.ts.ejs +15 -6
- package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.spec.ts.ejs +32 -10
- package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.ts.ejs +15 -5
- package/templates/mvc/ts/src/graphql/typeDefs/user.types.ts.ejs +1 -1
- package/templates/mvc/ts/src/index.ts.ejs +15 -3
- package/templates/mvc/ts/src/routes/api.spec.ts.ejs +6 -0
- package/templates/mvc/ts/src/routes/api.ts.ejs +15 -0
- package/templates/mvc/ts/src/utils/errorMessages.ts +1 -0
- package/templates/mvc/ts/src/utils/httpCodes.ts +1 -0
- package/templates/clean-architecture/js/src/interfaces/routes/api.js +0 -12
- package/templates/clean-architecture/js/src/usecases/CreateUser.js +0 -14
- package/templates/clean-architecture/js/src/usecases/DeleteUser.js +0 -11
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.js +0 -12
- package/templates/clean-architecture/js/src/usecases/UpdateUser.js +0 -11
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts +0 -13
- package/templates/clean-architecture/ts/src/usecases/createUser.ts +0 -13
- package/templates/clean-architecture/ts/src/usecases/deleteUser.ts +0 -9
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts +0 -10
- package/templates/clean-architecture/ts/src/usecases/updateUser.ts +0 -9
- package/templates/mvc/js/src/routes/api.js +0 -10
- 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. **
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
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
|
|
62
|
-
it('should
|
|
129
|
+
<%_ } else { -%>
|
|
130
|
+
it('should fetch users successfully', async () => {
|
|
63
131
|
const response = await request(SERVER_URL)
|
|
64
|
-
.
|
|
65
|
-
.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
expect(
|
|
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
|
|
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
|
|
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
|
});
|