From c612fd03e5cd7413c247d30d7b012053fddcc398 Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Fri, 13 Dec 2024 23:04:59 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=E2=9C=A8=20add=20@tasks/infrastructure?= =?UTF-8?q?-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" ],