diff --git a/src/main/java/nextstep/qna/domain/Answer.java b/src/main/java/nextstep/qna/domain/Answer.java index cf681811e7..08570ed9ed 100644 --- a/src/main/java/nextstep/qna/domain/Answer.java +++ b/src/main/java/nextstep/qna/domain/Answer.java @@ -30,11 +30,11 @@ public Answer(NsUser writer, Question question, String contents) { public Answer(Long id, NsUser writer, Question question, String contents) { this.id = id; - if(writer == null) { + if (writer == null) { throw new UnAuthorizedException(); } - if(question == null) { + if (question == null) { throw new NotFoundException(); } @@ -47,9 +47,9 @@ public Long getId() { return id; } - public Answer setDeleted(boolean deleted) { - this.deleted = deleted; - return this; + public DeleteHistory delete() { + this.deleted = true; + return new DeleteHistory(ContentType.ANSWER, id, writer, LocalDateTime.now()); } public boolean isDeleted() { @@ -60,6 +60,10 @@ public boolean isOwner(NsUser writer) { return this.writer.equals(writer); } + public boolean isNotOwner(NsUser writer) { + return !isOwner(writer); + } + public NsUser getWriter() { return writer; } diff --git a/src/main/java/nextstep/qna/domain/Question.java b/src/main/java/nextstep/qna/domain/Question.java index b623c52c76..e27d28bea9 100644 --- a/src/main/java/nextstep/qna/domain/Question.java +++ b/src/main/java/nextstep/qna/domain/Question.java @@ -1,5 +1,9 @@ package nextstep.qna.domain; +import nextstep.qna.CannotDeleteException; +import nextstep.qna.domain.qustion.QuestionDeletable; +import nextstep.qna.domain.qustion.QuestionDeletableAnswer; +import nextstep.qna.domain.qustion.QuestionDeletableWriter; import nextstep.users.domain.NsUser; import java.time.LocalDateTime; @@ -7,6 +11,10 @@ import java.util.List; public class Question { + + private static final List deletableConditions = List.of( + new QuestionDeletableAnswer(), new QuestionDeletableWriter()); + private Long id; private String title; @@ -68,13 +76,40 @@ public void addAnswer(Answer answer) { answers.add(answer); } + public List delete(NsUser loginUser) throws CannotDeleteException { + checkDeletable(loginUser); + + List deleteHistories = new ArrayList<>(); + deleted = true; + deleteHistories.add( + new DeleteHistory(ContentType.QUESTION, id, writer, LocalDateTime.now())); + for (Answer answer : answers) { + deleteHistories.add(answer.delete()); + } + return deleteHistories; + } + + private void checkDeletable(NsUser loginUser) throws CannotDeleteException { + for (QuestionDeletable condition : deletableConditions) { + condition.checkDeletable(this, loginUser); + } + } + public boolean isOwner(NsUser loginUser) { return writer.equals(loginUser); } - public Question setDeleted(boolean deleted) { - this.deleted = deleted; - return this; + public boolean answerEmpty() { + return answers.isEmpty(); + } + + public boolean allAnswerFromWriter() { + for (Answer answer : answers) { + if (answer.isNotOwner(writer)) { + return false; + } + } + return true; } public boolean isDeleted() { @@ -87,6 +122,7 @@ public List getAnswers() { @Override public String toString() { - return "Question [id=" + getId() + ", title=" + title + ", contents=" + contents + ", writer=" + writer + "]"; + return "Question [id=" + getId() + ", title=" + title + ", contents=" + contents + + ", writer=" + writer + "]"; } } diff --git a/src/main/java/nextstep/qna/domain/qustion/QuestionDeletable.java b/src/main/java/nextstep/qna/domain/qustion/QuestionDeletable.java new file mode 100644 index 0000000000..5805256eb8 --- /dev/null +++ b/src/main/java/nextstep/qna/domain/qustion/QuestionDeletable.java @@ -0,0 +1,9 @@ +package nextstep.qna.domain.qustion; + +import nextstep.qna.CannotDeleteException; +import nextstep.qna.domain.Question; +import nextstep.users.domain.NsUser; + +public interface QuestionDeletable { + void checkDeletable(Question question, NsUser loginUser) throws CannotDeleteException; +} diff --git a/src/main/java/nextstep/qna/domain/qustion/QuestionDeletableAnswer.java b/src/main/java/nextstep/qna/domain/qustion/QuestionDeletableAnswer.java new file mode 100644 index 0000000000..a981bc604a --- /dev/null +++ b/src/main/java/nextstep/qna/domain/qustion/QuestionDeletableAnswer.java @@ -0,0 +1,18 @@ +package nextstep.qna.domain.qustion; + +import nextstep.qna.CannotDeleteException; +import nextstep.qna.domain.Question; +import nextstep.users.domain.NsUser; + +public class QuestionDeletableAnswer implements QuestionDeletable { + + static final String REASON = "다른 사람이 쓴 답변이 있어 삭제할 수 없습니다."; + + @Override + public void checkDeletable(Question question, NsUser loginUser) throws CannotDeleteException { + if (question.answerEmpty() || question.allAnswerFromWriter()) { + return; + } + throw new CannotDeleteException(REASON); + } +} diff --git a/src/main/java/nextstep/qna/domain/qustion/QuestionDeletableWriter.java b/src/main/java/nextstep/qna/domain/qustion/QuestionDeletableWriter.java new file mode 100644 index 0000000000..a47005cd1d --- /dev/null +++ b/src/main/java/nextstep/qna/domain/qustion/QuestionDeletableWriter.java @@ -0,0 +1,18 @@ +package nextstep.qna.domain.qustion; + +import nextstep.qna.CannotDeleteException; +import nextstep.qna.domain.Question; +import nextstep.users.domain.NsUser; + +public class QuestionDeletableWriter implements QuestionDeletable { + + static final String REASON = "질문을 삭제할 권한이 없습니다."; + + @Override + public void checkDeletable(Question question, NsUser loginUser) throws CannotDeleteException { + if (question.isOwner(loginUser)) { + return; + } + throw new CannotDeleteException(REASON); + } +} diff --git a/src/main/java/nextstep/qna/service/QnAService.java b/src/main/java/nextstep/qna/service/QnAService.java index 5741c84d65..1663e754eb 100644 --- a/src/main/java/nextstep/qna/service/QnAService.java +++ b/src/main/java/nextstep/qna/service/QnAService.java @@ -8,9 +8,6 @@ import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; @Service("qnaService") public class QnAService { @@ -26,24 +23,6 @@ public class QnAService { @Transactional public void deleteQuestion(NsUser loginUser, long questionId) throws CannotDeleteException { Question question = questionRepository.findById(questionId).orElseThrow(NotFoundException::new); - if (!question.isOwner(loginUser)) { - throw new CannotDeleteException("질문을 삭제할 권한이 없습니다."); - } - - List answers = question.getAnswers(); - for (Answer answer : answers) { - if (!answer.isOwner(loginUser)) { - throw new CannotDeleteException("다른 사람이 쓴 답변이 있어 삭제할 수 없습니다."); - } - } - - List deleteHistories = new ArrayList<>(); - question.setDeleted(true); - deleteHistories.add(new DeleteHistory(ContentType.QUESTION, questionId, question.getWriter(), LocalDateTime.now())); - for (Answer answer : answers) { - answer.setDeleted(true); - deleteHistories.add(new DeleteHistory(ContentType.ANSWER, answer.getId(), answer.getWriter(), LocalDateTime.now())); - } - deleteHistoryService.saveAll(deleteHistories); + deleteHistoryService.saveAll(question.delete(loginUser)); } } diff --git a/src/test/java/nextstep/qna/domain/AnswerTest.java b/src/test/java/nextstep/qna/domain/AnswerTest.java index 8e80ffb429..25bac8ae9a 100644 --- a/src/test/java/nextstep/qna/domain/AnswerTest.java +++ b/src/test/java/nextstep/qna/domain/AnswerTest.java @@ -1,8 +1,23 @@ package nextstep.qna.domain; +import java.time.LocalDateTime; import nextstep.users.domain.NsUserTest; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; public class AnswerTest { public static final Answer A1 = new Answer(NsUserTest.JAVAJIGI, QuestionTest.Q1, "Answers Contents1"); public static final Answer A2 = new Answer(NsUserTest.SANJIGI, QuestionTest.Q1, "Answers Contents2"); + + @Test + void 삭제() { + Answer answer = new Answer(11L, NsUserTest.JAVAJIGI, QuestionTest.Q1, "Answers Contents1"); + assertThat(answer.isDeleted()).isFalse(); + + DeleteHistory history = answer.delete(); + assertThat(answer.isDeleted()).isTrue(); + assertThat(history).isEqualTo( + new DeleteHistory(ContentType.ANSWER, answer.getId(), answer.getWriter(), LocalDateTime.now())); + } } diff --git a/src/test/java/nextstep/qna/domain/QuestionTest.java b/src/test/java/nextstep/qna/domain/QuestionTest.java index 3b87823963..3e9963b297 100644 --- a/src/test/java/nextstep/qna/domain/QuestionTest.java +++ b/src/test/java/nextstep/qna/domain/QuestionTest.java @@ -1,8 +1,63 @@ package nextstep.qna.domain; +import nextstep.qna.CannotDeleteException; import nextstep.users.domain.NsUserTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; public class QuestionTest { public static final Question Q1 = new Question(NsUserTest.JAVAJIGI, "title1", "contents1"); public static final Question Q2 = new Question(NsUserTest.SANJIGI, "title2", "contents2"); + + + private Question question; + private Answer answer; + + @BeforeEach + public void setUp() { + question = new Question(1L, NsUserTest.JAVAJIGI, "title1", "contents1"); + answer = new Answer(11L, NsUserTest.JAVAJIGI, QuestionTest.Q1, "Answers Contents1"); + question.addAnswer(answer); + } + + @Test + public void delete_성공() throws Exception { + assertThat(question.isDeleted()).isFalse(); + List deleteHistories = question.delete(NsUserTest.JAVAJIGI); + + assertThat(question.isDeleted()).isTrue(); + assertThat(deleteHistories).isEqualTo(Arrays.asList( + new DeleteHistory(ContentType.QUESTION, question.getId(), question.getWriter(), LocalDateTime.now()), + new DeleteHistory(ContentType.ANSWER, answer.getId(), answer.getWriter(), LocalDateTime.now()))); + } + + @Test + public void delete_다른_사람이_쓴_글() { + assertThatThrownBy(() -> question.delete(NsUserTest.SANJIGI)).isInstanceOf(CannotDeleteException.class); + } + + @Test + public void delete_성공_질문자_답변자_같음() throws Exception { + List deleteHistories = question.delete(NsUserTest.JAVAJIGI); + + assertThat(question.isDeleted()).isTrue(); + assertThat(answer.isDeleted()).isTrue(); + + assertThat(deleteHistories).isEqualTo(Arrays.asList( + new DeleteHistory(ContentType.QUESTION, question.getId(), question.getWriter(), LocalDateTime.now()), + new DeleteHistory(ContentType.ANSWER, answer.getId(), answer.getWriter(), LocalDateTime.now()))); + } + + @Test + public void delete_답변_중_다른_사람이_쓴_글() { + assertThatThrownBy(() -> question.delete(NsUserTest.SANJIGI)).isInstanceOf(CannotDeleteException.class); + } + } diff --git a/src/test/java/nextstep/qna/domain/question/QuestionDeletableAnswerTest.java b/src/test/java/nextstep/qna/domain/question/QuestionDeletableAnswerTest.java new file mode 100644 index 0000000000..7769621902 --- /dev/null +++ b/src/test/java/nextstep/qna/domain/question/QuestionDeletableAnswerTest.java @@ -0,0 +1,42 @@ +package nextstep.qna.domain.question; + +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import nextstep.qna.CannotDeleteException; +import nextstep.qna.domain.AnswerTest; +import nextstep.qna.domain.Question; +import nextstep.qna.domain.qustion.QuestionDeletable; +import nextstep.qna.domain.qustion.QuestionDeletableAnswer; +import nextstep.users.domain.NsUserTest; +import org.junit.jupiter.api.Test; + +class QuestionDeletableAnswerTest { + + @Test + void 답변없음_삭제가능() { + Question question = new Question(NsUserTest.JAVAJIGI, "title1", "contents1"); + QuestionDeletable questionDeletable = new QuestionDeletableAnswer(); + + assertThatNoException().isThrownBy(() -> questionDeletable.checkDeletable(question, question.getWriter())); + } + + @Test + void 작성자와_같은답변_삭제가능() { + Question question = new Question(NsUserTest.JAVAJIGI, "title1", "contents1"); + QuestionDeletable questionDeletable = new QuestionDeletableAnswer(); + question.addAnswer(AnswerTest.A1); + + assertThatNoException().isThrownBy(() -> questionDeletable.checkDeletable(question, question.getWriter())); + } + + @Test + void 작성자와_다른답변_삭제불가() { + Question question = new Question(NsUserTest.JAVAJIGI, "title1", "contents1"); + QuestionDeletable questionDeletable = new QuestionDeletableAnswer(); + question.addAnswer(AnswerTest.A2); + + assertThatThrownBy(() -> questionDeletable.checkDeletable(question, question.getWriter())).isInstanceOf( + CannotDeleteException.class); + } +} diff --git a/src/test/java/nextstep/qna/domain/question/QuestionDeletableWriterTest.java b/src/test/java/nextstep/qna/domain/question/QuestionDeletableWriterTest.java new file mode 100644 index 0000000000..f64a7dbff4 --- /dev/null +++ b/src/test/java/nextstep/qna/domain/question/QuestionDeletableWriterTest.java @@ -0,0 +1,31 @@ +package nextstep.qna.domain.question; + +import nextstep.qna.CannotDeleteException; +import nextstep.qna.domain.Question; +import nextstep.qna.domain.qustion.QuestionDeletable; +import nextstep.qna.domain.qustion.QuestionDeletableWriter; +import nextstep.users.domain.NsUserTest; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class QuestionDeletableWriterTest { + + @Test + void 작성자라_삭제가능() { + Question question = new Question(NsUserTest.JAVAJIGI, "title1", "contents1"); + QuestionDeletable questionDeletable = new QuestionDeletableWriter(); + + assertThatNoException().isThrownBy(() -> questionDeletable.checkDeletable(question, NsUserTest.JAVAJIGI)); + } + + @Test + void 작성자가_아니라_삭제불가() { + Question question = new Question(NsUserTest.JAVAJIGI, "title1", "contents1"); + QuestionDeletable questionDeletable = new QuestionDeletableWriter(); + + assertThatThrownBy(() -> questionDeletable.checkDeletable(question, NsUserTest.SANJIGI)).isInstanceOf( + CannotDeleteException.class); + } +}