diff --git a/simple_sns/src/main/java/com/example/sns/controller/request/PostCreateRequest.java b/simple_sns/src/main/java/com/example/sns/controller/request/PostCreateRequest.java new file mode 100644 index 00000000..852d12e3 --- /dev/null +++ b/simple_sns/src/main/java/com/example/sns/controller/request/PostCreateRequest.java @@ -0,0 +1,12 @@ +package com.example.sns.controller.request; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class PostCreateRequest { + + private String title; + private String body; +} diff --git a/simple_sns/src/main/java/com/example/sns/model/entity/PostEntity.java b/simple_sns/src/main/java/com/example/sns/model/entity/PostEntity.java new file mode 100644 index 00000000..4bcca58c --- /dev/null +++ b/simple_sns/src/main/java/com/example/sns/model/entity/PostEntity.java @@ -0,0 +1,48 @@ +package com.example.sns.model.entity; + +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.Where; + +import javax.persistence.*; +import java.sql.Timestamp; +import java.time.Instant; + +@Entity +@Table(name = "posts") +@Getter +@Setter +@SQLDelete(sql = "UPDATE posts SET deleted_at = NOW() where id=?") +@Where(clause = "deleted_at IS NULL") +public class PostEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + private String title; + + @Column(columnDefinition = "TEXT") + private String body; + + @ManyToOne + @JoinColumn(name = "user_id") + private UserEntity user; + + private Timestamp registeredAt; + private Timestamp updatedAt; + + @Column(name = "deleted_at") + private Timestamp deletedAt; + + @PrePersist + void registeredAt() { + this.registeredAt = Timestamp.from(Instant.now()); + } + + @PreUpdate + void updatedAt() { + this.updatedAt = Timestamp.from(Instant.now()); + } +} diff --git a/simple_sns/src/main/java/com/example/sns/repository/PostEntityRepository.java b/simple_sns/src/main/java/com/example/sns/repository/PostEntityRepository.java new file mode 100644 index 00000000..0059d532 --- /dev/null +++ b/simple_sns/src/main/java/com/example/sns/repository/PostEntityRepository.java @@ -0,0 +1,9 @@ +package com.example.sns.repository; + +import com.example.sns.model.entity.PostEntity; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface PostEntityRepository extends JpaRepository { +} diff --git a/simple_sns/src/main/java/com/example/sns/service/PostService.java b/simple_sns/src/main/java/com/example/sns/service/PostService.java new file mode 100644 index 00000000..e82d8ef4 --- /dev/null +++ b/simple_sns/src/main/java/com/example/sns/service/PostService.java @@ -0,0 +1,38 @@ +package com.example.sns.service; + +import com.example.sns.exception.ErrorCode; +import com.example.sns.exception.SnsApplicationException; +import com.example.sns.model.entity.PostEntity; +import com.example.sns.model.entity.UserEntity; +import com.example.sns.repository.PostEntityRepository; +import com.example.sns.repository.UserEntityRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class PostService { + + private final PostEntityRepository postEntityRepository; + private final UserEntityRepository userEntityRepository; + + @Transactional + public void create(String title, String body, String username) { + + // TODO user find + UserEntity userEntity = userEntityRepository.findByUsername(username) + .orElseThrow( + () -> new SnsApplicationException( + ErrorCode.USER_NOT_FOUND, + String.format("%s not founded", username) + ) + ); + + // TODO post save + postEntityRepository.save(new PostEntity()); + + // TODO return + } + +} diff --git a/simple_sns/src/test/java/com/example/sns/controller/PostControllerTest.java b/simple_sns/src/test/java/com/example/sns/controller/PostControllerTest.java new file mode 100644 index 00000000..bd6a7526 --- /dev/null +++ b/simple_sns/src/test/java/com/example/sns/controller/PostControllerTest.java @@ -0,0 +1,55 @@ +package com.example.sns.controller; + +import com.example.sns.controller.request.PostCreateRequest; +import com.fasterxml.jackson.databind.ObjectMapper; +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.test.context.support.WithAnonymousUser; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc +public class PostControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Test + @WithMockUser + void 포스트작성() throws Exception { + + String title = "title"; + String body = "body"; + + mockMvc.perform(post("/api/v1/posts") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsBytes(new PostCreateRequest(title, body))) + ).andDo(print()) + .andExpect(status().isOk()); + } + + @Test + @WithAnonymousUser + void 포스트작성시_로그인하지않은경우() throws Exception { + + String title = "title"; + String body = "body"; + + mockMvc.perform(post("/api/v1/posts") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsBytes(new PostCreateRequest(title, body))) + ).andDo(print()) + .andExpect(status().isUnauthorized()); + } +} diff --git a/simple_sns/src/test/java/com/example/sns/service/PostServiceTest.java b/simple_sns/src/test/java/com/example/sns/service/PostServiceTest.java new file mode 100644 index 00000000..e81e41f1 --- /dev/null +++ b/simple_sns/src/test/java/com/example/sns/service/PostServiceTest.java @@ -0,0 +1,61 @@ +package com.example.sns.service; + +import com.example.sns.exception.ErrorCode; +import com.example.sns.exception.SnsApplicationException; +import com.example.sns.model.entity.PostEntity; +import com.example.sns.model.entity.UserEntity; +import com.example.sns.repository.PostEntityRepository; +import com.example.sns.repository.UserEntityRepository; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@SpringBootTest +public class PostServiceTest { + + @Autowired + private PostService postService; + + @MockBean + private PostEntityRepository postEntityRepository; + + @MockBean + private UserEntityRepository userEntityRepository; + + @Test + void 포스트작성이_성공한경우() { + String title = "title"; + String body = "body"; + String username = "username"; + + when(userEntityRepository.findByUsername(username)).thenReturn(Optional.of(mock(UserEntity.class))); + when(postEntityRepository.save(any())).thenReturn(mock(PostEntity.class)); + + assertDoesNotThrow(() -> postService.create(title, body, username)); + } + + @Test + void 포스트작성시_요청한유저가_존재하지않는경우() { + String title = "title"; + String body = "body"; + String username = "username"; + + when(userEntityRepository.findByUsername(username)).thenReturn(Optional.empty()); + when(postEntityRepository.save(any())).thenReturn(mock(PostEntity.class)); + + SnsApplicationException e = assertThrows( + SnsApplicationException.class, + () -> postService.create(title, body, username) + ); + + assertEquals(ErrorCode.USER_NOT_FOUND, e.getErrorCode()); + } +}