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.
- package/README.md +32 -0
- package/bin/index.js +143 -0
- package/lib/generator.js +145 -0
- package/lib/modules/app-setup.js +479 -0
- package/lib/modules/caching-setup.js +76 -0
- package/lib/modules/config-files.js +151 -0
- package/lib/modules/database-setup.js +116 -0
- package/lib/modules/kafka-setup.js +249 -0
- package/lib/modules/project-setup.js +32 -0
- package/lib/prompts.js +128 -0
- package/package.json +66 -0
- package/templates/clean-architecture/js/src/domain/models/User.js.ejs +11 -0
- package/templates/clean-architecture/js/src/errors/ApiError.js +14 -0
- package/templates/clean-architecture/js/src/errors/BadRequestError.js +11 -0
- package/templates/clean-architecture/js/src/errors/BadRequestError.spec.js.ejs +22 -0
- package/templates/clean-architecture/js/src/errors/NotFoundError.js +11 -0
- package/templates/clean-architecture/js/src/errors/NotFoundError.spec.js.ejs +22 -0
- package/templates/clean-architecture/js/src/index.js.ejs +56 -0
- package/templates/clean-architecture/js/src/infrastructure/config/env.js.ejs +47 -0
- package/templates/clean-architecture/js/src/infrastructure/log/logger.js +36 -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 +88 -0
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs +142 -0
- package/templates/clean-architecture/js/src/infrastructure/webserver/middleware/errorMiddleware.js +30 -0
- package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +93 -0
- package/templates/clean-architecture/js/src/infrastructure/webserver/swagger.js.ejs +6 -0
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.js.ejs +190 -0
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.spec.js.ejs +234 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/context.js.ejs +13 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/context.spec.js.ejs +31 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/index.js.ejs +5 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/index.js.ejs +6 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.js.ejs +27 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.spec.js.ejs +66 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/index.js.ejs +6 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/user.types.js.ejs +19 -0
- package/templates/clean-architecture/js/src/interfaces/routes/api.js.ejs +17 -0
- package/templates/clean-architecture/js/src/interfaces/routes/api.spec.js.ejs +38 -0
- package/templates/clean-architecture/js/src/usecases/CreateUser.js.ejs +14 -0
- package/templates/clean-architecture/js/src/usecases/CreateUser.spec.js.ejs +51 -0
- package/templates/clean-architecture/js/src/usecases/DeleteUser.js +11 -0
- package/templates/clean-architecture/js/src/usecases/DeleteUser.spec.js.ejs +47 -0
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.js +12 -0
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.spec.js.ejs +61 -0
- package/templates/clean-architecture/js/src/usecases/UpdateUser.js.ejs +11 -0
- package/templates/clean-architecture/js/src/usecases/UpdateUser.spec.js.ejs +48 -0
- package/templates/clean-architecture/js/src/utils/errorMessages.js +14 -0
- package/templates/clean-architecture/js/src/utils/httpCodes.js +9 -0
- package/templates/clean-architecture/ts/src/config/env.ts.ejs +46 -0
- package/templates/clean-architecture/ts/src/config/swagger.ts.ejs +6 -0
- package/templates/clean-architecture/ts/src/domain/user.ts.ejs +9 -0
- package/templates/clean-architecture/ts/src/errors/ApiError.ts +15 -0
- package/templates/clean-architecture/ts/src/errors/BadRequestError.spec.ts.ejs +22 -0
- package/templates/clean-architecture/ts/src/errors/BadRequestError.ts +9 -0
- package/templates/clean-architecture/ts/src/errors/NotFoundError.spec.ts.ejs +22 -0
- package/templates/clean-architecture/ts/src/errors/NotFoundError.ts +9 -0
- package/templates/clean-architecture/ts/src/index.ts.ejs +144 -0
- package/templates/clean-architecture/ts/src/infrastructure/log/logger.spec.ts.ejs +63 -0
- package/templates/clean-architecture/ts/src/infrastructure/log/logger.ts +36 -0
- package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +175 -0
- package/templates/clean-architecture/ts/src/infrastructure/repositories/userRepository.ts.ejs +125 -0
- package/templates/clean-architecture/ts/src/interfaces/controllers/userController.spec.ts.ejs +331 -0
- package/templates/clean-architecture/ts/src/interfaces/controllers/userController.ts.ejs +208 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/context.spec.ts.ejs +32 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/context.ts.ejs +17 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/index.ts.ejs +3 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/index.ts.ejs +4 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.spec.ts.ejs +68 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.ts.ejs +29 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/index.ts.ejs +4 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/user.types.ts.ejs +17 -0
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.spec.ts.ejs +40 -0
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts.ejs +18 -0
- package/templates/clean-architecture/ts/src/usecases/createUser.spec.ts.ejs +51 -0
- package/templates/clean-architecture/ts/src/usecases/createUser.ts.ejs +11 -0
- package/templates/clean-architecture/ts/src/usecases/deleteUser.spec.ts.ejs +47 -0
- package/templates/clean-architecture/ts/src/usecases/deleteUser.ts +9 -0
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.spec.ts.ejs +63 -0
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts +10 -0
- package/templates/clean-architecture/ts/src/usecases/updateUser.spec.ts.ejs +48 -0
- package/templates/clean-architecture/ts/src/usecases/updateUser.ts.ejs +10 -0
- package/templates/clean-architecture/ts/src/utils/errorMessages.ts +12 -0
- package/templates/clean-architecture/ts/src/utils/errorMiddleware.ts.ejs +27 -0
- package/templates/clean-architecture/ts/src/utils/httpCodes.ts +7 -0
- package/templates/common/.cursorrules.ejs +60 -0
- package/templates/common/.dockerignore +12 -0
- package/templates/common/.env.example.ejs +60 -0
- package/templates/common/.gitattributes +46 -0
- package/templates/common/.gitlab-ci.yml.ejs +86 -0
- package/templates/common/.lintstagedrc +6 -0
- package/templates/common/.prettierrc +7 -0
- package/templates/common/.snyk.ejs +45 -0
- package/templates/common/Dockerfile +73 -0
- package/templates/common/Jenkinsfile.ejs +87 -0
- package/templates/common/README.md.ejs +148 -0
- package/templates/common/_github/workflows/ci.yml.ejs +46 -0
- package/templates/common/_github/workflows/security.yml.ejs +36 -0
- package/templates/common/_gitignore +5 -0
- package/templates/common/_husky/pre-commit +4 -0
- package/templates/common/caching/clean/js/CreateUser.js.ejs +29 -0
- package/templates/common/caching/clean/js/DeleteUser.js.ejs +27 -0
- package/templates/common/caching/clean/js/GetAllUsers.js.ejs +37 -0
- package/templates/common/caching/clean/js/UpdateUser.js.ejs +27 -0
- package/templates/common/caching/clean/ts/createUser.ts.ejs +27 -0
- package/templates/common/caching/clean/ts/deleteUser.ts.ejs +24 -0
- package/templates/common/caching/clean/ts/getAllUsers.ts.ejs +34 -0
- package/templates/common/caching/clean/ts/updateUser.ts.ejs +25 -0
- package/templates/common/caching/js/memoryCache.js.ejs +60 -0
- package/templates/common/caching/js/memoryCache.spec.js.ejs +101 -0
- package/templates/common/caching/js/redisClient.js.ejs +75 -0
- package/templates/common/caching/js/redisClient.spec.js.ejs +147 -0
- package/templates/common/caching/ts/memoryCache.spec.ts.ejs +102 -0
- package/templates/common/caching/ts/memoryCache.ts.ejs +73 -0
- package/templates/common/caching/ts/redisClient.spec.ts.ejs +157 -0
- package/templates/common/caching/ts/redisClient.ts.ejs +89 -0
- package/templates/common/database/js/database.js.ejs +19 -0
- package/templates/common/database/js/database.spec.js.ejs +56 -0
- package/templates/common/database/js/models/User.js.ejs +91 -0
- package/templates/common/database/js/models/User.js.mongoose.ejs +35 -0
- package/templates/common/database/js/models/User.spec.js.ejs +94 -0
- package/templates/common/database/js/mongoose.js.ejs +33 -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/database.ts.ejs +21 -0
- package/templates/common/database/ts/models/User.spec.ts.ejs +100 -0
- package/templates/common/database/ts/models/User.ts.ejs +102 -0
- package/templates/common/database/ts/models/User.ts.mongoose.ejs +34 -0
- package/templates/common/database/ts/mongoose.spec.ts.ejs +42 -0
- package/templates/common/database/ts/mongoose.ts.ejs +28 -0
- package/templates/common/docker-compose.yml.ejs +159 -0
- package/templates/common/ecosystem.config.js.ejs +40 -0
- package/templates/common/eslint.config.mjs.ejs +77 -0
- package/templates/common/health/js/healthRoute.js.ejs +50 -0
- 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/health/ts/healthRoute.ts.ejs +49 -0
- package/templates/common/jest.config.js.ejs +32 -0
- package/templates/common/jest.e2e.config.js.ejs +8 -0
- package/templates/common/kafka/js/config/kafka.js +9 -0
- package/templates/common/kafka/js/config/kafka.spec.js.ejs +27 -0
- package/templates/common/kafka/js/messaging/baseConsumer.js.ejs +30 -0
- package/templates/common/kafka/js/messaging/baseConsumer.spec.js.ejs +58 -0
- package/templates/common/kafka/js/messaging/userEventSchema.js.ejs +12 -0
- package/templates/common/kafka/js/messaging/userEventSchema.spec.js.ejs +27 -0
- package/templates/common/kafka/js/messaging/welcomeEmailConsumer.js.ejs +44 -0
- package/templates/common/kafka/js/messaging/welcomeEmailConsumer.spec.js.ejs +86 -0
- package/templates/common/kafka/js/services/kafkaService.js.ejs +93 -0
- package/templates/common/kafka/js/services/kafkaService.spec.js.ejs +106 -0
- package/templates/common/kafka/js/utils/kafkaEvents.js.ejs +7 -0
- package/templates/common/kafka/ts/config/kafka.spec.ts.ejs +27 -0
- package/templates/common/kafka/ts/config/kafka.ts +7 -0
- package/templates/common/kafka/ts/messaging/baseConsumer.spec.ts.ejs +50 -0
- package/templates/common/kafka/ts/messaging/baseConsumer.ts.ejs +27 -0
- package/templates/common/kafka/ts/messaging/userEventSchema.spec.ts.ejs +51 -0
- package/templates/common/kafka/ts/messaging/userEventSchema.ts.ejs +12 -0
- package/templates/common/kafka/ts/messaging/welcomeEmailConsumer.spec.ts.ejs +86 -0
- package/templates/common/kafka/ts/messaging/welcomeEmailConsumer.ts.ejs +38 -0
- package/templates/common/kafka/ts/services/kafkaService.spec.ts.ejs +81 -0
- package/templates/common/kafka/ts/services/kafkaService.ts.ejs +95 -0
- package/templates/common/kafka/ts/utils/kafkaEvents.ts.ejs +5 -0
- package/templates/common/migrate-mongo-config.js.ejs +31 -0
- package/templates/common/migrations/init.js.ejs +23 -0
- package/templates/common/package.json.ejs +137 -0
- package/templates/common/prompts/add-feature.md.ejs +26 -0
- package/templates/common/prompts/project-context.md.ejs +43 -0
- package/templates/common/prompts/troubleshoot.md.ejs +28 -0
- package/templates/common/public/css/style.css +147 -0
- package/templates/common/scripts/run-e2e.js.ejs +63 -0
- package/templates/common/shutdown/js/gracefulShutdown.js.ejs +65 -0
- package/templates/common/shutdown/js/gracefulShutdown.spec.js.ejs +149 -0
- package/templates/common/shutdown/ts/gracefulShutdown.spec.ts.ejs +179 -0
- package/templates/common/shutdown/ts/gracefulShutdown.ts.ejs +59 -0
- package/templates/common/sonar-project.properties.ejs +27 -0
- package/templates/common/src/config/auth.js.ejs +19 -0
- package/templates/common/src/config/auth.ts.ejs +19 -0
- package/templates/common/src/controllers/authController.js.ejs +101 -0
- package/templates/common/src/controllers/authController.ts.ejs +101 -0
- package/templates/common/src/middleware/auth.js.ejs +20 -0
- package/templates/common/src/middleware/auth.ts.ejs +25 -0
- package/templates/common/src/middleware/upload.js.ejs +31 -0
- package/templates/common/src/middleware/upload.ts.ejs +32 -0
- package/templates/common/src/routes/authRoutes.js.ejs +20 -0
- package/templates/common/src/routes/authRoutes.ts.ejs +20 -0
- package/templates/common/src/tests/e2e/e2e.users.test.js.ejs +120 -0
- package/templates/common/src/tests/e2e/e2e.users.test.ts.ejs +120 -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/swagger.yml.ejs +118 -0
- package/templates/common/tsconfig.json +23 -0
- package/templates/common/views/ejs/index.ejs +55 -0
- package/templates/common/views/pug/index.pug +40 -0
- package/templates/db/mysql/V1__Initial_Setup.sql.ejs +10 -0
- package/templates/db/postgres/V1__Initial_Setup.sql.ejs +10 -0
- package/templates/mvc/js/src/config/env.js.ejs +46 -0
- package/templates/mvc/js/src/config/swagger.js.ejs +6 -0
- package/templates/mvc/js/src/controllers/userController.js.ejs +288 -0
- package/templates/mvc/js/src/controllers/userController.spec.js.ejs +481 -0
- package/templates/mvc/js/src/errors/ApiError.js +14 -0
- package/templates/mvc/js/src/errors/BadRequestError.js +11 -0
- package/templates/mvc/js/src/errors/BadRequestError.spec.js.ejs +22 -0
- package/templates/mvc/js/src/errors/NotFoundError.js +11 -0
- package/templates/mvc/js/src/errors/NotFoundError.spec.js.ejs +22 -0
- package/templates/mvc/js/src/graphql/context.js.ejs +7 -0
- package/templates/mvc/js/src/graphql/context.spec.js.ejs +29 -0
- package/templates/mvc/js/src/graphql/index.js.ejs +5 -0
- package/templates/mvc/js/src/graphql/resolvers/index.js.ejs +6 -0
- package/templates/mvc/js/src/graphql/resolvers/user.resolvers.js.ejs +25 -0
- package/templates/mvc/js/src/graphql/resolvers/user.resolvers.spec.js.ejs +64 -0
- package/templates/mvc/js/src/graphql/typeDefs/index.js.ejs +6 -0
- package/templates/mvc/js/src/graphql/typeDefs/user.types.js.ejs +19 -0
- package/templates/mvc/js/src/index.js.ejs +141 -0
- package/templates/mvc/js/src/routes/api.js.ejs +15 -0
- package/templates/mvc/js/src/routes/api.spec.js.ejs +41 -0
- package/templates/mvc/js/src/utils/errorMessages.js +14 -0
- package/templates/mvc/js/src/utils/errorMiddleware.js +29 -0
- package/templates/mvc/js/src/utils/httpCodes.js +9 -0
- package/templates/mvc/js/src/utils/logger.js +40 -0
- package/templates/mvc/js/src/utils/logger.spec.js.ejs +63 -0
- package/templates/mvc/ts/src/config/env.ts.ejs +45 -0
- package/templates/mvc/ts/src/config/swagger.ts.ejs +6 -0
- package/templates/mvc/ts/src/controllers/userController.spec.ts.ejs +481 -0
- package/templates/mvc/ts/src/controllers/userController.ts.ejs +292 -0
- package/templates/mvc/ts/src/errors/ApiError.ts +15 -0
- package/templates/mvc/ts/src/errors/BadRequestError.spec.ts.ejs +22 -0
- package/templates/mvc/ts/src/errors/BadRequestError.ts +9 -0
- package/templates/mvc/ts/src/errors/NotFoundError.spec.ts.ejs +27 -0
- package/templates/mvc/ts/src/errors/NotFoundError.ts +9 -0
- package/templates/mvc/ts/src/graphql/context.spec.ts.ejs +30 -0
- package/templates/mvc/ts/src/graphql/context.ts.ejs +12 -0
- package/templates/mvc/ts/src/graphql/index.ts.ejs +3 -0
- package/templates/mvc/ts/src/graphql/resolvers/index.ts.ejs +4 -0
- package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.spec.ts.ejs +68 -0
- package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.ts.ejs +29 -0
- package/templates/mvc/ts/src/graphql/typeDefs/index.ts.ejs +4 -0
- package/templates/mvc/ts/src/graphql/typeDefs/user.types.ts.ejs +17 -0
- package/templates/mvc/ts/src/index.ts.ejs +157 -0
- package/templates/mvc/ts/src/routes/api.spec.ts.ejs +59 -0
- package/templates/mvc/ts/src/routes/api.ts.ejs +17 -0
- package/templates/mvc/ts/src/utils/errorMessages.ts +12 -0
- package/templates/mvc/ts/src/utils/errorMiddleware.ts.ejs +27 -0
- package/templates/mvc/ts/src/utils/httpCodes.ts +7 -0
- package/templates/mvc/ts/src/utils/logger.spec.ts.ejs +63 -0
- package/templates/mvc/ts/src/utils/logger.ts +36 -0
package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { UserRepository } from '@/infrastructure/repositories/UserRepository';
|
|
2
|
+
import UserModel from '@/infrastructure/database/models/User';
|
|
3
|
+
|
|
4
|
+
// Mock DB Model Database Layer
|
|
5
|
+
jest.mock('@/infrastructure/database/models/User');
|
|
6
|
+
|
|
7
|
+
describe('UserRepository', () => {
|
|
8
|
+
let userRepository: UserRepository;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
userRepository = new UserRepository();
|
|
12
|
+
jest.clearAllMocks();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe('save', () => {
|
|
16
|
+
it('should save and return a newly created user (Happy Path)', async () => {
|
|
17
|
+
// Arrange
|
|
18
|
+
const payload = { id: '', name: 'TestUser', email: 'test@example.com' };
|
|
19
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
20
|
+
const mockDbRecord = { _id: { toString: () => '1' }, name: 'TestUser', email: 'test@example.com' };
|
|
21
|
+
(UserModel.create as jest.Mock).mockResolvedValue(mockDbRecord);
|
|
22
|
+
<%_ } else if (database === 'None') { -%>
|
|
23
|
+
(UserModel.create as jest.Mock).mockResolvedValue(payload);
|
|
24
|
+
<%_ } else { -%>
|
|
25
|
+
const mockDbRecord = { id: '1', name: 'TestUser', email: 'test@example.com' };
|
|
26
|
+
(UserModel.create as jest.Mock).mockResolvedValue(mockDbRecord);
|
|
27
|
+
<%_ } -%>
|
|
28
|
+
|
|
29
|
+
// Act
|
|
30
|
+
const result = await userRepository.save(payload);
|
|
31
|
+
|
|
32
|
+
// Assert
|
|
33
|
+
<%_ if (database === 'None') { -%>
|
|
34
|
+
expect(result.name).toEqual(payload.name)
|
|
35
|
+
<%_ } else { -%>
|
|
36
|
+
expect(result).toEqual({ id: '1', name: 'TestUser', email: 'test@example.com' });
|
|
37
|
+
expect(UserModel.create).toHaveBeenCalledWith({ name: payload.name, email: payload.email });
|
|
38
|
+
<%_ } -%>
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should throw an error when DB fails explicitly (Edge Case)', async () => {
|
|
42
|
+
<%_ if (database === 'None') { -%>
|
|
43
|
+
// Mocks do not naturally fail
|
|
44
|
+
<%_ } else { -%>
|
|
45
|
+
// Arrange
|
|
46
|
+
const payload = { id: '', name: 'FailUser', email: 'fail@example.com' };
|
|
47
|
+
const error = new Error('DB Connection Refused');
|
|
48
|
+
(UserModel.create as jest.Mock).mockRejectedValue(error);
|
|
49
|
+
|
|
50
|
+
// Act & Assert
|
|
51
|
+
await expect(userRepository.save(payload)).rejects.toThrow(error);
|
|
52
|
+
<%_ } -%>
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('getUsers', () => {
|
|
57
|
+
it('should return a list of mapped UserEntities (Happy Path)', async () => {
|
|
58
|
+
// Arrange
|
|
59
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
60
|
+
const mockDbRecords = [{ _id: { toString: () => '1' }, name: 'User1', email: 'user1@example.com' }];
|
|
61
|
+
(UserModel.find as jest.Mock).mockResolvedValue(mockDbRecords);
|
|
62
|
+
<%_ } else if (database === 'None') { -%>
|
|
63
|
+
const mockData = [{ id: '1', name: 'User1', email: 'user1@example.com' }];
|
|
64
|
+
(UserModel.find as jest.Mock).mockResolvedValue(mockData);
|
|
65
|
+
<%_ } else { -%>
|
|
66
|
+
const mockDbRecords = [{ id: '1', name: 'User1', email: 'user1@example.com' }];
|
|
67
|
+
(UserModel.findAll as jest.Mock).mockResolvedValue(mockDbRecords);
|
|
68
|
+
<%_ } -%>
|
|
69
|
+
|
|
70
|
+
// Act
|
|
71
|
+
const result = await userRepository.getUsers();
|
|
72
|
+
|
|
73
|
+
// Assert
|
|
74
|
+
expect(result).toHaveLength(1);
|
|
75
|
+
expect(result[0]).toEqual({ id: '1', name: 'User1', email: 'user1@example.com' });
|
|
76
|
+
<%_ if (database !== 'None') { -%>
|
|
77
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
78
|
+
expect(UserModel.find).toHaveBeenCalled();
|
|
79
|
+
<%_ } else { -%>
|
|
80
|
+
expect(UserModel.findAll).toHaveBeenCalled();
|
|
81
|
+
<%_ } -%>
|
|
82
|
+
<%_ } -%>
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe('update', () => {
|
|
87
|
+
it('should update and return the user (Happy Path)', async () => {
|
|
88
|
+
// Arrange
|
|
89
|
+
const id = '1';
|
|
90
|
+
const data = { name: 'Updated' };
|
|
91
|
+
const expectedUser = { id: '1', name: 'Updated', email: 'test@example.com' };
|
|
92
|
+
|
|
93
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
94
|
+
const mockDbRecord = { _id: { toString: () => '1' }, name: 'Updated', email: 'test@example.com' };
|
|
95
|
+
(UserModel.findByIdAndUpdate as jest.Mock).mockResolvedValue(mockDbRecord);
|
|
96
|
+
<%_ } else if (database === 'None') { -%>
|
|
97
|
+
(UserModel.update as jest.Mock).mockResolvedValue(expectedUser);
|
|
98
|
+
<%_ } else { -%>
|
|
99
|
+
const mockDbRecord = { id: '1', name: 'Updated', email: 'test@example.com', update: jest.fn().mockResolvedValue(true) };
|
|
100
|
+
(UserModel.findByPk as jest.Mock).mockResolvedValue(mockDbRecord);
|
|
101
|
+
<%_ } -%>
|
|
102
|
+
|
|
103
|
+
// Act
|
|
104
|
+
const result = await userRepository.update(id, data);
|
|
105
|
+
|
|
106
|
+
// Assert
|
|
107
|
+
expect(result?.name).toEqual(data.name);
|
|
108
|
+
<%_ if (database !== 'None') { -%>
|
|
109
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
110
|
+
expect(UserModel.findByIdAndUpdate).toHaveBeenCalled();
|
|
111
|
+
<%_ } else { -%>
|
|
112
|
+
expect(UserModel.findByPk).toHaveBeenCalled();
|
|
113
|
+
<%_ } -%>
|
|
114
|
+
<%_ } -%>
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should return null when user not found (Error Handling)', async () => {
|
|
118
|
+
// Arrange
|
|
119
|
+
const id = '999';
|
|
120
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
121
|
+
(UserModel.findByIdAndUpdate as jest.Mock).mockResolvedValue(null);
|
|
122
|
+
<%_ } else if (database === 'None') { -%>
|
|
123
|
+
(UserModel.update as jest.Mock).mockResolvedValue(null);
|
|
124
|
+
<%_ } else { -%>
|
|
125
|
+
(UserModel.findByPk as jest.Mock).mockResolvedValue(null);
|
|
126
|
+
<%_ } -%>
|
|
127
|
+
|
|
128
|
+
// Act
|
|
129
|
+
const result = await userRepository.update(id, { name: 'Fail' });
|
|
130
|
+
|
|
131
|
+
// Assert
|
|
132
|
+
expect(result).toBeNull();
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe('delete', () => {
|
|
137
|
+
it('should successfully delete a user (Happy Path)', async () => {
|
|
138
|
+
// Arrange
|
|
139
|
+
const id = '1';
|
|
140
|
+
|
|
141
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
142
|
+
(UserModel.findByIdAndDelete as jest.Mock).mockResolvedValue(true);
|
|
143
|
+
<%_ } else if (database === 'None') { -%>
|
|
144
|
+
(UserModel.destroy as jest.Mock).mockResolvedValue(true);
|
|
145
|
+
<%_ } else { -%>
|
|
146
|
+
const mockDbRecord = { id: '1', destroy: jest.fn().mockResolvedValue(true) };
|
|
147
|
+
(UserModel.findByPk as jest.Mock).mockResolvedValue(mockDbRecord);
|
|
148
|
+
<%_ } -%>
|
|
149
|
+
|
|
150
|
+
// Act
|
|
151
|
+
const result = await userRepository.delete(id);
|
|
152
|
+
|
|
153
|
+
// Assert
|
|
154
|
+
expect(result).toBe(true);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('should return false when user not found during deletion (Error Handling)', async () => {
|
|
158
|
+
// Arrange
|
|
159
|
+
const id = '999';
|
|
160
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
161
|
+
(UserModel.findByIdAndDelete as jest.Mock).mockResolvedValue(null);
|
|
162
|
+
<%_ } else if (database === 'None') { -%>
|
|
163
|
+
(UserModel.destroy as jest.Mock).mockResolvedValue(false);
|
|
164
|
+
<%_ } else { -%>
|
|
165
|
+
(UserModel.findByPk as jest.Mock).mockResolvedValue(null);
|
|
166
|
+
<%_ } -%>
|
|
167
|
+
|
|
168
|
+
// Act
|
|
169
|
+
const result = await userRepository.delete(id);
|
|
170
|
+
|
|
171
|
+
// Assert
|
|
172
|
+
expect(result).toBe(false);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
});
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { User as UserEntity } from '@/domain/user';
|
|
2
|
+
import UserModel from '@/infrastructure/database/models/User';
|
|
3
|
+
|
|
4
|
+
export class UserRepository {
|
|
5
|
+
async save(user: UserEntity): Promise<UserEntity> {
|
|
6
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
7
|
+
const userData = { name: user.name, email: user.email };
|
|
8
|
+
<% if (auth && auth !== 'None') { %>if (user.password) (userData as any).password = user.password;<% } %>
|
|
9
|
+
<% if (includeMulter) { %>if (user.imageUrl) (userData as any).imageUrl = user.imageUrl;<% } %>
|
|
10
|
+
const newUser = await UserModel.create(userData);
|
|
11
|
+
return {
|
|
12
|
+
id: newUser._id.toString(),
|
|
13
|
+
name: newUser.name,
|
|
14
|
+
email: newUser.email,
|
|
15
|
+
<% if (auth && auth !== 'None') { %>password: (newUser as any).password,<% } %>
|
|
16
|
+
<% if (includeMulter) { %>imageUrl: (newUser as any).imageUrl,<% } %>
|
|
17
|
+
};
|
|
18
|
+
<%_ } else if (database === 'None') { -%>
|
|
19
|
+
const userData = { name: user.name, email: user.email };
|
|
20
|
+
<% if (auth && auth !== 'None') { %>if (user.password) (userData as any).password = user.password;<% } %>
|
|
21
|
+
<% if (includeMulter) { %>if (user.imageUrl) (userData as any).imageUrl = user.imageUrl;<% } %>
|
|
22
|
+
const newUser = await UserModel.create(userData);
|
|
23
|
+
return {
|
|
24
|
+
id: newUser.id,
|
|
25
|
+
name: newUser.name,
|
|
26
|
+
email: newUser.email,
|
|
27
|
+
<% if (auth && auth !== 'None') { %>password: (newUser as any).password,<% } %>
|
|
28
|
+
<% if (includeMulter) { %>imageUrl: (newUser as any).imageUrl,<% } %>
|
|
29
|
+
};
|
|
30
|
+
<%_ } else { -%>
|
|
31
|
+
const userData = { name: user.name, email: user.email };
|
|
32
|
+
<% if (auth && auth !== 'None') { %>if (user.password) (userData as any).password = user.password;<% } %>
|
|
33
|
+
<% if (includeMulter) { %>if (user.imageUrl) (userData as any).imageUrl = user.imageUrl;<% } %>
|
|
34
|
+
const newUser = await UserModel.create(userData);
|
|
35
|
+
return {
|
|
36
|
+
id: newUser.id,
|
|
37
|
+
name: newUser.name,
|
|
38
|
+
email: newUser.email,
|
|
39
|
+
<% if (auth && auth !== 'None') { %>password: (newUser as any).password,<% } %>
|
|
40
|
+
<% if (includeMulter) { %>imageUrl: (newUser as any).imageUrl,<% } %>
|
|
41
|
+
};
|
|
42
|
+
<%_ } -%>
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async getUsers(): Promise<UserEntity[]> {
|
|
46
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
47
|
+
const users = await UserModel.find();
|
|
48
|
+
return users.map(user => ({
|
|
49
|
+
id: user._id.toString(),
|
|
50
|
+
name: user.name,
|
|
51
|
+
email: user.email,
|
|
52
|
+
<% if (auth && auth !== 'None') { %>password: (user as any).password,<% } %>
|
|
53
|
+
<% if (includeMulter) { %>imageUrl: (user as any).imageUrl,<% } %>
|
|
54
|
+
}));
|
|
55
|
+
<%_ } else if (database === 'None') { -%>
|
|
56
|
+
const users = await UserModel.find();
|
|
57
|
+
return users.map(user => ({
|
|
58
|
+
id: user.id,
|
|
59
|
+
name: user.name,
|
|
60
|
+
email: user.email,
|
|
61
|
+
<% if (auth && auth !== 'None') { %>password: (user as any).password,<% } %>
|
|
62
|
+
<% if (includeMulter) { %>imageUrl: (user as any).imageUrl,<% } %>
|
|
63
|
+
}));
|
|
64
|
+
<%_ } else { -%>
|
|
65
|
+
const users = await UserModel.findAll();
|
|
66
|
+
return users.map(user => ({
|
|
67
|
+
id: user.id,
|
|
68
|
+
name: user.name,
|
|
69
|
+
email: user.email,
|
|
70
|
+
<% if (auth && auth !== 'None') { %>password: (user as any).password,<% } %>
|
|
71
|
+
<% if (includeMulter) { %>imageUrl: (user as any).imageUrl,<% } %>
|
|
72
|
+
}));
|
|
73
|
+
<%_ } -%>
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async update(id: number | string, data: Partial<UserEntity>): Promise<UserEntity | null> {
|
|
77
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
78
|
+
const user = await UserModel.findByIdAndUpdate(id, data, { new: true });
|
|
79
|
+
if (!user) return null;
|
|
80
|
+
return {
|
|
81
|
+
id: user._id.toString(),
|
|
82
|
+
name: user.name,
|
|
83
|
+
email: user.email,
|
|
84
|
+
<% if (auth && auth !== 'None') { %>password: (user as any).password,<% } %>
|
|
85
|
+
<% if (includeMulter) { %>imageUrl: (user as any).imageUrl,<% } %>
|
|
86
|
+
};
|
|
87
|
+
<%_ } else if (database === 'None') { -%>
|
|
88
|
+
const { id: _, ...updateData } = data;
|
|
89
|
+
const user = await UserModel.update(id, updateData as Parameters<typeof UserModel.update>[1]);
|
|
90
|
+
if (!user) return null;
|
|
91
|
+
return {
|
|
92
|
+
id: user.id,
|
|
93
|
+
name: user.name,
|
|
94
|
+
email: user.email,
|
|
95
|
+
<% if (auth && auth !== 'None') { %>password: (user as any).password,<% } %>
|
|
96
|
+
<% if (includeMulter) { %>imageUrl: (user as any).imageUrl,<% } %>
|
|
97
|
+
};
|
|
98
|
+
<%_ } else { -%>
|
|
99
|
+
const user = await UserModel.findByPk(id);
|
|
100
|
+
if (!user) return null;
|
|
101
|
+
await user.update(data);
|
|
102
|
+
return {
|
|
103
|
+
id: user.id || 0,
|
|
104
|
+
name: user.name,
|
|
105
|
+
email: user.email,
|
|
106
|
+
<% if (auth && auth !== 'None') { %>password: (user as any).password,<% } %>
|
|
107
|
+
<% if (includeMulter) { %>imageUrl: (user as any).imageUrl,<% } %>
|
|
108
|
+
};
|
|
109
|
+
<%_ } -%>
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async delete(id: number | string): Promise<boolean> {
|
|
113
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
114
|
+
const result = await UserModel.findByIdAndDelete(id);
|
|
115
|
+
return !!result;
|
|
116
|
+
<%_ } else if (database === 'None') { -%>
|
|
117
|
+
return await UserModel.destroy(id);
|
|
118
|
+
<%_ } else { -%>
|
|
119
|
+
const user = await UserModel.findByPk(id);
|
|
120
|
+
if (!user) return false;
|
|
121
|
+
await user.destroy();
|
|
122
|
+
return true;
|
|
123
|
+
<%_ } -%>
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
<% if (communication !== 'GraphQL') { -%>
|
|
2
|
+
import { Request, Response, NextFunction } from 'express';
|
|
3
|
+
import { HTTP_STATUS } from '@/utils/httpCodes';
|
|
4
|
+
<% } -%>
|
|
5
|
+
import { ERROR_MESSAGES } from '@/utils/errorMessages';
|
|
6
|
+
import { UserController } from '@/interfaces/controllers/userController';
|
|
7
|
+
import CreateUser from '@/usecases/createUser';
|
|
8
|
+
import GetAllUsers from '@/usecases/getAllUsers';
|
|
9
|
+
import UpdateUser from '@/usecases/updateUser';
|
|
10
|
+
import DeleteUser from '@/usecases/deleteUser';
|
|
11
|
+
|
|
12
|
+
// Mock dependencies
|
|
13
|
+
jest.mock('@/infrastructure/repositories/UserRepository');
|
|
14
|
+
jest.mock('@/usecases/createUser');
|
|
15
|
+
jest.mock('@/usecases/getAllUsers');
|
|
16
|
+
jest.mock('@/usecases/updateUser');
|
|
17
|
+
jest.mock('@/usecases/deleteUser');
|
|
18
|
+
jest.mock('@/infrastructure/log/logger');
|
|
19
|
+
<%_ if (communication === 'Kafka') { -%>
|
|
20
|
+
jest.mock('@/infrastructure/messaging/kafkaClient', () => ({
|
|
21
|
+
kafkaService: {
|
|
22
|
+
sendMessage: jest.fn().mockResolvedValue(undefined)
|
|
23
|
+
},
|
|
24
|
+
KafkaService: jest.fn().mockImplementation(() => ({
|
|
25
|
+
sendMessage: jest.fn().mockResolvedValue(undefined)
|
|
26
|
+
}))
|
|
27
|
+
}));
|
|
28
|
+
const { kafkaService } = require('@/infrastructure/messaging/kafkaClient');
|
|
29
|
+
<%_ } -%>
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
describe('UserController (Clean Architecture)', () => {
|
|
33
|
+
let userController: UserController;
|
|
34
|
+
let mockCreateUserUseCase: jest.Mocked<CreateUser>;
|
|
35
|
+
let mockGetAllUsersUseCase: jest.Mocked<GetAllUsers>;
|
|
36
|
+
let mockUpdateUserUseCase: jest.Mocked<UpdateUser>;
|
|
37
|
+
let mockDeleteUserUseCase: jest.Mocked<DeleteUser>;
|
|
38
|
+
<% if (communication !== 'GraphQL') { -%>
|
|
39
|
+
let mockRequest: Partial<Request>;
|
|
40
|
+
let mockResponse: Partial<Response>;
|
|
41
|
+
let mockNext: NextFunction;
|
|
42
|
+
<% } -%>
|
|
43
|
+
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
// Clear all mocks
|
|
46
|
+
jest.resetAllMocks();
|
|
47
|
+
|
|
48
|
+
userController = new UserController();
|
|
49
|
+
|
|
50
|
+
// Retrieve the mocked instances created inside UserController constructor
|
|
51
|
+
mockCreateUserUseCase = (CreateUser as jest.Mock).mock.instances[0] as jest.Mocked<CreateUser>;
|
|
52
|
+
mockGetAllUsersUseCase = (GetAllUsers as jest.Mock).mock.instances[0] as jest.Mocked<GetAllUsers>;
|
|
53
|
+
mockUpdateUserUseCase = (UpdateUser as jest.Mock).mock.instances[0] as jest.Mocked<UpdateUser>;
|
|
54
|
+
mockDeleteUserUseCase = (DeleteUser as jest.Mock).mock.instances[0] as jest.Mocked<DeleteUser>;
|
|
55
|
+
|
|
56
|
+
<% if (communication !== 'GraphQL') { -%>
|
|
57
|
+
mockRequest = {
|
|
58
|
+
body: {}
|
|
59
|
+
};
|
|
60
|
+
mockResponse = {
|
|
61
|
+
json: jest.fn(),
|
|
62
|
+
status: jest.fn().mockReturnThis(),
|
|
63
|
+
};
|
|
64
|
+
mockNext = jest.fn();
|
|
65
|
+
<% } -%>
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('getUsers', () => {
|
|
69
|
+
it('should return successfully (Happy Path)', async () => {
|
|
70
|
+
// Arrange
|
|
71
|
+
const usersMock = [{ id: '1', name: 'Test', email: 'test@example.com' }];
|
|
72
|
+
mockGetAllUsersUseCase.execute.mockResolvedValue(usersMock);
|
|
73
|
+
|
|
74
|
+
// Act
|
|
75
|
+
<% if (communication === 'GraphQL') { -%>
|
|
76
|
+
const result = await userController.getUsers();
|
|
77
|
+
|
|
78
|
+
// Assert
|
|
79
|
+
expect(result).toEqual(usersMock);
|
|
80
|
+
<% } else { -%>
|
|
81
|
+
await userController.getUsers(mockRequest as Request, mockResponse as Response, mockNext);
|
|
82
|
+
|
|
83
|
+
// Assert
|
|
84
|
+
expect(mockResponse.json).toHaveBeenCalledWith(usersMock);
|
|
85
|
+
<% } -%>
|
|
86
|
+
expect(mockGetAllUsersUseCase.execute).toHaveBeenCalled();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should handle errors correctly (Error Handling)', async () => {
|
|
90
|
+
// Arrange
|
|
91
|
+
const error = new Error('UseCase Error');
|
|
92
|
+
mockGetAllUsersUseCase.execute.mockRejectedValue(error);
|
|
93
|
+
|
|
94
|
+
// Act & Assert
|
|
95
|
+
<% if (communication === 'GraphQL') { -%>
|
|
96
|
+
await expect(userController.getUsers()).rejects.toThrow(error);
|
|
97
|
+
<% } else { -%>
|
|
98
|
+
await userController.getUsers(mockRequest as Request, mockResponse as Response, mockNext);
|
|
99
|
+
expect(mockNext).toHaveBeenCalledWith(error);
|
|
100
|
+
<% } -%>
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should handle non-Error objects in catch block', async () => {
|
|
104
|
+
// Arrange
|
|
105
|
+
const error = 'String Error';
|
|
106
|
+
mockGetAllUsersUseCase.execute.mockRejectedValue(error);
|
|
107
|
+
|
|
108
|
+
// Act & Assert
|
|
109
|
+
<% if (communication === 'GraphQL') { -%>
|
|
110
|
+
await expect(userController.getUsers()).rejects.toEqual(error);
|
|
111
|
+
<% } else { -%>
|
|
112
|
+
await userController.getUsers(mockRequest as Request, mockResponse as Response, mockNext);
|
|
113
|
+
expect(mockNext).toHaveBeenCalledWith(error);
|
|
114
|
+
<% } -%>
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe('createUser', () => {
|
|
119
|
+
it('should successfully create a new user (Happy Path)', async () => {
|
|
120
|
+
// Arrange
|
|
121
|
+
const payload = { name: 'Alice', email: 'alice@example.com' };
|
|
122
|
+
<% if (communication === 'GraphQL') { -%>
|
|
123
|
+
const dataArg = payload;
|
|
124
|
+
<% } else { -%>
|
|
125
|
+
mockRequest.body = payload;
|
|
126
|
+
<% } -%>
|
|
127
|
+
const expectedUser = { id: '1', ...payload };
|
|
128
|
+
|
|
129
|
+
mockCreateUserUseCase.execute.mockResolvedValue(expectedUser);
|
|
130
|
+
|
|
131
|
+
// Act
|
|
132
|
+
<% if (communication === 'GraphQL') { -%>
|
|
133
|
+
const result = await userController.createUser(dataArg);
|
|
134
|
+
|
|
135
|
+
// Assert
|
|
136
|
+
expect(result).toEqual(expectedUser);
|
|
137
|
+
<% } else { -%>
|
|
138
|
+
await userController.createUser(mockRequest as Request, mockResponse as Response, mockNext);
|
|
139
|
+
|
|
140
|
+
// Assert
|
|
141
|
+
<%_ if (communication === 'Kafka') { -%>
|
|
142
|
+
const { kafkaService } = require('@/infrastructure/messaging/kafkaClient');
|
|
143
|
+
expect(kafkaService.sendMessage).toHaveBeenCalled();
|
|
144
|
+
<%_ } -%>
|
|
145
|
+
expect(mockResponse.status).toHaveBeenCalledWith(HTTP_STATUS.CREATED);
|
|
146
|
+
|
|
147
|
+
expect(mockResponse.json).toHaveBeenCalledWith(expectedUser);
|
|
148
|
+
<% } -%>
|
|
149
|
+
expect(mockCreateUserUseCase.execute).toHaveBeenCalledWith(payload.name, payload.email);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('should handle errors when creation fails (Error Handling)', async () => {
|
|
153
|
+
// Arrange
|
|
154
|
+
const error = new Error('Creation Error');
|
|
155
|
+
const payload = { name: 'Bob', email: 'bob@example.com' };
|
|
156
|
+
<% if (communication === 'GraphQL') { -%>
|
|
157
|
+
const dataArg = payload;
|
|
158
|
+
<% } else { -%>
|
|
159
|
+
mockRequest.body = payload;
|
|
160
|
+
<% } -%>
|
|
161
|
+
|
|
162
|
+
mockCreateUserUseCase.execute.mockRejectedValue(error);
|
|
163
|
+
|
|
164
|
+
// Act & Assert
|
|
165
|
+
<% if (communication === 'GraphQL') { -%>
|
|
166
|
+
await expect(userController.createUser(dataArg)).rejects.toThrow(error);
|
|
167
|
+
<% } else { -%>
|
|
168
|
+
await userController.createUser(mockRequest as Request, mockResponse as Response, mockNext);
|
|
169
|
+
expect(mockNext).toHaveBeenCalledWith(error);
|
|
170
|
+
<% } -%>
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('should handle non-Error objects in catch block when creation fails', async () => {
|
|
174
|
+
// Arrange
|
|
175
|
+
const error = 'Creation String Error';
|
|
176
|
+
const payload = { name: 'Bob', email: 'bob@example.com' };
|
|
177
|
+
<% if (communication === 'GraphQL') { -%>
|
|
178
|
+
const dataArg = payload;
|
|
179
|
+
<% } else { -%>
|
|
180
|
+
mockRequest.body = payload;
|
|
181
|
+
<% } -%>
|
|
182
|
+
|
|
183
|
+
mockCreateUserUseCase.execute.mockRejectedValue(error);
|
|
184
|
+
|
|
185
|
+
// Act & Assert
|
|
186
|
+
<% if (communication === 'GraphQL') { -%>
|
|
187
|
+
await expect(userController.createUser(dataArg)).rejects.toEqual(error);
|
|
188
|
+
<% } else { -%>
|
|
189
|
+
await userController.createUser(mockRequest as Request, mockResponse as Response, mockNext);
|
|
190
|
+
expect(mockNext).toHaveBeenCalledWith(error);
|
|
191
|
+
<% } -%>
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
describe('updateUser', () => {
|
|
196
|
+
it('should successfully update a user (Happy Path)', async () => {
|
|
197
|
+
// Arrange
|
|
198
|
+
const id = '1';
|
|
199
|
+
const payload = { name: 'Alice Updated' };
|
|
200
|
+
<% if (communication === 'GraphQL') { -%>
|
|
201
|
+
const idArg = id;
|
|
202
|
+
const dataArg = payload;
|
|
203
|
+
<% } else { -%>
|
|
204
|
+
mockRequest.params = { id };
|
|
205
|
+
mockRequest.body = payload;
|
|
206
|
+
<% } -%>
|
|
207
|
+
const expectedUser = { id, name: 'Alice Updated', email: 'alice@example.com' };
|
|
208
|
+
|
|
209
|
+
mockUpdateUserUseCase.execute.mockResolvedValue(expectedUser as any);
|
|
210
|
+
|
|
211
|
+
// Act
|
|
212
|
+
<% if (communication === 'GraphQL') { -%>
|
|
213
|
+
const result = await userController.updateUser(idArg, dataArg);
|
|
214
|
+
expect(result).toEqual(expectedUser);
|
|
215
|
+
<% } else { -%>
|
|
216
|
+
await userController.updateUser(mockRequest as Request, mockResponse as Response, mockNext);
|
|
217
|
+
expect(mockResponse.json).toHaveBeenCalledWith(expectedUser);
|
|
218
|
+
<% } -%>
|
|
219
|
+
|
|
220
|
+
<%_ if (communication === 'Kafka') { -%>
|
|
221
|
+
const { kafkaService } = require('@/infrastructure/messaging/kafkaClient');
|
|
222
|
+
expect(kafkaService.sendMessage).toHaveBeenCalled();
|
|
223
|
+
<%_ } -%>
|
|
224
|
+
expect(mockUpdateUserUseCase.execute).toHaveBeenCalledWith(id, payload);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('should handle 404/errors when user not found or update fails', async () => {
|
|
228
|
+
// Arrange
|
|
229
|
+
const id = '999';
|
|
230
|
+
<%_ if (communication === 'GraphQL') { -%>
|
|
231
|
+
const idArg = id;
|
|
232
|
+
const dataArg = { name: 'Fail' };
|
|
233
|
+
mockUpdateUserUseCase.execute.mockResolvedValue(null);
|
|
234
|
+
await expect(userController.updateUser(idArg, dataArg)).rejects.toThrow(ERROR_MESSAGES.USER_NOT_FOUND);
|
|
235
|
+
<%_ } else { -%>
|
|
236
|
+
mockRequest.params = { id };
|
|
237
|
+
mockRequest.body = { name: 'Fail' };
|
|
238
|
+
mockUpdateUserUseCase.execute.mockResolvedValue(null);
|
|
239
|
+
|
|
240
|
+
// Act
|
|
241
|
+
await userController.updateUser(mockRequest as Request, mockResponse as Response, mockNext);
|
|
242
|
+
expect(mockResponse.status).toHaveBeenCalledWith(HTTP_STATUS.NOT_FOUND);
|
|
243
|
+
<%_ } -%>
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('should handle database errors during update (Error Handling)', async () => {
|
|
247
|
+
// Arrange
|
|
248
|
+
const id = '1';
|
|
249
|
+
const error = new Error('Database Error');
|
|
250
|
+
mockUpdateUserUseCase.execute.mockRejectedValue(error);
|
|
251
|
+
<%_ if (communication === 'GraphQL') { -%>
|
|
252
|
+
await expect(userController.updateUser(id, { name: 'Fail' })).rejects.toThrow(error);
|
|
253
|
+
<%_ } else { -%>
|
|
254
|
+
mockRequest.params = { id };
|
|
255
|
+
await userController.updateUser(mockRequest as Request, mockResponse as Response, mockNext);
|
|
256
|
+
expect(mockNext).toHaveBeenCalledWith(error);
|
|
257
|
+
<%_ } -%>
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
describe('deleteUser', () => {
|
|
262
|
+
it('should successfully delete a user (Happy Path)', async () => {
|
|
263
|
+
// Arrange
|
|
264
|
+
const id = '1';
|
|
265
|
+
<% if (communication === 'GraphQL') { -%>
|
|
266
|
+
const idArg = id;
|
|
267
|
+
<% } else { -%>
|
|
268
|
+
mockRequest.params = { id };
|
|
269
|
+
<% } -%>
|
|
270
|
+
mockDeleteUserUseCase.execute.mockResolvedValue(true);
|
|
271
|
+
|
|
272
|
+
// Act
|
|
273
|
+
<% if (communication === 'GraphQL') { -%>
|
|
274
|
+
const result = await userController.deleteUser(idArg);
|
|
275
|
+
expect(result).toBe(true);
|
|
276
|
+
<% } else { -%>
|
|
277
|
+
await userController.deleteUser(mockRequest as Request, mockResponse as Response, mockNext);
|
|
278
|
+
expect(mockResponse.status).toHaveBeenCalledWith(HTTP_STATUS.OK);
|
|
279
|
+
<% } -%>
|
|
280
|
+
|
|
281
|
+
<%_ if (communication === 'Kafka') { -%>
|
|
282
|
+
const { kafkaService } = require('@/infrastructure/messaging/kafkaClient');
|
|
283
|
+
expect(kafkaService.sendMessage).toHaveBeenCalled();
|
|
284
|
+
<%_ } -%>
|
|
285
|
+
expect(mockDeleteUserUseCase.execute).toHaveBeenCalledWith(id);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it('should throw error if user not found during deletion (Error Handling)', async () => {
|
|
289
|
+
// Arrange
|
|
290
|
+
const id = '999';
|
|
291
|
+
<%_ if (communication === 'GraphQL') { -%>
|
|
292
|
+
mockDeleteUserUseCase.execute.mockResolvedValue(false);
|
|
293
|
+
await expect(userController.deleteUser(id)).rejects.toThrow(ERROR_MESSAGES.USER_NOT_FOUND);
|
|
294
|
+
<%_ } else { -%>
|
|
295
|
+
mockRequest.params = { id };
|
|
296
|
+
mockDeleteUserUseCase.execute.mockResolvedValue(false);
|
|
297
|
+
await userController.deleteUser(mockRequest as Request, mockResponse as Response, mockNext);
|
|
298
|
+
expect(mockResponse.status).toHaveBeenCalledWith(HTTP_STATUS.NOT_FOUND);
|
|
299
|
+
<%_ } -%>
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it('should handle database errors during deletion (Error Handling)', async () => {
|
|
303
|
+
// Arrange
|
|
304
|
+
const id = '1';
|
|
305
|
+
const error = new Error('Database Error');
|
|
306
|
+
mockDeleteUserUseCase.execute.mockRejectedValue(error);
|
|
307
|
+
<%_ if (communication === 'GraphQL') { -%>
|
|
308
|
+
await expect(userController.deleteUser(id)).rejects.toThrow(error);
|
|
309
|
+
<%_ } else { -%>
|
|
310
|
+
mockRequest.params = { id };
|
|
311
|
+
await userController.deleteUser(mockRequest as Request, mockResponse as Response, mockNext);
|
|
312
|
+
expect(mockNext).toHaveBeenCalledWith(error);
|
|
313
|
+
<%_ } -%>
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
describe('createUser Error Paths', () => {
|
|
319
|
+
it('should handle database errors during creation (Error Handling)', async () => {
|
|
320
|
+
const error = new Error('Database Error');
|
|
321
|
+
mockCreateUserUseCase.execute.mockRejectedValue(error);
|
|
322
|
+
<%_ if (communication === 'GraphQL') { -%>
|
|
323
|
+
await expect(userController.createUser({ name: 'Alice', email: 'alice@example.com' })).rejects.toThrow(error);
|
|
324
|
+
<%_ } else { -%>
|
|
325
|
+
mockRequest.body = { name: 'Alice', email: 'alice@example.com' };
|
|
326
|
+
await userController.createUser(mockRequest as Request, mockResponse as Response, mockNext);
|
|
327
|
+
expect(mockNext).toHaveBeenCalledWith(error);
|
|
328
|
+
<%_ } -%>
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
});
|