content: package alexp.blog.controller;
  
  import alexp.blog.AbstractIntegrationTest;
  import alexp.blog.model.Comment;
  import alexp.blog.model.Post;
  import alexp.blog.model.PostEditDto;
  import alexp.blog.utils.HsqldbSequenceResetter;
  import com.github.springtestdbunit.annotation.*;
  import com.github.springtestdbunit.assertion.DatabaseAssertionMode;
  import org.hamcrest.Matchers;
  import org.junit.Test;
  import org.springframework.http.MediaType;
  
  import static alexp.blog.utils.SecurityUtils.userAdmin;
  import static alexp.blog.utils.SecurityUtils.userAlice;
  import static alexp.blog.utils.SecurityUtils.userBob;
  import static org.hamcrest.CoreMatchers.equalTo;
  import static org.hamcrest.CoreMatchers.hasItems;
  import static org.hamcrest.CoreMatchers.is;
  import static org.hamcrest.Matchers.*;
  import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
  import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
  import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
  import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
  
  @DatabaseSetup("data.xml")
  @DbUnitConfiguration(databaseOperationLookup = HsqldbSequenceResetter.class)
  public class PostsControllerIT extends AbstractIntegrationTest {
  
      @Test
      @ExpectedDatabase("data.xml")
      public void shouldShowPostsPage() throws Exception {
          mockMvc.perform(get("/"))
                  .andExpect(status().isOk())
                  .andExpect(view().name("posts"))
                  .andExpect(model().attribute("postsPage", hasProperty("totalElements", equalTo(2L))));
      }
  
      @Test
      @DatabaseSetup("data.xml")
      public void shouldGetPostsList() throws Exception {
          mockMvc.perform(get("/posts").accept(MediaType.APPLICATION_JSON))
                  .andExpect(status().isOk())
                  .andExpect(jsonPath("$", hasSize(2)))
                  .andExpect(jsonPath("$[0].id", is("2")))
                  .andExpect(jsonPath("$[0].title", is("meow title")))
                  .andExpect(jsonPath("$[1].id", is("1")))
                  .andExpect(jsonPath("$[1].title", is("hello title")));
      }
  
      @Test
      @ExpectedDatabase("data-posts-voted.xml")
      @DatabaseSetup("data-posts-voted.xml")
      @DatabaseTearDown(value = "data.xml", type = DatabaseOperation.TRUNCATE_TABLE) // to reset id sequence, otherwise other tests that insert and check id will fail on ExpectedDatabase
      public void shouldGetTopPostsList() throws Exception {
          mockMvc.perform(get("/posts/top").accept(MediaType.APPLICATION_JSON))
                  .andExpect(status().isOk())
                  .andExpect(jsonPath("$", hasSize(2)))
                  .andExpect(jsonPath("$[0].id", is("1")))
                  .andExpect(jsonPath("$[0].title", is("hello title")))
                  .andExpect(jsonPath("$[1].id", is("2")))
                  .andExpect(jsonPath("$[1].title", is("meow title")));
      }
  
      @Test
      @ExpectedDatabase("data.xml")
      public void shouldShowPostPage() throws Exception {
          mockMvc.perform(get("/posts/1"))
                  .andExpect(status().isOk())
                  .andExpect(view().name("post"))
                  .andExpect(model().attributeDoesNotExist("comment"));
      }
  
      @Test
      @ExpectedDatabase("data.xml")
      public void shouldShowCommentFormWhenAuthorized() throws Exception {
          mockMvc.perform(get("/posts/1").with(userBob()))
                  .andExpect(status().isOk())
                  .andExpect(view().name("post"))
                  .andExpect(model().attribute("comment", instanceOf(Comment.class)));
      }
  
      @Test
      @ExpectedDatabase("data.xml")
      public void shouldReturn404WhenPostNotExists() throws Exception {
          mockMvc.perform(get("/posts/999"))
                  .andExpect(status().isNotFound());
      }
  
      @Test
      @ExpectedDatabase("data.xml")
      public void shouldShowCreatePostPageIfAdmin() throws Exception {
          mockMvc.perform(get("/posts/create").with(userAdmin()))
                  .andExpect(status().isOk())
                  .andExpect(view().name("editpost"))
                  .andExpect(model().attribute("post", instanceOf(PostEditDto.class)))
                  .andExpect(model().attribute("edit", is(equalTo(false))));
      }
  
      @Test
      @ExpectedDatabase("data.xml")
      public void shouldDenyCreatePostIfNotAdmin() throws Exception {
          mockMvc.perform(get("/posts/create"))
                  .andExpect(status().isFound())
                  .andExpect(redirectedUrlPattern("**/login"));
  
          mockMvc.perform(get("/posts/create").with(userBob()))
                  .andExpect(status().isForbidden());
  
          mockMvc.perform(post("/posts/create").with(userBob()).with(csrf()))
                  .andExpect(status().isForbidden());
      }
  
      @Test
      @ExpectedDatabase("data.xml")
      public void shouldReturnAddPostFormWithErrorsWhenSubmittedInvalidPost() throws Exception {
          mockMvc.perform(post("/posts/create").with(userAdmin()).with(csrf())
                  .contentType(MediaType.APPLICATION_FORM_URLENCODED))
                  .andExpect(status().isOk())
                  .andExpect(model().attributeHasFieldErrors("post", "title"))
                  .andExpect(model().attributeHasFieldErrors("post", "text"))
                  .andExpect(model().attribute("edit", is(equalTo(false))));
  
          String title = "post title";
          String text = "too short";
  
          mockMvc.perform(post("/posts/create").with(userAdmin()).with(csrf())
                  .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                  .param("title", title)
                  .param("text", text))
                  .andExpect(status().isOk())
                  .andExpect(model().attributeHasFieldErrors("post", "text"))
                  .andExpect(model().attribute("post", hasProperty("title", Matchers.is(equalTo(title)))))
                  .andExpect(model().attribute("post", hasProperty("text", Matchers.is(equalTo(text)))))
                  .andExpect(model().attribute("edit", is(equalTo(false))));
      }
  
      @Test
      @ExpectedDatabase(value = "data-posts-added.xml", assertionMode = DatabaseAssertionMode.NON_STRICT_UNORDERED)
      @DatabaseTearDown(value = "data.xml", type = DatabaseOperation.TRUNCATE_TABLE) // to reset id sequence, otherwise other tests that insert tags will fail on ExpectedDatabase
      public void shouldAddPosts() throws Exception {
          String title = "new post title";
          String text = "new post short text===cut===new post full text Lorem ipsum";
          String tags = "c++, java, hello world";
  
          mockMvc.perform(post("/posts/create").with(userAdmin()).with(csrf())
                  .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                  .param("title", title)
                  .param("text", text)
                  .param("tags", tags))
                  .andExpect(status().isFound())
                  .andExpect(model().hasNoErrors())
                  .andExpect(view().name("redirect:/posts"));
  
          String text2 = "new post 2 text Lorem ipsum dolor sit amet, consectetur adipiscing elit";
          String tags2 = "java";
  
          mockMvc.perform(post("/posts/create").with(userAdmin()).with(csrf())
                  .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                  .param("title", title)
                  .param("text", text2)
                  .param("tags", tags2))
                  .andExpect(status().isFound())
                  .andExpect(model().hasNoErrors())
                  .andExpect(view().name("redirect:/posts"));
      }
  
      @Test
      @ExpectedDatabase("data.xml")
      public void shouldShowEditPostPageIfAdmin() throws Exception {
          mockMvc.perform(get("/posts/1/edit").with(userAdmin()))
                  .andExpect(status().isOk())
                  .andExpect(view().name("editpost"))
                  .andExpect(model().attribute("post", hasProperty("id", is(Matchers.equalTo(1L)))))
                  .andExpect(model().attribute("edit", is(equalTo(true))));
      }
  
      @Test
      @ExpectedDatabase("data.xml")
      public void shouldDenyEditPostIfNotAdmin() throws Exception {
          mockMvc.perform(get("/posts/1/edit"))
                  .andExpect(status().isFound())
                  .andExpect(redirectedUrlPattern("**/login"));
  
          mockMvc.perform(get("/posts/1/edit").with(userBob()))
                  .andExpect(status().isForbidden());
  
          mockMvc.perform(post("/posts/1/edit").with(userBob()).with(csrf()))
                  .andExpect(status().isForbidden());
      }
  
      @Test
      @ExpectedDatabase("data.xml")
      public void shouldReturnEditPostFormWithErrorsWhenSubmittedInvalidPost() throws Exception {
          mockMvc.perform(post("/posts/1/edit").with(userAdmin()).with(csrf())
                  .contentType(MediaType.APPLICATION_FORM_URLENCODED))
                  .andExpect(status().isOk())
                  .andExpect(model().attributeHasFieldErrors("post", "title"))
                  .andExpect(model().attributeHasFieldErrors("post", "text"))
                  .andExpect(model().attribute("post", hasProperty("id", is(Matchers.equalTo(1L)))))
                  .andExpect(model().attribute("edit", is(equalTo(true))));
  
          String title = "post title";
          String text = "too short";
  
          mockMvc.perform(post("/posts/1/edit").with(userAdmin()).with(csrf())
                  .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                  .param("title", title)
                  .param("text", text))
                  .andExpect(status().isOk())
                  .andExpect(model().attributeHasFieldErrors("post", "text"))
                  .andExpect(model().attribute("post", hasProperty("title", Matchers.is(equalTo(title)))))
                  .andExpect(model().attribute("post", hasProperty("text", Matchers.is(equalTo(text)))))
                  .andExpect(model().attribute("post", hasProperty("id", is(Matchers.equalTo(1L)))))
                  .andExpect(model().attribute("edit", is(equalTo(true))));
      }
  
      @Test
      @ExpectedDatabase(value = "data-post-edited.xml", assertionMode = DatabaseAssertionMode.NON_STRICT_UNORDERED)
      @DatabaseTearDown(value = "data.xml", type = DatabaseOperation.TRUNCATE_TABLE) // to reset id sequence, otherwise other tests that insert tags will fail on ExpectedDatabase
      public void shouldEditPosts() throws Exception {
          String title = "edited title";
          String text = "edited Lorem ipsum dolor sit amet, consectetur adipiscing elit";
          String tags = "c, c++, c#";
  
          mockMvc.perform(post("/posts/1/edit").with(userAdmin()).with(csrf())
                  .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                  .param("title", title)
                  .param("text", text)
                  .param("tags", tags))
                  .andExpect(status().isFound())
                  .andExpect(model().hasNoErrors())
                  .andExpect(view().name("redirect:/posts/1"));
  
          String text2 = "edited short===cut===edited Lorem ipsum dolor sit amet, consectetur adipiscing elit";
          String tags2 = "c++, meow";
  
          mockMvc.perform(post("/posts/2/edit").with(userAdmin()).with(csrf())
                  .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                  .param("title", title)
                  .param("text", text2)
                  .param("tags", tags2))
                  .andExpect(status().isFound())
                  .andExpect(model().hasNoErrors())
                  .andExpect(view().name("redirect:/posts/2"));
      }
  
      @Test
      @ExpectedDatabase("data.xml")
      public void shouldDenyHidePostIfNotAdmin() throws Exception {
          mockMvc.perform(post("/posts/1/hide").with(csrf())
                  .contentType(MediaType.APPLICATION_FORM_URLENCODED))
                  .andExpect(status().isFound())
                  .andExpect(redirectedUrlPattern("**/login"));
  
          mockMvc.perform(post("/posts/1/hide").with(userBob()).with(csrf())
                  .contentType(MediaType.APPLICATION_FORM_URLENCODED))
                  .andExpect(status().isForbidden());
      }
  
      @Test
      @ExpectedDatabase("data.xml")
      public void shouldDenyUnhidePostIfNotAdmin() throws Exception {
          mockMvc.perform(post("/posts/1/unhide").with(csrf())
                  .contentType(MediaType.APPLICATION_FORM_URLENCODED))
                  .andExpect(status().isFound())
                  .andExpect(redirectedUrlPattern("**/login"));
  
          mockMvc.perform(post("/posts/1/unhide").with(userBob()).with(csrf())
                  .contentType(MediaType.APPLICATION_FORM_URLENCODED))
                  .andExpect(status().isForbidden());
      }
  
      @Test
      @ExpectedDatabase("data-post-hidden.xml")
      public void shouldHidePost() throws Exception {
          mockMvc.perform(post("/posts/1/hide").with(userAdmin()).with(csrf())
                  .contentType(MediaType.APPLICATION_FORM_URLENCODED))
                  .andExpect(status().isOk())
                  .andExpect(content().string("ok"));
  
          mockMvc.perform(get("/posts/1"))
                  .andExpect(status().isNotFound());
  
          mockMvc.perform(get("/posts/1").with(userBob()))
                  .andExpect(status().isNotFound());
  
          mockMvc.perform(get("/posts/1").with(userAdmin()))
                  .andExpect(status().isOk());
      }
  
      @Test
      @ExpectedDatabase("data.xml")
      @DatabaseSetup("data-post-hidden.xml")
      public void shouldUnhidePost() throws Exception {
          mockMvc.perform(post("/posts/1/unhide").with(userAdmin()).with(csrf())
                  .contentType(MediaType.APPLICATION_FORM_URLENCODED))
                  .andExpect(status().isOk())
                  .andExpect(content().string("ok"));
      }
  
      @Test
      @ExpectedDatabase("data-post-hidden.xml")
      @DatabaseSetup("data-post-hidden.xml")
      public void shouldNotShowHiddenPostIfNotAdmin() throws Exception {
          mockMvc.perform(get("/").with(userBob()))
                  .andExpect(status().isOk())
                  .andExpect(view().name("posts"))
                  .andExpect(model().attribute("postsPage", hasProperty("totalElements", equalTo(1L))));
  
          mockMvc.perform(get("/"))
                  .andExpect(status().isOk())
                  .andExpect(view().name("posts"))
                  .andExpect(model().attribute("postsPage", hasProperty("totalElements", equalTo(1L))));
      }
  
      @Test
      @ExpectedDatabase("data-post-hidden.xml")
      @DatabaseSetup("data-post-hidden.xml")
      public void shouldShowHiddenPostIfAdmin() throws Exception {
          mockMvc.perform(get("/").with(userAdmin()))
                  .andExpect(status().isOk())
                  .andExpect(view().name("posts"))
                  .andExpect(model().attribute("postsPage", hasProperty("totalElements", equalTo(2L))));
      }
  
      @Test
      @ExpectedDatabase("data.xml")
      public void shouldDenyDeletePostIfNotAdmin() throws Exception {
          mockMvc.perform(post("/posts/1/delete").with(csrf())
                  .contentType(MediaType.APPLICATION_FORM_URLENCODED))
                  .andExpect(status().isFound())
                  .andExpect(redirectedUrlPattern("**/login"));
  
          mockMvc.perform(post("/posts/1/delete").with(userBob()).with(csrf())
                  .contentType(MediaType.APPLICATION_FORM_URLENCODED))
                  .andExpect(status().isForbidden());
      }
  
      @Test
      @ExpectedDatabase("data-post-deleted.xml")
      public void shouldDeletePost() throws Exception {
          mockMvc.perform(post("/posts/1/delete").with(userAdmin()).with(csrf())
                  .contentType(MediaType.APPLICATION_FORM_URLENCODED))
                  .andExpect(status().isOk())
                  .andExpect(content().string("ok"));
      }
  
      @Test
      @ExpectedDatabase("data.xml")
      public void shouldShowPostsByTag() throws Exception {
          mockMvc.perform(get("/posts?tagged=c++"))
                  .andExpect(status().isOk())
                  .andExpect(view().name("posts"))
                  .andExpect(model().attribute("postsPage", hasProperty("totalElements", equalTo(2L))));
  
          mockMvc.perform(get("/posts?tagged=meow"))
                  .andExpect(status().isOk())
                  .andExpect(view().name("posts"))
                  .andExpect(model().attribute("postsPage", hasProperty("totalElements", equalTo(1L))))
                  .andExpect(model().attribute("postsPage", hasItems(hasProperty("id", equalTo(2L)))));
  
          mockMvc.perform(get("/posts?tagged=c++, hello world"))
                  .andExpect(status().isOk())
                  .andExpect(view().name("posts"))
                  .andExpect(model().attribute("postsPage", hasProperty("totalElements", equalTo(1L))))
                  .andExpect(model().attribute("postsPage", hasItems(hasProperty("id", equalTo(1L)))));
      }
  
      @Test
      @ExpectedDatabase("data.xml")
      public void shouldShowNoPostsWhenTagNotExists() throws Exception {
          mockMvc.perform(get("/posts?tagged=not exists"))
                  .andExpect(status().isOk())
                  .andExpect(view().name("posts"))
                  .andExpect(model().attribute("postsPage", hasProperty("totalElements", equalTo(0L))));
      }
  
      @Test
      @ExpectedDatabase("data-post-hidden.xml")
      @DatabaseSetup("data-post-hidden.xml")
      public void shouldNotShowHiddenPostsByTagIfNotAdmin() throws Exception {
          mockMvc.perform(get("/posts?tagged=c++"))
                  .andExpect(status().isOk())
                  .andExpect(view().name("posts"))
                  .andExpect(model().attribute("postsPage", hasProperty("totalElements", equalTo(1L))));
      }
  
      @Test
      @ExpectedDatabase("data-post-hidden.xml")
      @DatabaseSetup("data-post-hidden.xml")
      public void shouldShowHiddenPostsByTagIfAdmin() throws Exception {
          mockMvc.perform(get("/posts?tagged=c++").with(userAdmin()))
                  .andExpect(status().isOk())
                  .andExpect(view().name("posts"))
                  .andExpect(model().attribute("postsPage", hasProperty("totalElements", equalTo(2L))));
      }
  
      @Test
      @ExpectedDatabase("data-posts-voted.xml")
      public void shouldVote() throws Exception {
          mockMvc.perform(post("/posts/1/like").with(userBob()).with(csrf()))
                  .andExpect(status().isOk())
                  .andExpect(content().string("ok"));
  
          mockMvc.perform(post("/posts/2/dislike").with(userBob()).with(csrf()))
                  .andExpect(status().isOk())
                  .andExpect(content().string("ok"));
      }
  
      @Test
      @ExpectedDatabase("data.xml")
      public void shouldDenyVoteMoreThanOnce() throws Exception {
          mockMvc.perform(post("/posts/1/like").with(userAlice()).with(csrf()))
                  .andExpect(status().isOk())
                  .andExpect(content().string("already_voted"));
  
          mockMvc.perform(post("/posts/1/dislike").with(userAlice()).with(csrf()))
                  .andExpect(status().isOk())
                  .andExpect(content().string("already_voted"));
      }
  }