Add Spring Cloud Refresh Context smoke tests

Closes gh-26
This commit is contained in:
Olga MaciaszekSharma
2023-09-14 18:29:13 +02:00
committed by Sébastien Deleuze
parent 888b9e0c02
commit dae8b34de9
25 changed files with 619 additions and 5 deletions

View File

@@ -126,3 +126,22 @@ h|test
|===
== Cloud
[%header,cols="4"]
|===
h|Smoke Test
h|appTest
h|checkpointRestoreAppTest
h|test
|context-refresh-http
|image:https://ci.spring.io/api/v1/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-http-app-test/badge[link=https://ci.spring.io/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-http-app-test]
|image:https://ci.spring.io/api/v1/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-http-cr-app-test/badge[link=https://ci.spring.io/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-http-cr-app-test]
|image:https://ci.spring.io/api/v1/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-http-test/badge[link=https://ci.spring.io/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-http-test]
|context-refresh
|
|image:https://ci.spring.io/api/v1/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-cr-app-test/badge[link=https://ci.spring.io/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-cr-app-test]
|image:https://ci.spring.io/api/v1/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-test/badge[link=https://ci.spring.io/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-test]
|===

View File

@@ -55,3 +55,11 @@ groups:
- name: spring-kafka-streams
app_test: true
test: false
- name: cloud
smoke_tests:
- name: context-refresh-http
app_test: true
test: true
- name: context-refresh
app_test: false
test: false

View File

@@ -0,0 +1,33 @@
plugins {
id "java"
id "org.springframework.boot"
id "org.springframework.cr.smoke-test"
id 'io.spring.dependency-management'
}
ext {
set('springCloudVersion', "2023.0.0-SNAPSHOT")
}
dependencies {
implementation(platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES))
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.crac:crac:$cracVersion")
implementation(project(":cr-listener"))
implementation("org.springframework.cloud:spring-cloud-starter")
implementation("org.springframework.boot:spring-boot-starter-actuator")
testImplementation("org.springframework.boot:spring-boot-starter-test")
appTestImplementation(project(":cr-smoke-test-support"))
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
crSmokeTest {
webApplication = true
}

View File

@@ -0,0 +1,2 @@
simple.test=testVal
test=propVal

View File

@@ -0,0 +1,81 @@
package com.example.contextrefreshhttp;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
import org.springframework.cr.smoketest.support.junit.ApplicationTest;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.assertj.core.api.Assertions.assertThat;
@ApplicationTest
public class ContextRefreshHttpApplicationTests {
@AfterAll
static void cleanUp() throws IOException {
editExternalConfigurationProperties("""
simple.test=testVal
test=propVal
""");
}
@Test
void refreshScopeBean(WebTestClient webClient) throws IOException {
webClient.get()
.uri("/refresh")
.exchange()
.expectStatus()
.isOk()
.expectBody()
.consumeWith(result -> assertThat(new String(result.getResponseBodyContent())).isEqualTo("propVal"));
editExternalConfigurationProperties("""
simple.test=testVal
test=propValNew
""");
webClient.post().uri("/actuator/refresh").exchange().expectStatus().isOk();
webClient.get()
.uri("/refresh")
.exchange()
.expectStatus()
.isOk()
.expectBody()
.consumeWith(result -> assertThat(new String(result.getResponseBodyContent())).isEqualTo("propValNew"));
}
@Test
void configurationProperties(WebTestClient webClient) throws IOException {
webClient.get()
.uri("/simple")
.exchange()
.expectStatus()
.isOk()
.expectBody()
.consumeWith(result -> assertThat(new String(result.getResponseBodyContent())).isEqualTo("testVal"));
editExternalConfigurationProperties("""
simple.test=testValNew
test=propVal
""");
webClient.post().uri("/actuator/refresh").exchange().expectStatus().isOk();
webClient.get()
.uri("/simple")
.exchange()
.expectStatus()
.isOk()
.expectBody()
.consumeWith(result -> assertThat(new String(result.getResponseBodyContent())).isEqualTo("testValNew"));
}
private static void editExternalConfigurationProperties(String newFileContent) throws IOException {
Files.writeString(Path.of("././dev.properties"), newFileContent, Charset.defaultCharset(),
StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
}
}

View File

@@ -0,0 +1,17 @@
package com.example.contextrefreshhttp;
import com.example.contextrefreshhttp.config.SimpleConfigurationProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@SpringBootApplication
@EnableConfigurationProperties(SimpleConfigurationProperties.class)
public class ContextRefreshHttpApplication {
public static void main(String[] args) {
SpringApplication.run(ContextRefreshHttpApplication.class, args);
}
}

View File

@@ -0,0 +1,22 @@
package com.example.contextrefreshhttp;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/refresh")
@RefreshScope
public class RefreshScopeController {
@Value("${test}")
private String test;
@GetMapping
String test() {
return test;
}
}

View File

@@ -0,0 +1,24 @@
package com.example.contextrefreshhttp;
import com.example.contextrefreshhttp.config.SimpleConfigurationProperties;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/simple")
public class SimpleController {
private final SimpleConfigurationProperties properties;
public SimpleController(SimpleConfigurationProperties properties) {
this.properties = properties;
}
@GetMapping
String test() {
return properties.getTest();
}
}

View File

@@ -0,0 +1,18 @@
package com.example.contextrefreshhttp.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "simple")
public class SimpleConfigurationProperties {
private String test = "default";
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
}

View File

@@ -0,0 +1,17 @@
spring:
cloud:
refresh:
on-restart:
enabled: false
application:
name: context-refresh-http
config:
import: file:./cloud/context-refresh-http/dev.properties
management:
endpoints:
web:
exposure:
include:
- refresh
- env

View File

@@ -0,0 +1,77 @@
package com.example.contextrefreshhttp;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ContextRefreshHttpTests {
@Autowired
private TestRestTemplate restTemplate;
@AfterAll
static void setUp() throws IOException {
editExternalConfigurationProperties("""
simple.test=testVal
test=propVal
""");
}
@Test
void refreshScopeBean() throws IOException {
String property = this.restTemplate.getForObject("/refresh", String.class);
assertThat(property).isEqualTo("propVal");
editExternalConfigurationProperties("""
simple.test=testVal
test=propValNew
""");
RequestEntity<Void> request = RequestEntity.post("/actuator/refresh")
.contentType(MediaType.APPLICATION_JSON)
.build();
this.restTemplate.postForObject("/actuator/refresh", request, String.class);
String updatedProperty = this.restTemplate.getForObject("/refresh", String.class);
assertThat(updatedProperty).isEqualTo("propValNew");
}
@Test
void configurationProperties() throws IOException {
String property = this.restTemplate.getForObject("/simple", String.class);
assertThat(property).isEqualTo("testVal");
editExternalConfigurationProperties("""
simple.test=testValNew
test=propVal
""");
RequestEntity<Void> request = RequestEntity.post("/actuator/refresh")
.contentType(MediaType.APPLICATION_JSON)
.build();
this.restTemplate.postForObject("/actuator/refresh", request, String.class);
String updatedProperty = this.restTemplate.getForObject("/simple", String.class);
assertThat(updatedProperty).isEqualTo("testValNew");
}
static void editExternalConfigurationProperties(String newFileContent) throws IOException {
Files.writeString(Path.of("././dev.properties"), newFileContent, Charset.defaultCharset(),
StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
}
}

View File

@@ -0,0 +1,17 @@
spring:
cloud:
refresh:
on-restart:
enabled: false
application:
name: context-refresh-http
config:
import: file:././dev.properties
management:
endpoints:
web:
exposure:
include:
- refresh
- env

View File

@@ -0,0 +1,37 @@
plugins {
id "java"
id "org.springframework.boot"
id "org.springframework.cr.smoke-test"
id 'io.spring.dependency-management'
}
ext {
set('springCloudVersion', "2023.0.0-SNAPSHOT")
}
dependencies {
implementation(platform(
org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES))
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.crac:crac:$cracVersion")
implementation(project(":cr-listener"))
implementation("org.springframework.cloud:spring-cloud-starter")
testImplementation("org.springframework.boot:spring-boot-starter-test")
appTestImplementation(project(":cr-smoke-test-support"))
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
crSmokeTest {
webApplication = true
}
// `@ApplicationTest`s need to only be executed during the `checkpointRestoreAppTest and not `appTest`
// (we're checking for a value that we want to have been modified in between these two phases).
project.gradle.startParameter.excludedTaskNames.add("appTest")

View File

@@ -0,0 +1,2 @@
simple.test=testVal
test=propVal

View File

@@ -0,0 +1,57 @@
package com.example.contextrefresh;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
import org.springframework.cr.smoketest.support.junit.ApplicationTest;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.assertj.core.api.Assertions.assertThat;
@ApplicationTest
public class ContextRefreshApplicationTests {
// Bring the config file back to init values before next app start,
// since the app updates the property file after properties set
@AfterAll
static void cleanUp() throws IOException {
editExternalConfigurationProperties("""
simple.test=testVal
test=propVal
""");
}
@Test
void refreshScopeBean(WebTestClient webClient) {
webClient.get()
.uri("/refresh")
.exchange()
.expectStatus()
.isOk()
.expectBody()
.consumeWith(result -> assertThat(new String(result.getResponseBodyContent())).isEqualTo("propValNew"));
}
@Test
void configurationProperties(WebTestClient webClient) {
webClient.get()
.uri("/simple")
.exchange()
.expectStatus()
.isOk()
.expectBody()
.consumeWith(result -> assertThat(new String(result.getResponseBodyContent())).isEqualTo("testValNew"));
}
private static void editExternalConfigurationProperties(String newFileContent) throws IOException {
Files.writeString(Path.of("./dev.properties"), newFileContent, Charset.defaultCharset(),
StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
}
}

View File

@@ -0,0 +1,50 @@
package com.example.contextrefresh;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ConfigUpdateController {
private static final Log LOG = LogFactory.getLog(ConfigUpdateController.class);
@Value("${config.path}")
private String path;
@EventListener(ApplicationStartedEvent.class)
public void doSomethingAfterStartup() throws IOException {
if (LOG.isDebugEnabled()) {
LOG.debug("Updating file values");
}
editExternalConfigurationProperties("""
simple.test=testValNew
test=propValNew
""");
}
@GetMapping("/reset")
void reset() throws IOException {
editExternalConfigurationProperties("""
simple.test=testVal
test=propVal
""");
}
private void editExternalConfigurationProperties(String newFileContent) throws IOException {
Files.writeString(Path.of(path), newFileContent, Charset.defaultCharset(), StandardOpenOption.WRITE,
StandardOpenOption.TRUNCATE_EXISTING);
}
}

View File

@@ -0,0 +1,17 @@
package com.example.contextrefresh;
import com.example.contextrefresh.config.SimpleConfigurationProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@SpringBootApplication
@EnableConfigurationProperties(SimpleConfigurationProperties.class)
public class ContextRefreshApplication {
public static void main(String[] args) {
SpringApplication.run(ContextRefreshApplication.class, args);
}
}

View File

@@ -0,0 +1,22 @@
package com.example.contextrefresh;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/refresh")
@RefreshScope
public class RefreshScopeController {
@Value("${test}")
private String test;
@GetMapping
String test() {
return test;
}
}

View File

@@ -0,0 +1,24 @@
package com.example.contextrefresh;
import com.example.contextrefresh.config.SimpleConfigurationProperties;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/simple")
public class SimpleController {
private final SimpleConfigurationProperties properties;
public SimpleController(SimpleConfigurationProperties properties) {
this.properties = properties;
}
@GetMapping
String test() {
return properties.getTest();
}
}

View File

@@ -0,0 +1,18 @@
package com.example.contextrefresh.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "simple")
public class SimpleConfigurationProperties {
private String test = "default";
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
}

View File

@@ -0,0 +1,8 @@
spring:
application:
name: context-refresh
config:
import: file:./cloud/context-refresh/dev.properties
config:
path: ./cloud/context-refresh/dev.properties

View File

@@ -0,0 +1,37 @@
package com.example.contextrefresh;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ContextRefreshTests {
@Autowired
private TestRestTemplate restTemplate;
@AfterEach
void cleanUp() {
restTemplate.getForObject("/reset", Void.class);
}
// Verify for initial values only
@Test
void refreshScopeBean() {
String property = this.restTemplate.getForObject("/refresh", String.class);
assertThat(property).isEqualTo("propVal");
}
// Verify for initial values only
@Test
void configurationProperties() {
String property = this.restTemplate.getForObject("/simple", String.class);
assertThat(property).isEqualTo("testVal");
}
}

View File

@@ -0,0 +1,8 @@
spring:
application:
name: context-refresh
config:
import: file:././dev.properties
config:
path: ././dev.properties

View File

@@ -12,11 +12,10 @@ class WebClientConfiguration {
@Bean
WebClient webClient(WebClient.Builder builder, ClientHttpConnector clientHttpConnector) {
// TODO Check why run-dev-container.sh is broken by the commented line below
//String host = env("HTTPBIN_HOST", "localhost");
// String host = env("HTTPBIN_HOST", "localhost");
String host = "localhost";
int port = env("HTTPBIN_PORT_8080", 8080);
return builder.baseUrl("http://%s:%d/".formatted(host, port))
.clientConnector(clientHttpConnector).build();
return builder.baseUrl("http://%s:%d/".formatted(host, port)).clientConnector(clientHttpConnector).build();
}
private static String env(String name, String def) {

View File

@@ -27,8 +27,8 @@ rootProject.name="spring-checkpoint-restore-smoke-tests"
include "cr-smoke-test-support"
include "cr-listener"
["boot", "data", "framework", "integration"].each { group ->
["boot", "cloud", "data", "framework", "integration"].each { group ->
file(group).eachDirMatch(~/[a-z].*/) { smokeTest ->
include "$group:${smokeTest.name}"
}
}
}