nodejs-quickstart-structure 1.13.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +4 -3
- package/lib/generator.js +17 -3
- package/lib/modules/app-setup.js +111 -19
- package/lib/modules/caching-setup.js +13 -0
- package/lib/modules/config-files.js +25 -62
- package/lib/modules/database-setup.js +35 -30
- package/lib/modules/kafka-setup.js +78 -10
- package/package.json +1 -2
- package/templates/clean-architecture/js/src/errors/BadRequestError.js +1 -1
- package/templates/clean-architecture/js/src/errors/BadRequestError.spec.js.ejs +21 -0
- package/templates/clean-architecture/js/src/errors/NotFoundError.js +1 -1
- package/templates/clean-architecture/js/src/errors/NotFoundError.spec.js.ejs +21 -0
- package/templates/clean-architecture/js/src/infrastructure/log/logger.spec.js.ejs +63 -0
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +2 -3
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs +81 -0
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.js.ejs +8 -4
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.spec.js.ejs +102 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/context.spec.js.ejs +31 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.spec.js.ejs +49 -0
- package/templates/clean-architecture/js/src/interfaces/routes/api.spec.js.ejs +38 -0
- package/templates/clean-architecture/js/src/usecases/CreateUser.spec.js.ejs +51 -0
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.spec.js.ejs +61 -0
- package/templates/clean-architecture/ts/src/errors/BadRequestError.spec.ts.ejs +21 -0
- package/templates/clean-architecture/ts/src/errors/BadRequestError.ts +1 -1
- package/templates/clean-architecture/ts/src/errors/NotFoundError.spec.ts.ejs +21 -0
- package/templates/clean-architecture/ts/src/errors/NotFoundError.ts +1 -1
- package/templates/clean-architecture/ts/src/infrastructure/log/logger.spec.ts.ejs +64 -0
- package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +85 -0
- package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.ts.ejs +2 -3
- package/templates/clean-architecture/ts/src/interfaces/controllers/userController.spec.ts.ejs +166 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/context.spec.ts.ejs +32 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.spec.ts.ejs +51 -0
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.spec.ts.ejs +40 -0
- package/templates/clean-architecture/ts/src/usecases/createUser.spec.ts.ejs +51 -0
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.spec.ts.ejs +63 -0
- package/templates/clean-architecture/ts/src/utils/errorMiddleware.ts.ejs +1 -2
- package/templates/common/caching/js/memoryCache.spec.js.ejs +101 -0
- package/templates/common/caching/js/redisClient.spec.js.ejs +149 -0
- package/templates/common/caching/ts/memoryCache.spec.ts.ejs +102 -0
- package/templates/common/caching/ts/redisClient.spec.ts.ejs +157 -0
- package/templates/common/database/js/database.spec.js.ejs +56 -0
- package/templates/common/database/js/models/User.js.ejs +22 -0
- package/templates/common/database/js/models/User.spec.js.ejs +84 -0
- package/templates/common/database/js/mongoose.spec.js.ejs +43 -0
- package/templates/common/database/ts/database.spec.ts.ejs +56 -0
- package/templates/common/database/ts/models/User.spec.ts.ejs +84 -0
- package/templates/common/database/ts/models/User.ts.ejs +26 -0
- package/templates/common/database/ts/mongoose.spec.ts.ejs +42 -0
- package/templates/common/eslint.config.mjs.ejs +11 -2
- package/templates/common/health/js/healthRoute.spec.js.ejs +70 -0
- package/templates/common/health/ts/healthRoute.spec.ts.ejs +76 -0
- package/templates/common/jest.config.js.ejs +19 -5
- package/templates/common/kafka/js/config/kafka.spec.js.ejs +21 -0
- package/templates/common/kafka/js/services/kafkaService.js.ejs +9 -5
- package/templates/common/kafka/js/services/kafkaService.spec.js.ejs +60 -0
- package/templates/common/kafka/ts/config/kafka.spec.ts.ejs +21 -0
- package/templates/common/kafka/ts/services/kafkaService.spec.ts.ejs +61 -0
- package/templates/common/kafka/ts/services/kafkaService.ts.ejs +1 -1
- package/templates/common/package.json.ejs +0 -3
- package/templates/common/shutdown/js/gracefulShutdown.spec.js.ejs +160 -0
- package/templates/common/shutdown/ts/gracefulShutdown.spec.ts.ejs +158 -0
- package/templates/common/src/utils/errorMiddleware.spec.js.ejs +79 -0
- package/templates/common/src/utils/errorMiddleware.spec.ts.ejs +94 -0
- package/templates/common/tsconfig.json +1 -1
- package/templates/mvc/js/src/controllers/userController.js.ejs +4 -31
- package/templates/mvc/js/src/controllers/userController.spec.js.ejs +170 -0
- package/templates/mvc/js/src/errors/BadRequestError.js +1 -1
- package/templates/mvc/js/src/errors/BadRequestError.spec.js.ejs +21 -0
- package/templates/mvc/js/src/errors/NotFoundError.js +1 -1
- package/templates/mvc/js/src/errors/NotFoundError.spec.js.ejs +21 -0
- package/templates/mvc/js/src/graphql/context.spec.js.ejs +29 -0
- package/templates/mvc/js/src/graphql/resolvers/user.resolvers.spec.js.ejs +47 -0
- package/templates/mvc/js/src/index.js.ejs +1 -1
- package/templates/mvc/js/src/routes/api.spec.js.ejs +36 -0
- package/templates/mvc/js/src/utils/logger.spec.js.ejs +63 -0
- package/templates/mvc/ts/src/controllers/userController.spec.ts.ejs +185 -0
- package/templates/mvc/ts/src/controllers/userController.ts.ejs +4 -31
- package/templates/mvc/ts/src/errors/BadRequestError.spec.ts.ejs +21 -0
- package/templates/mvc/ts/src/errors/BadRequestError.ts +1 -1
- package/templates/mvc/ts/src/errors/NotFoundError.spec.ts.ejs +21 -0
- package/templates/mvc/ts/src/errors/NotFoundError.ts +1 -1
- package/templates/mvc/ts/src/graphql/context.spec.ts.ejs +30 -0
- package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.spec.ts.ejs +51 -0
- package/templates/mvc/ts/src/routes/api.spec.ts.ejs +40 -0
- package/templates/mvc/ts/src/utils/errorMiddleware.ts.ejs +1 -2
- package/templates/mvc/ts/src/utils/logger.spec.ts.ejs +64 -0
- package/docs/demo.gif +0 -0
- package/docs/generateCase.md +0 -265
- package/docs/generatorFlow.md +0 -233
- package/docs/releaseNoteRule.md +0 -42
- package/docs/ruleDevelop.md +0 -30
- package/templates/common/tests/health.test.ts.ejs +0 -24
|
@@ -14,19 +14,15 @@ const getUsers = async () => {
|
|
|
14
14
|
try {
|
|
15
15
|
<%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
|
|
16
16
|
const users = await cacheService.getOrSet('users:all', async () => {
|
|
17
|
-
<%_ if (database === 'MongoDB') { -%>
|
|
17
|
+
<%_ if (database === 'MongoDB' || database === 'None') { -%>
|
|
18
18
|
return await User.find();
|
|
19
|
-
<%_ } else if (database === 'None') { -%>
|
|
20
|
-
return User.mockData;
|
|
21
19
|
<%_ } else { -%>
|
|
22
20
|
return await User.findAll();
|
|
23
21
|
<%_ } -%>
|
|
24
22
|
}, 60);
|
|
25
23
|
<%_ } else { -%>
|
|
26
|
-
<%_ if (database === 'MongoDB') { -%>
|
|
24
|
+
<%_ if (database === 'MongoDB' || database === 'None') { -%>
|
|
27
25
|
const users = await User.find();
|
|
28
|
-
<%_ } else if (database === 'None') { -%>
|
|
29
|
-
const users = User.mockData;
|
|
30
26
|
<%_ } else { -%>
|
|
31
27
|
const users = await User.findAll();
|
|
32
28
|
<%_ } -%>
|
|
@@ -41,20 +37,11 @@ const getUsers = async () => {
|
|
|
41
37
|
const createUser = async (data) => {
|
|
42
38
|
try {
|
|
43
39
|
const { name, email } = data;
|
|
44
|
-
<%_ if (database === 'None') { -%>
|
|
45
|
-
const newUser = { id: String(User.mockData.length + 1), name, email };
|
|
46
|
-
User.mockData.push(newUser);
|
|
47
|
-
<%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
|
|
48
|
-
await cacheService.del('users:all');
|
|
49
|
-
<%_ } -%>
|
|
50
|
-
return newUser;
|
|
51
|
-
<%_ } else { -%>
|
|
52
40
|
const user = await User.create({ name, email });
|
|
53
41
|
<%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
|
|
54
42
|
await cacheService.del('users:all');
|
|
55
43
|
<%_ } -%>
|
|
56
44
|
return user;
|
|
57
|
-
<%_ } -%>
|
|
58
45
|
} catch (error) {
|
|
59
46
|
logger.error('Error creating user:', error);
|
|
60
47
|
throw error;
|
|
@@ -65,19 +52,15 @@ const getUsers = async (req, res, next) => {
|
|
|
65
52
|
try {
|
|
66
53
|
<%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
|
|
67
54
|
const users = await cacheService.getOrSet('users:all', async () => {
|
|
68
|
-
<%_ if (database === 'MongoDB') { -%>
|
|
55
|
+
<%_ if (database === 'MongoDB' || database === 'None') { -%>
|
|
69
56
|
return await User.find();
|
|
70
|
-
<%_ } else if (database === 'None') { -%>
|
|
71
|
-
return User.mockData;
|
|
72
57
|
<%_ } else { -%>
|
|
73
58
|
return await User.findAll();
|
|
74
59
|
<%_ } -%>
|
|
75
60
|
}, 60);
|
|
76
61
|
<%_ } else { -%>
|
|
77
|
-
<%_ if (database === 'MongoDB') { -%>
|
|
62
|
+
<%_ if (database === 'MongoDB' || database === 'None') { -%>
|
|
78
63
|
const users = await User.find();
|
|
79
|
-
<%_ } else if (database === 'None') { -%>
|
|
80
|
-
const users = User.mockData;
|
|
81
64
|
<%_ } else { -%>
|
|
82
65
|
const users = await User.findAll();
|
|
83
66
|
<%_ } -%>
|
|
@@ -92,20 +75,11 @@ const getUsers = async (req, res, next) => {
|
|
|
92
75
|
const createUser = async (req, res, next) => {
|
|
93
76
|
try {
|
|
94
77
|
const { name, email } = req.body;
|
|
95
|
-
<%_ if (database === 'None') { -%>
|
|
96
|
-
const newUser = { id: String(User.mockData.length + 1), name, email };
|
|
97
|
-
User.mockData.push(newUser);
|
|
98
|
-
<%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
|
|
99
|
-
await cacheService.del('users:all');
|
|
100
|
-
<%_ } -%>
|
|
101
|
-
res.status(HTTP_STATUS.CREATED).json(newUser);
|
|
102
|
-
<%_ } else { -%>
|
|
103
78
|
const user = await User.create({ name, email });
|
|
104
79
|
<%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
|
|
105
80
|
await cacheService.del('users:all');
|
|
106
81
|
<%_ } -%>
|
|
107
82
|
res.status(HTTP_STATUS.CREATED).json(user);
|
|
108
|
-
<%_ } -%>
|
|
109
83
|
} catch (error) {
|
|
110
84
|
logger.error('Error creating user:', error);
|
|
111
85
|
next(error);
|
|
@@ -114,4 +88,3 @@ const createUser = async (req, res, next) => {
|
|
|
114
88
|
<% } -%>
|
|
115
89
|
|
|
116
90
|
module.exports = { getUsers, createUser };
|
|
117
|
-
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
<% if (communication !== 'GraphQL') { -%>
|
|
2
|
+
const HTTP_STATUS = require('@/utils/httpCodes');
|
|
3
|
+
<% } -%>
|
|
4
|
+
const { getUsers, createUser } = require('@/controllers/userController');
|
|
5
|
+
const User = require('@/models/User');
|
|
6
|
+
<%_ if (caching === 'Redis') { -%>
|
|
7
|
+
const cacheService = require('@/config/redisClient');
|
|
8
|
+
<%_ } else if (caching === 'Memory Cache') { -%>
|
|
9
|
+
const cacheService = require('@/config/memoryCache');
|
|
10
|
+
<%_ } -%>
|
|
11
|
+
|
|
12
|
+
// Mock dependencies
|
|
13
|
+
jest.mock('@/models/User');
|
|
14
|
+
<%_ if (caching === 'Redis') { -%>
|
|
15
|
+
jest.mock('@/config/redisClient', () => ({
|
|
16
|
+
getOrSet: jest.fn(),
|
|
17
|
+
del: jest.fn()
|
|
18
|
+
}));
|
|
19
|
+
<%_ } else if (caching === 'Memory Cache') { -%>
|
|
20
|
+
jest.mock('@/config/memoryCache', () => ({
|
|
21
|
+
getOrSet: jest.fn(),
|
|
22
|
+
del: jest.fn()
|
|
23
|
+
}));
|
|
24
|
+
<%_ } -%>
|
|
25
|
+
jest.mock('@/utils/logger');
|
|
26
|
+
|
|
27
|
+
describe('UserController', () => {
|
|
28
|
+
<% if (communication !== 'GraphQL') { -%>
|
|
29
|
+
let mockRequest;
|
|
30
|
+
let mockResponse;
|
|
31
|
+
let mockNext;
|
|
32
|
+
<% } -%>
|
|
33
|
+
|
|
34
|
+
beforeEach(() => {
|
|
35
|
+
<% if (communication !== 'GraphQL') { -%>
|
|
36
|
+
mockRequest = {};
|
|
37
|
+
mockResponse = {
|
|
38
|
+
json: jest.fn(),
|
|
39
|
+
status: jest.fn().mockReturnThis(),
|
|
40
|
+
};
|
|
41
|
+
mockNext = jest.fn();
|
|
42
|
+
<% } -%>
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
afterEach(() => {
|
|
46
|
+
jest.clearAllMocks();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('getUsers', () => {
|
|
50
|
+
it('should return successfully (Happy Path)', async () => {
|
|
51
|
+
// Arrange
|
|
52
|
+
const usersMock = [{ id: '1', name: 'Test', email: 'test@example.com' }];
|
|
53
|
+
<%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
|
|
54
|
+
cacheService.getOrSet.mockResolvedValue(usersMock);
|
|
55
|
+
<%_ } else { -%>
|
|
56
|
+
<%_ if (database === 'MongoDB' || database === 'None') { -%>
|
|
57
|
+
User.find.mockResolvedValue(usersMock);
|
|
58
|
+
<%_ } else { -%>
|
|
59
|
+
User.findAll.mockResolvedValue(usersMock);
|
|
60
|
+
<%_ } -%>
|
|
61
|
+
<%_ } -%>
|
|
62
|
+
|
|
63
|
+
// Act
|
|
64
|
+
<% if (communication === 'GraphQL') { -%>
|
|
65
|
+
const result = await getUsers();
|
|
66
|
+
|
|
67
|
+
// Assert
|
|
68
|
+
expect(result).toEqual(usersMock);
|
|
69
|
+
<% } else { -%>
|
|
70
|
+
await getUsers(mockRequest, mockResponse, mockNext);
|
|
71
|
+
|
|
72
|
+
// Assert
|
|
73
|
+
expect(mockResponse.json).toHaveBeenCalledWith(usersMock);
|
|
74
|
+
<% } -%>
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should handle errors correctly (Error Handling)', async () => {
|
|
78
|
+
// Arrange
|
|
79
|
+
const error = new Error('Database Error');
|
|
80
|
+
<%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
|
|
81
|
+
cacheService.getOrSet.mockRejectedValue(error);
|
|
82
|
+
<%_ } else { -%>
|
|
83
|
+
<%_ if (database === 'MongoDB' || database === 'None') { -%>
|
|
84
|
+
User.find.mockRejectedValue(error);
|
|
85
|
+
<%_ } else { -%>
|
|
86
|
+
User.findAll.mockRejectedValue(error);
|
|
87
|
+
<%_ } -%>
|
|
88
|
+
<%_ } -%>
|
|
89
|
+
|
|
90
|
+
// Act & Assert
|
|
91
|
+
<% if (communication === 'GraphQL') { -%>
|
|
92
|
+
await expect(getUsers()).rejects.toThrow(error);
|
|
93
|
+
<% } else { -%>
|
|
94
|
+
await getUsers(mockRequest, mockResponse, mockNext);
|
|
95
|
+
expect(mockNext).toHaveBeenCalledWith(error);
|
|
96
|
+
<% } -%>
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe('createUser', () => {
|
|
101
|
+
it('should successfully create a new user (Happy Path)', async () => {
|
|
102
|
+
// Arrange
|
|
103
|
+
const payload = { name: 'Alice', email: 'alice@example.com' };
|
|
104
|
+
<% if (communication === 'GraphQL') { -%>
|
|
105
|
+
const dataArg = payload;
|
|
106
|
+
<% } else { -%>
|
|
107
|
+
mockRequest.body = payload;
|
|
108
|
+
<% } -%>
|
|
109
|
+
|
|
110
|
+
const expectedUser = { id: '1', ...payload };
|
|
111
|
+
<%_ if (database === 'MongoDB' || database === 'None') { -%>
|
|
112
|
+
User.create.mockResolvedValue(expectedUser);
|
|
113
|
+
<%_ if (database === 'None') { -%>User.mockData = [expectedUser];<%_ } -%>
|
|
114
|
+
<%_ } else { -%>
|
|
115
|
+
User.create.mockResolvedValue(expectedUser);
|
|
116
|
+
<%_ } -%>
|
|
117
|
+
|
|
118
|
+
// Act
|
|
119
|
+
<% if (communication === 'GraphQL') { -%>
|
|
120
|
+
const result = await createUser(dataArg);
|
|
121
|
+
|
|
122
|
+
// Assert
|
|
123
|
+
<%_ if (database === 'None') { -%>
|
|
124
|
+
expect(result.name).toBe(payload.name);
|
|
125
|
+
expect(result.email).toBe(payload.email);
|
|
126
|
+
expect(User.mockData.length).toBe(1);
|
|
127
|
+
<%_ } else { -%>
|
|
128
|
+
expect(result).toEqual(expectedUser);
|
|
129
|
+
expect(User.create).toHaveBeenCalledWith(payload);
|
|
130
|
+
<%_ } -%>
|
|
131
|
+
<% } else { -%>
|
|
132
|
+
await createUser(mockRequest, mockResponse, mockNext);
|
|
133
|
+
|
|
134
|
+
// Assert
|
|
135
|
+
expect(mockResponse.status).toHaveBeenCalledWith(HTTP_STATUS.CREATED);
|
|
136
|
+
<%_ if (database === 'None') { -%>
|
|
137
|
+
expect(mockResponse.json).toHaveBeenCalled();
|
|
138
|
+
expect(User.mockData.length).toBe(1);
|
|
139
|
+
<%_ } else { -%>
|
|
140
|
+
expect(mockResponse.json).toHaveBeenCalledWith(expectedUser);
|
|
141
|
+
expect(User.create).toHaveBeenCalledWith(payload);
|
|
142
|
+
<%_ } -%>
|
|
143
|
+
<% } -%>
|
|
144
|
+
<%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
|
|
145
|
+
expect(cacheService.del).toHaveBeenCalledWith('users:all');
|
|
146
|
+
<%_ } -%>
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should handle errors when creation fails (Error Handling)', async () => {
|
|
150
|
+
// Arrange
|
|
151
|
+
const error = new Error('Creation Error');
|
|
152
|
+
const payload = { name: 'Bob', email: 'bob@example.com' };
|
|
153
|
+
<% if (communication === 'GraphQL') { -%>
|
|
154
|
+
const dataArg = payload;
|
|
155
|
+
<% } else { -%>
|
|
156
|
+
mockRequest.body = payload;
|
|
157
|
+
<% } -%>
|
|
158
|
+
|
|
159
|
+
User.create.mockRejectedValue(error);
|
|
160
|
+
|
|
161
|
+
// Act & Assert
|
|
162
|
+
<% if (communication === 'GraphQL') { -%>
|
|
163
|
+
await expect(createUser(dataArg)).rejects.toThrow(error);
|
|
164
|
+
<% } else { -%>
|
|
165
|
+
await createUser(mockRequest, mockResponse, mockNext);
|
|
166
|
+
expect(mockNext).toHaveBeenCalledWith(error);
|
|
167
|
+
<% } -%>
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const { BadRequestError } = require('@/errors/BadRequestError');
|
|
2
|
+
const { ApiError } = require('@/errors/ApiError');
|
|
3
|
+
const HTTP_STATUS = require('@/utils/httpCodes');
|
|
4
|
+
|
|
5
|
+
describe('BadRequestError', () => {
|
|
6
|
+
it('should extend ApiError', () => {
|
|
7
|
+
const error = new BadRequestError();
|
|
8
|
+
expect(error).toBeInstanceOf(ApiError);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('should have default message "Bad request"', () => {
|
|
12
|
+
const error = new BadRequestError();
|
|
13
|
+
expect(error.message).toBe('Bad request');
|
|
14
|
+
expect(error.statusCode).toBe(HTTP_STATUS.BAD_REQUEST);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should accept a custom message', () => {
|
|
18
|
+
const error = new BadRequestError('Custom bad request');
|
|
19
|
+
expect(error.message).toBe('Custom bad request');
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const { NotFoundError } = require('@/errors/NotFoundError');
|
|
2
|
+
const { ApiError } = require('@/errors/ApiError');
|
|
3
|
+
const HTTP_STATUS = require('@/utils/httpCodes');
|
|
4
|
+
|
|
5
|
+
describe('NotFoundError', () => {
|
|
6
|
+
it('should extend ApiError', () => {
|
|
7
|
+
const error = new NotFoundError();
|
|
8
|
+
expect(error).toBeInstanceOf(ApiError);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('should have default message "Resource not found"', () => {
|
|
12
|
+
const error = new NotFoundError();
|
|
13
|
+
expect(error.message).toBe('Resource not found');
|
|
14
|
+
expect(error.statusCode).toBe(HTTP_STATUS.NOT_FOUND);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should accept a custom message', () => {
|
|
18
|
+
const error = new NotFoundError('User not found');
|
|
19
|
+
expect(error.message).toBe('User not found');
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -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,47 @@
|
|
|
1
|
+
const { userResolvers } = require('@/graphql/resolvers/user.resolvers');
|
|
2
|
+
|
|
3
|
+
const mockGetUsers = jest.fn().mockResolvedValue([{ id: '1', name: 'John Doe', email: 'john@example.com' }]);
|
|
4
|
+
const mockCreateUser = jest.fn().mockResolvedValue({ id: '1', name: 'Jane', email: 'jane@example.com' });
|
|
5
|
+
|
|
6
|
+
jest.mock('@/controllers/userController', () => ({
|
|
7
|
+
getUsers: (...args) => mockGetUsers(...args),
|
|
8
|
+
createUser: (...args) => mockCreateUser(...args)
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
describe('User Resolvers', () => {
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
jest.clearAllMocks();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe('Query.getAllUsers', () => {
|
|
17
|
+
it('should return all users', async () => {
|
|
18
|
+
const result = await userResolvers.Query.getAllUsers();
|
|
19
|
+
expect(result).toEqual([{ id: '1', name: 'John Doe', email: 'john@example.com' }]);
|
|
20
|
+
expect(mockGetUsers).toHaveBeenCalledTimes(1);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('Mutation.createUser', () => {
|
|
25
|
+
it('should create and return a new user', async () => {
|
|
26
|
+
const result = await userResolvers.Mutation.createUser(null, { name: 'Jane', email: 'jane@example.com' });
|
|
27
|
+
expect(result).toEqual({ id: '1', name: 'Jane', email: 'jane@example.com' });
|
|
28
|
+
expect(mockCreateUser).toHaveBeenCalledWith({ name: 'Jane', email: 'jane@example.com' });
|
|
29
|
+
expect(mockCreateUser).toHaveBeenCalledTimes(1);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
33
|
+
describe('User.id', () => {
|
|
34
|
+
it('should return parent.id if available', () => {
|
|
35
|
+
const parent = { id: '123' };
|
|
36
|
+
const result = userResolvers.User.id(parent);
|
|
37
|
+
expect(result).toBe('123');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should fallback to parent._id if id is not available', () => {
|
|
41
|
+
const parent = { _id: '456' };
|
|
42
|
+
const result = userResolvers.User.id(parent);
|
|
43
|
+
expect(result).toBe('456');
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
<%_ } -%>
|
|
47
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const express = require('express');
|
|
2
2
|
const cors = require('cors');
|
|
3
|
-
<%_ if (communication === 'REST APIs') { -%>const apiRoutes = require('./routes/api');<%_ }
|
|
3
|
+
<%_ if (communication === 'REST APIs') { -%>const apiRoutes = require('./routes/api');<%_ } %>
|
|
4
4
|
const healthRoutes = require('./routes/healthRoute');
|
|
5
5
|
<%_ if (communication === 'Kafka') { -%>const { connectKafka, sendMessage } = require('./services/kafkaService');<%_ } -%>
|
|
6
6
|
<%_ if (communication === 'GraphQL') { -%>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const request = require('supertest');
|
|
2
|
+
const express = require('express');
|
|
3
|
+
const router = require('@/routes/api');
|
|
4
|
+
|
|
5
|
+
const mockGetUsers = jest.fn().mockImplementation((req, res) => res.status(200).json([{ id: '1', name: 'John Doe' }]));
|
|
6
|
+
const mockCreateUser = jest.fn().mockImplementation((req, res) => res.status(201).json({ id: '1', name: 'Test' }));
|
|
7
|
+
|
|
8
|
+
jest.mock('@/controllers/userController', () => ({
|
|
9
|
+
getUsers: (...args) => mockGetUsers(...args),
|
|
10
|
+
createUser: (...args) => mockCreateUser(...args)
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
describe('ApiRoutes', () => {
|
|
14
|
+
let app;
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
app = express();
|
|
18
|
+
app.use(express.json());
|
|
19
|
+
app.use('/api', router);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('GET /api/users should call controller.getUsers', async () => {
|
|
23
|
+
await request(app)
|
|
24
|
+
.get('/api/users');
|
|
25
|
+
|
|
26
|
+
expect(mockGetUsers).toHaveBeenCalledTimes(1);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('POST /api/users should call controller.createUser', async () => {
|
|
30
|
+
await request(app)
|
|
31
|
+
.post('/api/users')
|
|
32
|
+
.send({ name: 'Test', email: 'test@example.com' });
|
|
33
|
+
|
|
34
|
+
expect(mockCreateUser).toHaveBeenCalledTimes(1);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
jest.mock('winston-daily-rotate-file');
|
|
2
|
+
jest.mock('winston', () => {
|
|
3
|
+
const mockLogger = {
|
|
4
|
+
add: jest.fn(),
|
|
5
|
+
info: jest.fn(),
|
|
6
|
+
error: jest.fn(),
|
|
7
|
+
warn: jest.fn()
|
|
8
|
+
};
|
|
9
|
+
const format = {
|
|
10
|
+
combine: jest.fn(),
|
|
11
|
+
timestamp: jest.fn(),
|
|
12
|
+
json: jest.fn(),
|
|
13
|
+
simple: jest.fn()
|
|
14
|
+
};
|
|
15
|
+
const transports = {
|
|
16
|
+
Console: jest.fn(),
|
|
17
|
+
DailyRotateFile: jest.fn()
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
format,
|
|
21
|
+
transports,
|
|
22
|
+
createLogger: jest.fn().mockReturnValue(mockLogger)
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
<% if (architecture === 'MVC') { -%>
|
|
27
|
+
const logger = require('@/utils/logger');
|
|
28
|
+
<% } else { -%>
|
|
29
|
+
const logger = require('@/infrastructure/log/logger');
|
|
30
|
+
<% } -%>
|
|
31
|
+
|
|
32
|
+
describe('Logger', () => {
|
|
33
|
+
it('should export a logger instance', () => {
|
|
34
|
+
expect(logger).toBeDefined();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should have info method', () => {
|
|
38
|
+
expect(typeof logger.info).toBe('function');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should have error method', () => {
|
|
42
|
+
expect(typeof logger.error).toBe('function');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should call info', () => {
|
|
46
|
+
logger.info('test message');
|
|
47
|
+
expect(logger.info).toHaveBeenCalledWith('test message');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should call error', () => {
|
|
51
|
+
logger.error('test error');
|
|
52
|
+
expect(logger.error).toHaveBeenCalledWith('test error');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should use JSON format in production environment', () => {
|
|
56
|
+
const winston = require('winston');
|
|
57
|
+
jest.resetModules();
|
|
58
|
+
process.env.NODE_ENV = 'production';
|
|
59
|
+
require('@/utils/logger');
|
|
60
|
+
expect(winston.format.json).toHaveBeenCalled();
|
|
61
|
+
process.env.NODE_ENV = 'test';
|
|
62
|
+
});
|
|
63
|
+
});
|