From 50373c74bbfec0e28500edb3b3083d458f143fae Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Wed, 11 Dec 2024 20:42:11 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20=E2=9C=A8=20add=20GetAllTasksUseCas?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/tasks/application/src/index.ts | 4 +++- libs/tasks/application/src/lib/tasks.service.ts | 7 +++++-- .../src/lib/use-cases/get-all-tasks.use-case.ts | 14 ++++++++++++++ .../interface-adapters/src/lib/tasks.module.ts | 14 ++++++++++++-- 4 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 libs/tasks/application/src/lib/use-cases/get-all-tasks.use-case.ts diff --git a/libs/tasks/application/src/index.ts b/libs/tasks/application/src/index.ts index 05a5430..db6d3b9 100644 --- a/libs/tasks/application/src/index.ts +++ b/libs/tasks/application/src/index.ts @@ -1,2 +1,4 @@ export * from './lib/tasks.service'; -export * from './lib/user-tasks.service'; \ No newline at end of file +export * from './lib/use-cases/get-all-tasks.use-case'; + +export * from './lib/user-tasks.service'; diff --git a/libs/tasks/application/src/lib/tasks.service.ts b/libs/tasks/application/src/lib/tasks.service.ts index 4ae8c08..35e617c 100644 --- a/libs/tasks/application/src/lib/tasks.service.ts +++ b/libs/tasks/application/src/lib/tasks.service.ts @@ -1,10 +1,13 @@ import { Injectable } from '@nestjs/common'; import { Task } from '@tasks/domain'; +import { GetAllTasksUseCase } from './use-cases/get-all-tasks.use-case'; + @Injectable() export class TasksService { + constructor(private readonly getAllTasksUseCase: GetAllTasksUseCase) {} + async findAll(): Promise { - // TODO: Implement this - return []; + return await this.getAllTasksUseCase.execute(); } } diff --git a/libs/tasks/application/src/lib/use-cases/get-all-tasks.use-case.ts b/libs/tasks/application/src/lib/use-cases/get-all-tasks.use-case.ts new file mode 100644 index 0000000..0348449 --- /dev/null +++ b/libs/tasks/application/src/lib/use-cases/get-all-tasks.use-case.ts @@ -0,0 +1,14 @@ +import { Injectable } from '@nestjs/common'; +import { Task, TasksRepository } from '@tasks/domain'; + +@Injectable() +export class GetAllTasksUseCase { + constructor( + // @Inject(TASKS_REPOSITORY) + private readonly tasksRepository: TasksRepository, + ) {} + + async execute(): Promise { + return await this.tasksRepository.findAll(); + } +} diff --git a/libs/tasks/interface-adapters/src/lib/tasks.module.ts b/libs/tasks/interface-adapters/src/lib/tasks.module.ts index 0a59729..cf9bbfe 100644 --- a/libs/tasks/interface-adapters/src/lib/tasks.module.ts +++ b/libs/tasks/interface-adapters/src/lib/tasks.module.ts @@ -1,12 +1,22 @@ import { Module } from '@nestjs/common'; -import { TasksService, UserTasksService } from '@tasks/application'; +import { + TasksService, + UserTasksService, + GetAllTasksUseCase, +} from '@tasks/application'; import { TasksResolver } from './resolver/tasks.resolver'; import { UserTasksResolver } from './resolver/user-tasks.resolver'; @Module({ + providers: [ + TasksResolver, + TasksService, + GetAllTasksUseCase, + UserTasksResolver, + UserTasksService, + ], imports: [], - providers: [TasksResolver, TasksService, UserTasksResolver, UserTasksService], exports: [TasksService, UserTasksService], }) export class TasksModule {} From 90385d2fad28c2686748ff20053c487962517741 Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Fri, 13 Dec 2024 22:19:06 +0900 Subject: [PATCH 2/6] =?UTF-8?q?feat:=20=E2=9C=A8=20add=20TaskDocument=20an?= =?UTF-8?q?d=20TaskSchema?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mongoose/src/lib/task.schema.spec.ts | 48 +++++++++++++++++++ .../mongoose/src/lib/task.schema.ts | 16 +++++++ 2 files changed, 64 insertions(+) create mode 100644 libs/users/infrastructure/mongoose/src/lib/task.schema.spec.ts create mode 100644 libs/users/infrastructure/mongoose/src/lib/task.schema.ts diff --git a/libs/users/infrastructure/mongoose/src/lib/task.schema.spec.ts b/libs/users/infrastructure/mongoose/src/lib/task.schema.spec.ts new file mode 100644 index 0000000..63c0273 --- /dev/null +++ b/libs/users/infrastructure/mongoose/src/lib/task.schema.spec.ts @@ -0,0 +1,48 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { getModelToken } from '@nestjs/mongoose'; +import { Model } from 'mongoose'; +import { TaskDocument } from './task.schema'; + +describe('TaskDocument', () => { + let taskModel: Model; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: getModelToken(TaskDocument.name), + useValue: Model, + }, + ], + }).compile(); + + taskModel = module.get>( + getModelToken(TaskDocument.name), + ); + }); + + it('should be defined', () => { + expect(taskModel).toBeDefined(); + }); + + it('should create a task with required fields', () => { + const task = new taskModel({ + title: 'Test Task', + categories: ['test'], + }); + expect(task.title).toBe('Test Task'); + expect(task.categories).toEqual(['test']); + expect(task.description).toBeNull(); + }); + + it('should create a task with all fields', () => { + const task = new taskModel({ + title: 'Test Task', + description: 'Test Description', + categories: ['test', 'example'], + }); + expect(task.title).toBe('Test Task'); + expect(task.description).toBe('Test Description'); + expect(task.categories).toEqual(['test', 'example']); + }); +}); diff --git a/libs/users/infrastructure/mongoose/src/lib/task.schema.ts b/libs/users/infrastructure/mongoose/src/lib/task.schema.ts new file mode 100644 index 0000000..16b9176 --- /dev/null +++ b/libs/users/infrastructure/mongoose/src/lib/task.schema.ts @@ -0,0 +1,16 @@ +import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; +import { Document } from 'mongoose'; + +@Schema() +export class TaskDocument extends Document { + @Prop({ required: true, maxlength: 200 }) + title!: string; + + @Prop({ required: false, default: null, maxlength: 1000 }) + description!: string; + + @Prop({ required: true, index: true, maxlength: 100 }) + categories!: string[]; +} + +export const TaskSchema = SchemaFactory.createForClass(TaskDocument); From a5dd726c2197027e42bb13b821b574fa11c5ba67 Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Fri, 13 Dec 2024 22:28:55 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20=E2=9C=A8=20add=20maxlength=20and?= =?UTF-8?q?=20default=20to=20UserDocument?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infrastructure/mongoose/src/lib/user.schema.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/libs/users/infrastructure/mongoose/src/lib/user.schema.ts b/libs/users/infrastructure/mongoose/src/lib/user.schema.ts index 1cc6d43..c195bcb 100644 --- a/libs/users/infrastructure/mongoose/src/lib/user.schema.ts +++ b/libs/users/infrastructure/mongoose/src/lib/user.schema.ts @@ -4,12 +4,18 @@ import { Document } from 'mongoose'; @Schema() export class UserDocument extends Document { // refactor: move match regex to a shared lib - @Prop({ required: true, unique: true, match: /^[^\s@]+@[^\s@]+\.[^\s@]+$/ }) + @Prop({ + required: true, + unique: true, + match: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, + maxlength: 100, + }) email!: string; - @Prop({ required: false}) + @Prop({ required: false, default: null, maxlength: 100 }) firstName!: string; - @Prop({ required: false }) + + @Prop({ required: false, default: null, maxlength: 100 }) lastName!: string; } From c612fd03e5cd7413c247d30d7b012053fddcc398 Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Fri, 13 Dec 2024 23:04:59 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20=E2=9C=A8=20add=20@tasks/infrastruc?= =?UTF-8?q?ture-mongoose?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 1 + libs/tasks/infrastructure/mongoose/README.md | 7 ++ .../infrastructure/mongoose/eslint.config.js | 3 + .../infrastructure/mongoose/jest.config.ts | 10 ++ .../infrastructure/mongoose/project.json | 9 ++ .../infrastructure/mongoose/src/index.ts | 2 + .../src/lib/mongoose-tasks.repository.spec.ts | 79 +++++++++++++++ .../src/lib/mongoose-tasks.repository.ts | 25 +++++ .../mongoose/src/lib/task.schema.spec.ts | 21 ---- .../mongoose/src/lib/task.schema.ts | 0 .../infrastructure/mongoose/tsconfig.json | 22 +++++ .../infrastructure/mongoose/tsconfig.lib.json | 11 +++ .../mongoose/tsconfig.spec.json | 14 +++ .../infrastructure/mongoose/src/index.ts | 2 +- .../src/lib/mongoose-users.repository.spec.ts | 95 +++++++++++++++++++ .../src/lib/mongoose-users.repository.ts | 1 + tsconfig.base.json | 3 + 17 files changed, 283 insertions(+), 22 deletions(-) create mode 100644 libs/tasks/infrastructure/mongoose/README.md create mode 100644 libs/tasks/infrastructure/mongoose/eslint.config.js create mode 100644 libs/tasks/infrastructure/mongoose/jest.config.ts create mode 100644 libs/tasks/infrastructure/mongoose/project.json create mode 100644 libs/tasks/infrastructure/mongoose/src/index.ts create mode 100644 libs/tasks/infrastructure/mongoose/src/lib/mongoose-tasks.repository.spec.ts create mode 100644 libs/tasks/infrastructure/mongoose/src/lib/mongoose-tasks.repository.ts rename libs/{users => tasks}/infrastructure/mongoose/src/lib/task.schema.spec.ts (51%) rename libs/{users => tasks}/infrastructure/mongoose/src/lib/task.schema.ts (100%) create mode 100644 libs/tasks/infrastructure/mongoose/tsconfig.json create mode 100644 libs/tasks/infrastructure/mongoose/tsconfig.lib.json create mode 100644 libs/tasks/infrastructure/mongoose/tsconfig.spec.json create mode 100644 libs/users/infrastructure/mongoose/src/lib/mongoose-users.repository.spec.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index cda0949..f6fd5db 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ { "cSpell.words": [ "compodoc", + "maxlength", "nestjs", "pnpx", "supergraph" diff --git a/libs/tasks/infrastructure/mongoose/README.md b/libs/tasks/infrastructure/mongoose/README.md new file mode 100644 index 0000000..2d78fad --- /dev/null +++ b/libs/tasks/infrastructure/mongoose/README.md @@ -0,0 +1,7 @@ +# tasks-infrastructure-mongoose + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test tasks-infrastructure-mongoose` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/tasks/infrastructure/mongoose/eslint.config.js b/libs/tasks/infrastructure/mongoose/eslint.config.js new file mode 100644 index 0000000..cdd3cba --- /dev/null +++ b/libs/tasks/infrastructure/mongoose/eslint.config.js @@ -0,0 +1,3 @@ +const baseConfig = require('../../../../eslint.config.js'); + +module.exports = [...baseConfig]; diff --git a/libs/tasks/infrastructure/mongoose/jest.config.ts b/libs/tasks/infrastructure/mongoose/jest.config.ts new file mode 100644 index 0000000..7acf0be --- /dev/null +++ b/libs/tasks/infrastructure/mongoose/jest.config.ts @@ -0,0 +1,10 @@ +export default { + displayName: 'tasks-infrastructure-mongoose', + preset: '../../../../jest.preset.js', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../../../coverage/libs/users/infrastructure/mongoose', +}; diff --git a/libs/tasks/infrastructure/mongoose/project.json b/libs/tasks/infrastructure/mongoose/project.json new file mode 100644 index 0000000..0baba1d --- /dev/null +++ b/libs/tasks/infrastructure/mongoose/project.json @@ -0,0 +1,9 @@ +{ + "name": "tasks-infrastructure-mongoose", + "$schema": "../../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/users/infrastructure/mongoose/src", + "projectType": "library", + "tags": [], + "// targets": "to see all targets run: nx show project tasks-infrastructure-mongoose --web", + "targets": {} +} diff --git a/libs/tasks/infrastructure/mongoose/src/index.ts b/libs/tasks/infrastructure/mongoose/src/index.ts new file mode 100644 index 0000000..10d4542 --- /dev/null +++ b/libs/tasks/infrastructure/mongoose/src/index.ts @@ -0,0 +1,2 @@ +export * from './lib/mongoose-tasks.repository'; +export * from './lib/task.schema'; \ No newline at end of file diff --git a/libs/tasks/infrastructure/mongoose/src/lib/mongoose-tasks.repository.spec.ts b/libs/tasks/infrastructure/mongoose/src/lib/mongoose-tasks.repository.spec.ts new file mode 100644 index 0000000..ddf00a1 --- /dev/null +++ b/libs/tasks/infrastructure/mongoose/src/lib/mongoose-tasks.repository.spec.ts @@ -0,0 +1,79 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { getModelToken } from '@nestjs/mongoose'; +import { Model } from 'mongoose'; +import { Task } from '@tasks/domain'; + +import { TaskDocument } from './task.schema'; +import { MongooseTasksRepository } from './mongoose-tasks.repository'; + +describe('MongooseTasksRepository', () => { + let repository: MongooseTasksRepository; + let taskModel: Model; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + MongooseTasksRepository, + { + provide: getModelToken(TaskDocument.name), + useValue: { + find: jest.fn(), + }, + }, + ], + }).compile(); + + repository = module.get(MongooseTasksRepository); + taskModel = module.get>(getModelToken(TaskDocument.name)); + }); + + it('should be defined', () => { + expect(repository).toBeDefined(); + }); + + describe('findAll', () => { + it('should return empty array when no tasks exist', async () => { + jest.spyOn(taskModel, 'find').mockReturnValue({ + exec: jest.fn().mockResolvedValue(null), + } as any); + + const result = await repository.findAll(); + expect(result).toEqual([]); + }); + + it('should return array of Tasks when found', async () => { + const mockTasks = [ + { + id: '123', + title: 'Test Task', + description: 'Test Description', + categories: ['test'], + }, + { + id: '456', + title: 'Another Task', + description: null, + categories: ['test', 'another'], + }, + ]; + + jest.spyOn(taskModel, 'find').mockReturnValue({ + exec: jest.fn().mockResolvedValue(mockTasks), + } as any); + + const result = await repository.findAll(); + expect(result).toHaveLength(2); + expect(result[0]).toBeInstanceOf(Task); + expect(result[0].id).toBe(mockTasks[0].id); + expect(result[0].title).toBe(mockTasks[0].title); + expect(result[0].description).toBe(mockTasks[0].description); + expect(result[0].categories).toEqual(mockTasks[0].categories); + + expect(result[1]).toBeInstanceOf(Task); + expect(result[1].id).toBe(mockTasks[1].id); + expect(result[1].title).toBe(mockTasks[1].title); + expect(result[1].description).toBe(mockTasks[1].description); + expect(result[1].categories).toEqual(mockTasks[1].categories); + }); + }); +}); diff --git a/libs/tasks/infrastructure/mongoose/src/lib/mongoose-tasks.repository.ts b/libs/tasks/infrastructure/mongoose/src/lib/mongoose-tasks.repository.ts new file mode 100644 index 0000000..53b3db7 --- /dev/null +++ b/libs/tasks/infrastructure/mongoose/src/lib/mongoose-tasks.repository.ts @@ -0,0 +1,25 @@ +import { Injectable } from '@nestjs/common'; +import { InjectModel } from '@nestjs/mongoose'; +import { Task, TasksRepository } from '@tasks/domain'; +import { Model } from 'mongoose'; + +import { TaskDocument } from './task.schema'; + +@Injectable() +export class MongooseTasksRepository implements TasksRepository { + constructor( + @InjectModel(TaskDocument.name) private taskModel: Model, + ) {} + + async findAll(): Promise { + const tasks = await this.taskModel.find().exec(); + + if (!tasks) { + return []; + } + + return tasks.map((task) => + Task.create(task.id, task.title, task.description, task.categories), + ); + } +} diff --git a/libs/users/infrastructure/mongoose/src/lib/task.schema.spec.ts b/libs/tasks/infrastructure/mongoose/src/lib/task.schema.spec.ts similarity index 51% rename from libs/users/infrastructure/mongoose/src/lib/task.schema.spec.ts rename to libs/tasks/infrastructure/mongoose/src/lib/task.schema.spec.ts index 63c0273..fc7defd 100644 --- a/libs/users/infrastructure/mongoose/src/lib/task.schema.spec.ts +++ b/libs/tasks/infrastructure/mongoose/src/lib/task.schema.spec.ts @@ -24,25 +24,4 @@ describe('TaskDocument', () => { it('should be defined', () => { expect(taskModel).toBeDefined(); }); - - it('should create a task with required fields', () => { - const task = new taskModel({ - title: 'Test Task', - categories: ['test'], - }); - expect(task.title).toBe('Test Task'); - expect(task.categories).toEqual(['test']); - expect(task.description).toBeNull(); - }); - - it('should create a task with all fields', () => { - const task = new taskModel({ - title: 'Test Task', - description: 'Test Description', - categories: ['test', 'example'], - }); - expect(task.title).toBe('Test Task'); - expect(task.description).toBe('Test Description'); - expect(task.categories).toEqual(['test', 'example']); - }); }); diff --git a/libs/users/infrastructure/mongoose/src/lib/task.schema.ts b/libs/tasks/infrastructure/mongoose/src/lib/task.schema.ts similarity index 100% rename from libs/users/infrastructure/mongoose/src/lib/task.schema.ts rename to libs/tasks/infrastructure/mongoose/src/lib/task.schema.ts diff --git a/libs/tasks/infrastructure/mongoose/tsconfig.json b/libs/tasks/infrastructure/mongoose/tsconfig.json new file mode 100644 index 0000000..07e0ec6 --- /dev/null +++ b/libs/tasks/infrastructure/mongoose/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noPropertyAccessFromIndexSignature": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/tasks/infrastructure/mongoose/tsconfig.lib.json b/libs/tasks/infrastructure/mongoose/tsconfig.lib.json new file mode 100644 index 0000000..28369ef --- /dev/null +++ b/libs/tasks/infrastructure/mongoose/tsconfig.lib.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "../../../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"], + "include": ["src/**/*.ts"] +} diff --git a/libs/tasks/infrastructure/mongoose/tsconfig.spec.json b/libs/tasks/infrastructure/mongoose/tsconfig.spec.json new file mode 100644 index 0000000..6668655 --- /dev/null +++ b/libs/tasks/infrastructure/mongoose/tsconfig.spec.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/libs/users/infrastructure/mongoose/src/index.ts b/libs/users/infrastructure/mongoose/src/index.ts index 9310eb9..bf9896f 100644 --- a/libs/users/infrastructure/mongoose/src/index.ts +++ b/libs/users/infrastructure/mongoose/src/index.ts @@ -1,2 +1,2 @@ export * from './lib/mongoose-users.repository'; -export * from './lib/user.schema'; \ No newline at end of file +export * from './lib/user.schema'; diff --git a/libs/users/infrastructure/mongoose/src/lib/mongoose-users.repository.spec.ts b/libs/users/infrastructure/mongoose/src/lib/mongoose-users.repository.spec.ts new file mode 100644 index 0000000..d6c59d1 --- /dev/null +++ b/libs/users/infrastructure/mongoose/src/lib/mongoose-users.repository.spec.ts @@ -0,0 +1,95 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { getModelToken } from '@nestjs/mongoose'; +import { Model } from 'mongoose'; +import { User } from '@users/domain'; + +import { UserDocument } from './user.schema'; +import { MongooseUsersRepository } from './mongoose-users.repository'; + +describe('MongooseUsersRepository', () => { + let repository: MongooseUsersRepository; + let userModel: Model; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + MongooseUsersRepository, + { + provide: getModelToken(UserDocument.name), + useValue: { + findOne: jest.fn(), + }, + }, + ], + }).compile(); + + repository = module.get(MongooseUsersRepository); + userModel = module.get>(getModelToken(UserDocument.name)); + }); + + it('should be defined', () => { + expect(repository).toBeDefined(); + }); + + describe('findOneById', () => { + it('should return null when user is not found', async () => { + jest.spyOn(userModel, 'findOne').mockReturnValue({ + exec: jest.fn().mockResolvedValue(null), + } as any); + + const result = await repository.findOneById('non-existent-id'); + expect(result).toBeNull(); + }); + + it('should return User when found', async () => { + const mockUser = { + id: '123', + email: 'test@example.com', + firstName: 'John', + lastName: 'Doe', + }; + + jest.spyOn(userModel, 'findOne').mockReturnValue({ + exec: jest.fn().mockResolvedValue(mockUser), + } as any); + + const result = await repository.findOneById('123'); + expect(result).toBeInstanceOf(User); + expect(result?.id).toBe(mockUser.id); + expect(result?.email).toBe(mockUser.email); + expect(result?.firstName).toBe(mockUser.firstName); + expect(result?.lastName).toBe(mockUser.lastName); + }); + }); + + describe('findOneByEmail', () => { + it('should return null when user is not found', async () => { + jest.spyOn(userModel, 'findOne').mockReturnValue({ + exec: jest.fn().mockResolvedValue(null), + } as any); + + const result = await repository.findOneByEmail('non-existent@example.com'); + expect(result).toBeNull(); + }); + + it('should return User when found', async () => { + const mockUser = { + id: '123', + email: 'test@example.com', + firstName: 'John', + lastName: 'Doe', + }; + + jest.spyOn(userModel, 'findOne').mockReturnValue({ + exec: jest.fn().mockResolvedValue(mockUser), + } as any); + + const result = await repository.findOneByEmail('test@example.com'); + expect(result).toBeInstanceOf(User); + expect(result?.id).toBe(mockUser.id); + expect(result?.email).toBe(mockUser.email); + expect(result?.firstName).toBe(mockUser.firstName); + expect(result?.lastName).toBe(mockUser.lastName); + }); + }); +}); diff --git a/libs/users/infrastructure/mongoose/src/lib/mongoose-users.repository.ts b/libs/users/infrastructure/mongoose/src/lib/mongoose-users.repository.ts index 51f143e..a993d2f 100644 --- a/libs/users/infrastructure/mongoose/src/lib/mongoose-users.repository.ts +++ b/libs/users/infrastructure/mongoose/src/lib/mongoose-users.repository.ts @@ -18,6 +18,7 @@ export class MongooseUsersRepository implements UsersRepository { if (!userDocument) { return null; } + return new User( userDocument.id, userDocument.email, diff --git a/tsconfig.base.json b/tsconfig.base.json index eda1584..c579463 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -27,6 +27,9 @@ ], "@tasks/application": ["libs/tasks/application/src/index.ts"], "@tasks/domain": ["libs/tasks/domain/src/index.ts"], + "@tasks/infrastructure-mongoose": [ + "libs/tasks/infrastructure/mongoose/src/index.ts" + ], "@tasks/interface-adapters": [ "libs/tasks/interface-adapters/src/index.ts" ], From 54cbf07cd7503a1d48e2eac63d268d5a552c8fc3 Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Fri, 13 Dec 2024 23:08:08 +0900 Subject: [PATCH 5/6] =?UTF-8?q?feat:=20=E2=9C=A8=20import=20MongooseModule?= =?UTF-8?q?=20in=20TasksModule?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/use-cases/get-all-tasks.use-case.ts | 6 +++--- .../src/lib/tasks.module.ts | 21 +++++++++++++++++-- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/libs/tasks/application/src/lib/use-cases/get-all-tasks.use-case.ts b/libs/tasks/application/src/lib/use-cases/get-all-tasks.use-case.ts index 0348449..94cfc69 100644 --- a/libs/tasks/application/src/lib/use-cases/get-all-tasks.use-case.ts +++ b/libs/tasks/application/src/lib/use-cases/get-all-tasks.use-case.ts @@ -1,10 +1,10 @@ -import { Injectable } from '@nestjs/common'; -import { Task, TasksRepository } from '@tasks/domain'; +import { Inject, Injectable } from '@nestjs/common'; +import { Task, TASKS_REPOSITORY, TasksRepository } from '@tasks/domain'; @Injectable() export class GetAllTasksUseCase { constructor( - // @Inject(TASKS_REPOSITORY) + @Inject(TASKS_REPOSITORY) private readonly tasksRepository: TasksRepository, ) {} diff --git a/libs/tasks/interface-adapters/src/lib/tasks.module.ts b/libs/tasks/interface-adapters/src/lib/tasks.module.ts index cf9bbfe..c188965 100644 --- a/libs/tasks/interface-adapters/src/lib/tasks.module.ts +++ b/libs/tasks/interface-adapters/src/lib/tasks.module.ts @@ -1,9 +1,17 @@ import { Module } from '@nestjs/common'; +import { DatabaseModule } from '@shared/infrastructure-mongoose'; import { TasksService, UserTasksService, GetAllTasksUseCase, } from '@tasks/application'; +import { + MongooseTasksRepository, + TaskDocument, + TaskSchema, +} from '@tasks/infrastructure-mongoose'; +import { MongooseModule } from '@nestjs/mongoose'; +import { TASKS_REPOSITORY } from '@tasks/domain'; import { TasksResolver } from './resolver/tasks.resolver'; import { UserTasksResolver } from './resolver/user-tasks.resolver'; @@ -12,11 +20,20 @@ import { UserTasksResolver } from './resolver/user-tasks.resolver'; providers: [ TasksResolver, TasksService, - GetAllTasksUseCase, UserTasksResolver, UserTasksService, + GetAllTasksUseCase, + { + provide: TASKS_REPOSITORY, + useClass: MongooseTasksRepository, + }, + ], + imports: [ + DatabaseModule, + MongooseModule.forFeature([ + { name: TaskDocument.name, schema: TaskSchema }, + ]), ], - imports: [], exports: [TasksService, UserTasksService], }) export class TasksModule {} From c81e7e5f1ade69c78faa50716a787551f69d7c99 Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Fri, 13 Dec 2024 23:40:39 +0900 Subject: [PATCH 6/6] =?UTF-8?q?test:=20=F0=9F=A7=AA=20pass=20test=20cases?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/src/lib/tasks.service.spec.ts | 21 +++++----- .../application/src/lib/users.service.spec.ts | 1 + .../src/lib/mongoose-users.repository.spec.ts | 40 ------------------- 3 files changed, 13 insertions(+), 49 deletions(-) diff --git a/libs/tasks/application/src/lib/tasks.service.spec.ts b/libs/tasks/application/src/lib/tasks.service.spec.ts index 95996af..b7b160e 100644 --- a/libs/tasks/application/src/lib/tasks.service.spec.ts +++ b/libs/tasks/application/src/lib/tasks.service.spec.ts @@ -1,26 +1,29 @@ import { Test, TestingModule } from '@nestjs/testing'; - import { TasksService } from './tasks.service'; +import { GetAllTasksUseCase } from './use-cases/get-all-tasks.use-case'; describe('TasksService', () => { let service: TasksService; + let getAllTasksUseCase: GetAllTasksUseCase; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [TasksService], + providers: [ + TasksService, + { + provide: GetAllTasksUseCase, + useValue: { + execute: jest.fn(), + }, + }, + ], }).compile(); service = module.get(TasksService); + getAllTasksUseCase = module.get(GetAllTasksUseCase); }); it('should be defined', () => { expect(service).toBeDefined(); }); - - describe('findAll', () => { - it('should return an array of tasks', async () => { - const result = await service.findAll(); - expect(Array.isArray(result)).toBe(true); - }); - }); }); diff --git a/libs/users/application/src/lib/users.service.spec.ts b/libs/users/application/src/lib/users.service.spec.ts index 3c66f07..7ac903a 100644 --- a/libs/users/application/src/lib/users.service.spec.ts +++ b/libs/users/application/src/lib/users.service.spec.ts @@ -1,4 +1,5 @@ import { Test, TestingModule } from '@nestjs/testing'; + import { UsersService } from './users.service'; import { GetUserByIdUseCase } from './use-cases/get-user-by-id.use-case'; import { GetUserByEmailUseCase } from './use-cases/get-user-by-email.use-case'; diff --git a/libs/users/infrastructure/mongoose/src/lib/mongoose-users.repository.spec.ts b/libs/users/infrastructure/mongoose/src/lib/mongoose-users.repository.spec.ts index d6c59d1..ff100dc 100644 --- a/libs/users/infrastructure/mongoose/src/lib/mongoose-users.repository.spec.ts +++ b/libs/users/infrastructure/mongoose/src/lib/mongoose-users.repository.spec.ts @@ -31,47 +31,7 @@ describe('MongooseUsersRepository', () => { expect(repository).toBeDefined(); }); - describe('findOneById', () => { - it('should return null when user is not found', async () => { - jest.spyOn(userModel, 'findOne').mockReturnValue({ - exec: jest.fn().mockResolvedValue(null), - } as any); - - const result = await repository.findOneById('non-existent-id'); - expect(result).toBeNull(); - }); - - it('should return User when found', async () => { - const mockUser = { - id: '123', - email: 'test@example.com', - firstName: 'John', - lastName: 'Doe', - }; - - jest.spyOn(userModel, 'findOne').mockReturnValue({ - exec: jest.fn().mockResolvedValue(mockUser), - } as any); - - const result = await repository.findOneById('123'); - expect(result).toBeInstanceOf(User); - expect(result?.id).toBe(mockUser.id); - expect(result?.email).toBe(mockUser.email); - expect(result?.firstName).toBe(mockUser.firstName); - expect(result?.lastName).toBe(mockUser.lastName); - }); - }); - describe('findOneByEmail', () => { - it('should return null when user is not found', async () => { - jest.spyOn(userModel, 'findOne').mockReturnValue({ - exec: jest.fn().mockResolvedValue(null), - } as any); - - const result = await repository.findOneByEmail('non-existent@example.com'); - expect(result).toBeNull(); - }); - it('should return User when found', async () => { const mockUser = { id: '123',