Commit 51b24085 authored by Colin DAMON's avatar Colin DAMON
Browse files

Cucumber integration

parent 49176d36
...@@ -35,10 +35,10 @@ stages: ...@@ -35,10 +35,10 @@ stages:
- cd $PROJECT_FOLDER - cd $PROJECT_FOLDER
script: script:
- ./mvnw -B -Pprod,swagger verify - ./mvnw -B -Pprod,swagger verify
- awk -F"," '{ branches += $6 + $7; covered += $7 } END { print covered, "/", branches, "branches covered"; print 100*covered/branches, "%covered" }' target/jacoco-aggregate/index.csv - awk -F"," '{ branches += $6 + $7; covered += $7 } END { print covered, "/", branches, "branches covered"; print 100*covered/branches, "%covered" }' target/jacoco-aggregate/jacoco.csv
artifacts: artifacts:
reports: reports:
junit: $PROJECT_FOLDER/target/test-results/TEST-*.xml junit: $PROJECT_FOLDER/target/test-results/**/TEST-*.xml
paths: paths:
- $PROJECT_FOLDER/target/jacoco-aggregate - $PROJECT_FOLDER/target/jacoco-aggregate
expire_in: 1 day expire_in: 1 day
......
...@@ -55,6 +55,7 @@ ...@@ -55,6 +55,7 @@
<validation-api.version>2.0.1.Final</validation-api.version> <validation-api.version>2.0.1.Final</validation-api.version>
<jaxb-runtime.version>2.3.3</jaxb-runtime.version> <jaxb-runtime.version>2.3.3</jaxb-runtime.version>
<mapstruct.version>1.3.1.Final</mapstruct.version> <mapstruct.version>1.3.1.Final</mapstruct.version>
<cucumber.version>6.4.0</cucumber.version>
<!-- Plugin versions --> <!-- Plugin versions -->
<maven-clean-plugin.version>3.1.0</maven-clean-plugin.version> <maven-clean-plugin.version>3.1.0</maven-clean-plugin.version>
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version> <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
...@@ -280,6 +281,30 @@ ...@@ -280,6 +281,30 @@
<artifactId>metrics-core</artifactId> <artifactId>metrics-core</artifactId>
</dependency> </dependency>
<!-- jhipster-needle-maven-add-dependency --> <!-- jhipster-needle-maven-add-dependency -->
<!-- Cucumber -->
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>${cucumber.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit</artifactId>
<version>${cucumber.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-spring</artifactId>
<version>${cucumber.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
...@@ -778,6 +803,7 @@ ...@@ -778,6 +803,7 @@
<excludes> <excludes>
<exclude>**/*IT*</exclude> <exclude>**/*IT*</exclude>
<exclude>**/*IntTest*</exclude> <exclude>**/*IntTest*</exclude>
<exclude>**/*CucumberTest*</exclude>
</excludes> </excludes>
</configuration> </configuration>
</plugin> </plugin>
...@@ -795,6 +821,7 @@ ...@@ -795,6 +821,7 @@
<includes> <includes>
<include>**/*IT*</include> <include>**/*IT*</include>
<include>**/*IntTest*</include> <include>**/*IntTest*</include>
<include>**/*CucumberTest*</include>
</includes> </includes>
</configuration> </configuration>
<executions> <executions>
......
package com.ippon.borestop.config; package com.ippon.borestop.config;
import com.ippon.borestop.common.infrastructure.Generated; import com.ippon.borestop.common.infrastructure.Generated;
import com.ippon.borestop.security.*; import com.ippon.borestop.security.AuthoritiesConstants;
import com.ippon.borestop.security.jwt.*; import com.ippon.borestop.security.jwt.JWTConfigurer;
import com.ippon.borestop.security.jwt.TokenProvider;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
...@@ -15,6 +16,10 @@ import org.springframework.security.config.http.SessionCreationPolicy; ...@@ -15,6 +16,10 @@ import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter; import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter;
import org.springframework.web.filter.CorsFilter; import org.springframework.web.filter.CorsFilter;
import org.zalando.problem.spring.web.advice.security.SecurityProblemSupport; import org.zalando.problem.spring.web.advice.security.SecurityProblemSupport;
...@@ -58,6 +63,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { ...@@ -58,6 +63,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
// @formatter:off // @formatter:off
http http
.csrf() .csrf()
.csrfTokenRepository(csrfTokenRepository())
.disable() .disable()
.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class) .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling() .exceptionHandling()
...@@ -91,10 +97,23 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { ...@@ -91,10 +97,23 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
.and() .and()
.httpBasic() .httpBasic()
.and() .and()
.apply(securityConfigurerAdapter()); .apply(securityConfigurerAdapter())
.and()
.securityContext()
.securityContextRepository(securityContextRepository());
// @formatter:on // @formatter:on
} }
@Bean
public SecurityContextRepository securityContextRepository() {
return new HttpSessionSecurityContextRepository();
}
@Bean
public CsrfTokenRepository csrfTokenRepository() {
return CookieCsrfTokenRepository.withHttpOnlyFalse();
}
private JWTConfigurer securityConfigurerAdapter() { private JWTConfigurer securityConfigurerAdapter() {
return new JWTConfigurer(tokenProvider); return new JWTConfigurer(tokenProvider);
} }
......
Feature: Accounts management
Scenario: Can't get authentication for not authenticated user
Given I am not logged in
When I get my account information
Then I should not be authorized
Scenario: Get account informations
Given I am logged in as "admin"
When I get my account information
Then My login should be "admin"
And My email should be "admin@localhost"
\ No newline at end of file
package com.ippon.borestop.cucumber;
import com.ippon.borestop.BorestopApp;
import com.ippon.borestop.cucumber.CucumberConfiguration.CucumberSecurityContextConfiguration;
import com.ippon.borestop.infrastructure.primay.CucumberTestContext;
import io.cucumber.java.Before;
import io.cucumber.spring.CucumberContextConfiguration;
import io.github.jhipster.config.JHipsterConstants;
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.http.client.BufferingClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.web.client.RestTemplate;
@CucumberContextConfiguration
@ActiveProfiles(JHipsterConstants.SPRING_PROFILE_TEST)
@SpringBootTest(classes = { BorestopApp.class, CucumberSecurityContextConfiguration.class }, webEnvironment = WebEnvironment.RANDOM_PORT)
public class CucumberConfiguration {
@Autowired
private TestRestTemplate rest;
@Before
public void loadInterceptors() {
ClientHttpRequestFactory requestFactory = new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory());
RestTemplate template = rest.getRestTemplate();
template.setRequestFactory(requestFactory);
template.setInterceptors(List.of(mockedCsrfTokenInterceptor(), saveLastResultInterceptor()));
template.getMessageConverters().add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8));
}
private ClientHttpRequestInterceptor mockedCsrfTokenInterceptor() {
return (request, body, execution) -> {
request.getHeaders().add("mocked-csrf-token", "MockedToken");
return execution.execute(request, body);
};
}
private ClientHttpRequestInterceptor saveLastResultInterceptor() {
return (request, body, execution) -> {
ClientHttpResponse response = execution.execute(request, body);
CucumberTestContext.addResponse(request, response);
return response;
};
}
@TestConfiguration
public static class CucumberSecurityContextConfiguration {
@Bean
@Primary
public SecurityContextRepository securityContextRepository() {
return new MockedSecurityContextRepository();
}
@Bean
@Primary
public CsrfTokenRepository csrfTokenRepository() {
return new MockedCsrfTokenRepository();
}
}
}
package com.ippon.borestop.cucumber;
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import org.junit.runner.RunWith;
@RunWith(Cucumber.class)
@CucumberOptions(
glue = "com.ippon.borestop",
plugin = { "pretty", "json:target/cucumber/cucumber.json", "html:target/cucumber/cucumber.htm" },
features = "src/test/features"
)
public class CucumberTest {}
package com.ippon.borestop.cucumber;
import static org.assertj.core.api.Assertions.assertThat;
import com.ippon.borestop.infrastructure.primay.CucumberTestContext;
import io.cucumber.java.en.Then;
import org.springframework.http.HttpStatus;
public class HttpSteps {
@Then("I can't find document")
public void shouldGetNotFoundResult() {
assertThat(CucumberTestContext.getStatus()).isEqualTo(HttpStatus.NOT_FOUND);
}
@Then("I should not be authorized")
public void shouldNotBeAuthorized() {
assertThat(CucumberTestContext.getStatus()).isIn(HttpStatus.UNAUTHORIZED, HttpStatus.FORBIDDEN);
}
}
package com.ippon.borestop.cucumber;
import static org.mockito.Mockito.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.csrf.CsrfTokenRepository;
public class MockedCsrfTokenRepository implements CsrfTokenRepository {
private static final CsrfToken TOKEN = buildCsrfToken();
private static CsrfToken buildCsrfToken() {
CsrfToken token = mock(CsrfToken.class);
when(token.getHeaderName()).thenReturn("mocked-csrf-token");
when(token.getParameterName()).thenReturn("mocked-csrf-token");
when(token.getToken()).thenReturn("MockedToken");
return token;
}
@Override
public CsrfToken generateToken(HttpServletRequest request) {
return TOKEN;
}
@Override
public void saveToken(CsrfToken token, HttpServletRequest request, HttpServletResponse response) {}
@Override
public CsrfToken loadToken(HttpServletRequest request) {
return TOKEN;
}
}
package com.ippon.borestop.cucumber;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.web.context.HttpRequestResponseHolder;
import org.springframework.security.web.context.SecurityContextRepository;
public class MockedSecurityContextRepository implements SecurityContextRepository {
private Authentication authentication;
public void authentication(Authentication authentication) {
this.authentication = authentication;
}
@Override
public boolean containsContext(HttpServletRequest request) {
return authentication != null;
}
@Override
public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
return new SecurityContextImpl(authentication);
}
@Override
public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {}
}
package com.ippon.borestop.infrastructure.primay;
import static org.assertj.core.api.Assertions.assertThat;
import com.ippon.borestop.cucumber.MockedSecurityContextRepository;
import com.ippon.borestop.security.AuthoritiesConstants;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
public class AuthenticationSteps {
@Autowired
private MockedSecurityContextRepository contexts;
private static final Map<String, Authentication> USERS = Map.of(
"admin",
new TestingAuthenticationToken("admin", "N/A", AuthorityUtils.createAuthorityList(AuthoritiesConstants.ADMIN))
);
@Given("I am logged in as {string}")
public void authenticateUser(String username) {
Authentication authentication = USERS.get(username);
SecurityContextHolder.getContext().setAuthentication(authentication);
contexts.authentication(authentication);
}
@Given("I logout")
@Given("I am not logged in")
public void logout() {
contexts.authentication(null);
}
@Then("I should get an authorization error")
public void authorizationError() {
assertThat(CucumberTestContext.getStatus()).isEqualTo(HttpStatus.FORBIDDEN);
}
}
package com.ippon.borestop.infrastructure.primay;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.spi.json.JsonProvider;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Deque;
import java.util.Optional;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.function.Function;
import java.util.stream.Collectors;
import net.minidev.json.JSONArray;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.StreamUtils;
public final class CucumberTestContext {
private static final Deque<RestQuery> queries = new ConcurrentLinkedDeque<>();
private static JsonProvider jsonReader = Configuration.defaultConfiguration().jsonProvider();
private CucumberTestContext() {}
public static void addResponse(HttpRequest request, ClientHttpResponse response) {
queries.addFirst(new RestQuery(request, response));
}
public static HttpStatus getStatus() {
return queries.getFirst().getStatus();
}
public static <T> T getResponse(Class<T> responseClass) {
return queries.getFirst().getResponse().map(response -> TestJson.readFromJson(response, responseClass)).orElse(null);
}
public static Object getElement(String jsonPath) {
return queries.getFirst().getResponse().map(toElement(jsonPath)).orElse(null);
}
public static Object getElement(String uri, String jsonPath) {
return queries
.stream()
.filter(query -> query.forUri(uri))
.findFirst()
.flatMap(response -> response.response.map(toElement(jsonPath)))
.orElse(null);
}
private static Function<String, Object> toElement(String jsonPath) {
return response -> {
Object element = JsonPath.read(jsonReader.parse(response), jsonPath);
if (element instanceof JSONArray) {
JSONArray elements = (JSONArray) element;
if (elements.size() == 0) {
return null;
}
return elements.stream().map(Object::toString).collect(Collectors.joining(", "));
}
return element;
};
}
public static String getCreatedWorkingFolderId() {
return (String) CucumberTestContext.getElement("working-folders", "$.id");
}
public static void reset() {
queries.clear();
}
private static class RestQuery {
private final String uri;
private final HttpStatus status;
private final Optional<String> response;
public RestQuery(HttpRequest request, ClientHttpResponse response) {
uri = request.getURI().toString();
try {
status = response.getStatusCode();
this.response = readResponse(response);
} catch (IOException e) {
throw new AssertionError(e.getMessage(), e);
}
}
private Optional<String> readResponse(ClientHttpResponse response) throws IOException {
try {
return Optional.of(StreamUtils.copyToString(response.getBody(), Charset.defaultCharset()));
} catch (Exception e) {
return Optional.empty();
}
}
private boolean forUri(String uri) {
return this.uri.contains(uri);
}
private HttpStatus getStatus() {
return status;
}
private Optional<String> getResponse() {
return response;
}
}
}
package com.ippon.borestop.web.rest;
import static org.assertj.core.api.Assertions.assertThat;
import com.ippon.borestop.infrastructure.primay.CucumberTestContext;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.web.client.TestRestTemplate;
public class AccountsSteps {
@Autowired
private TestRestTemplate rest;
@When("I get my account information")
public void getAccountInforamtion() {
rest.getForEntity("/api/account", Void.class);
}
@Then("My login should be {string}")
public void shouldHaveLogin(String login) {
assertThat(CucumberTestContext.getElement("$.login")).isEqualTo(login);
}
@Then("My email should be {string}")
public void shouldHaveEmail(String email) {
assertThat(CucumberTestContext.getElement("$.email")).isEqualTo(email);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment