response; // output
17 |
18 | @When("^the client calls /version$")
19 | public void the_client_issues_GET_version() throws Throwable {
20 | response = template.getForEntity("/application/version", String.class);
21 | }
22 |
23 | @Then("^the client receives status code of (\\d+)$")
24 | public void the_client_receives_status_code_of(int statusCode) throws Throwable {
25 | HttpStatus currentStatusCode = response.getStatusCode();
26 | assertThat("status code is incorrect : " +
27 | response.getBody(), currentStatusCode.value(), is(statusCode));
28 | }
29 |
30 | @And("^the client receives server version (.+)$")
31 | public void the_client_receives_server_version_body(String version) throws Throwable {
32 | assertThat(response.getBody(), is(version));
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/test/java/com/test/sampleapp/retry/Retry.java:
--------------------------------------------------------------------------------
1 | package com.test.sampleapp.retry;
2 |
3 | /**
4 | * Created by MRomeh on 05/09/2017.
5 | */
6 |
7 | import java.lang.annotation.ElementType;
8 | import java.lang.annotation.Retention;
9 | import java.lang.annotation.RetentionPolicy;
10 | import java.lang.annotation.Target;
11 |
12 | /**
13 | * Retries a unit-test according to the attributes set here
14 | *
15 | * The class containing the test(s) decorated with this annotation must have a public field of type {@link RetryRule}
16 | */
17 | @Retention(RetentionPolicy.RUNTIME)
18 | @Target({ElementType.METHOD, ElementType.TYPE})
19 | public @interface Retry {
20 | /**
21 | * @return the number of times to try this method before the failure is propagated through
22 | */
23 | int times() default 3;
24 |
25 | /**
26 | * @return how long to sleep between invocations of the unit tests, in milliseconds
27 | */
28 | long timeout() default 0;
29 | }
30 |
--------------------------------------------------------------------------------
/src/test/java/com/test/sampleapp/retry/RetryException.java:
--------------------------------------------------------------------------------
1 | package com.test.sampleapp.retry;
2 |
3 | /**
4 | * Created by MRomeh on 05/09/2017.
5 | */
6 |
7 | import javax.validation.constraints.NotNull;
8 | import java.io.PrintWriter;
9 | import java.io.StringWriter;
10 | import com.test.sampleapp.retry.*;
11 |
12 | /**
13 | * An exception thrown to signal that a retry operation (executed via {@link RetryRule}) has retried more than the
14 | * allowed number of times, and has still failed.
15 | */
16 | public final class RetryException extends RuntimeException {
17 |
18 | private RetryException(@NotNull String message) {
19 | super(message);
20 | }
21 |
22 | /**
23 | * @param errors the errors for each attempt at running this test-case
24 | */
25 | @NotNull
26 | public static RetryException from(@NotNull Throwable[] errors) {
27 | final StringBuilder msg = new StringBuilder("Invoked methods still failed after " + errors.length + " attempts.");
28 | for (int i = 0; i < errors.length; i++) {
29 | final Throwable error = errors[i];
30 | msg.append('\n');
31 | msg.append("Attempt #").append(i).append(" threw exception:");
32 | msg.append(stackTraceAsString(error));
33 | }
34 | return new RetryException(msg.toString());
35 | }
36 |
37 | @NotNull
38 | private static String stackTraceAsString(@NotNull Throwable t) {
39 | final StringWriter errors = new StringWriter();
40 | t.printStackTrace(new PrintWriter(errors));
41 | return errors.toString();
42 | }
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/src/test/java/com/test/sampleapp/retry/RetryRule.java:
--------------------------------------------------------------------------------
1 | package com.test.sampleapp.retry;
2 |
3 | /**
4 | * Created by MRomeh on 05/09/2017.
5 | */
6 |
7 | import org.junit.rules.TestRule;
8 | import org.junit.runner.Description;
9 | import org.junit.runners.model.Statement;
10 | import com.test.sampleapp.retry.*;
11 | import javax.validation.constraints.NotNull;
12 | import java.util.Arrays;
13 |
14 | public final class RetryRule implements TestRule {
15 |
16 | @NotNull
17 | private Throwable[] errors = new Throwable[0];
18 |
19 | private int currentAttempt = 0;
20 |
21 | @Override
22 | public Statement apply(final Statement base, final Description description) {
23 | final Retry retryAnnotation = description.getAnnotation(Retry.class);
24 | if (retryAnnotation == null) {
25 | return base;
26 | }
27 | final int times = retryAnnotation.times();
28 | if (times <= 0) {
29 | throw new IllegalArgumentException(
30 | "@" + Retry.class.getSimpleName() + " cannot be used with a \"times\" parameter less than 1"
31 | );
32 | }
33 | final long timeout = retryAnnotation.timeout();
34 | if (timeout < 0) {
35 | throw new IllegalArgumentException(
36 | "@" + Retry.class.getSimpleName() + " cannot be used with a \"timeout\" parameter less than 0"
37 | );
38 | }
39 |
40 | errors = new Throwable[times];
41 |
42 | return new Statement() {
43 | @Override
44 | public void evaluate() throws Throwable {
45 | while (currentAttempt < times) {
46 | try {
47 | base.evaluate();
48 | return;
49 | } catch (Throwable t) {
50 | errors[currentAttempt] = t;
51 | currentAttempt++;
52 | Thread.sleep(timeout);
53 | }
54 | }
55 | throw RetryException.from(errors);
56 | }
57 | };
58 | }
59 |
60 | /**
61 | * @return an array representing the errors that have been encountered so far. {@code errors()[0]} corresponds to the
62 | * Throwable encountered when running the test-case for the first time, {@code errors()[1]} corresponds to the
63 | * Throwable encountered when running the test-case for the second time, and so on.
64 | */
65 | @NotNull
66 | public Throwable[] errors() {
67 | return Arrays.copyOfRange(errors, 0, currentAttempt);
68 | }
69 |
70 | /**
71 | * A convenience method to return the {@link Throwable} that was encountered on the last invocation of this test-case.
72 | * Returns {@code null} if this is the first invocation of the test-case.
73 | */
74 | public Throwable lastError() {
75 | final int currentAttempt = currentAttempt();
76 | final Throwable[] errors = errors();
77 | if (currentAttempt == 0) {
78 | return null;
79 | }
80 | return errors[currentAttempt - 1];
81 | }
82 |
83 | /**
84 | * @return the current attempt (0-indexed). 0 is the very first attempt, 1 is the next one, and so on.
85 | */
86 | public int currentAttempt() {
87 | return currentAttempt;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/test/java/com/test/sampleapp/sanity/ApplicationSanityCheck_ITT.java:
--------------------------------------------------------------------------------
1 | package com.test.sampleapp.sanity;
2 |
3 |
4 | import com.test.sampleapp.retry.RetryRule;
5 | import org.junit.Before;
6 | import org.junit.Rule;
7 | import org.junit.Test;
8 | import org.springframework.http.client.SimpleClientHttpRequestFactory;
9 | import org.springframework.web.client.RestTemplate;
10 | import java.net.InetSocketAddress;
11 | import java.net.Proxy;
12 | import java.net.URL;
13 | import com.test.sampleapp.retry.Retry;
14 |
15 | import static org.junit.Assert.assertTrue;
16 |
17 |
18 | public class ApplicationSanityCheck_ITT {
19 | @Rule
20 | public final RetryRule retry = new RetryRule();
21 | private int port = 8080;
22 | private RestTemplate template;
23 | private URL base;
24 |
25 | @Before
26 | public void setUp() throws Exception {
27 | this.base = new URL("http://localhost:" + port + "/");
28 | // disabled proxy config to run locally
29 | SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
30 | // just added for showing how to configure the proxy
31 | // Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("userproxy.glb.ebc.local", 8080));
32 | //requestFactory.setProxy(proxy);
33 | template = new RestTemplate(requestFactory);
34 |
35 |
36 | }
37 |
38 | // and retry in case of failure 3 times with 20 seconds delay between each try
39 | @Test
40 | @Retry(times = 3, timeout = 20000)
41 | public void test_is_server_up() {
42 | assertTrue(template.getForEntity(base + "/actuator/health", String.class).getStatusCode().is2xxSuccessful());
43 |
44 | }
45 |
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/test/resources/features/health.feature:
--------------------------------------------------------------------------------
1 | Feature: the health can be retrieved
2 | Scenario: client makes call to GET /health
3 | When the client calls /health
4 | Then the client receives response status code of 200
--------------------------------------------------------------------------------
/src/test/resources/features/version.feature:
--------------------------------------------------------------------------------
1 | Feature: the version can be retrieved
2 | Scenario: client makes call to GET /version
3 | When the client calls /version
4 | Then the client receives status code of 200
5 | And the client receives server version 1.0
--------------------------------------------------------------------------------
/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | ${FILE_LOG_PATTERN}
10 | utf8
11 |
12 |
13 |
14 |
15 | ${LOG_HOME}/application.log
16 | true
17 |
18 | ${FILE_LOG_PATTERN}
19 |
20 |
21 | application.log.%i
22 |
23 |
25 | 10MB
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/target/classes/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | jackson:
3 | default-property-inclusion: non_null
4 | jpa:
5 | hibernate:
6 | ddl-auto: update
7 | datasource:
8 | tomcat:
9 | max-active: 10
10 | max-idle: 10000
11 | max-wait: 10000
12 | test-on-borrow: true
13 | url: jdbc:h2:file:${dataBaseUrl}
14 | data-username: sa
15 | data-password:
16 | driver-class-name: org.h2.Driver
17 |
18 | dataBaseUrl: ./h2/alerts_db;DB_CLOSE_ON_EXIT=FALSE
19 |
20 |
21 | initialDelay: 10000
22 | fixedDelay: 60000
23 |
24 | server:
25 | tomcat:
26 | accessLog.enabled: true
27 | basedir: "${LOG_PATH}/Application"
28 | accessLogPattern: "%h %l %u %t \"%r\" %s %b %D"
29 |
30 |
31 |
--------------------------------------------------------------------------------
/target/classes/bootstrap.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | # change the application name for your project name , this name is reserved for the maven archetype code generation
3 | application:
4 | name: Application
5 |
6 | ---
7 | # DO NOT FORGET TO ADD YOUR YAML CONFIG FILE IN config server as shown below
8 | spring:
9 | cloud:
10 | config:
11 | failFast: true
12 | server:
13 | bootstrap: true
14 | git:
15 | uri: https://github.com/Romeh/config.git
16 |
--------------------------------------------------------------------------------
/target/classes/com/test/sampleapp/Application.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/classes/com/test/sampleapp/Application.class
--------------------------------------------------------------------------------
/target/classes/com/test/sampleapp/SwaggerConfiguration.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/classes/com/test/sampleapp/SwaggerConfiguration.class
--------------------------------------------------------------------------------
/target/classes/com/test/sampleapp/config/ApplicationConfig.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/classes/com/test/sampleapp/config/ApplicationConfig.class
--------------------------------------------------------------------------------
/target/classes/com/test/sampleapp/domain/ApplicationItem$ApplicationItemBuilder.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/classes/com/test/sampleapp/domain/ApplicationItem$ApplicationItemBuilder.class
--------------------------------------------------------------------------------
/target/classes/com/test/sampleapp/domain/ApplicationItem.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/classes/com/test/sampleapp/domain/ApplicationItem.class
--------------------------------------------------------------------------------
/target/classes/com/test/sampleapp/exceptions/NotAllowedOperationException.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/classes/com/test/sampleapp/exceptions/NotAllowedOperationException.class
--------------------------------------------------------------------------------
/target/classes/com/test/sampleapp/exceptions/ResourceNotFoundException.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/classes/com/test/sampleapp/exceptions/ResourceNotFoundException.class
--------------------------------------------------------------------------------
/target/classes/com/test/sampleapp/repository/ApplicationRepository.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/classes/com/test/sampleapp/repository/ApplicationRepository.class
--------------------------------------------------------------------------------
/target/classes/com/test/sampleapp/service/ApplicationService.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/classes/com/test/sampleapp/service/ApplicationService.class
--------------------------------------------------------------------------------
/target/classes/com/test/sampleapp/service/ExternalServiceWithSpringRetryAndCircuitBreaker.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/classes/com/test/sampleapp/service/ExternalServiceWithSpringRetryAndCircuitBreaker.class
--------------------------------------------------------------------------------
/target/classes/com/test/sampleapp/service/ScheduledServiceAction.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/classes/com/test/sampleapp/service/ScheduledServiceAction.class
--------------------------------------------------------------------------------
/target/classes/com/test/sampleapp/web/ApplicationController.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/classes/com/test/sampleapp/web/ApplicationController.class
--------------------------------------------------------------------------------
/target/classes/com/test/sampleapp/web/ErrorHandler$ERROR_CODE.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/classes/com/test/sampleapp/web/ErrorHandler$ERROR_CODE.class
--------------------------------------------------------------------------------
/target/classes/com/test/sampleapp/web/ErrorHandler.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/classes/com/test/sampleapp/web/ErrorHandler.class
--------------------------------------------------------------------------------
/target/classes/com/test/sampleapp/web/dto/ApplicationEntry$ApplicationEntryBuilder.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/classes/com/test/sampleapp/web/dto/ApplicationEntry$ApplicationEntryBuilder.class
--------------------------------------------------------------------------------
/target/classes/com/test/sampleapp/web/dto/ApplicationEntry.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/classes/com/test/sampleapp/web/dto/ApplicationEntry.class
--------------------------------------------------------------------------------
/target/classes/com/test/sampleapp/web/dto/CustomErrorResponse.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/classes/com/test/sampleapp/web/dto/CustomErrorResponse.class
--------------------------------------------------------------------------------
/target/classes/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | ${FILE_LOG_PATTERN}
10 | utf8
11 |
12 |
13 |
14 |
15 | ${LOG_HOME}/Application.log
16 | true
17 |
18 | ${FILE_LOG_PATTERN}
19 |
20 |
21 | Application.log.%i
22 |
23 |
25 | 10MB
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/target/test-classes/com/test/sampleapp/acceptance/ApplicationE2E.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/test-classes/com/test/sampleapp/acceptance/ApplicationE2E.class
--------------------------------------------------------------------------------
/target/test-classes/com/test/sampleapp/integration/CucumberIntegrationIT.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/test-classes/com/test/sampleapp/integration/CucumberIntegrationIT.class
--------------------------------------------------------------------------------
/target/test-classes/com/test/sampleapp/integration/CucumberRoot.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/test-classes/com/test/sampleapp/integration/CucumberRoot.class
--------------------------------------------------------------------------------
/target/test-classes/com/test/sampleapp/integration/GetHealthStep.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/test-classes/com/test/sampleapp/integration/GetHealthStep.class
--------------------------------------------------------------------------------
/target/test-classes/com/test/sampleapp/integration/GetVersionStep.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/test-classes/com/test/sampleapp/integration/GetVersionStep.class
--------------------------------------------------------------------------------
/target/test-classes/com/test/sampleapp/retry/Retry.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/test-classes/com/test/sampleapp/retry/Retry.class
--------------------------------------------------------------------------------
/target/test-classes/com/test/sampleapp/retry/RetryException.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/test-classes/com/test/sampleapp/retry/RetryException.class
--------------------------------------------------------------------------------
/target/test-classes/com/test/sampleapp/retry/RetryRule$1.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/test-classes/com/test/sampleapp/retry/RetryRule$1.class
--------------------------------------------------------------------------------
/target/test-classes/com/test/sampleapp/retry/RetryRule.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/test-classes/com/test/sampleapp/retry/RetryRule.class
--------------------------------------------------------------------------------
/target/test-classes/com/test/sampleapp/sanity/ApplicationSanityCheck_ITT.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Romeh/spring-boot-sample-app/6f4168d091228891e74d3a4894acec38b9008f77/target/test-classes/com/test/sampleapp/sanity/ApplicationSanityCheck_ITT.class
--------------------------------------------------------------------------------
/target/test-classes/features/health.feature:
--------------------------------------------------------------------------------
1 | Feature: the health can be retrieved
2 | Scenario: client makes call to GET /health
3 | When the client calls /health
4 | Then the client receives response status code of 200
--------------------------------------------------------------------------------
/target/test-classes/features/version.feature:
--------------------------------------------------------------------------------
1 | Feature: the version can be retrieved
2 | Scenario: client makes call to GET /version
3 | When the client calls /version
4 | Then the client receives status code of 200
5 | And the client receives server version 1.0
--------------------------------------------------------------------------------
/target/test-classes/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | ${FILE_LOG_PATTERN}
10 | utf8
11 |
12 |
13 |
14 |
15 | ${LOG_HOME}/application.log
16 | true
17 |
18 | ${FILE_LOG_PATTERN}
19 |
20 |
21 | application.log.%i
22 |
23 |
25 | 10MB
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------