/*
* Copyright 2016 EuregJUG.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package eu.euregjug.site.events;
import com.fasterxml.jackson.databind.ObjectMapper;
import eu.euregjug.site.config.SecurityTestConfig;
import eu.euregjug.site.events.EventEntity.Status;
import eu.euregjug.site.events.EventEntity.Type;
import eu.euregjug.site.posts.PostEntity;
import eu.euregjug.site.posts.PostRepository;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Optional;
import static java.util.stream.Collectors.toList;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.nullValue;
import org.joor.Reflect;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.json.JacksonTester;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.config.EnableSpringDataWebSupport;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
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.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
*
* @author Michael J. Simons, 2016-07-14
*/
@RunWith(SpringRunner.class)
@ActiveProfiles("test")
@WebMvcTest(EventApiController.class)
@EnableSpringDataWebSupport // Needed to enable resolving of Pageable and other parameters
@Import(SecurityTestConfig.class) // Needed to get rid of default CSRF protection
@AutoConfigureRestDocs(
outputDir = "target/generated-snippets",
uriHost = "euregjug.eu",
uriPort = 80
)
public class EventApiControllerTest {
@Autowired
private MockMvc mvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean
private EventRepository eventRepository;
@MockBean
private PostRepository postRepository;
@MockBean
private RegistrationRepository registrationRepository;
private JacksonTester<EventEntity> json;
private final List<EventEntity> events;
public EventApiControllerTest() {
ZonedDateTime eventDate;
eventDate = ZonedDateTime.of(2016, 9, 14, 18, 0, 0, 0, ZoneId.of("Europe/Berlin"));
final EventEntity event1 = Reflect.on(
new EventEntity(GregorianCalendar.from(eventDate), "name-1", "description-1")
).set("id", 42).get();
event1.setDuration(60);
eventDate = ZonedDateTime.of(2016, 11, 22, 18, 0, 0, 0, ZoneId.of("Europe/Berlin"));
final EventEntity event2 = Reflect.on(
new EventEntity(GregorianCalendar.from(eventDate), "name-2", "description-2")
).set("id", 23).get();
event2.setDuration(90);
this.events = Arrays.asList(event1, event2);
}
@Before
public void setup() {
JacksonTester.initFields(this, objectMapper);
}
@Test
public void createShouldWork() throws Exception {
final EventEntity eventEntity = new EventEntity(Calendar.getInstance(), "Mark Paluch - Hallo, ich bin Redis", "Mark spricht in diesem Vortrag über den Open Source NoSQL Data Store Redis. Der Vortrag ist eine Einführung in Redis und veranschaulicht mit Hilfe von Code-Beispielen, wie Redis mit Spring Data, Hibernate OGM und plain Java verwendet werden kann. Der Vortrag findet bei Thinking Networks in Aachen statt.");
eventEntity.setNeedsRegistration(true);
eventEntity.setType(Type.talk);
eventEntity.setPost(new PostEntity(new Date(), "foo", "foo", "foo"));
Reflect.on(eventEntity).set("id", 30).get();
when(this.eventRepository.save(any(EventEntity.class))).then(invocation -> {
// Do the stuff JPA would do for us...
final EventEntity rv = invocation.getArgumentAt(0, EventEntity.class);
Assert.assertThat(rv.getId(), nullValue());
return Reflect.on(rv).call("prePersistAndUpdate").set("id", 4712).get();
});
this.mvc
.perform(
post("/api/events")
.content(this.json.write(eventEntity).getJson())
.contentType(APPLICATION_JSON)
.principal(() -> "euregjug")
)
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id", equalTo(4712)))
.andExpect(jsonPath("$.name", equalTo(eventEntity.getName())))
.andExpect(jsonPath("$.post").doesNotExist())
.andExpect(jsonPath("$.status", equalTo("open")))
.andDo(document("api/events/create",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint())
));
verify(this.eventRepository).save(any(EventEntity.class));
verifyNoMoreInteractions(this.eventRepository, this.postRepository, this.registrationRepository);
}
@Test
public void addPostShouldWork() throws Exception {
final ZonedDateTime eventDate = ZonedDateTime.of(2016, 9, 14, 18, 0, 0, 0, ZoneId.of("Europe/Berlin"));
final EventEntity event = Reflect.on(
new EventEntity(GregorianCalendar.from(eventDate), "name-1", "description-1")
).set("id", 42).get();
final PostEntity post = Reflect.on(
new PostEntity(new Date(), "ruckblick-zum-aim42-vortrag-mit-gernot-starke", "Rückblick zum aim42 Vortrag mit Gernot Starke", "Am 7. April lud die Euregio JUG zusammen mit der http://www.inside-online.de/de/[inside Unternehmensgruppe] zu einem Vortrag von Gernot Starke zum Thema _aim42 - software architecture improvement_ ein.")
).call("updateUpdatedAt").set("id", 23).get();
when(this.eventRepository.findOne(1)).thenReturn(Optional.empty());
when(this.eventRepository.findOne(42)).thenReturn(Optional.of(event));
when(this.postRepository.findOne(2)).thenReturn(Optional.empty());
when(this.postRepository.findOne(23)).thenReturn(Optional.of(post));
this.mvc.perform(
put("/api/events/{id}/post/{postId}", 1, 2)
.principal(() -> "euregjug")
).andExpect(status().isNotFound());
this.mvc.perform(
put("/api/events/{id}/post/{postId}", 42, 2)
.principal(() -> "euregjug")
).andExpect(status().isNotFound());
this.mvc.perform(
put("/api/events/{id}/post/{postId}", 1, 23)
.principal(() -> "euregjug")
).andExpect(status().isNotFound());
this.mvc.perform(
put("/api/events/{id}/post/{postId}", 42, 23)
.principal(() -> "euregjug")
).andExpect(status().isOk())
.andExpect(jsonPath("$.name", equalTo("name-1")))
.andExpect(jsonPath("$.description", equalTo("description-1")))
.andExpect(jsonPath("$.post.slug", equalTo(post.getSlug())))
.andExpect(jsonPath("$.post.title", equalTo(post.getTitle())))
.andExpect(jsonPath("$.post.content", equalTo(post.getContent())))
.andDo(document("api/events/-id-/post/-postId-",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint())
));
verify(this.eventRepository, times(2)).findOne(1);
verify(this.eventRepository, times(2)).findOne(42);
verify(this.postRepository, times(2)).findOne(2);
verify(this.postRepository, times(2)).findOne(23);
verifyNoMoreInteractions(this.eventRepository, this.postRepository, this.registrationRepository);
}
@Test
public void getShouldWork() throws Exception {
final PageImpl page = new PageImpl(this.events);
when(this.eventRepository.findAll(any(Pageable.class))).thenReturn(page);
this.mvc
.perform(
get("/api/events")
.principal(() -> "euregjug")
)
.andExpect(status().isOk())
.andExpect(jsonPath("$.numberOfElements", equalTo(2)))
.andExpect(jsonPath("$.totalElements", equalTo(2)))
.andExpect(jsonPath("$.totalPages", equalTo(1)))
.andExpect(jsonPath("$.content", hasSize(2)))
.andExpect(jsonPath("$.content[0].name", equalTo("name-1")))
.andExpect(jsonPath("$.content[0].description", equalTo("description-1")))
.andExpect(jsonPath("$.content[0].duration", equalTo(60)))
.andExpect(jsonPath("$.content[1].name", equalTo("name-2")))
.andExpect(jsonPath("$.content[1].description", equalTo("description-2")))
.andExpect(jsonPath("$.content[1].duration", equalTo(90)))
.andDo(document("api/events/get",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint())
));
verify(this.eventRepository).findAll(any(Pageable.class));
verifyNoMoreInteractions(this.eventRepository, this.postRepository, this.registrationRepository);
}
@Test
public void getRegistrationsShouldWork() throws Exception {
final List<RegistrationEntity> registrations = this.events.stream()
.filter(event -> event.getId() == 42)
.map(event -> new RegistrationEntity(event, "mail" + event.getId() + "@euregjug.eu", "name " + event.getId(), "vorname " + event.getId(), event.getId() == 42))
.collect(toList());
when(this.registrationRepository.findAllByEventId(42)).thenReturn(registrations);
this.mvc
.perform(
get("/api/events/{id}/registrations", 42)
.principal(() -> "euregjug")
)
.andExpect(status().isOk())
.andExpect(jsonPath("$.[0].email", equalTo("mail42@euregjug.eu")))
.andExpect(jsonPath("$.[0].name", equalTo("name 42")))
.andExpect(jsonPath("$.[0].firstName", equalTo("vorname 42")))
.andExpect(jsonPath("$.[0].subscribeToNewsletter", equalTo(true)))
.andDo(document("api/events/-id-/registrations",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint())
));
verify(this.registrationRepository).findAllByEventId(42);
verifyNoMoreInteractions(this.eventRepository, this.postRepository, this.registrationRepository);
}
@Test
public void updateShouldShouldWork() throws Exception {
final EventEntity updateEntity = new EventEntity(Calendar.getInstance(), "NewName", "NewDescription");
updateEntity.setDuration(90);
updateEntity.setNeedsRegistration(true);
updateEntity.setType(Type.talk);
updateEntity.setSpeaker("Mark Paluch");
updateEntity.setLocation("Thinking Networks AG\n"
+ "Markt 45-47\n"
+ "D-52062 Aachen");
final EventEntity oldEntity = Reflect.on(
new EventEntity(Calendar.getInstance(), "Mark Paluch - Hallo, ich bin Redis", "Mark spricht in diesem Vortrag über den Open Source NoSQL Data Store Redis. Der Vortrag ist eine Einführung in Redis und veranschaulicht mit Hilfe von Code-Beispielen, wie Redis mit Spring Data, Hibernate OGM und plain Java verwendet werden kann. Der Vortrag findet bei Thinking Networks in Aachen statt.")
).set("id", 42).get();
oldEntity.setDuration(60);
oldEntity.setNeedsRegistration(false);
oldEntity.setType(Type.meetup);
oldEntity.setStatus(Status.open);
when(this.eventRepository.findOne(23)).thenReturn(Optional.empty());
when(this.eventRepository.findOne(42)).thenReturn(Optional.of(oldEntity));
this.mvc
.perform(
put("/api/events/{id}", 23)
.content(this.json.write(updateEntity).getJson())
.contentType(APPLICATION_JSON)
.principal(() -> "euregjug")
)
.andExpect(status().isNotFound())
.andDo(document("api/events/update/notfound",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint())
));
this.mvc
.perform(
put("/api/events/{id}", 42)
.content(this.json.write(updateEntity).getJson())
.contentType(APPLICATION_JSON)
.principal(() -> "euregjug")
)
.andExpect(status().isOk())
.andExpect(jsonPath("$.id", equalTo(42)))
.andExpect(jsonPath("$.name", equalTo(oldEntity.getName())))
.andExpect(jsonPath("$.description", equalTo(updateEntity.getDescription())))
.andExpect(jsonPath("$.duration", equalTo(updateEntity.getDuration())))
.andExpect(jsonPath("$.needsRegistration", equalTo(true)))
.andExpect(jsonPath("$.speaker", equalTo(updateEntity.getSpeaker())))
.andExpect(jsonPath("$.location", equalTo(updateEntity.getLocation())))
.andExpect(jsonPath("$.type", equalTo("talk")))
.andExpect(jsonPath("$.status", equalTo("open")));
updateEntity.setStatus(EventEntity.Status.closed);
this.mvc
.perform(
put("/api/events/{id}", 42)
.content(this.json.write(updateEntity).getJson())
.contentType(APPLICATION_JSON)
.principal(() -> "euregjug")
)
.andExpect(status().isOk())
.andExpect(jsonPath("$.id", equalTo(42)))
.andExpect(jsonPath("$.name", equalTo(oldEntity.getName())))
.andExpect(jsonPath("$.description", equalTo(updateEntity.getDescription())))
.andExpect(jsonPath("$.duration", equalTo(updateEntity.getDuration())))
.andExpect(jsonPath("$.needsRegistration", equalTo(true)))
.andExpect(jsonPath("$.speaker", equalTo(updateEntity.getSpeaker())))
.andExpect(jsonPath("$.location", equalTo(updateEntity.getLocation())))
.andExpect(jsonPath("$.type", equalTo("talk")))
.andExpect(jsonPath("$.status", equalTo("closed")))
.andDo(document("api/events/update/updated",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint())
));
verify(this.eventRepository).findOne(23);
verify(this.eventRepository, times(2)).findOne(42);
verifyNoMoreInteractions(this.eventRepository, this.postRepository, this.registrationRepository);
}
}