Skip to content

Commit

Permalink
✅ test: feed 조회, feed 조회수 업데이트 API, QueryFeedDto 테스트 작성
Browse files Browse the repository at this point in the history
  • Loading branch information
CodeVac513 committed Dec 2, 2024
1 parent 9f1e5d1 commit 90843dc
Show file tree
Hide file tree
Showing 4 changed files with 296 additions and 203 deletions.
83 changes: 83 additions & 0 deletions server/test/feed/dto/query-feed.dto.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { validate } from 'class-validator';
import { QueryFeedDto } from '../../../src/feed/dto/query-feed.dto';

describe('QueryFeedDto Test', () => {
//given
let queryFeedDto: QueryFeedDto;

beforeEach(() => {
queryFeedDto = new QueryFeedDto();
});

it('limit에 1보다 작은 값을 입력하면 유효성 검사에 실패한다.', async () => {
//given
queryFeedDto.limit = -1;

//when
const errors = await validate(queryFeedDto);

//then
expect(errors).toHaveLength(1);
expect(errors[0].constraints).toHaveProperty('min');
});

it('limit에 자연수가 아닌 실수를 입력하면 유효성 검사에 실패한다.', async () => {
//given
queryFeedDto.limit = 1.254;

//when
const errors = await validate(queryFeedDto);

//then
expect(errors).toHaveLength(1);
expect(errors[0].constraints).toHaveProperty('isInt');
});

it('limit에 문자열을 입력하면 유효성 검사에 실패한다.', async () => {
//given
queryFeedDto.limit = 'abcdefg' as any;

//when
const errors = await validate(queryFeedDto);

//then
expect(errors).toHaveLength(1);
expect(errors[0].constraints).toHaveProperty('isInt');
});

it('lastId에 음수를 입력하면 유효성 검사에 실패한다.', async () => {
//given
queryFeedDto.lastId = -1;

//when
const errors = await validate(queryFeedDto);

//then
expect(errors).toHaveLength(1);
expect(errors[0].constraints).toHaveProperty('min');
});

it('lastId에 자연수가 아닌 실수를 입력하면 유효성 검사에 실패한다.', async () => {
//given
queryFeedDto.lastId = 1.254;

//when
const errors = await validate(queryFeedDto);

//then
expect(errors).toHaveLength(1);
expect(errors[0].constraints).toHaveProperty('isInt');
});

it('lastId에 문자열을 입력하면 유효성 검사에 실패한다.', async () => {
//given
queryFeedDto.lastId = 'abcdefg' as any;

//when
const errors = await validate(queryFeedDto);

//then
expect(errors).toHaveLength(1);
expect(errors[0].constraints).toHaveProperty('isInt');
});
});
285 changes: 104 additions & 181 deletions server/test/feed/feed.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -1,204 +1,127 @@
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { FeedFixture } from './fixture/feedFixture';
import { RedisService } from '../../src/common/redis/redis.service';
import { redisKeys } from '../../src/common/redis/redis.constant';
import { FeedFixture } from '../fixture/feed.fixture';
import { FeedRepository } from '../../src/feed/feed.repository';
import { RssAcceptRepository } from '../../src/rss/rss.repository';
import { RssFixture } from '../fixture/rss.fixture';

describe('Feed E2E Test', () => {
describe('GET api/feed E2E Test', () => {
let app: INestApplication;
let redisService: RedisService;
const testFeedId = 1;
const testIp = `1.1.1.1`;
const latestId = 20;

beforeAll(async () => {
app = global.testApp;
redisService = app.get(RedisService);
const feedRepository = app.get(FeedRepository);
const rssAcceptRepository = app.get(RssAcceptRepository);

const blog = await rssAcceptRepository.save({
name: 'test',
userName: 'test',
email: '[email protected]',
rssUrl: 'https://test.com/rss',
});
const blog = await rssAcceptRepository.save(RssFixture.createRssFixture());

const feeds = Array.from({ length: 20 }).map((_, i) => {
const title = `Test Feed ${i + 1}`;
const path = `http://test.com/post/${i + 1}`;
const thumbnail = `http://test.com/thumbnail/${i + 1}`;
return FeedFixture.createFeedFixture(blog, { title, path, thumbnail });
const feeds = Array.from({ length: latestId }).map((_, i) => {
return FeedFixture.createFeedFixture(blog, _, i + 1);
});

await Promise.all([
feedRepository.save(feeds),
redisService.redisClient.sadd(`feed:${testFeedId}:ip`, testIp),
await Promise.all([feedRepository.save(feeds)]);
});

it('lastId가 없으면 최신 피드부터 전송한다.', async () => {
//given
const testQuery = { limit: 5 };

//when
const response = await request(app.getHttpServer())
.get('/api/feed')
.query(testQuery);
const feedIdArray = [];
for (const feed of response.body.data.result) {
feedIdArray.push(feed.id);
}

//then
expect(response.status).toBe(200);
expect(feedIdArray).toStrictEqual([
latestId,
latestId - 1,
latestId - 2,
latestId - 3,
latestId - 4,
]);
expect(response.body.data.hasMore).toBe(true);
expect(response.body.data.lastId).toBe(16);
});

describe('GET /api/feed', () => {
describe('페이지네이션이 정상적으로 동작한다.', () => {
it('lastId가 없으면 최신 피드부터 전송한다.', async () => {
//given
const testQuery = { limit: 5 };

//when
const response = await request(app.getHttpServer())
.get('/api/feed')
.query(testQuery);

//then
expect(response.status).toBe(200);
expect(response.body.message).toBe('피드 조회 완료');
expect(response.body.data.result).toHaveLength(5);
expect(response.body.data.hasMore).toBe(true);
expect(response.body.data.lastId).toBe(16);
});

it('lastId가 있으면 해당 피드 다음 순서부터 전송한다.', async () => {
//given
const testQuery = { limit: 5, lastId: 11 };

//when
const response = await request(app.getHttpServer())
.get('/api/feed')
.query(testQuery);

//then
expect(response.status).toBe(200);
expect(response.body.message).toBe('피드 조회 완료');
expect(response.body.data.result).toHaveLength(5);
expect(response.body.data.hasMore).toBe(true);
expect(response.body.data.lastId).toBe(6);
});

it('남은 피드 개수보다 limit의 크기가 커도 정상적으로 동작한다.', async () => {
//given
const testQuery = { limit: 15, lastId: 10 };

//when
const response = await request(app.getHttpServer())
.get('/api/feed')
.query(testQuery);

//then
expect(response.status).toBe(200);
expect(response.body.message).toBe('피드 조회 완료');
expect(response.body.data.result).toHaveLength(9);
expect(response.body.data.hasMore).toBe(false);
expect(response.body.data.lastId).toBe(1);
});

it('남은 피드 개수가 0이면 lastId 0, 빈 배열로 응답한다.', async () => {
//given
const testQuery = { limit: 15, lastId: 1 };

//when
const response = await request(app.getHttpServer())
.get('/api/feed')
.query(testQuery);

//then
expect(response.status).toBe(200);
expect(response.body.message).toBe('피드 조회 완료');
expect(response.body.data.result).toHaveLength(0);
expect(response.body.data.hasMore).toBe(false);
expect(response.body.data.lastId).toBe(0);
});
});
it('lastId가 있으면 해당 피드 다음 순서부터 전송한다.', async () => {
//given
const testQuery = { limit: 5, lastId: 11 };

//when
const response = await request(app.getHttpServer())
.get('/api/feed')
.query(testQuery);
const feedIdArray = [];
for (const feed of response.body.data.result) {
feedIdArray.push(feed.id);
}

//then
expect(response.status).toBe(200);
expect(feedIdArray).toStrictEqual([
testQuery.lastId - 1,
testQuery.lastId - 2,
testQuery.lastId - 3,
testQuery.lastId - 4,
testQuery.lastId - 5,
]);
expect(response.body.data.hasMore).toBe(true);
expect(response.body.data.lastId).toBe(6);
});

describe('POST /api/feed/:feedId', () => {
describe('Redis에 해당 IP가 없을 때', () => {
it('쿠키가 없는 경우', async () => {
//given
const testNewIp = `123.234.123.234`;

try {
//when
const response = await request(app.getHttpServer())
.post(`/api/feed/${testFeedId}`)
.set('X-Forwarded-For', testNewIp);

//then
const feedDailyViewCount = parseInt(
await redisService.redisClient.zscore(
redisKeys.FEED_TREND_KEY,
testFeedId.toString(),
),
);

expect(response.status).toBe(200);
expect(response.body.message).toBe(
'요청이 성공적으로 처리되었습니다.',
);
expect(feedDailyViewCount).toBe(1);
} finally {
//cleanup
await Promise.all([
redisService.redisClient.zrem(
redisKeys.FEED_TREND_KEY,
testFeedId.toString(),
),
redisService.redisClient.srem(`feed:${testFeedId}:ip`, testNewIp),
]);
}
});
});
it('limit의 크기보다 남은 Feed의 개수가 적은 경우면 정상적으로 동작한다.', async () => {
//given
const testQuery = { limit: 15, lastId: 10 };

//when
const response = await request(app.getHttpServer())
.get('/api/feed')
.query(testQuery);
const feedIdArray = [];
for (const feed of response.body.data.result) {
feedIdArray.push(feed.id);
}

//then
expect(response.status).toBe(200);
expect(feedIdArray).toStrictEqual([
testQuery.lastId - 1,
testQuery.lastId - 2,
testQuery.lastId - 3,
testQuery.lastId - 4,
testQuery.lastId - 5,
testQuery.lastId - 6,
testQuery.lastId - 7,
testQuery.lastId - 8,
testQuery.lastId - 9,
]);
expect(response.body.data.hasMore).toBe(false);
expect(response.body.data.lastId).toBe(1);
});

describe('Redis에 해당 IP가 있을 때', () => {
it('쿠키가 있는 경우', async () => {
//when
const response = await request(app.getHttpServer())
.post(`/api/feed/${testFeedId}`)
.set('Cookie', `View_count_${testFeedId}=${testFeedId}`)
.set('X-Forwarded-For', testIp);

//then
const feedDailyViewCount = await redisService.redisClient.zscore(
redisKeys.FEED_TREND_KEY,
testFeedId.toString(),
);
expect(response.status).toBe(200);
expect(response.body.message).toBe('요청이 성공적으로 처리되었습니다.');
expect(feedDailyViewCount).toBeNull();
});

it('쿠키가 없는 경우', async () => {
//when
const response = await request(app.getHttpServer())
.post(`/api/feed/${testFeedId}`)
.set('X-Forwarded-For', testIp);

//then
const feedDailyViewCount = await redisService.redisClient.zscore(
redisKeys.FEED_TREND_KEY,
testFeedId.toString(),
);
expect(response.status).toBe(200);
expect(response.body.message).toBe('요청이 성공적으로 처리되었습니다.');
expect(feedDailyViewCount).toBeNull();
});

describe('잘못된 요청을 보냈을 때', () => {
it('해당 피드 ID가 존재하지 않는 경우', async () => {
//given
const notExistFeedId = 50000;

//when
const response = await request(app.getHttpServer()).post(
`/api/feed/${notExistFeedId}`,
);

//then
expect(response.status).toBe(404);
expect(response.body.message).toBe(
`${notExistFeedId}번 피드를 찾을 수 없습니다.`,
);
});
});
});
it('남은 피드 개수가 0이면 lastId 0, 빈 배열로 응답한다.', async () => {
//given
const testQuery = { limit: 15, lastId: 1 };

//when
const response = await request(app.getHttpServer())
.get('/api/feed')
.query(testQuery);
const feedIdArray = [];
for (const feed of response.body.data.result) {
feedIdArray.push(feed.id);
}

//then
expect(response.status).toBe(200);
expect(feedIdArray).toStrictEqual([]);
expect(response.body.data.hasMore).toBe(false);
expect(response.body.data.lastId).toBe(0);
});
});
Loading

0 comments on commit 90843dc

Please sign in to comment.