Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -745,5 +745,84 @@ public class PostQueryControllerTest extends ControllerTestSupport {
)
));
}

@Test
void 최근_포스트_목록을_조회한다() throws Exception {
// given
PostResponseDto recentPost = new PostResponseDto(
1L,
"최근 포스트 제목",
"최근 포스트 내용입니다",
List.of("#해시태그1"),
Instant.parse("2024-01-15T10:30:00Z"),
"작성자1",
10,
5,
"https://example.com/image1.jpg",
PostType.FREE,
null,
null,
null,
null
);

PageResponseDto<PostResponseDto> response = new PageResponseDto<>(
List.of(recentPost),
null,
false
);
Comment on lines +752 to +773

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

현재 테스트는 FREE 타입의 포스트만 포함하고 있어, STORYCURATION 타입 포스트에만 존재하는 primaryComposer, additionalComposers 필드에 대한 문서가 불완전하게 생성됩니다. REST Docs 문서의 완성도를 높이기 위해, 다양한 포스트 타입을 포함하는 테스트 데이터를 생성하여 응답에 포함시키는 것을 권장합니다. 이렇게 하면 API 사용자가 각 포스트 타입에 따른 응답 구조를 더 명확하게 이해할 수 있습니다.

예를 들어, 다음과 같이 다양한 타입의 포스트를 생성할 수 있습니다:

// given
var composerSummary = new PostResponseDto.ComposerSummary(1L, "바흐", com.daramg.server.composer.domain.Era.BAROQUE, com.daramg.server.composer.domain.Continent.EUROPE);
var anotherComposer = new PostResponseDto.ComposerSummary(2L, "모차르트", com.daramg.server.composer.domain.Era.CLASSICAL, com.daramg.server.composer.domain.Continent.EUROPE);

PostResponseDto freePost = new PostResponseDto(
        1L, "최근 포스트 제목 (FREE)", "자유 포스트 내용입니다.", List.of("#자유"),
        Instant.parse("2024-01-15T10:30:00Z"), "작성자1", 10, 5,
        "https://example.com/image1.jpg", PostType.FREE, null, null, true, false
);

PostResponseDto storyPost = new PostResponseDto(
        2L, "최근 포스트 제목 (STORY)", "스토리 포스트 내용입니다.", List.of("#스토리", "#바흐"),
        Instant.parse("2024-01-14T11:00:00Z"), "작성자2", 20, 8,
        "https://example.com/image2.jpg", PostType.STORY, composerSummary, null, false, true
);

PostResponseDto curationPost = new PostResponseDto(
        3L, "최근 포스트 제목 (CURATION)", "큐레이션 포스트 내용입니다.", List.of("#큐레이션", "#클래식"),
        Instant.parse("2024-01-13T12:00:00Z"), "작성자3", 30, 15,
        "https://example.com/image3.jpg", PostType.CURATION, composerSummary, List.of(anotherComposer), true, true
);

PageResponseDto<PostResponseDto> response = new PageResponseDto<>(
        List.of(freePost, storyPost, curationPost),
        "bmV4dEN1cnNvcg==", // Base64 encoded cursor
        true
);

이렇게 하면 primaryComposer, additionalComposers, nextCursor 필드가 포함된 응답 예시가 문서에 생성됩니다.


when(postQueryService.getRecentPosts(any(PageRequestDto.class), any())).thenReturn(response);

// when
ResultActions result = mockMvc.perform(get("/posts/recent")
.param("size", "10")
.contentType(MediaType.APPLICATION_JSON)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

GET 요청에서는 요청 본문이 없으므로 contentType을 설정하는 것은 의미가 없습니다. 클라이언트가 수신하고자 하는 미디어 타입을 지정하려면 accept(MediaType.APPLICATION_JSON)를 사용해야 합니다.

Suggested change
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)

);

// then
result.andExpect(status().isOk())
.andDo(document("최근_포스트_목록_조회",
resource(ResourceSnippetParameters.builder()
.tag("Post Query API")
.summary("최근 포스트 목록 조회")
.description("7일 이내 발행된 모든 타입(FREE, STORY, CURATION)의 포스트를 최신순으로 조회합니다.")
.queryParameters(
parameterWithName("cursor").description("다음 페이지 조회를 위한 커서 (Base64 인코딩된 문자열). 첫 페이지 조회 시 생략 가능")
.optional(),
parameterWithName("size").description("한 페이지에 조회할 포스트 개수. 생략 시 기본값: 10")
.optional()
)
.responseFields(
fieldWithPath("content").type(JsonFieldType.ARRAY).description("포스트 목록"),
fieldWithPath("content[].id").type(JsonFieldType.NUMBER).description("포스트 ID"),
fieldWithPath("content[].title").type(JsonFieldType.STRING).description("포스트 제목"),
fieldWithPath("content[].content").type(JsonFieldType.STRING).description("포스트 내용"),
fieldWithPath("content[].hashtags").type(JsonFieldType.ARRAY).description("해시태그 목록"),
fieldWithPath("content[].createdAt").type(JsonFieldType.STRING).description("생성일시 (ISO 8601 형식)"),
fieldWithPath("content[].writerNickname").type(JsonFieldType.STRING).description("작성자 닉네임"),
fieldWithPath("content[].likeCount").type(JsonFieldType.NUMBER).description("좋아요 개수"),
fieldWithPath("content[].commentCount").type(JsonFieldType.NUMBER).description("댓글 개수"),
fieldWithPath("content[].thumbnailImageUrl").type(JsonFieldType.STRING).description("썸네일 이미지 URL").optional(),
fieldWithPath("content[].type").type(JsonFieldType.STRING).description("포스트 타입 (FREE, CURATION, STORY)"),
fieldWithPath("content[].primaryComposer").type(JsonFieldType.OBJECT).description("대표 작곡가 정보 (STORY, CURATION 타입일 때만 존재)").optional(),
fieldWithPath("content[].primaryComposer.id").type(JsonFieldType.NUMBER).description("작곡가 ID").optional(),
fieldWithPath("content[].primaryComposer.koreanName").type(JsonFieldType.STRING).description("작곡가 한글 이름").optional(),
fieldWithPath("content[].primaryComposer.era").type(JsonFieldType.STRING).description("작곡가 시대 (Era)").optional(),
fieldWithPath("content[].primaryComposer.continent").type(JsonFieldType.STRING).description("작곡가 대륙 (Continent)").optional(),
fieldWithPath("content[].additionalComposers").type(JsonFieldType.ARRAY).description("추가 작곡가 목록 (CURATION 타입일 때만 존재)").optional(),
fieldWithPath("content[].additionalComposers[].id").type(JsonFieldType.NUMBER).description("추가 작곡가 ID").optional(),
fieldWithPath("content[].additionalComposers[].koreanName").type(JsonFieldType.STRING).description("추가 작곡가 한글 이름").optional(),
fieldWithPath("content[].additionalComposers[].era").type(JsonFieldType.STRING).description("추가 작곡가 시대 (Era)").optional(),
fieldWithPath("content[].additionalComposers[].continent").type(JsonFieldType.STRING).description("추가 작곡가 대륙 (Continent)").optional(),
fieldWithPath("content[].isLiked").type(JsonFieldType.BOOLEAN).description("로그인한 유저의 좋아요 여부 (비로그인 시 null)").optional(),
fieldWithPath("content[].isScrapped").type(JsonFieldType.BOOLEAN).description("로그인한 유저의 스크랩 여부 (비로그인 시 null)").optional(),
fieldWithPath("nextCursor").type(JsonFieldType.STRING).description("다음 페이지 커서 (마지막 페이지인 경우 null)").optional(),
fieldWithPath("hasNext").type(JsonFieldType.BOOLEAN).description("다음 페이지 존재 여부")
)
.build()
)
));
}
}

Loading