-
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.
- Loading branch information
Showing
16 changed files
with
474 additions
and
7 deletions.
There are no files selected for viewing
8 changes: 8 additions & 0 deletions
8
src/app/application/group/command/disablePortfolio/DisablePortfolioCommand.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 |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { ICommand } from '@nestjs/cqrs'; | ||
|
||
export class DisablePortfolioCommand implements ICommand { | ||
constructor( | ||
readonly groupId: string, | ||
readonly requesterUserId: string, | ||
) {} | ||
} |
98 changes: 98 additions & 0 deletions
98
src/app/application/group/command/disablePortfolio/DisablePortfolioCommandHandler.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 |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import { Test } from '@nestjs/testing'; | ||
import { advanceTo, clear } from 'jest-date-mock'; | ||
|
||
import { DisablePortfolioCommand } from '@sight/app/application/group/command/disablePortfolio/DisablePortfolioCommand'; | ||
import { DisablePortfolioCommandHandler } from '@sight/app/application/group/command/disablePortfolio/DisablePortfolioCommandHandler'; | ||
|
||
import { Group } from '@sight/app/domain/group/model/Group'; | ||
import { | ||
GroupRepository, | ||
IGroupRepository, | ||
} from '@sight/app/domain/group/IGroupRepository'; | ||
|
||
import { DomainFixture } from '@sight/__test__/fixtures'; | ||
import { Message } from '@sight/constant/message'; | ||
|
||
describe('DisablePortfolioCommandHandler', () => { | ||
let handler: DisablePortfolioCommandHandler; | ||
let groupRepository: jest.Mocked<IGroupRepository>; | ||
|
||
beforeAll(async () => { | ||
advanceTo(new Date()); | ||
|
||
const testModule = await Test.createTestingModule({ | ||
providers: [ | ||
DisablePortfolioCommandHandler, | ||
{ provide: GroupRepository, useValue: {} }, | ||
], | ||
}).compile(); | ||
|
||
handler = testModule.get(DisablePortfolioCommandHandler); | ||
groupRepository = testModule.get(GroupRepository); | ||
}); | ||
|
||
afterAll(() => { | ||
clear(); | ||
}); | ||
|
||
describe('handle', () => { | ||
let group: Group; | ||
|
||
const groupId = 'groupId'; | ||
const groupAdminUserId = 'groupAdminUserId'; | ||
|
||
beforeEach(() => { | ||
group = DomainFixture.generateGroup({ | ||
adminUserId: groupAdminUserId, | ||
hasPortfolio: true, | ||
}); | ||
|
||
groupRepository.findById = jest.fn().mockResolvedValue(group); | ||
|
||
groupRepository.save = jest.fn(); | ||
}); | ||
|
||
test('그룹이 존재하지 않는다면 예외를 발생시켜야 한다', async () => { | ||
groupRepository.findById = jest.fn().mockResolvedValue(null); | ||
|
||
await expect( | ||
handler.execute(new DisablePortfolioCommand(groupId, groupAdminUserId)), | ||
).rejects.toThrowError(Message.GROUP_NOT_FOUND); | ||
}); | ||
|
||
test('요청자가 그룹장이 아니라면 예외를 발생시켜야 한다', async () => { | ||
const otherUserId = 'otherUserId'; | ||
|
||
await expect( | ||
handler.execute(new DisablePortfolioCommand(groupId, otherUserId)), | ||
).rejects.toThrowError(Message.ONLY_GROUP_ADMIN_CAN_EDIT_GROUP); | ||
}); | ||
|
||
test('그룹이 고객센터 그룹이라면 예외를 발생시켜야 한다', async () => { | ||
jest.spyOn(group, 'isCustomerServiceGroup').mockReturnValue(true); | ||
|
||
await expect( | ||
handler.execute(new DisablePortfolioCommand(groupId, groupAdminUserId)), | ||
).rejects.toThrowError(Message.CANNOT_MODIFY_CUSTOMER_SERVICE_GROUP); | ||
}); | ||
|
||
test('그룹의 포트폴리오를 비활성화 시켜야 한다', async () => { | ||
jest.spyOn(group, 'disablePortfolio'); | ||
|
||
await handler.execute( | ||
new DisablePortfolioCommand(groupId, groupAdminUserId), | ||
); | ||
|
||
expect(group.disablePortfolio).toBeCalled(); | ||
}); | ||
|
||
test('그룹을 저장해야 한다', async () => { | ||
await handler.execute( | ||
new DisablePortfolioCommand(groupId, groupAdminUserId), | ||
); | ||
|
||
expect(groupRepository.save).toBeCalledWith(group); | ||
expect(groupRepository.save).toBeCalledTimes(1); | ||
}); | ||
}); | ||
}); |
48 changes: 48 additions & 0 deletions
48
src/app/application/group/command/disablePortfolio/DisablePortfolioCommandHandler.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 |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; | ||
import { | ||
ForbiddenException, | ||
Inject, | ||
NotFoundException, | ||
UnprocessableEntityException, | ||
} from '@nestjs/common'; | ||
|
||
import { DisablePortfolioCommand } from '@sight/app/application/group/command/disablePortfolio/DisablePortfolioCommand'; | ||
|
||
import { | ||
GroupRepository, | ||
IGroupRepository, | ||
} from '@sight/app/domain/group/IGroupRepository'; | ||
|
||
import { Message } from '@sight/constant/message'; | ||
|
||
@CommandHandler(DisablePortfolioCommand) | ||
export class DisablePortfolioCommandHandler | ||
implements ICommandHandler<DisablePortfolioCommand> | ||
{ | ||
constructor( | ||
@Inject(GroupRepository) | ||
private readonly groupRepository: IGroupRepository, | ||
) {} | ||
|
||
async execute(command: DisablePortfolioCommand): Promise<void> { | ||
const { groupId, requesterUserId } = command; | ||
|
||
const group = await this.groupRepository.findById(groupId); | ||
if (!group) { | ||
throw new NotFoundException(Message.GROUP_NOT_FOUND); | ||
} | ||
|
||
if (group.adminUserId !== requesterUserId) { | ||
throw new ForbiddenException(Message.ONLY_GROUP_ADMIN_CAN_EDIT_GROUP); | ||
} | ||
|
||
if (group.isCustomerServiceGroup()) { | ||
throw new UnprocessableEntityException( | ||
Message.CANNOT_MODIFY_CUSTOMER_SERVICE_GROUP, | ||
); | ||
} | ||
|
||
group.disablePortfolio(); | ||
await this.groupRepository.save(group); | ||
} | ||
} |
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
155 changes: 155 additions & 0 deletions
155
src/app/application/group/eventHandler/GroupPortfolioDisabledHandler.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 |
---|---|---|
@@ -0,0 +1,155 @@ | ||
import { Test } from '@nestjs/testing'; | ||
import { advanceTo, clear } from 'jest-date-mock'; | ||
import { ClsService } from 'nestjs-cls'; | ||
|
||
import { IRequester } from '@sight/core/auth/IRequester'; | ||
import { UserRole } from '@sight/core/auth/UserRole'; | ||
import { MessageBuilder } from '@sight/core/message/MessageBuilder'; | ||
|
||
import { GroupPortfolioDisabledHandler } from '@sight/app/application/group/eventHandler/GroupPortfolioDisabledHandler'; | ||
|
||
import { PRACTICE_GROUP_ID } from '@sight/app/domain/group/model/constant'; | ||
import { Group } from '@sight/app/domain/group/model/Group'; | ||
import { | ||
ISlackSender, | ||
SlackSender, | ||
} from '@sight/app/domain/adapter/ISlackSender'; | ||
import { | ||
GroupLogger, | ||
IGroupLogger, | ||
} from '@sight/app/domain/group/IGroupLogger'; | ||
import { | ||
GroupMemberRepository, | ||
IGroupMemberRepository, | ||
} from '@sight/app/domain/group/IGroupMemberRepository'; | ||
import { | ||
GroupRepository, | ||
IGroupRepository, | ||
} from '@sight/app/domain/group/IGroupRepository'; | ||
import { | ||
IUserRepository, | ||
UserRepository, | ||
} from '@sight/app/domain/user/IUserRepository'; | ||
|
||
import { DomainFixture } from '@sight/__test__/fixtures'; | ||
import { Point } from '@sight/constant/point'; | ||
|
||
describe('GroupPortfolioDisabledHandler', () => { | ||
let handler: GroupPortfolioDisabledHandler; | ||
let messageBuilder: MessageBuilder; | ||
let clsService: jest.Mocked<ClsService>; | ||
let groupRepository: jest.Mocked<IGroupRepository>; | ||
let groupMemberRepository: jest.Mocked<IGroupMemberRepository>; | ||
let userRepository: jest.Mocked<IUserRepository>; | ||
let groupLogger: jest.Mocked<IGroupLogger>; | ||
let slackSender: jest.Mocked<ISlackSender>; | ||
|
||
beforeAll(async () => { | ||
advanceTo(new Date()); | ||
|
||
const testModule = await Test.createTestingModule({ | ||
providers: [ | ||
GroupPortfolioDisabledHandler, | ||
MessageBuilder, | ||
{ provide: ClsService, useValue: {} }, | ||
{ provide: GroupRepository, useValue: {} }, | ||
{ provide: GroupMemberRepository, useValue: {} }, | ||
{ provide: UserRepository, useValue: {} }, | ||
{ provide: GroupLogger, useValue: {} }, | ||
{ provide: SlackSender, useValue: {} }, | ||
], | ||
}).compile(); | ||
|
||
handler = testModule.get(GroupPortfolioDisabledHandler); | ||
messageBuilder = testModule.get(MessageBuilder); | ||
clsService = testModule.get(ClsService); | ||
groupRepository = testModule.get(GroupRepository); | ||
groupMemberRepository = testModule.get(GroupMemberRepository); | ||
userRepository = testModule.get(UserRepository); | ||
groupLogger = testModule.get(GroupLogger); | ||
slackSender = testModule.get(SlackSender); | ||
}); | ||
|
||
afterAll(() => { | ||
clear(); | ||
}); | ||
|
||
describe('handle', () => { | ||
let group: Group; | ||
|
||
const groupId = 'groupId'; | ||
const requester: IRequester = { | ||
userId: 'requesterUserId', | ||
role: UserRole.USER, | ||
}; | ||
|
||
beforeEach(() => { | ||
group = DomainFixture.generateGroup(); | ||
|
||
clsService.get = jest.fn().mockReturnValue(requester); | ||
groupRepository.findById = jest.fn().mockResolvedValue(group); | ||
groupMemberRepository.findByGroupId = jest.fn().mockResolvedValue([]); | ||
userRepository.findByIds = jest.fn().mockResolvedValue([]); | ||
|
||
jest.spyOn(messageBuilder, 'build'); | ||
groupLogger.log = jest.fn(); | ||
userRepository.save = jest.fn(); | ||
slackSender.send = jest.fn(); | ||
}); | ||
|
||
test('그룹이 존재하지 않으면 아무 동작도 하지 않아야 한다', async () => { | ||
groupRepository.findById = jest.fn().mockResolvedValue(null); | ||
|
||
await handler.handle({ groupId }); | ||
|
||
expect(groupRepository.findById).toBeCalledWith(groupId); | ||
}); | ||
|
||
test('그룹 로그를 남겨야 한다', async () => { | ||
await handler.handle({ groupId }); | ||
|
||
expect(groupLogger.log).toBeCalledTimes(1); | ||
}); | ||
|
||
test('모든 그룹 멤버들에게서 포인트를 회수해야 한다', async () => { | ||
const user = DomainFixture.generateUser(); | ||
const groupMember = DomainFixture.generateGroupMember({ | ||
userId: user.id, | ||
}); | ||
|
||
groupMemberRepository.findByGroupId = jest | ||
.fn() | ||
.mockResolvedValue([groupMember]); | ||
userRepository.findByIds = jest.fn().mockResolvedValue([user]); | ||
jest.spyOn(user, 'grantPoint'); | ||
|
||
await handler.handle({ groupId }); | ||
|
||
expect(user.grantPoint).toBeCalledTimes(1); | ||
expect(user.grantPoint).toBeCalledWith( | ||
-Point.GROUP_ENABLED_PORTFOLIO, | ||
expect.any(String), | ||
); | ||
|
||
expect(userRepository.save).toBeCalledTimes(1); | ||
expect(userRepository.save).toBeCalledWith(user); | ||
}); | ||
|
||
test('그룹 활용 실습 그룹이면 포인트를 회수하지 않아야 한다', async () => { | ||
const practiceGroup = DomainFixture.generateGroup({ | ||
id: PRACTICE_GROUP_ID, | ||
}); | ||
groupRepository.findById = jest.fn().mockResolvedValue(practiceGroup); | ||
|
||
await handler.handle({ groupId }); | ||
|
||
expect(userRepository.save).not.toBeCalled(); | ||
}); | ||
|
||
test('메시지를 보내야 한다', async () => { | ||
await handler.handle({ groupId }); | ||
|
||
expect(slackSender.send).toBeCalledTimes(1); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.