/*
* Copyright (c) [2011-2016] "Pivotal Software, Inc." / "Neo Technology" / "Graph Aware Ltd."
*
* This product is licensed to you under the Apache License, Version 2.0 (the "License").
* You may not use this product except in compliance with the License.
*
* This product may include a number of subcomponents with
* separate copyright notices and license terms. Your use of the source
* code for these subcomponents is subject to the terms and
* conditions of the subcomponent's license, as noted in the LICENSE file.
*
*/
package org.springframework.data.neo4j.web;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.junit.*;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.neo4j.ogm.session.SessionFactory;
import org.neo4j.ogm.testutil.MultiDriverTestClass;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
import org.springframework.data.neo4j.transaction.Neo4jTransactionManager;
import org.springframework.data.neo4j.web.domain.User;
import org.springframework.data.neo4j.web.repo.UserRepository;
import org.springframework.data.neo4j.web.support.OpenSessionInViewInterceptor;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* @author Michal Bachman
* @author Mark Angrish
*/
@WebAppConfiguration
@ContextConfiguration(classes = {WebIntegrationTests.WebAppContext.class})
@RunWith(SpringJUnit4ClassRunner.class)
@Ignore("Why is this failing?")
public class WebIntegrationTests extends MultiDriverTestClass {
@Autowired
private UserRepository userRepository;
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
private User adam;
private User daniela;
private User michal;
private User vince;
@Transactional
@Before
public void setUp() {
Mockito.reset(userRepository);
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
adam = new User("Adam");
daniela = new User("Daniela");
michal = new User("Michal");
vince = new User("Vince");
adam.befriend(daniela);
daniela.befriend(michal);
michal.befriend(vince);
// see the @Ignored test below. This doesn't appear to use the same
// transaction definition as the one created in the test method, even though the tx manager is the same.
// consequently the tx definition created by the test method is replaced and the test fails.
// reason: repository is marked as transactional. However, if we remove the @Transactional
// from the repo and make the test class transactional, the test method
// shouldNotShareSessionBetweenRequestsWithDifferentSession() fails, because the session is bound
// to the transaction
userRepository.save(adam);
}
@Test
public void shouldNotShareSessionBetweenRequestsWithDifferentSession() throws Exception {
mockMvc.perform(get("/user/{uuid}/friends", adam.getUuid()))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.content().string("Daniela"));
mockMvc.perform(get("/user/{uuid}/friends", vince.getUuid()))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.content().string("Michal"));
}
@Test
public void shouldShareSessionBetweenRequestsDuringSameSession() throws Exception {
MockHttpSession session = new MockHttpSession();
mockMvc.perform(get("/user/{uuid}/immediateFriends", adam.getUuid()).session(session))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.content().string("Daniela"));
mockMvc.perform(get("/user/{uuid}/immediateFriends", daniela.getUuid()).session(session))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.content().string("Adam Michal"));
mockMvc.perform(get("/user/{uuid}/immediateFriends", michal.getUuid()).session(session))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.content().string("Daniela Vince"));
mockMvc.perform(get("/user/{uuid}/immediateFriends", vince.getUuid()).session(session))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.content().string("Michal"));
}
@Test
public void shouldNotShareSessionBetweenMultiThreadedRequests() throws Exception {
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
final int j = i;
executor.submit(new Runnable() {
@Override
public void run() {
if (j % 2 == 0) {
try {
mockMvc.perform(get("/user/{uuid}/friends", adam.getUuid()))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.content().string("Daniela"));
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
try {
mockMvc.perform(get("/user/{uuid}/friends", vince.getUuid()))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.content().string("Michal"));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
});
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);
}
@Configuration
@EnableWebMvc
@ComponentScan({"org.springframework.data.neo4j.web.controller", "org.springframework.data.neo4j.web.service"})
@EnableNeo4jRepositories("org.springframework.data.neo4j.web.repo")
@EnableTransactionManagement
static class WebAppContext extends WebMvcConfigurerAdapter {
@Bean
public OpenSessionInViewInterceptor openSessionInViewInterceptor() {
OpenSessionInViewInterceptor openSessionInViewInterceptor = new OpenSessionInViewInterceptor();
openSessionInViewInterceptor.setSessionFactory(sessionFactory());
return openSessionInViewInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addWebRequestInterceptor(openSessionInViewInterceptor());
}
@Bean
public PlatformTransactionManager transactionManager() {
return new Neo4jTransactionManager(sessionFactory());
}
@Bean
public SessionFactory sessionFactory() {
return new SessionFactory(getBaseConfiguration().build(), "org.springframework.data.neo4j.web.domain");
}
}
}