Add Smoke Tests for a client/server Function execution use case, configured with SBDG auto-configuration on the client-side and SDG Function annotation config on the server-side.

This commit is contained in:
John Blum
2019-10-21 15:29:35 -07:00
parent c6e21ffde8
commit 3053bac169
7 changed files with 365 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
# This file is generated by the 'io.freefair.lombok' Gradle plugin
config.stopBubbling = true

View File

@@ -0,0 +1,21 @@
plugins {
id "io.freefair.lombok" version "4.1.2"
}
apply plugin: 'io.spring.convention.spring-test'
description = "Smoke Tests asserting the proper execution of an Apache Geode Function using Spring Data for Apache Geode Function annotation support in a Spring Boot context."
dependencies {
implementation project(':spring-geode-starter')
implementation "org.assertj:assertj-core"
implementation "org.projectlombok:lombok"
testImplementation project(':spring-geode-starter-test')
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2019 the original author or authors.
*
* 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
*
* https://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 example.app.petclinic.function;
import org.springframework.data.gemfire.function.annotation.FunctionId;
import org.springframework.data.gemfire.function.annotation.OnRegion;
import example.app.petclinic.model.Pet;
/**
* Function interface declaring all {@link Pet} services administered by the Pet Clinic.
*
* @author John Blum
* @see example.app.petclinic.model.Pet
* @since 1.2.1
*/
@OnRegion(region = "Pets")
public interface PetServiceFunctionExecutions {
@FunctionId(("AdministerPetVaccinations"))
void administerPetVaccinations();
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2019 the original author or authors.
*
* 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
*
* https://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 example.app.petclinic.model;
import java.time.LocalDateTime;
import org.springframework.data.annotation.Id;
import org.springframework.data.gemfire.mapping.annotation.Region;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
/**
* Abstract Data Type (ADT) modeling a pet.
*
* @author John Blum
* @see org.springframework.data.annotation.Id
* @see org.springframework.data.gemfire.mapping.annotation.Region
* @since 1.2.1
*/
@Region("Pets")
@ToString(of = "name")
@EqualsAndHashCode(of = "name")
@RequiredArgsConstructor(staticName = "newPet")
@SuppressWarnings("unused")
public class Pet {
@Getter
private LocalDateTime vaccinationDateTime;
@Id @NonNull @Getter
private String name;
@Getter
private Type petType;
public Pet as(Type petType) {
this.petType = petType;
return this;
}
public void vaccinate() {
this.vaccinationDateTime = LocalDateTime.now();
}
public enum Type {
CAT,
DOG,
RABBIT,
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2019 the original author or authors.
*
* 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
*
* https://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 example.app.petclinic.repo;
import org.springframework.data.repository.CrudRepository;
import example.app.petclinic.model.Pet;
/**
* Data Access Object (DAO) containing basic CRUD and simple Query data access operations on {@link Pet} objects.
*
* @author John Blum
* @see org.springframework.data.repository.CrudRepository
* @see example.app.petclinic.model.Pet
* @since 1.2.1
*/
public interface PetRepository extends CrudRepository<Pet, String> {
}

View File

@@ -0,0 +1,185 @@
/*
* Copyright 2019 the original author or authors.
*
* 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
*
* https://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 example.app.petclinic;
import static org.assertj.core.api.Assertions.assertThat;
import java.time.LocalDateTime;
import java.util.Optional;
import java.util.Set;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.execute.Function;
import org.apache.geode.cache.execute.FunctionContext;
import org.apache.geode.cache.execute.RegionFunctionContext;
import org.apache.shiro.util.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;
import org.springframework.data.gemfire.config.annotation.CacheServerApplication;
import org.springframework.data.gemfire.config.annotation.EnableEntityDefinedRegions;
import org.springframework.data.gemfire.config.annotation.EnablePdx;
import org.springframework.data.gemfire.config.annotation.PeerCacheApplication;
import org.springframework.data.gemfire.function.annotation.GemfireFunction;
import org.springframework.data.gemfire.function.config.EnableGemfireFunctions;
import org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import example.app.petclinic.function.PetServiceFunctionExecutions;
import example.app.petclinic.model.Pet;
import example.app.petclinic.repo.PetRepository;
/**
* Smoke Tests asserting the proper injection and execution of an Apache Geode {@link Function} using Spring Data
* for Apache Geode {@link Function} annotation support in a Spring (Boot) context.
*
* @author John Blum
* @see org.junit.Test
* @see org.apache.geode.cache.execute.Function
* @see org.springframework.boot.autoconfigure.SpringBootApplication
* @see org.springframework.boot.test.context.SpringBootTest
* @see org.springframework.context.annotation.AnnotationConfigApplicationContext
* @see org.springframework.context.annotation.Bean
* @see org.springframework.context.annotation.Profile
* @see org.springframework.data.gemfire.config.annotation.CacheServerApplication
* @see org.springframework.data.gemfire.config.annotation.PeerCacheApplication
* @see org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport
* @see org.springframework.test.context.ActiveProfiles
* @see org.springframework.test.context.junit4.SpringRunner
* @see PetServiceFunctionExecutions
* @see example.app.petclinic.model.Pet
* @see example.app.petclinic.repo.PetRepository
* @since 1.2.1
*/
@ActiveProfiles("petclinic-client-function-execution")
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
@SuppressWarnings("unused")
public class PetClinicApplicationSmokeTests extends ForkingClientServerIntegrationTestsSupport {
@BeforeClass
public static void startGeodeServer() throws Exception {
startGemFireServer(GeodeServerTestConfiguration.class,
"-Dspring.profiles.active=petclinic-server-function-execution");
}
private Pet castle = Pet.newPet("Castle").as(Pet.Type.CAT);
private Pet cocoa = Pet.newPet("Cocoa").as(Pet.Type.CAT);
private Pet maha = Pet.newPet("Maha").as(Pet.Type.DOG);
private Pet mittens = Pet.newPet("Mittens").as(Pet.Type.CAT);
private Set<Pet> pets = CollectionUtils.asSet(castle, cocoa, maha, mittens);
@Autowired
private PetRepository petRepository;
@Autowired
private PetServiceFunctionExecutions petServiceFunctions;
@Before
public void setup() {
assertThat(this.petRepository.count()).isEqualTo(0);
this.pets.forEach(pet -> assertThat(pet.getVaccinationDateTime()).isNull());
this.petRepository.saveAll(this.pets);
assertThat(this.petRepository.count()).isEqualTo(this.pets.size());
}
@Test
public void administerPetVaccinationsIsSuccessful() {
LocalDateTime beforeVaccinations = LocalDateTime.now();
this.petServiceFunctions.administerPetVaccinations();
LocalDateTime afterVaccinations = LocalDateTime.now();
this.petRepository.findAll().forEach(pet -> {
assertThat(pet.getVaccinationDateTime())
.describedAs("Vaccinations [%s] for [%s] was not correct", pet.getVaccinationDateTime(), pet)
.isAfterOrEqualTo(beforeVaccinations);
assertThat(pet.getVaccinationDateTime()).isBeforeOrEqualTo(afterVaccinations);
});
}
@Profile("petclinic-client-function-execution")
@EnableEntityDefinedRegions(basePackageClasses = Pet.class)
@SpringBootApplication(scanBasePackageClasses = PetClinicApplicationSmokeTests.class)
static class GeodeClientTestConfiguration { }
@Profile("petclinic-peer-function-execution")
@PeerCacheApplication(name = "PetClinicApplicationSmokeTests")
@EnableEntityDefinedRegions(basePackageClasses = Pet.class)
@SpringBootApplication(scanBasePackageClasses = PetClinicApplicationSmokeTests.class)
static class GeodePeerTestConfiguration { }
@Profile("petclinic-server-function-execution")
@CacheServerApplication(name = "PetClinicApplicationSmokeTestsServer")
@EnableEntityDefinedRegions(basePackageClasses = Pet.class)
@EnableGemfireFunctions
@EnablePdx
static class GeodeServerTestConfiguration {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(GeodeServerTestConfiguration.class);
applicationContext.registerShutdownHook();
}
@Bean
PetServiceFunctions petServiceFunctions() {
return new PetServiceFunctions();
}
}
public static class PetServiceFunctions {
@GemfireFunction(id = "AdministerPetVaccinations", optimizeForWrite = true)
public void administerPetVaccinations(FunctionContext functionContext) {
Optional.ofNullable(functionContext)
.filter(RegionFunctionContext.class::isInstance)
.map(RegionFunctionContext.class::cast)
.map(RegionFunctionContext::getDataSet)
.map(Region::values)
.ifPresent(pets -> pets.forEach(pet -> {
Pet resolvePet = (Pet) pet;
resolvePet.vaccinate();
((RegionFunctionContext) functionContext).getDataSet().put(resolvePet.getName(), resolvePet);
}));
}
}
}

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<statusListener class="ch.qos.logback.core.status.NopStatusListener"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %5p %40.40c:%4L - %m%n</pattern>
</encoder>
</appender>
<logger name="ch.qos.logback" level="${logback.log.level:-ERROR}"/>
<logger name="org.apache" level="${logback.log.level:-ERROR}"/>
<logger name="org.springframework" level="${logback.log.level:-ERROR}"/>
<root level="${logback.log.level:-ERROR}">
<appender-ref ref="console"/>
</root>
</configuration>