diff --git a/libs/tasks/application/src/index.ts b/libs/tasks/application/src/index.ts index db6d3b9..d450914 100644 --- a/libs/tasks/application/src/index.ts +++ b/libs/tasks/application/src/index.ts @@ -1,4 +1,9 @@ +// tasks export * from './lib/tasks.service'; export * from './lib/use-cases/get-all-tasks.use-case'; +// user tasks export * from './lib/user-tasks.service'; +export * from './lib/use-cases/get-all-user-tasks.use-case'; +export * from './lib/use-cases/create-some-user-tasks.use-case'; +export * from './lib/use-cases/update-some-user-tasks.use-case'; diff --git a/libs/tasks/application/src/lib/tasks.service.spec.ts b/libs/tasks/application/src/lib/tasks.service.spec.ts index b7b160e..85a3af6 100644 --- a/libs/tasks/application/src/lib/tasks.service.spec.ts +++ b/libs/tasks/application/src/lib/tasks.service.spec.ts @@ -1,29 +1,45 @@ import { Test, TestingModule } from '@nestjs/testing'; import { TasksService } from './tasks.service'; -import { GetAllTasksUseCase } from './use-cases/get-all-tasks.use-case'; +import { TASKS_REPOSITORY, TasksRepository, Task } from '@tasks/domain'; describe('TasksService', () => { let service: TasksService; - let getAllTasksUseCase: GetAllTasksUseCase; + let mockTasksRepository: TasksRepository; beforeEach(async () => { + mockTasksRepository = { + findAll: jest.fn(), + }; + const module: TestingModule = await Test.createTestingModule({ providers: [ TasksService, { - provide: GetAllTasksUseCase, - useValue: { - execute: jest.fn(), - }, + provide: TASKS_REPOSITORY, + useValue: mockTasksRepository, }, ], }).compile(); service = module.get(TasksService); - getAllTasksUseCase = module.get(GetAllTasksUseCase); }); it('should be defined', () => { expect(service).toBeDefined(); }); + + describe('findMany', () => { + it('should return all tasks', async () => { + const mockTasks = [ + Task.create('task-1', 'Task 1', 'Description 1', ['category-1']), + Task.create('task-2', 'Task 2', 'Description 2', ['category-2']), + ]; + mockTasksRepository.findAll = jest.fn().mockResolvedValue(mockTasks); + + const result = await service.findAll(); + + expect(result).toEqual(mockTasks); + expect(mockTasksRepository.findAll).toHaveBeenCalled(); + }); + }); }); diff --git a/libs/tasks/application/src/lib/tasks.service.ts b/libs/tasks/application/src/lib/tasks.service.ts index 35e617c..b71d851 100644 --- a/libs/tasks/application/src/lib/tasks.service.ts +++ b/libs/tasks/application/src/lib/tasks.service.ts @@ -1,13 +1,14 @@ -import { Injectable } from '@nestjs/common'; -import { Task } from '@tasks/domain'; - -import { GetAllTasksUseCase } from './use-cases/get-all-tasks.use-case'; +import { Inject, Injectable } from '@nestjs/common'; +import { Task, TASKS_REPOSITORY, TasksRepository } from '@tasks/domain'; @Injectable() export class TasksService { - constructor(private readonly getAllTasksUseCase: GetAllTasksUseCase) {} + constructor( + @Inject(TASKS_REPOSITORY) + private readonly tasksRepository: TasksRepository, + ) {} async findAll(): Promise { - return await this.getAllTasksUseCase.execute(); + return await this.tasksRepository.findAll(); } } diff --git a/libs/tasks/application/src/lib/use-cases/create-some-user-tasks.use-case.spec.ts b/libs/tasks/application/src/lib/use-cases/create-some-user-tasks.use-case.spec.ts new file mode 100644 index 0000000..fd678db --- /dev/null +++ b/libs/tasks/application/src/lib/use-cases/create-some-user-tasks.use-case.spec.ts @@ -0,0 +1,47 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { UserTasksService } from '../user-tasks.service'; +import { CreateSomeUserTasksUseCase } from './create-some-user-tasks.use-case'; + +describe('CreateSomeUserTasksUseCase', () => { + let useCase: CreateSomeUserTasksUseCase; + let userTasksService: UserTasksService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + CreateSomeUserTasksUseCase, + { + provide: UserTasksService, + useValue: { + createSome: jest.fn(), + }, + }, + ], + }).compile(); + + useCase = module.get(CreateSomeUserTasksUseCase); + userTasksService = module.get(UserTasksService); + }); + + it('should be defined', () => { + expect(useCase).toBeDefined(); + }); + + describe('execute', () => { + it('should create multiple user tasks', async () => { + const userId = 'test-user-id'; + const tasks = [ + { id: '1', createdAt: new Date() }, + { id: '2', createdAt: new Date() }, + ]; + const expectedResult = 'success'; + + jest.spyOn(userTasksService, 'createSome').mockResolvedValue(expectedResult); + + const result = await useCase.execute(userId, tasks); + + expect(result).toEqual(expectedResult); + expect(userTasksService.createSome).toHaveBeenCalledWith(userId, tasks); + }); + }); +}); diff --git a/libs/tasks/application/src/lib/use-cases/create-some-user-tasks.use-case.ts b/libs/tasks/application/src/lib/use-cases/create-some-user-tasks.use-case.ts new file mode 100644 index 0000000..4656e6c --- /dev/null +++ b/libs/tasks/application/src/lib/use-cases/create-some-user-tasks.use-case.ts @@ -0,0 +1,14 @@ +import { Injectable } from '@nestjs/common'; +import { UserTasksService } from '../user-tasks.service'; + +@Injectable() +export class CreateSomeUserTasksUseCase { + constructor(private readonly userTasksService: UserTasksService) {} + + async execute( + userId: string, + tasks: { id: string; createdAt: Date }[], + ): Promise { + return await this.userTasksService.createSome(userId, tasks); + } +} diff --git a/libs/tasks/application/src/lib/use-cases/get-all-tasks.use-case.spec.ts b/libs/tasks/application/src/lib/use-cases/get-all-tasks.use-case.spec.ts new file mode 100644 index 0000000..97b2cd8 --- /dev/null +++ b/libs/tasks/application/src/lib/use-cases/get-all-tasks.use-case.spec.ts @@ -0,0 +1,50 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { TasksService } from '../tasks.service'; +import { GetAllTasksUseCase } from './get-all-tasks.use-case'; +import { Task } from '@tasks/domain'; + +describe('GetAllTasksUseCase', () => { + let useCase: GetAllTasksUseCase; + let tasksService: TasksService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + GetAllTasksUseCase, + { + provide: TasksService, + useValue: { + findAll: jest.fn(), + }, + }, + ], + }).compile(); + + useCase = module.get(GetAllTasksUseCase); + tasksService = module.get(TasksService); + }); + + it('should be defined', () => { + expect(useCase).toBeDefined(); + }); + + describe('execute', () => { + it('should return all tasks', async () => { + const expectedTasks: Task[] = [ + { + id: '1', + title: 'Test Task', + description: 'Test Description', + categories: ['category-1'], + }, + ]; + + jest.spyOn(tasksService, 'findAll').mockResolvedValue(expectedTasks); + + const result = await useCase.execute(); + + expect(result).toEqual(expectedTasks); + expect(tasksService.findAll).toHaveBeenCalled(); + }); + }); +}); 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 94cfc69..c23cdc5 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,14 +1,13 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { Task, TASKS_REPOSITORY, TasksRepository } from '@tasks/domain'; +import { Injectable } from '@nestjs/common'; +import { Task } from '@tasks/domain'; + +import { TasksService } from '../tasks.service'; @Injectable() export class GetAllTasksUseCase { - constructor( - @Inject(TASKS_REPOSITORY) - private readonly tasksRepository: TasksRepository, - ) {} + constructor(private readonly tasksService: TasksService) {} async execute(): Promise { - return await this.tasksRepository.findAll(); + return await this.tasksService.findAll(); } } diff --git a/libs/tasks/application/src/lib/use-cases/get-all-user-tasks.use-case.spec.ts b/libs/tasks/application/src/lib/use-cases/get-all-user-tasks.use-case.spec.ts new file mode 100644 index 0000000..65dc2bb --- /dev/null +++ b/libs/tasks/application/src/lib/use-cases/get-all-user-tasks.use-case.spec.ts @@ -0,0 +1,49 @@ +import { Test } from '@nestjs/testing'; +import { TaskStatusEnum, UserTask } from '@tasks/domain'; +import { GetAllUserTasksUseCase } from './get-all-user-tasks.use-case'; +import { UserTasksService } from '../user-tasks.service'; + +describe('GetAllUserTasksUseCase', () => { + let useCase: GetAllUserTasksUseCase; + let userTasksService: UserTasksService; + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + providers: [ + GetAllUserTasksUseCase, + { + provide: UserTasksService, + useValue: { + findMany: jest.fn(), + }, + }, + ], + }).compile(); + + useCase = moduleRef.get(GetAllUserTasksUseCase); + userTasksService = moduleRef.get(UserTasksService); + }); + + describe('execute', () => { + it('should return all user tasks', async () => { + const userId = 'test-user-id'; + const expectedTasks: UserTask[] = [ + { + id: '1', + userId, + taskId: '1', + createdAt: new Date('2024-01-01T00:00:00.000Z'), + status: TaskStatusEnum.TODO, + updatedAt: null, + }, + ]; + + jest.spyOn(userTasksService, 'findMany').mockResolvedValue(expectedTasks); + + const result = await useCase.execute(userId); + + expect(result).toBe(expectedTasks); + expect(userTasksService.findMany).toHaveBeenCalledWith(userId); + }); + }); +}); diff --git a/libs/tasks/application/src/lib/use-cases/get-all-user-tasks.use-case.ts b/libs/tasks/application/src/lib/use-cases/get-all-user-tasks.use-case.ts new file mode 100644 index 0000000..576d20a --- /dev/null +++ b/libs/tasks/application/src/lib/use-cases/get-all-user-tasks.use-case.ts @@ -0,0 +1,13 @@ +import { Injectable } from "@nestjs/common"; +import { UserTask } from "@tasks/domain"; + +import { UserTasksService } from "../user-tasks.service"; + +@Injectable() +export class GetAllUserTasksUseCase { + constructor(private readonly userTasksService: UserTasksService) {} + + async execute(userId: string): Promise { + return await this.userTasksService.findMany(userId); + } +} diff --git a/libs/tasks/application/src/lib/use-cases/update-some-user-tasks.use-case.spec.ts b/libs/tasks/application/src/lib/use-cases/update-some-user-tasks.use-case.spec.ts new file mode 100644 index 0000000..bdc4a85 --- /dev/null +++ b/libs/tasks/application/src/lib/use-cases/update-some-user-tasks.use-case.spec.ts @@ -0,0 +1,47 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { UserTasksService } from '../user-tasks.service'; +import { UpdateSomeUserTasksUseCase } from './update-some-user-tasks.use-case'; + +describe('UpdateSomeUserTasksUseCase', () => { + let useCase: UpdateSomeUserTasksUseCase; + let userTasksService: UserTasksService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + UpdateSomeUserTasksUseCase, + { + provide: UserTasksService, + useValue: { + updateSome: jest.fn(), + }, + }, + ], + }).compile(); + + useCase = module.get(UpdateSomeUserTasksUseCase); + userTasksService = module.get(UserTasksService); + }); + + it('should be defined', () => { + expect(useCase).toBeDefined(); + }); + + describe('execute', () => { + it('should update multiple user tasks', async () => { + const userId = 'test-user-id'; + const userTasks = [ + { id: '1', updatedAt: new Date() }, + { id: '2', updatedAt: new Date() }, + ]; + const expectedResult = 'success'; + + jest.spyOn(userTasksService, 'updateSome').mockResolvedValue(expectedResult); + + const result = await useCase.execute(userId, userTasks); + + expect(result).toEqual(expectedResult); + expect(userTasksService.updateSome).toHaveBeenCalledWith(userId, userTasks); + }); + }); +}); diff --git a/libs/tasks/application/src/lib/use-cases/update-some-user-tasks.use-case.ts b/libs/tasks/application/src/lib/use-cases/update-some-user-tasks.use-case.ts new file mode 100644 index 0000000..70864ca --- /dev/null +++ b/libs/tasks/application/src/lib/use-cases/update-some-user-tasks.use-case.ts @@ -0,0 +1,14 @@ +import { Injectable } from '@nestjs/common'; +import { UserTasksService } from '../user-tasks.service'; + +@Injectable() +export class UpdateSomeUserTasksUseCase { + constructor(private readonly userTasksService: UserTasksService) {} + + async execute( + userId: string, + userTasks: { id: string; updatedAt: Date }[], + ): Promise { + return await this.userTasksService.updateSome(userId, userTasks); + } +} diff --git a/libs/tasks/application/src/lib/user-tasks.service.ts b/libs/tasks/application/src/lib/user-tasks.service.ts index 793b87e..39ff6a1 100644 --- a/libs/tasks/application/src/lib/user-tasks.service.ts +++ b/libs/tasks/application/src/lib/user-tasks.service.ts @@ -1,10 +1,9 @@ import { Injectable } from '@nestjs/common'; +import { UserTask } from '@tasks/domain'; @Injectable() export class UserTasksService { - async findMany( - userId: string, - ): Promise<[]> { + async findMany(userId: string): Promise { // TODO: Implement this return []; } diff --git a/libs/tasks/domain/src/index.ts b/libs/tasks/domain/src/index.ts index b6b8850..0e8b7cc 100644 --- a/libs/tasks/domain/src/index.ts +++ b/libs/tasks/domain/src/index.ts @@ -1,6 +1,10 @@ -export * from './lib/entities/task.entity'; -export * from './lib/entities/user-task.entity'; +// tasks +export * from './lib/tasks-repository.interface'; +export * from './lib/entities/task.entity'; export * from './lib/value-objects/task-status.enum'; -export * from './lib/tasks.repository'; +// user tasks +export * from './lib/user-tasks-repository.interface'; + +export * from './lib/entities/user-task.entity'; diff --git a/libs/tasks/domain/src/lib/entities/user-task.entity.spec.ts b/libs/tasks/domain/src/lib/entities/user-task.entity.spec.ts index c587c9d..7215c67 100644 --- a/libs/tasks/domain/src/lib/entities/user-task.entity.spec.ts +++ b/libs/tasks/domain/src/lib/entities/user-task.entity.spec.ts @@ -14,40 +14,4 @@ describe('UserTask', () => { expect(task.createdAt).toBeInstanceOf(Date); }); }); - - describe('markAsInProgress', () => { - it('should mark task as in progress', () => { - const task = UserTask.create('id', 'userId', 'taskId'); - const inProgressTask = task.markAsInProgress(); - - expect(inProgressTask.status).toBe(TaskStatusEnum.IN_PROGRESS); - expect(inProgressTask.updatedAt).toBeInstanceOf(Date); - }); - - it('should return same instance if already in progress', () => { - const task = UserTask.create('id', 'userId', 'taskId'); - const inProgressTask = task.markAsInProgress(); - const sameTask = inProgressTask.markAsInProgress(); - - expect(sameTask).toBe(inProgressTask); - }); - }); - - describe('markAsDone', () => { - it('should mark task as done', () => { - const task = UserTask.create('id', 'userId', 'taskId'); - const doneTask = task.markAsDone(); - - expect(doneTask.status).toBe(TaskStatusEnum.DONE); - expect(doneTask.updatedAt).toBeInstanceOf(Date); - }); - - it('should return same instance if already done', () => { - const task = UserTask.create('id', 'userId', 'taskId'); - const doneTask = task.markAsDone(); - const sameTask = doneTask.markAsDone(); - - expect(sameTask).toBe(doneTask); - }); - }); }); diff --git a/libs/tasks/domain/src/lib/entities/user-task.entity.ts b/libs/tasks/domain/src/lib/entities/user-task.entity.ts index 6d53d95..7314404 100644 --- a/libs/tasks/domain/src/lib/entities/user-task.entity.ts +++ b/libs/tasks/domain/src/lib/entities/user-task.entity.ts @@ -23,38 +23,4 @@ export class UserTask { TaskStatusEnum.TODO, ); } - - /** - * Marks the task as in progress - */ - markAsInProgress(): UserTask { - if (this.status === TaskStatusEnum.IN_PROGRESS) { - return this; - } - return new UserTask( - this.id, - this.userId, - this.taskId, - this.createdAt, - new Date(), - TaskStatusEnum.IN_PROGRESS, - ); - } - - /** - * Marks the task as done - */ - markAsDone(): UserTask { - if (this.status === TaskStatusEnum.DONE) { - return this; - } - return new UserTask( - this.id, - this.userId, - this.taskId, - this.createdAt, - new Date(), - TaskStatusEnum.DONE, - ); - } } diff --git a/libs/tasks/domain/src/lib/tasks.repository.spec.ts b/libs/tasks/domain/src/lib/tasks-repository.interface.spec.ts similarity index 89% rename from libs/tasks/domain/src/lib/tasks.repository.spec.ts rename to libs/tasks/domain/src/lib/tasks-repository.interface.spec.ts index f7ec9c6..4221f5c 100644 --- a/libs/tasks/domain/src/lib/tasks.repository.spec.ts +++ b/libs/tasks/domain/src/lib/tasks-repository.interface.spec.ts @@ -1,5 +1,5 @@ import { Task } from './entities/task.entity'; -import { TasksRepository } from './tasks.repository'; +import { TasksRepository } from './tasks-repository.interface'; class MockTasksRepository implements TasksRepository { async findAll(): Promise { diff --git a/libs/tasks/domain/src/lib/tasks.repository.ts b/libs/tasks/domain/src/lib/tasks-repository.interface.ts similarity index 100% rename from libs/tasks/domain/src/lib/tasks.repository.ts rename to libs/tasks/domain/src/lib/tasks-repository.interface.ts diff --git a/libs/tasks/domain/src/lib/user-tasks.repository.spec.ts b/libs/tasks/domain/src/lib/user-tasks-repository.interface.spec.ts similarity index 79% rename from libs/tasks/domain/src/lib/user-tasks.repository.spec.ts rename to libs/tasks/domain/src/lib/user-tasks-repository.interface.spec.ts index de2bdd3..f75d300 100644 --- a/libs/tasks/domain/src/lib/user-tasks.repository.spec.ts +++ b/libs/tasks/domain/src/lib/user-tasks-repository.interface.spec.ts @@ -1,8 +1,8 @@ -import { Task } from './entities/task.entity'; -import { UserTasksRepository } from './user-tasks.repository'; +import { UserTask } from './entities/user-task.entity'; +import { UserTasksRepository } from './user-tasks-repository.interface'; class MockUserTasksRepository implements UserTasksRepository { - async findAll(userId: string): Promise { + async findMany(userId: string): Promise { return []; } @@ -30,9 +30,8 @@ describe('UserTasksRepository', () => { describe('findAll', () => { it('should return all tasks for a given user', async () => { - const tasks = await repository.findAll('user-1'); - expect(Array.isArray(tasks)).toBe(true); - expect(tasks.every((task) => task instanceof Task)).toBe(true); + const userTasks = await repository.findMany('user-1'); + expect(userTasks).toEqual([]); }); }); diff --git a/libs/tasks/domain/src/lib/user-tasks.repository.ts b/libs/tasks/domain/src/lib/user-tasks-repository.interface.ts similarity index 85% rename from libs/tasks/domain/src/lib/user-tasks.repository.ts rename to libs/tasks/domain/src/lib/user-tasks-repository.interface.ts index 46a26f9..801a4d9 100644 --- a/libs/tasks/domain/src/lib/user-tasks.repository.ts +++ b/libs/tasks/domain/src/lib/user-tasks-repository.interface.ts @@ -1,7 +1,7 @@ -import { Task } from './entities/task.entity'; +import { UserTask } from './entities/user-task.entity'; export interface UserTasksRepository { - findAll(userId: string): Promise; + findMany(userId: string): Promise; /** * Creates tasks for a specific user diff --git a/libs/tasks/interface-adapters/src/lib/dto/user-task.dto.spec.ts b/libs/tasks/interface-adapters/src/lib/dto/user-task.dto.spec.ts index 947ef1b..73820cd 100644 --- a/libs/tasks/interface-adapters/src/lib/dto/user-task.dto.spec.ts +++ b/libs/tasks/interface-adapters/src/lib/dto/user-task.dto.spec.ts @@ -8,16 +8,13 @@ describe('UserTaskDto', () => { const createdAt = new Date(); const updatedAt = new Date(); const taskId = '456'; - const task = new TaskDto('456', 'Test Task', 'Test Description', []); const userId = '789'; - const user = new UserDto('789', 'test@example.com', null, null); const userTaskDto = new UserTaskDto( id, createdAt, updatedAt, taskId, - task, userId ); @@ -25,7 +22,6 @@ describe('UserTaskDto', () => { expect(userTaskDto.createdAt).toBe(createdAt); expect(userTaskDto.updatedAt).toBe(updatedAt); expect(userTaskDto.taskId).toBe(taskId); - expect(userTaskDto.task).toBe(task); expect(userTaskDto.userId).toBe(userId); }); @@ -43,7 +39,6 @@ describe('UserTaskDto', () => { createdAt, updatedAt, taskId, - task, userId ); @@ -51,7 +46,6 @@ describe('UserTaskDto', () => { expect(userTaskDto.createdAt).toBe(createdAt); expect(userTaskDto.updatedAt).toBeNull(); expect(userTaskDto.taskId).toBe(taskId); - expect(userTaskDto.task).toBeNull(); expect(userTaskDto.userId).toBe(userId); }); }); diff --git a/libs/tasks/interface-adapters/src/lib/dto/user-task.dto.ts b/libs/tasks/interface-adapters/src/lib/dto/user-task.dto.ts index 7b03a68..7f023c7 100644 --- a/libs/tasks/interface-adapters/src/lib/dto/user-task.dto.ts +++ b/libs/tasks/interface-adapters/src/lib/dto/user-task.dto.ts @@ -1,8 +1,6 @@ import { Field, ID, ObjectType } from '@nestjs/graphql'; import { IsDate, IsNotEmpty, IsOptional, IsUUID } from 'class-validator'; -import { TaskDto } from './task.dto'; - @ObjectType() export class UserTaskDto { @Field(() => ID) @@ -25,10 +23,6 @@ export class UserTaskDto { @IsUUID() taskId: string; - @Field(() => TaskDto, { nullable: true }) - @IsOptional() - task: TaskDto | null; - @Field(() => String) @IsNotEmpty() @IsUUID() @@ -39,14 +33,12 @@ export class UserTaskDto { createdAt: Date, updatedAt: Date | null, taskId: string, - task: TaskDto | null, - userId: string + userId: string, ) { this.id = id; this.createdAt = createdAt; this.updatedAt = updatedAt; this.taskId = taskId; - this.task = task; this.userId = userId; } } diff --git a/libs/tasks/interface-adapters/src/lib/resolver/tasks.resolver.spec.ts b/libs/tasks/interface-adapters/src/lib/resolver/tasks.resolver.spec.ts index d066f08..7fd1da2 100644 --- a/libs/tasks/interface-adapters/src/lib/resolver/tasks.resolver.spec.ts +++ b/libs/tasks/interface-adapters/src/lib/resolver/tasks.resolver.spec.ts @@ -1,37 +1,49 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { TasksService } from '@tasks/application'; +import { GetAllTasksUseCase } from '@tasks/application'; import { TasksResolver } from './tasks.resolver'; describe('TasksResolver', () => { let resolver: TasksResolver; - let service: TasksService; + let getAllTasksUseCase: GetAllTasksUseCase; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ TasksResolver, { - provide: TasksService, + provide: GetAllTasksUseCase, useValue: { - findAll: jest.fn().mockResolvedValue([]), + execute: jest.fn().mockResolvedValue([]), }, }, ], }).compile(); resolver = module.get(TasksResolver); - service = module.get(TasksService); + getAllTasksUseCase = module.get(GetAllTasksUseCase); }); it('should be defined', () => { expect(resolver).toBeDefined(); }); - describe('findAllTasks', () => { - it('should return all tasks', async () => { + describe('getTasks', () => { + it('should return tasks', async () => { + const mockTasks = [ + { + id: 'task-1', + title: 'Task 1', + description: 'Description 1', + categories: [], + }, + ]; + + jest.spyOn(getAllTasksUseCase, 'execute').mockResolvedValue(mockTasks); + const result = await resolver.getTasks(); - expect(result).toEqual([]); + expect(result).toEqual(mockTasks); + expect(getAllTasksUseCase.execute).toHaveBeenCalled(); }); }); }); diff --git a/libs/tasks/interface-adapters/src/lib/resolver/tasks.resolver.ts b/libs/tasks/interface-adapters/src/lib/resolver/tasks.resolver.ts index d25f1a0..b629d48 100644 --- a/libs/tasks/interface-adapters/src/lib/resolver/tasks.resolver.ts +++ b/libs/tasks/interface-adapters/src/lib/resolver/tasks.resolver.ts @@ -1,16 +1,16 @@ +import { Injectable } from '@nestjs/common'; import { Query, Resolver } from '@nestjs/graphql'; -import { TasksService } from '@tasks/application'; +import { GetAllTasksUseCase } from '@tasks/application'; import { TaskDto } from '../dto/task.dto'; -import { Injectable } from '@nestjs/common'; @Injectable() @Resolver(() => [TaskDto]) export class TasksResolver { - constructor(private tasksService: TasksService) {} + constructor(private getAllTasksUseCase: GetAllTasksUseCase) {} @Query(() => [TaskDto]) async getTasks(): Promise { - return this.tasksService.findAll(); + return this.getAllTasksUseCase.execute(); } } diff --git a/libs/tasks/interface-adapters/src/lib/resolver/user-tasks.resolver.spec.ts b/libs/tasks/interface-adapters/src/lib/resolver/user-tasks.resolver.spec.ts index 5f41f75..0d12656 100644 --- a/libs/tasks/interface-adapters/src/lib/resolver/user-tasks.resolver.spec.ts +++ b/libs/tasks/interface-adapters/src/lib/resolver/user-tasks.resolver.spec.ts @@ -1,61 +1,136 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { UserTasksService } from '@tasks/application'; - +import { BadRequestException } from '@nestjs/common'; +import { + GetAllUserTasksUseCase, + CreateSomeUserTasksUseCase, + UpdateSomeUserTasksUseCase, +} from '@tasks/application'; +import { UserTaskDto } from '../dto/user-task.dto'; import { UserTasksResolver } from './user-tasks.resolver'; +import { TaskStatusEnum } from '@tasks/domain'; describe('UserTasksResolver', () => { let resolver: UserTasksResolver; - let service: UserTasksService; + let getAllUserTasksUseCase: GetAllUserTasksUseCase; + let createSomeUserTasksUseCase: CreateSomeUserTasksUseCase; + let updateSomeUserTasksUseCase: UpdateSomeUserTasksUseCase; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ UserTasksResolver, { - provide: UserTasksService, + provide: GetAllUserTasksUseCase, + useValue: { + execute: jest.fn().mockResolvedValue([]), + }, + }, + { + provide: CreateSomeUserTasksUseCase, useValue: { - findMany: jest.fn().mockResolvedValue([]), - createSome: jest.fn().mockResolvedValue({ created: true }), - updateSome: jest.fn().mockResolvedValue({ updated: true }), + execute: jest.fn().mockResolvedValue('success'), + }, + }, + { + provide: UpdateSomeUserTasksUseCase, + useValue: { + execute: jest.fn().mockResolvedValue('success'), }, }, ], }).compile(); resolver = module.get(UserTasksResolver); - service = module.get(UserTasksService); + getAllUserTasksUseCase = module.get( + GetAllUserTasksUseCase, + ); + createSomeUserTasksUseCase = module.get( + CreateSomeUserTasksUseCase, + ); + updateSomeUserTasksUseCase = module.get( + UpdateSomeUserTasksUseCase, + ); }); it('should be defined', () => { expect(resolver).toBeDefined(); }); - describe('findUserTasks', () => { - it('should return user tasks for given userId', async () => { - const userId = 'test-user-id'; - const result = await resolver.getUserTasks(userId); - expect(service.findMany).toHaveBeenCalledWith(userId); - expect(result).toEqual([]); + describe('getUserTasks', () => { + it('should throw BadRequestException when userId is empty', async () => { + await expect(resolver.getUserTasks('')).rejects.toThrow( + BadRequestException, + ); + }); + + it('should return user tasks for valid userId', async () => { + const mockUserId = 'valid-user-id'; + const mockUserTasks = [ + { + id: 'task-1', + createdAt: new Date(), + updatedAt: null, + taskId: 'task-id-1', + userId: mockUserId, + status: TaskStatusEnum.TODO, + }, + ]; + + jest + .spyOn(getAllUserTasksUseCase, 'execute') + .mockResolvedValue(mockUserTasks); + + const result = await resolver.getUserTasks(mockUserId); + expect(result).toEqual( + mockUserTasks.map( + (task) => + new UserTaskDto( + task.id, + task.createdAt, + task.updatedAt, + task.taskId, + task.userId, + ), + ), + ); }); }); describe('createUserTasks', () => { - it('should create user tasks', async () => { - const userId = 'test-user-id'; - const tasks = [{ id: 'task-1', createdAt: new Date() }]; - const result = await resolver.createUserTasks(userId, tasks); - expect(service.createSome).toHaveBeenCalledWith(userId, tasks); - expect(result).toBeDefined(); + it('should create user tasks successfully', async () => { + const mockUserId = 'user-id'; + const mockTasks = [ + { taskId: 'task-id', createdAt: new Date(), id: 'task-id' }, + ]; + + const result = await resolver.createUserTasks(mockUserId, mockTasks); + expect(result).toBe('success'); + expect(createSomeUserTasksUseCase.execute).toHaveBeenCalledWith( + mockUserId, + mockTasks, + ); }); }); describe('updateUserTasks', () => { - it('should update user tasks', async () => { - const userId = 'test-user-id'; - const userTasks = [{ id: 'user-task-1', updatedAt: new Date() }]; - const result = await resolver.updateUserTasks(userId, userTasks); - expect(service.updateSome).toHaveBeenCalledWith(userId, userTasks); - expect(result).toBeDefined(); + it('should update user tasks successfully', async () => { + const mockUserId = 'user-id'; + const mockUserTasks = [ + { + id: 'task-id', + createdAt: new Date(), + updatedAt: new Date(), + taskId: 'task-id', + userId: 'user-id', + }, + ]; + + const result = await resolver.updateUserTasks(mockUserId, mockUserTasks); + expect(result).toBe('success'); + expect(updateSomeUserTasksUseCase.execute).toHaveBeenCalledWith( + mockUserId, + mockUserTasks, + ); }); }); }); diff --git a/libs/tasks/interface-adapters/src/lib/resolver/user-tasks.resolver.ts b/libs/tasks/interface-adapters/src/lib/resolver/user-tasks.resolver.ts index d93df01..23b6c93 100644 --- a/libs/tasks/interface-adapters/src/lib/resolver/user-tasks.resolver.ts +++ b/libs/tasks/interface-adapters/src/lib/resolver/user-tasks.resolver.ts @@ -1,11 +1,10 @@ -import { - BadRequestException, - Injectable, - InternalServerErrorException, - Logger, -} from '@nestjs/common'; +import { BadRequestException, Injectable } from '@nestjs/common'; import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'; -import { UserTasksService } from '@tasks/application'; +import { + GetAllUserTasksUseCase, + CreateSomeUserTasksUseCase, + UpdateSomeUserTasksUseCase, +} from '@tasks/application'; import { CreateUserTaskDto } from '../dto/create-user-task.dto'; import { UpdateUserTaskDto } from '../dto/update-user-task.dto'; @@ -14,8 +13,11 @@ import { UserTaskDto } from '../dto/user-task.dto'; @Injectable() @Resolver(() => [UserTaskDto]) export class UserTasksResolver { - private readonly logger = new Logger(UserTasksResolver.name); - constructor(private userTasksService: UserTasksService) {} + constructor( + private readonly getUserTasksUseCase: GetAllUserTasksUseCase, + private readonly createSomeUserTasksUseCase: CreateSomeUserTasksUseCase, + private readonly updateSomeUserTasksUseCase: UpdateSomeUserTasksUseCase, + ) {} @Query(() => [UserTaskDto]) async getUserTasks( @@ -24,16 +26,21 @@ export class UserTasksResolver { }) userId: string, ): Promise { - try { - if (!userId?.trim()) { - throw new BadRequestException('Invalid userId'); // TODO: using error codes - } - - return this.userTasksService.findMany(userId); - } catch (error) { - this.logger.error('FindUserTasks error:', error); - throw new InternalServerErrorException('Failed to fetch user tasks'); // TODO: using error codes + if (!userId?.trim()) { + throw new BadRequestException('Invalid userId'); // TODO: using error codes } + const userTasks = await this.getUserTasksUseCase.execute(userId); + + return userTasks.map( + (userTask) => + new UserTaskDto( + userTask.id, + userTask.createdAt, + userTask.updatedAt, + userTask.taskId, + userTask.userId, + ), + ); } @Mutation(() => String) @@ -42,7 +49,7 @@ export class UserTasksResolver { @Args('tasks', { type: () => [CreateUserTaskDto] }) tasks: CreateUserTaskDto[], ): Promise { - return this.userTasksService.createSome(userId, tasks); + return this.createSomeUserTasksUseCase.execute(userId, tasks); } @Mutation(() => String) @@ -51,6 +58,6 @@ export class UserTasksResolver { @Args('userTasks', { type: () => [UpdateUserTaskDto] }) userTasks: UpdateUserTaskDto[], ): Promise { - return this.userTasksService.updateSome(userId, userTasks); + return this.updateSomeUserTasksUseCase.execute(userId, userTasks); } } diff --git a/libs/tasks/interface-adapters/src/lib/tasks.module.ts b/libs/tasks/interface-adapters/src/lib/tasks.module.ts index c188965..e0835fc 100644 --- a/libs/tasks/interface-adapters/src/lib/tasks.module.ts +++ b/libs/tasks/interface-adapters/src/lib/tasks.module.ts @@ -4,6 +4,9 @@ import { TasksService, UserTasksService, GetAllTasksUseCase, + GetAllUserTasksUseCase, + CreateSomeUserTasksUseCase, + UpdateSomeUserTasksUseCase, } from '@tasks/application'; import { MongooseTasksRepository, @@ -23,6 +26,9 @@ import { UserTasksResolver } from './resolver/user-tasks.resolver'; UserTasksResolver, UserTasksService, GetAllTasksUseCase, + GetAllUserTasksUseCase, + CreateSomeUserTasksUseCase, + UpdateSomeUserTasksUseCase, { provide: TASKS_REPOSITORY, useClass: MongooseTasksRepository,