Skip to content
Merged
Show file tree
Hide file tree
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
@@ -0,0 +1,13 @@
package com.sofa.linkiving.domain.report.controller;

import com.sofa.linkiving.domain.member.entity.Member;
import com.sofa.linkiving.domain.report.dto.request.ReportReq;
import com.sofa.linkiving.global.common.BaseResponse;

import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;

@Tag(name = "Report", description = "오류 및 버그 제보 관리 API")
public interface ReportApi {
BaseResponse<String> createReport(Member member, @Valid ReportReq res);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.sofa.linkiving.domain.report.controller;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.sofa.linkiving.domain.member.entity.Member;
import com.sofa.linkiving.domain.report.dto.request.ReportReq;
import com.sofa.linkiving.domain.report.service.ReportService;
import com.sofa.linkiving.global.common.BaseResponse;
import com.sofa.linkiving.security.annotation.AuthMember;

import lombok.RequiredArgsConstructor;

@RestController
@RequestMapping("/v1/report")
@RequiredArgsConstructor
public class ReportController implements ReportApi {
private final ReportService reportService;

@Override
@PostMapping
public BaseResponse<String> createReport(@AuthMember Member member, @RequestBody ReportReq res) {
reportService.create(member, res.content());
return BaseResponse.noContent("제보가 성공적으로 접수되었습니다.");
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.sofa.linkiving.domain.report.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

public record ReportReq(
@NotBlank(message = "제보 내용은 필수입니다.")
@Schema(description = "버그 및 오류 내용")
@Size(max = 1000, message = "제보 내용은 1000자를 초과할 수 없습니다")
String content
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import org.springframework.stereotype.Service;

import com.sofa.linkiving.domain.member.entity.Member;
import com.sofa.linkiving.domain.report.entity.Report;
import com.sofa.linkiving.domain.report.repository.ReportRepository;

import lombok.RequiredArgsConstructor;
Expand All @@ -10,4 +12,13 @@
@RequiredArgsConstructor
public class ReportCommandService {
private final ReportRepository reportRepository;

public Report save(Member member, String content) {
Report report = Report.builder()
.member(member)
.content(content)
.build();

return reportRepository.save(report);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.sofa.linkiving.domain.member.entity.Member;

import lombok.RequiredArgsConstructor;

@Service
@Transactional
@RequiredArgsConstructor
public class ReportService {
private final ReportCommandService reportCommandService;

public void create(Member member, String content) {
reportCommandService.save(member, content);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.sofa.linkiving.domain.report.integration;

import static org.assertj.core.api.Assertions.*;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import java.util.List;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.transaction.annotation.Transactional;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.sofa.linkiving.domain.member.entity.Member;
import com.sofa.linkiving.domain.member.enums.Role;
import com.sofa.linkiving.domain.member.repository.MemberRepository;
import com.sofa.linkiving.domain.report.dto.request.ReportReq;
import com.sofa.linkiving.domain.report.entity.Report;
import com.sofa.linkiving.domain.report.repository.ReportRepository;
import com.sofa.linkiving.security.userdetails.CustomMemberDetail;

@SpringBootTest
@AutoConfigureMockMvc
@Transactional
@ActiveProfiles("test")
public class ReportApiIntegrationTest {
@Autowired
private MockMvc mockMvc;

@Autowired
private ObjectMapper objectMapper;

@Autowired
private ReportRepository reportRepository;

@Autowired
private MemberRepository memberRepository;

private Member testMember;
private UserDetails testUserDetails;

@BeforeEach
void setUp() {
testMember = memberRepository.save(Member.builder()
.email("reportUser@test.com")
.password("password")
.build());

testUserDetails = new CustomMemberDetail(testMember, Role.USER);
}

@Test
@DisplayName("제보 생성 요청 시 DB에 저장되고 204 No Content를 반환한다")
void shouldCreateReportSuccessfully() throws Exception {
// given
ReportReq requestDto = new ReportReq("잘못된 링크 정보를 수정해주세요.");

// when & then
mockMvc.perform(
post("/v1/report")
.with(csrf())
.with(user(testUserDetails))
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(requestDto))
)
.andExpect(status().isOk());

List<Report> reports = reportRepository.findAll();
assertThat(reports).hasSize(1);
assertThat(reports.get(0).getContent()).isEqualTo("잘못된 링크 정보를 수정해주세요.");
assertThat(reports.get(0).getMember().getId()).isEqualTo(testMember.getId());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.sofa.linkiving.domain.report.service;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.BDDMockito.*;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import com.sofa.linkiving.domain.member.entity.Member;
import com.sofa.linkiving.domain.report.entity.Report;
import com.sofa.linkiving.domain.report.repository.ReportRepository;

@ExtendWith(MockitoExtension.class)
@DisplayName("ReportCommandService 단위 테스트")
public class ReportCommandServiceTest {

@InjectMocks
private ReportCommandService reportCommandService;

@Mock
private ReportRepository reportRepository;

@Test
@DisplayName("제보 내용을 저장할 수 있다")
void shouldSaveReport() {
// given
Member member = Member.builder()
.email("test@example.com")
.build();
String content = "버그 제보합니다.";

Report report = Report.builder()
.member(member)
.content(content)
.build();

given(reportRepository.save(any(Report.class))).willReturn(report);

// when
Report savedReport = reportCommandService.save(member, content);

// then
assertThat(savedReport).isNotNull();
assertThat(savedReport.getContent()).isEqualTo(content);
assertThat(savedReport.getMember()).isEqualTo(member);

verify(reportRepository, times(1)).save(any(Report.class));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.sofa.linkiving.domain.report.service;

import static org.mockito.BDDMockito.*;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import com.sofa.linkiving.domain.member.entity.Member;

@ExtendWith(MockitoExtension.class)
@DisplayName("ReportService 단위 테스트")
public class ReportServiceTest {
@InjectMocks
private ReportService reportService;

@Mock
private ReportCommandService reportCommandService;

@Test
@DisplayName("제보 생성 요청 시 CommandService에게 저장을 위임한다")
void shouldDelegateToCommandService() {
// given
Member member = mock(Member.class);
String content = "불건전한 링크가 포함되어 있습니다.";

// when
reportService.create(member, content);

// then
verify(reportCommandService, times(1)).save(member, content);
}
}