Skip to content

Commit 86f330a

Browse files
authored
Merge pull request #55 from PBTP/test/image
test: ImageService, ImageConsumer, 테스트 코드 작성
2 parents 683dbdd + 78a870e commit 86f330a

File tree

10 files changed

+185
-21
lines changed

10 files changed

+185
-21
lines changed

src/common/cloud/aws/sqs/presentation/s3-image-created-event-message.dto.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ import {
77
} from 'class-validator';
88
import { Type } from 'class-transformer';
99

10-
class S3Bucket {
10+
export class S3Bucket {
1111
@IsString()
1212
@IsNotEmpty()
1313
name: string;
1414
}
1515

16-
class S3Object {
16+
export class S3Object {
1717
@IsString()
1818
@IsNotEmpty()
1919
key: string;
@@ -31,7 +31,7 @@ class S3Object {
3131
sequencer: string;
3232
}
3333

34-
class S3Detail {
34+
export class S3Detail {
3535
@IsString()
3636
@IsNotEmpty()
3737
version: string;

src/common/image/port/image.repository.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ export const IMAGE_REPOSITORY = Symbol('IImageRepository');
99
export interface IImageRepository {
1010
create(image: Partial<Image>): Image;
1111

12+
getOne(image: Partial<Image>): Promise<Image>;
13+
1214
findOne(image: Partial<Image>): Promise<Image | undefined>;
1315

1416
save(image: Image): Promise<Image>;
@@ -22,6 +24,12 @@ export class ImageRepository implements IImageRepository {
2224
return this.imageDB.create(image).toModel();
2325
}
2426

27+
async getOne(image: Partial<ImageDto>): Promise<Image> {
28+
return this.imageDB
29+
.findOneOrFail({ where: { imageUrl: image.imageUrl } })
30+
.then((v) => v?.toModel());
31+
}
32+
2533
async findOne(image: Partial<ImageDto>): Promise<Image | undefined> {
2634
return this.imageDB
2735
.findOne({ where: { imageUrl: image.imageUrl } })

src/common/sender/sms/application/sms.service.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Injectable, Logger } from '@nestjs/common';
22
import { ConfigService } from '@nestjs/config';
33
import { SolapiMessageService } from 'solapi';
4+
import { BadRequestException } from '@nestjs/common/exceptions';
45
export const SMS_SERVICE = Symbol('SMS_SERVICE');
56

67
export interface ISmsService {
@@ -24,6 +25,10 @@ export class SmsService implements ISmsService {
2425
}
2526

2627
async send(phoneNumber: string, message: string): Promise<boolean> {
28+
if (!phoneNumber || !message) {
29+
throw new BadRequestException('전화번호 또는 메시지가 없습니다.');
30+
}
31+
2732
return await this.solapiMessageService
2833
.sendOne({
2934
to: phoneNumber,
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
PresignedUrlDto,
66
} from '../../src/common/cloud/aws/s3/presentation/presigned-url.dto';
77

8-
export class FakeCloudStorageService implements ICloudStorage {
8+
export class FakeCloudStorage implements ICloudStorage {
99
async generatePreSignedUrl(
1010
key: string,
1111
metadata: ImageMetaDataDto,

test/mock/fake.image.repository.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,18 @@ export class FakeImageRepository implements IImageRepository {
77
create(image: Partial<Image>): Image {
88
return image as Image;
99
}
10-
async findOne(image: Partial<Image>): Promise<Image> {
10+
async getOne(image: Partial<Image>): Promise<Image> {
1111
const findImage = this.images.find((i) => i.imageUrl === image.imageUrl);
1212
if (!findImage) {
1313
throw new Error('존재하지 않는 이미지입니다.');
1414
}
1515
return findImage;
1616
}
17+
18+
async findOne(image: Partial<Image>): Promise<Image | undefined> {
19+
return this.images.find((i) => i.imageUrl === image.imageUrl);
20+
}
21+
1722
async save(image: Image): Promise<Image> {
1823
this.images.push(image);
1924
return image;

test/unit/auth/application/auth.service.test.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { AuthService } from '../../../../src/auth/application/auth.service';
22
import { CustomerService } from '../../../../src/customer/application/customer.service';
33
import { FakeCustomerRepository } from '../../../mock/fake.customer.repository';
44
import { ImageService } from '../../../../src/common/image/application/image.service';
5-
import { FakeCloudStorageService } from '../../../mock/fake.cloud-storage.service';
5+
import { FakeCloudStorage } from '../../../mock/fake.cloud-storage';
66
import { FakeImageRepository } from '../../../mock/fake.image.repository';
77
import { JwtService, TokenExpiredError } from '@nestjs/jwt';
88
import { FakeConfigService } from '../../../mock/fake.config.service';
@@ -54,10 +54,7 @@ describe('AuthService', () => {
5454
customerService = new CustomerService(
5555
new FakeCustomerRepository(),
5656
new SecurityService(new FakeConfigService()),
57-
new ImageService(
58-
new FakeCloudStorageService(),
59-
new FakeImageRepository(),
60-
),
57+
new ImageService(new FakeCloudStorage(), new FakeImageRepository()),
6158
new FakeUuidHolder(),
6259
new FakeDateHolder(date),
6360
);
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { ImageConsumer } from '../../../../src/common/image/application/image.consumer';
2+
import { ImageService } from '../../../../src/common/image/application/image.service';
3+
import { Message } from 'aws-sdk/clients/sqs';
4+
import { BadRequestException } from '@nestjs/common/exceptions';
5+
import { FakeConfigService } from '../../../mock/fake.config.service';
6+
import { FakeCloudStorage } from '../../../mock/fake.cloud-storage';
7+
import { FakeImageRepository } from '../../../mock/fake.image.repository';
8+
9+
describe('ImageConsumer', () => {
10+
let consumer: ImageConsumer;
11+
12+
beforeEach(() => {
13+
consumer = new ImageConsumer(
14+
new ImageService(new FakeCloudStorage(), new FakeImageRepository()),
15+
new FakeConfigService(),
16+
);
17+
});
18+
19+
test('consumeMessage', async () => {
20+
// given
21+
const uuid = 'test-uuid';
22+
const env = `test`;
23+
24+
const message = {
25+
Body: JSON.stringify({
26+
version: '2.0',
27+
id: 'test-id',
28+
source: 'aws:s3',
29+
account: '123456789012',
30+
time: '2023-10-01T12:00:00Z',
31+
region: 'us-east-1',
32+
resources: ['arn:aws:s3:::example-bucket'],
33+
detail: {
34+
version: '2.0',
35+
bucket: {
36+
name: 'example-bucket',
37+
},
38+
object: {
39+
key: `${env}/images/${uuid}/image.jpg`,
40+
size: 1024,
41+
etag: 'd41d8cd98f00b204e9800998ecf8427e',
42+
sequencer: '0055AED6DCD90281E5',
43+
},
44+
requester: 'arn:aws:iam::123456789012:user/test-user',
45+
reason: 'PutObject',
46+
},
47+
}),
48+
} as unknown as Message;
49+
50+
// when
51+
const result = await consumer.consumeMessage(message);
52+
53+
// then
54+
console.table(result);
55+
expect(result).toEqual({
56+
imageUrl: `https://image.mgmg.life/${env}/images/${uuid}/image.jpg`,
57+
uuid: uuid,
58+
});
59+
});
60+
61+
test('consumeMessage with empty message body', async () => {
62+
// given
63+
const message = {
64+
Body: '',
65+
} as unknown as Message;
66+
67+
// when && then
68+
await expect(consumer.consumeMessage(message)).rejects.toThrow(
69+
BadRequestException,
70+
);
71+
});
72+
73+
test('Message 안 object key에 대해 /images 와 파일명 사이에 uuid가 존재하지 않으면 BadRequestException 발생 ', async () => {
74+
// given
75+
const env = 'test';
76+
const message = {
77+
Body: JSON.stringify({
78+
version: '2.0',
79+
id: 'test-id',
80+
source: 'aws:s3',
81+
account: '123456789012',
82+
time: '2023-10-01T12:00:00Z',
83+
region: 'us-east-1',
84+
resources: ['arn:aws:s3:::example-bucket'],
85+
detail: {
86+
version: '2.0',
87+
bucket: {
88+
name: 'example-bucket',
89+
},
90+
object: {
91+
key: `${env}/images//image.jpg`,
92+
size: 1024,
93+
etag: 'd41d8cd98f00b204e9800998ecf8427e',
94+
sequencer: '0055AED6DCD90281E5',
95+
},
96+
requester: 'arn:aws:iam::123456789012:user/test-user',
97+
reason: 'PutObject',
98+
},
99+
}),
100+
} as unknown as Message;
101+
102+
// when && then
103+
await expect(consumer.consumeMessage(message)).rejects.toThrow(
104+
BadRequestException,
105+
);
106+
});
107+
});
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { ImageService } from '../../../../src/common/image/application/image.service';
2+
import { FakeCloudStorage } from '../../../mock/fake.cloud-storage';
3+
import { FakeImageRepository } from '../../../mock/fake.image.repository';
4+
5+
describe('ImageService', () => {
6+
let service: ImageService;
7+
let repo: FakeImageRepository;
8+
9+
beforeEach(() => {
10+
repo = new FakeImageRepository();
11+
service = new ImageService(new FakeCloudStorage(), repo);
12+
});
13+
14+
test('PreSignedUrl을 발급한다 ', async () => {
15+
// given
16+
const key = 'key';
17+
const metadata = [{ fileName: 'fileName', fileSize: 1 }];
18+
19+
// when
20+
const result = await service.generatePreSignedUrls(key, metadata);
21+
22+
// then
23+
console.table(result);
24+
expect(result).toEqual([
25+
{
26+
url: key,
27+
expiredTime: 60,
28+
fileName: 'fileName',
29+
fileSize: 1,
30+
},
31+
]);
32+
});
33+
34+
test('이미지를 저장한다.', async () => {
35+
// given
36+
const saveUrl = 'imageUrl';
37+
const dto = { imageUrl: saveUrl };
38+
39+
// when
40+
const result = await service.create(dto);
41+
42+
// then
43+
expect(result).toEqual({ imageUrl: saveUrl });
44+
repo.getOne({ imageUrl: saveUrl }).then((v) => {
45+
console.table(v);
46+
expect(v).toEqual({ imageUrl: saveUrl });
47+
});
48+
});
49+
});

test/unit/customer/application/customer.service.test.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { FakeConfigService } from '../../../mock/fake.config.service';
44
import { FakeCustomerRepository } from '../../../mock/fake.customer.repository';
55
import { SecurityService } from '../../../../src/auth/application/security.service';
66
import { ImageService } from '../../../../src/common/image/application/image.service';
7-
import { FakeCloudStorageService } from '../../../mock/fake.cloud-storage.service';
7+
import { FakeCloudStorage } from '../../../mock/fake.cloud-storage';
88
import { FakeImageRepository } from '../../../mock/fake.image.repository';
99
import { CustomerDto } from '../../../../src/customer/presentation/customer.dto';
1010
import { Builder } from 'builder-pattern';
@@ -23,10 +23,7 @@ describe('CustomerService', () => {
2323
service = new CustomerService(
2424
new FakeCustomerRepository(),
2525
new SecurityService(configService),
26-
new ImageService(
27-
new FakeCloudStorageService(),
28-
new FakeImageRepository(),
29-
),
26+
new ImageService(new FakeCloudStorage(), new FakeImageRepository()),
3027
new FakeUuidHolder(),
3128
new FakeDateHolder(date),
3229
);

test/unit/customer/presentation/customer.controller.test.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@ import { CustomerService } from '../../../../src/customer/application/customer.s
33
import { FakeCustomerRepository } from '../../../mock/fake.customer.repository';
44
import { FakeSecurityService } from '../../../mock/fake.security.service';
55
import { ImageService } from '../../../../src/common/image/application/image.service';
6-
import { FakeCloudStorageService } from '../../../mock/fake.cloud-storage.service';
6+
import { FakeCloudStorage } from '../../../mock/fake.cloud-storage';
77
import { FakeImageRepository } from '../../../mock/fake.image.repository';
88
import { FakeDateHolder, FakeUuidHolder } from '../../../mock/fake.holder';
99
import { Builder } from 'builder-pattern';
1010
import { CustomerDto } from '../../../../src/customer/presentation/customer.dto';
1111
import { AuthProvider } from '../../../../src/auth/presentation/user.dto';
1212

13-
1413
describe('CustomerController', () => {
1514
let customerController: CustomerController;
1615
const date = new Date();
@@ -19,10 +18,7 @@ describe('CustomerController', () => {
1918
const customerService = new CustomerService(
2019
new FakeCustomerRepository(),
2120
new FakeSecurityService(),
22-
new ImageService(
23-
new FakeCloudStorageService(),
24-
new FakeImageRepository(),
25-
),
21+
new ImageService(new FakeCloudStorage(), new FakeImageRepository()),
2622
new FakeUuidHolder(),
2723
new FakeDateHolder(date),
2824
);

0 commit comments

Comments
 (0)