package com.pingcap.community.blogserver.controller;

import com.pingcap.community.blogserver.testutils.PopulateDataExtension;
import com.pingcap.community.blogserver.testutils.StringUtils;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith;
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.test.web.servlet.MockMvc;

import javax.servlet.http.Cookie;

import static com.pingcap.community.blogserver.testutils.RequestPostProcessors.*;
import static org.hamcrest.Matchers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@AutoConfigureMockMvc
@ExtendWith(PopulateDataExtension.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
class PostControllerTest {

  @Autowired
  private MockMvc mockMvc;

  private static final String SEARCH_IMPORTER_TOKEN = "55bbeb82-c4a4-40ff-a571-f99948382b66";

  private static final String EXTERNAL_IMPORTER_TOKEN = "699ed0d5-3abb-4932-bb4a-308b0cdeae44";

  @Test
  @Order(101)
  void getLatestPosts() throws Exception {
    mockMvc
        .perform(
            get("/api/posts/latest").queryParam("size", "5")
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.content[*].id", containsInRelativeOrder(10, 9, 8, 7, 6)))
        .andExpect(jsonPath("$.content[*].slug", containsInRelativeOrder("sha10", "sha9", "sha8", "sha7", "sha6")))
        .andExpect(jsonPath("$.content[*].status",  containsInRelativeOrder("PUBLISHED", "PUBLISHED", "PUBLISHED", "PUBLISHED", "PUBLISHED")))
        .andExpect(jsonPath("$.content[*].createdAt",  containsInRelativeOrder(notNullValue(), notNullValue(), notNullValue(), notNullValue(), notNullValue())))
        .andExpect(jsonPath("$.content[*].lastModifiedAt",  containsInRelativeOrder(notNullValue(), notNullValue(), notNullValue(), notNullValue(), notNullValue())))
        .andExpect(jsonPath("$.page.number", equalTo(1)))
        .andExpect(jsonPath("$.page.size", equalTo(5)));
  }

  @Test
  @Order(101)
  void getLatestPostsByCategory() throws Exception {
    mockMvc
        .perform(
            get("/api/posts/latest")
                .queryParam("size", "5")
                .queryParam("categoryID", "1002")
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.content[*].id", containsInRelativeOrder(10, 9, 8)))
        .andExpect(jsonPath("$.content[*].slug", containsInRelativeOrder("sha10", "sha9", "sha8")))
        .andExpect(jsonPath("$.content[*].status",  containsInRelativeOrder("PUBLISHED", "PUBLISHED", "PUBLISHED")))
        .andExpect(jsonPath("$.content[*].createdAt",  containsInRelativeOrder(notNullValue(), notNullValue(), notNullValue())))
        .andExpect(jsonPath("$.content[*].lastModifiedAt",  containsInRelativeOrder(notNullValue(), notNullValue(), notNullValue())))
        .andExpect(jsonPath("$.page.number", equalTo(1)))
        .andExpect(jsonPath("$.page.size", equalTo(5)))
        .andExpect(jsonPath("$.page.totalElements", equalTo(3)));
  }


  @Test
  @Order(101)
  void getLatestPostsByTag() throws Exception {
    mockMvc
        .perform(
            get("/api/posts/latest")
                .queryParam("size", "5")
                .queryParam("tagID", "1005")
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.content[*].id", containsInRelativeOrder(6, 5, 4, 3, 2)))
        .andExpect(jsonPath("$.content[*].slug", containsInRelativeOrder("sha6", "sha5", "sha4", "sha3", "sha2")))
        .andExpect(jsonPath("$.content[*].status",  containsInRelativeOrder("PUBLISHED", "PUBLISHED", "PUBLISHED", "PUBLISHED", "PUBLISHED")))
        .andExpect(jsonPath("$.content[*].createdAt",  containsInRelativeOrder(notNullValue(), notNullValue(), notNullValue(), notNullValue(), notNullValue())))
        .andExpect(jsonPath("$.content[*].lastModifiedAt",  containsInRelativeOrder(notNullValue(), notNullValue(), notNullValue(), notNullValue(), notNullValue())))
        .andExpect(jsonPath("$.page.number", equalTo(1)))
        .andExpect(jsonPath("$.page.size", equalTo(5)))
        .andExpect(jsonPath("$.page.totalElements", equalTo(6)));
  }

  @Test
  @Order(101)
  void readerGetLatestPostsInSearchView() throws Exception {
    mockMvc
        .perform(
            get("/api/posts/latest/view/search")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isForbidden());
  }

  @Test
  @Order(101)
  void userWithTokenGetLatestPostsInSearchView() throws Exception {
    String tokenHeader = String.format("Bearer %s", SEARCH_IMPORTER_TOKEN);
    mockMvc
        .perform(
            get("/api/posts/latest/view/search")
                .queryParam("size", "5")
                .header("Authorization", tokenHeader)
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.content[*].id", containsInRelativeOrder(10, 9, 8, 7, 6)))
        .andExpect(jsonPath("$.content[*].slug", containsInRelativeOrder("sha10", "sha9", "sha8", "sha7", "sha6")))
        .andExpect(jsonPath("$.content[*].status",  containsInRelativeOrder("PUBLISHED", "PUBLISHED", "PUBLISHED", "PUBLISHED", "PUBLISHED")))
        .andExpect(jsonPath("$.content[*].createdAt",  containsInRelativeOrder(notNullValue(), notNullValue(), notNullValue(), notNullValue(), notNullValue())))
        .andExpect(jsonPath("$.content[*].lastModifiedAt",  containsInRelativeOrder(notNullValue(), notNullValue(), notNullValue(), notNullValue(), notNullValue())))
        .andExpect(jsonPath("$.page.number", equalTo(1)))
        .andExpect(jsonPath("$.page.size", equalTo(5)));
  }

  @Test
  @Order(101)
  void readerGetLatestPostsInMarkdownView() throws Exception {
    mockMvc
        .perform(
            get("/api/posts/latest/view/markdown")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isForbidden());
  }

  @Test
  @Order(101)
  void userWithTokenGetLatestPostsInMarkdownView() throws Exception {
    String tokenHeader = String.format("Bearer %s", EXTERNAL_IMPORTER_TOKEN);
    mockMvc
        .perform(
            get("/api/posts/latest/view/markdown")
                .queryParam("size", "5")
                .header("Authorization", tokenHeader)
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.content[*].id").doesNotExist())
        .andExpect(jsonPath("$.content[*].slug", containsInRelativeOrder("sha10", "sha9", "sha8", "sha7", "sha6")))
        .andExpect(jsonPath("$.content[*].markdownText").exists())
        .andExpect(jsonPath("$.content[*].publishedAt").exists())
        .andExpect(jsonPath("$.content[*].createdAt").doesNotExist())
        .andExpect(jsonPath("$.content[*].lastModifiedAt").doesNotExist())
        .andExpect(jsonPath("$.page.number", equalTo(1)))
        .andExpect(jsonPath("$.page.size", equalTo(5)));
  }

  @Test
  @Order(101)
  void readerGetLatestPostsInHTMLView() throws Exception {
    mockMvc
      .perform(
          get("/api/posts/latest/view/html")
              .cookie(new Cookie("dev_sid", "0"))
              .with(reader1User())
      )
      .andExpect(status().isForbidden());
  }

  @Test
  @Order(101)
  void userWithTokenGetLatestPostsInHTMLView() throws Exception {
    String tokenHeader = String.format("Bearer %s", EXTERNAL_IMPORTER_TOKEN);
    mockMvc
        .perform(
            get("/api/posts/latest/view/html")
                .queryParam("size", "5")
                .header("Authorization", tokenHeader)
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.content[*].id").doesNotExist())
        .andExpect(jsonPath("$.content[*].slug", containsInRelativeOrder("sha10", "sha9", "sha8", "sha7", "sha6")))
        .andExpect(jsonPath("$.content[*].htmlText").exists())
        .andExpect(jsonPath("$.content[*].publishedAt").exists())
        .andExpect(jsonPath("$.content[*].createdAt").doesNotExist())
        .andExpect(jsonPath("$.content[*].lastModifiedAt").doesNotExist())
        .andExpect(jsonPath("$.page.number", equalTo(1)))
        .andExpect(jsonPath("$.page.size", equalTo(5)));
  }

  @Test
  @Order(102)
  void getRecommendPosts() throws Exception {
    mockMvc
        .perform(
            get("/api/posts/recommend").queryParam("size", "5")
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.content[*].id", containsInRelativeOrder(201, 202, 10, 9, 8)))
        .andExpect(jsonPath("$.content[*].slug", containsInRelativeOrder("sha201", "sha202", "sha10", "sha9", "sha8")))
        .andExpect(jsonPath("$.content[*].status",  containsInRelativeOrder("PUBLISHED", "PUBLISHED", "PUBLISHED", "PUBLISHED", "PUBLISHED")))
        .andExpect(jsonPath("$.content[*].createdAt",  containsInRelativeOrder(notNullValue(), notNullValue(), notNullValue(), notNullValue(), notNullValue())))
        .andExpect(jsonPath("$.content[*].lastModifiedAt",  containsInRelativeOrder(notNullValue(), notNullValue(), notNullValue(), notNullValue(), notNullValue())))
        .andExpect(jsonPath("$.page.number", equalTo(1)))
        .andExpect(jsonPath("$.page.size", equalTo(5)));
  }

  @Test
  @Order(103)
  void nonLoginUserGetPosts() throws Exception {
    mockMvc
        .perform(
            get("/api/posts")
                .queryParam("size", "5")
                .queryParam("status", "PENDING")
                .queryParam("sort", "id,desc")
        )
        .andExpect(status().isForbidden());
  }

  @Test
  @Order(104)
  void readerUserGetPosts() throws Exception {
    mockMvc
        .perform(
            get("/api/posts")
                .queryParam("size", "5")
                .queryParam("status", "PENDING")
                .queryParam("sort", "id,desc")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isForbidden());
  }

  @Test
  @Order(105)
  void editorUserGetPosts() throws Exception {
    mockMvc
        .perform(
            get("/api/posts")
                .queryParam("size", "5")
                .queryParam("status", "PENDING")
                .queryParam("sort", "id,desc")
                .cookie(new Cookie("dev_sid", "0"))
                .with(editor1User())
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.content[*].id", containsInRelativeOrder(302, 301)))
        .andExpect(jsonPath("$.content[*].slug", containsInRelativeOrder("sha302", "sha301")))
        .andExpect(jsonPath("$.content[*].status",  containsInRelativeOrder("PENDING", "PENDING")))
        .andExpect(jsonPath("$.content[*].createdAt",  containsInRelativeOrder(notNullValue(), notNullValue(), notNullValue(), notNullValue(), notNullValue())))
        .andExpect(jsonPath("$.content[*].lastModifiedAt",  containsInRelativeOrder(notNullValue(), notNullValue(), notNullValue(), notNullValue(), notNullValue())))
        .andExpect(jsonPath("$.page.number", equalTo(1)))
        .andExpect(jsonPath("$.page.size", equalTo(5)));
  }

  @Test
  @Order(107)
  void getPostDetailByID() throws Exception {
    mockMvc
        .perform(
            get("/api/posts/1")
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.id", equalTo(1)))
        .andExpect(jsonPath("$.slug", equalTo("sha1")))
        .andExpect(jsonPath("$.liked", equalTo(false)))
        .andExpect(jsonPath("$.likes", equalTo(2)))
        .andExpect(jsonPath("$.favorited", equalTo(false)))
        .andExpect(jsonPath("$.favorites", equalTo(2)))
        .andExpect(jsonPath("$.shares", equalTo(2)))
        .andExpect(jsonPath("$.comments", equalTo(2)))
        .andExpect(jsonPath("$.visits", equalTo(2)));
  }

  @Test
  @Order(108)
  void getDeletedPostDetail() throws Exception {
    mockMvc
        .perform(
            get("/api/posts/11")
        )
        .andExpect(status().isNotFound());
  }

  @Test
  @Order(110)
  void readerGetPendingReviewPostDetail() throws Exception {
    mockMvc
        .perform(
            get("/api/posts/301")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.id", equalTo(301)))
        .andExpect(jsonPath("$.slug", equalTo("sha301")));
  }

  @Test
  @Order(110)
  void readerGetPendingReviewPostDetailOfAnotherAuthor() throws Exception {
    mockMvc
        .perform(
            get("/api/posts/302")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isForbidden());
  }

  @Test
  @Order(110)
  void editorGetPendingReviewPostDetail() throws Exception {
    mockMvc
        .perform(
            get("/api/posts/301")
                .cookie(new Cookie("dev_sid", "0"))
                .with(editor1User())
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.id", equalTo(301)))
        .andExpect(jsonPath("$.slug", equalTo("sha301")));
  }

  @Test
  @Order(111)
  void getPostDetailBySlug() throws Exception {
    mockMvc
        .perform(
            get("/api/posts/sha1/detail")
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.id", equalTo(1)))
        .andExpect(jsonPath("$.slug", equalTo("sha1")))
        .andExpect(jsonPath("$.liked", equalTo(false)))
        .andExpect(jsonPath("$.likes", equalTo(2)))
        .andExpect(jsonPath("$.favorited", equalTo(false)))
        .andExpect(jsonPath("$.favorites", equalTo(2)))
        .andExpect(jsonPath("$.shares", equalTo(2)))
        .andExpect(jsonPath("$.comments", equalTo(2)))
        .andExpect(jsonPath("$.visits", equalTo(2)));
  }

  @Test
  @Order(111)
  void readerGetPostDetailBySlug() throws Exception {
    mockMvc
        .perform(
            get("/api/posts/sha1/detail")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.id", equalTo(1)))
        .andExpect(jsonPath("$.slug", equalTo("sha1")))
        .andExpect(jsonPath("$.liked", equalTo(true)))
        .andExpect(jsonPath("$.likes", equalTo(2)))
        .andExpect(jsonPath("$.favorited", equalTo(true)))
        .andExpect(jsonPath("$.favorites", equalTo(2)))
        .andExpect(jsonPath("$.shares", equalTo(2)))
        .andExpect(jsonPath("$.comments", equalTo(2)))
        .andExpect(jsonPath("$.visits", equalTo(2)));
  }

  @Test
  @Order(112)
  void getDeletedPostDetailBySlug() throws Exception {
    mockMvc
        .perform(
            get("/api/posts/sha11/detail")
        )
        .andExpect(status().isNotFound());
  }

  @Test
  @Order(113)
  void readerGetPendingReviewPostDetailBySlug() throws Exception {
    mockMvc
        .perform(
            get("/api/posts/sha301/detail")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.id", equalTo(301)))
        .andExpect(jsonPath("$.slug", equalTo("sha301")));
  }

  @Test
  @Order(113)
  void readerGetPendingReviewPostDetailOfAnotherAuthorBySlug() throws Exception {
    mockMvc
        .perform(
            get("/api/posts/sha302/detail")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isForbidden());
  }

  @Test
  @Order(114)
  void editorGetPendingReviewPostDetailBySlug() throws Exception {
    mockMvc
        .perform(
            get("/api/posts/sha301/detail")
                .cookie(new Cookie("dev_sid", "0"))
                .with(editor1User())
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.id", equalTo(301)))
        .andExpect(jsonPath("$.slug", equalTo("sha301")));
  }

  @Test
  @Order(114)
  void editorGetPendingReviewPostDetailBySlugWithVisit() throws Exception {
    mockMvc
        .perform(
            get("/api/posts/sha301/detail?visit=true")
                .cookie(new Cookie("dev_sid", "0"))
                .with(editor1User())
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.id", equalTo(301)))
        .andExpect(jsonPath("$.slug", equalTo("sha301")));
  }

  @Test
  @Order(115)
  void readerUserGetPostDetailBySlugInSearchView() throws Exception {
    mockMvc
        .perform(
            get("/api/posts/sha1/detail/view/search")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isForbidden());
  }

  @Test
  @Order(116)
  void searchSupplierUserGetPostDetailBySlug() throws Exception {
    mockMvc
        .perform(
            get("/api/posts/sha1/detail/view/search")
                .cookie(new Cookie("dev_sid", "0"))
                .with(searchSupplierUser())
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.id", equalTo(1)))
        .andExpect(jsonPath("$.slug", equalTo("sha1")))
        .andExpect(jsonPath("$.author.openid", notNullValue()));
  }

  @Test
  @Order(117)
  void searchSupplierUserGetPostDetailBySlugAndAuthByHeader() throws Exception {
    String tokenHeader = String.format("Bearer %s", SEARCH_IMPORTER_TOKEN);
    mockMvc
        .perform(
            get("/api/posts/sha1/detail/view/search")
                .header("Authorization", tokenHeader)
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.id", equalTo(1)))
        .andExpect(jsonPath("$.slug", equalTo("sha1")))
        .andExpect(jsonPath("$.author.openid", notNullValue()));
  }

  @Test
  @Order(201)
  void nonLoginUserCreatePost() throws Exception {
    String json = """
        {
          title: "title",
          content: "[{\\"type\\": \\"heading\\", \\"depth\\": 1, \\"children\\": [{ \\"text\\": \\"Heading1\\" }]}]",
          status: "ORIGINAL",
          category: 1001,
          tags: [1001, 1002]
        }
        """;

    mockMvc
        .perform(
            post("/api/posts")
                .contentType(MediaType.APPLICATION_JSON)
                .content(json)
        )
        .andExpect(status().isUnauthorized());
  }

  @Test
  @Order(202)
  void readerCreatePost() throws Exception {
    String json = """
        {
          "title": "title",
          "content": "[{\\"type\\": \\"heading\\", \\"depth\\": 1, \\"children\\": [{ \\"text\\": \\"Heading1\\" }]}]",
          "status": "ORIGINAL",
          "category": 1001,
          "tags": [1001, 1002]
        }
        """;

    mockMvc
        .perform(
            post("/api/posts")
                .contentType(MediaType.APPLICATION_JSON)
                .content(json)
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.id", notNullValue()))
        .andExpect(jsonPath("$.slug", notNullValue()))
        .andExpect(jsonPath("$.category.slug", equalTo("management-and-operation")))
        .andExpect(jsonPath("$.tags[*].slug", containsInAnyOrder("install-and-deploy", "migrate")));
  }

  @Test
  @Order(203)
  void postAuthorSavePost() throws Exception {
    String json = """
        {
          "title": "title",
          "content": "[{\\"type\\": \\"heading\\", \\"depth\\": 1, \\"children\\": [{ \\"text\\": \\"Heading1\\" }]}]",
          "origin": "REPOST",
          "category": 1001,
          "tags": [1001, 1002]
        }
        """;

    mockMvc
        .perform(
            put("/api/posts/601")
                .contentType(MediaType.APPLICATION_JSON)
                .content(json)
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.id", equalTo(601)))
        .andExpect(jsonPath("$.slug", equalTo("sha601")))
        .andExpect(jsonPath("$.origin", equalTo("REPOST")))
        .andExpect(jsonPath("$.category.id", equalTo(1001)))
        .andExpect(jsonPath("$.tags[*].id", containsInAnyOrder(1001, 1002)));
  }

  @Test
  @Order(204)
  void postAuthorSubmitPost() throws Exception {
    mockMvc
        .perform(
            patch("/api/posts/601/submit")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isOk());
  }

  @Test
  @Order(205)
  void postAuthorCancelSubmitPost() throws Exception {
    mockMvc
        .perform(
            patch("/api/posts/601/submit/cancel")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isOk());
  }

  @Test
  @Order(206)
  void readerPublishPost() throws Exception {
    mockMvc
        .perform(
            patch("/api/posts/301/publish")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isForbidden());
  }

  @Test
  @Order(207)
  void authorPublishPost() throws Exception {
    mockMvc
        .perform(
            patch("/api/posts/303/publish")
                .cookie(new Cookie("dev_sid", "0"))
                .with(author1User())
        )
        .andExpect(status().isOk());
  }

  @Test
  @Order(208)
  void authorPublishPostOfAnotherAuthor() throws Exception {
    mockMvc
        .perform(
            patch("/api/posts/302/publish")
                .cookie(new Cookie("dev_sid", "0"))
                .with(author1User())
        )
        .andExpect(status().isForbidden());
  }

  @Test
  @Order(209)
  void editorPublishPost() throws Exception {
    mockMvc
        .perform(
            patch("/api/posts/301/publish")
                .cookie(new Cookie("dev_sid", "0"))
                .with(editor1User())
        )
        .andExpect(status().isOk());
    mockMvc
        .perform(
            patch("/api/posts/302/publish")
                .cookie(new Cookie("dev_sid", "0"))
                .with(editor1User())
        )
        .andExpect(status().isOk());
  }

  @Test
  @Order(210)
  void postAuthorCancelPublishPost() throws Exception {
    mockMvc
        .perform(
            patch("/api/posts/301/publish/cancel")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isOk());
  }

  @Test
  @Order(211)
  void editorAuthorCancelPublishPost() throws Exception {
    mockMvc
        .perform(
            patch("/api/posts/302/publish/cancel")
                .cookie(new Cookie("dev_sid", "0"))
                .with(editor1User())
        )
        .andExpect(status().isOk());
  }

  @Test
  @Order(212)
  void rejectPostWithTooLongReason() throws Exception {
    String json = String.format("{ \"reason\": \"%s\" }", StringUtils.getLongText(1200));

    mockMvc
        .perform(
            patch("/api/posts/304/reject")
                .contentType(MediaType.APPLICATION_JSON)
                .content(json)
                .cookie(new Cookie("dev_sid", "0"))
                .with(editor1User())
        )
        .andExpect(status().isBadRequest())
        .andExpect(jsonPath("$.errCode", equalTo("REQUEST_PARAMETER_ERROR")));
  }

  @Test
  @Order(212)
  void rejectPost() throws Exception {
    mockMvc
        .perform(
            patch("/api/posts/304/reject")
                .cookie(new Cookie("dev_sid", "0"))
                .with(editor1User())
        )
        .andExpect(status().isOk());
  }

  @Test
  @Order(213)
  void deletePost() throws Exception {
    mockMvc
        .perform(
            delete("/api/posts/401")
                .cookie(new Cookie("dev_sid", "0"))
                .with(editor1User())
        )
        .andExpect(status().isOk());
  }

  @Test
  @Order(301)
  void recommendPost() throws Exception {
    mockMvc
        .perform(
            patch("/api/posts/10/recommend")
                .cookie(new Cookie("dev_sid", "0"))
                .with(editor1User())
        )
        .andExpect(status().isOk());
  }

  @Test
  @Order(302)
  void cancelRecommendPost() throws Exception {
    mockMvc
        .perform(
            patch("/api/posts/10/recommend/cancel")
                .cookie(new Cookie("dev_sid", "0"))
                .with(editor1User())
        )
        .andExpect(status().isOk());
  }

  @Test
  @Order(303)
  void likePost() throws Exception {
    mockMvc
        .perform(
            patch("/api/posts/10/like")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isOk());
  }

  @Test
  @Order(304)
  void cancelLikePost() throws Exception {
    mockMvc
        .perform(
            patch("/api/posts/10/like/cancel")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isOk());
  }

  @Test
  @Order(305)
  void favoritePost() throws Exception {
    mockMvc
        .perform(
            patch("/api/posts/10/favorite")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isOk());
  }

  @Test
  @Order(306)
  void cancelFavoritePost() throws Exception {
    mockMvc
        .perform(
            patch("/api/posts/10/favorite/cancel")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isOk());
  }

  @Test
  @Order(307)
  void sharePost() throws Exception {
    mockMvc
        .perform(
            post("/api/posts/10/share")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.shared", equalTo(false)))
        .andExpect(jsonPath("$.shareID", hasLength(8)));
  }

  @Test
  @Order(307)
  void sharePostWhichHasBeenShared() throws Exception {
    mockMvc
        .perform(
            post("/api/posts/1/share")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.shared", equalTo(true)))
        .andExpect(jsonPath("$.shareID", equalTo("04b5de6c")));
  }

  @Test
  @Order(308)
  void getPostEvents() throws Exception {
    mockMvc
        .perform(
            get("/api/posts/10/events")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isOk());
  }

  @Test
  @Order(401)
  void addPostComments() throws Exception {
    String json = """
        {
          "content": "comment"
        }
        """;

    mockMvc
        .perform(
            post("/api/posts/10/comments")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
                .contentType(MediaType.APPLICATION_JSON)
                .content(json)
        )
        .andExpect(status().isOk());
  }

  @Test
  @Order(402)
  void getPostComments() throws Exception {
    mockMvc
        .perform(
            get("/api/posts/501/comments")
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.content.*", hasSize(4)))
        .andExpect(jsonPath("$.page.totalElements", equalTo(4)));
  }

  @Test
  @Order(403)
  void removeAnotherUserPostComment() throws Exception {
    mockMvc
        .perform(
            delete("/api/posts/501/comments/102")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isForbidden());
  }

  @Test
  @Order(403)
  void removeOwnPostComment() throws Exception {
    mockMvc
        .perform(
            delete("/api/posts/501/comments/101")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isOk());
  }

  @Test
  @Order(403)
  void removeOwnDeletedComment() throws Exception {
    mockMvc
        .perform(
            delete("/api/posts/501/comments/103")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        // FIXME: Should be notFound()
        .andExpect(status().isForbidden());
  }

}