-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #123 from zhumeisongsong/feature/refactor-user-ser…
…vice refactor: ♻️ user module
- Loading branch information
Showing
11 changed files
with
295 additions
and
146 deletions.
There are no files selected for viewing
70 changes: 37 additions & 33 deletions
70
libs/users/application/src/lib/use-cases/get-user-by-email.use-case.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,45 +1,49 @@ | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { NotFoundException } from '@nestjs/common'; | ||
import { GetUserByEmailUseCase } from './get-user-by-email.use-case'; | ||
import { UsersRepository } from '@users/domain'; | ||
import { UsersService } from '../users.service'; | ||
|
||
describe('GetUserByEmailUseCase', () => { | ||
let getUserByEmailUseCase: GetUserByEmailUseCase; | ||
let usersRepository: UsersRepository; | ||
|
||
beforeEach(() => { | ||
usersRepository = { | ||
findOneById: jest.fn(), | ||
findOneByEmail: jest.fn(), | ||
}; | ||
getUserByEmailUseCase = new GetUserByEmailUseCase(usersRepository); | ||
let useCase: GetUserByEmailUseCase; | ||
let usersService: UsersService; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
providers: [ | ||
GetUserByEmailUseCase, | ||
{ | ||
provide: UsersService, | ||
useValue: { | ||
findOneByEmail: jest.fn(), | ||
}, | ||
}, | ||
], | ||
}).compile(); | ||
|
||
useCase = module.get<GetUserByEmailUseCase>(GetUserByEmailUseCase); | ||
usersService = module.get<UsersService>(UsersService); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(useCase).toBeDefined(); | ||
}); | ||
|
||
describe('execute', () => { | ||
it('should return user when found', async () => { | ||
const mockUser = { | ||
id: '1', | ||
email: '[email protected]', | ||
firstName: 'John', | ||
lastName: 'Doe', | ||
}; | ||
(usersRepository.findOneByEmail as jest.Mock).mockResolvedValue(mockUser); | ||
|
||
const result = await getUserByEmailUseCase.execute('[email protected]'); | ||
|
||
expect(result).toEqual(mockUser); | ||
expect(usersRepository.findOneByEmail).toHaveBeenCalledWith( | ||
'[email protected]', | ||
); | ||
}); | ||
it('should return a user when found', async () => { | ||
const mockUser = { id: '1', email: '[email protected]', firstName: 'John', lastName: 'Doe' }; | ||
jest.spyOn(usersService, 'findOneByEmail').mockResolvedValue(mockUser); | ||
|
||
it('should return null when user not found', async () => { | ||
(usersRepository.findOneByEmail as jest.Mock).mockResolvedValue(null); | ||
const result = await useCase.execute('[email protected]'); | ||
|
||
expect(result).toBe(mockUser); | ||
expect(usersService.findOneByEmail).toHaveBeenCalledWith('[email protected]'); | ||
}); | ||
|
||
const result = await getUserByEmailUseCase.execute('[email protected]'); | ||
it('should throw NotFoundException when user is not found', async () => { | ||
jest.spyOn(usersService, 'findOneByEmail').mockRejectedValue(new NotFoundException()); | ||
|
||
expect(result).toBeNull(); | ||
expect(usersRepository.findOneByEmail).toHaveBeenCalledWith( | ||
'[email protected]', | ||
); | ||
await expect(useCase.execute('[email protected]')).rejects.toThrow(NotFoundException); | ||
expect(usersService.findOneByEmail).toHaveBeenCalledWith('[email protected]'); | ||
}); | ||
}); | ||
}); |
12 changes: 6 additions & 6 deletions
12
libs/users/application/src/lib/use-cases/get-user-by-email.use-case.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,14 @@ | ||
import { Inject, Injectable } from "@nestjs/common"; | ||
import { User, USERS_REPOSITORY, UsersRepository } from "@users/domain"; | ||
import { Injectable } from "@nestjs/common"; | ||
import { User } from "@users/domain"; | ||
import { UsersService } from "../users.service"; | ||
|
||
@Injectable() | ||
export class GetUserByEmailUseCase { | ||
constructor( | ||
@Inject(USERS_REPOSITORY) | ||
private readonly usersRepository: UsersRepository, | ||
private readonly usersService: UsersService, | ||
) {} | ||
|
||
async execute(email: string): Promise<User | null> { | ||
return this.usersRepository.findOneByEmail(email); | ||
async execute(email: string): Promise<User> { | ||
return this.usersService.findOneByEmail(email); | ||
} | ||
} |
66 changes: 37 additions & 29 deletions
66
libs/users/application/src/lib/use-cases/get-user-by-id.use-case.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,49 @@ | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { NotFoundException } from '@nestjs/common'; | ||
import { GetUserByIdUseCase } from './get-user-by-id.use-case'; | ||
import { UsersRepository } from '@users/domain'; | ||
import { UsersService } from '../users.service'; | ||
|
||
describe('GetUserByIdUseCase', () => { | ||
let getUserByIdUseCase: GetUserByIdUseCase; | ||
let usersRepository: UsersRepository; | ||
|
||
beforeEach(() => { | ||
usersRepository = { | ||
findOneById: jest.fn(), | ||
findOneByEmail: jest.fn(), | ||
}; | ||
getUserByIdUseCase = new GetUserByIdUseCase(usersRepository); | ||
let useCase: GetUserByIdUseCase; | ||
let usersService: UsersService; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
providers: [ | ||
GetUserByIdUseCase, | ||
{ | ||
provide: UsersService, | ||
useValue: { | ||
findOneById: jest.fn(), | ||
}, | ||
}, | ||
], | ||
}).compile(); | ||
|
||
useCase = module.get<GetUserByIdUseCase>(GetUserByIdUseCase); | ||
usersService = module.get<UsersService>(UsersService); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(useCase).toBeDefined(); | ||
}); | ||
|
||
describe('execute', () => { | ||
it('should return user when found', async () => { | ||
const mockUser = { | ||
id: '1', | ||
email: '[email protected]', | ||
firstName: 'John', | ||
lastName: 'Doe', | ||
}; | ||
(usersRepository.findOneById as jest.Mock).mockResolvedValue(mockUser); | ||
|
||
const result = await getUserByIdUseCase.execute('1'); | ||
|
||
expect(result).toEqual(mockUser); | ||
expect(usersRepository.findOneById).toHaveBeenCalledWith('1'); | ||
}); | ||
it('should return a user when found', async () => { | ||
const mockUser = { id: '1', email: '[email protected]', firstName: 'John', lastName: 'Doe' }; | ||
jest.spyOn(usersService, 'findOneById').mockResolvedValue(mockUser); | ||
|
||
it('should return null when user not found', async () => { | ||
(usersRepository.findOneById as jest.Mock).mockResolvedValue(null); | ||
const result = await useCase.execute('1'); | ||
|
||
expect(result).toBe(mockUser); | ||
expect(usersService.findOneById).toHaveBeenCalledWith('1'); | ||
}); | ||
|
||
const result = await getUserByIdUseCase.execute('1'); | ||
it('should throw NotFoundException when user is not found', async () => { | ||
jest.spyOn(usersService, 'findOneById').mockRejectedValue(new NotFoundException()); | ||
|
||
expect(result).toBeNull(); | ||
expect(usersRepository.findOneById).toHaveBeenCalledWith('1'); | ||
await expect(useCase.execute('1')).rejects.toThrow(NotFoundException); | ||
expect(usersService.findOneById).toHaveBeenCalledWith('1'); | ||
}); | ||
}); | ||
}); |
15 changes: 7 additions & 8 deletions
15
libs/users/application/src/lib/use-cases/get-user-by-id.use-case.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,13 @@ | ||
import { Inject, Injectable } from '@nestjs/common'; | ||
import { User, USERS_REPOSITORY, UsersRepository } from '@users/domain'; | ||
import { Injectable } from '@nestjs/common'; | ||
import { User } from '@users/domain'; | ||
|
||
import { UsersService } from '../users.service'; | ||
|
||
@Injectable() | ||
export class GetUserByIdUseCase { | ||
constructor( | ||
@Inject(USERS_REPOSITORY) | ||
private readonly usersRepository: UsersRepository, | ||
) {} | ||
constructor(private readonly usersService: UsersService) {} | ||
|
||
async execute(id: string): Promise<User | null> { | ||
return await this.usersRepository.findOneById(id); | ||
async execute(id: string): Promise<User> { | ||
return await this.usersService.findOneById(id); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,87 +1,75 @@ | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
|
||
import { NotFoundException } from '@nestjs/common'; | ||
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'; | ||
import { USERS_REPOSITORY } from '@users/domain'; | ||
import { userError } from '@zhumeisong/common-error-exception'; | ||
|
||
describe('UsersService', () => { | ||
let service: UsersService; | ||
let getUserByIdUseCase: GetUserByIdUseCase; | ||
let getUserByEmailUseCase: GetUserByEmailUseCase; | ||
let usersRepository: any; | ||
|
||
beforeEach(async () => { | ||
usersRepository = { | ||
findOneById: jest.fn(), | ||
findOneByEmail: jest.fn(), | ||
}; | ||
|
||
const module: TestingModule = await Test.createTestingModule({ | ||
providers: [ | ||
UsersService, | ||
{ | ||
provide: GetUserByIdUseCase, | ||
useValue: { | ||
execute: jest.fn(), | ||
}, | ||
}, | ||
{ | ||
provide: GetUserByEmailUseCase, | ||
useValue: { | ||
execute: jest.fn(), | ||
}, | ||
provide: USERS_REPOSITORY, | ||
useValue: usersRepository, | ||
}, | ||
], | ||
}).compile(); | ||
|
||
service = module.get<UsersService>(UsersService); | ||
getUserByIdUseCase = module.get<GetUserByIdUseCase>(GetUserByIdUseCase); | ||
getUserByEmailUseCase = module.get<GetUserByEmailUseCase>(GetUserByEmailUseCase); | ||
}); | ||
|
||
describe('getUserById', () => { | ||
it('should return user when found', async () => { | ||
const mockUser = { | ||
id: '1', | ||
email: '[email protected]', | ||
firstName: 'John', | ||
lastName: 'Doe', | ||
}; | ||
(getUserByIdUseCase.execute as jest.Mock).mockResolvedValue(mockUser); | ||
it('should be defined', () => { | ||
expect(service).toBeDefined(); | ||
}); | ||
|
||
describe('findOneById', () => { | ||
it('should return a user when found', async () => { | ||
const mockUser = { id: '1', email: '[email protected]', firstName: 'John', lastName: 'Doe' }; | ||
usersRepository.findOneById.mockResolvedValue(mockUser); | ||
|
||
const result = await service.findOneById('1'); | ||
|
||
expect(result).toEqual(mockUser); | ||
expect(getUserByIdUseCase.execute).toHaveBeenCalledWith('1'); | ||
expect(result).toBe(mockUser); | ||
expect(usersRepository.findOneById).toHaveBeenCalledWith('1'); | ||
}); | ||
|
||
it('should return null when user not found', async () => { | ||
(getUserByIdUseCase.execute as jest.Mock).mockResolvedValue(null); | ||
|
||
const result = await service.findOneById('1'); | ||
it('should throw NotFoundException when user is not found', async () => { | ||
usersRepository.findOneById.mockResolvedValue(null); | ||
|
||
expect(result).toBeNull(); | ||
expect(getUserByIdUseCase.execute).toHaveBeenCalledWith('1'); | ||
await expect(service.findOneById('1')).rejects.toThrow( | ||
new NotFoundException(userError.NOT_FOUND) | ||
); | ||
expect(usersRepository.findOneById).toHaveBeenCalledWith('1'); | ||
}); | ||
}); | ||
|
||
describe('getUserByEmail', () => { | ||
it('should return user when found', async () => { | ||
const mockUser = { | ||
id: '1', | ||
email: '[email protected]', | ||
firstName: 'John', | ||
lastName: 'Doe', | ||
}; | ||
(getUserByEmailUseCase.execute as jest.Mock).mockResolvedValue(mockUser); | ||
describe('findOneByEmail', () => { | ||
it('should return a user when found', async () => { | ||
const mockUser = { id: '1', email: '[email protected]', firstName: 'John', lastName: 'Doe' }; | ||
usersRepository.findOneByEmail.mockResolvedValue(mockUser); | ||
|
||
const result = await service.findOneByEmail('test@example.com'); | ||
const result = await service.findOneByEmail('test@test.com'); | ||
|
||
expect(result).toEqual(mockUser); | ||
expect(getUserByEmailUseCase.execute).toHaveBeenCalledWith('test@example.com'); | ||
expect(result).toBe(mockUser); | ||
expect(usersRepository.findOneByEmail).toHaveBeenCalledWith('test@test.com'); | ||
}); | ||
|
||
it('should return null when user not found', async () => { | ||
(getUserByEmailUseCase.execute as jest.Mock).mockResolvedValue(null); | ||
|
||
const result = await service.findOneByEmail('[email protected]'); | ||
it('should throw NotFoundException when user is not found', async () => { | ||
usersRepository.findOneByEmail.mockResolvedValue(null); | ||
|
||
expect(result).toBeNull(); | ||
expect(getUserByEmailUseCase.execute).toHaveBeenCalledWith('[email protected]'); | ||
await expect(service.findOneByEmail('[email protected]')).rejects.toThrow( | ||
new NotFoundException(userError.NOT_FOUND) | ||
); | ||
expect(usersRepository.findOneByEmail).toHaveBeenCalledWith('[email protected]'); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,29 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import { User } from '@users/domain'; | ||
|
||
import { GetUserByIdUseCase } from './use-cases/get-user-by-id.use-case'; | ||
import { GetUserByEmailUseCase } from './use-cases/get-user-by-email.use-case'; | ||
import { Inject, Injectable, NotFoundException } from '@nestjs/common'; | ||
import { User, USERS_REPOSITORY, UsersRepository } from '@users/domain'; | ||
import { userError } from '@zhumeisong/common-error-exception'; | ||
|
||
@Injectable() | ||
export class UsersService { | ||
constructor( | ||
private readonly getUserByIdUseCase: GetUserByIdUseCase, | ||
private readonly getUserByEmailUseCase: GetUserByEmailUseCase, | ||
@Inject(USERS_REPOSITORY) | ||
private readonly usersRepository: UsersRepository, | ||
) {} | ||
|
||
async findOneById(id: string): Promise<User | null> { | ||
return this.getUserByIdUseCase.execute(id); | ||
async findOneById(id: string): Promise<User> { | ||
const user = await this.usersRepository.findOneById(id); | ||
if (!user) { | ||
throw new NotFoundException(userError.NOT_FOUND); | ||
} | ||
|
||
return user; | ||
} | ||
|
||
async findOneByEmail(email: string): Promise<User | null> { | ||
return this.getUserByEmailUseCase.execute(email); | ||
async findOneByEmail(email: string): Promise<User> { | ||
const user = await this.usersRepository.findOneByEmail(email); | ||
if (!user) { | ||
throw new NotFoundException(userError.NOT_FOUND); | ||
} | ||
|
||
return user; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
export * from './lib/user.entity'; | ||
export * from './lib/users.repository'; | ||
export * from './lib/users-repository.interface'; |
2 changes: 1 addition & 1 deletion
2
...s/domain/src/lib/users.repository.spec.ts → ...rc/lib/users-repository.interface.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.