rest call test (untested)

This commit is contained in:
Andreas Isler
2026-07-07 10:45:47 +02:00
parent aaef7116f8
commit c32442166c
8 changed files with 398 additions and 4 deletions
@@ -13,6 +13,7 @@ import java.util.List;
/** /**
* Reads an .xlsx workbook (created with Excel or LibreOffice) from the classpath * Reads an .xlsx workbook (created with Excel or LibreOffice) from the classpath
* and returns all cells as Strings: book > sheets > rows > cells. * and returns all cells as Strings: book > sheets > rows > cells.
*
* Restrictions per sheet: * Restrictions per sheet:
* - The column count is defined by the first row: it starts with the 1st cell * - The column count is defined by the first row: it starts with the 1st cell
* and ends at the 1st empty cell * and ends at the 1st empty cell
@@ -40,6 +40,7 @@ import java.lang.annotation.*;
@ExtendWith(TemplateExtension.class) @ExtendWith(TemplateExtension.class)
@ExtendWith(TenantSetupExtension.class) @ExtendWith(TenantSetupExtension.class)
@ExtendWith(TestMailServerExtension.class) @ExtendWith(TestMailServerExtension.class)
@ExtendWith(TestRestServerExtension.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS) @TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Transactional @Transactional
@SpringBootTest @SpringBootTest
@@ -12,6 +12,8 @@ import com.flowable.audit.api.runtime.AuditInstance;
import com.flowable.core.spring.security.SecurityUtils; import com.flowable.core.spring.security.SecurityUtils;
import com.flowable.platform.service.task.CompleteFormRepresentation; import com.flowable.platform.service.task.CompleteFormRepresentation;
import com.flowable.platform.service.task.PlatformTaskService; import com.flowable.platform.service.task.PlatformTaskService;
import com.flowable.serviceregistry.api.runtime.ServiceInvocationResultResponse;
import com.flowable.serviceregistry.api.runtime.ServiceRegistryRuntimeService;
import com.github.wnameless.json.flattener.JsonFlattener; import com.github.wnameless.json.flattener.JsonFlattener;
import com.github.wnameless.json.unflattener.JsonUnflattener; import com.github.wnameless.json.unflattener.JsonUnflattener;
import jakarta.mail.Address; import jakarta.mail.Address;
@@ -35,6 +37,8 @@ import org.junit.jupiter.params.provider.Arguments;
import org.junit.platform.commons.util.StringUtils; import org.junit.platform.commons.util.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
@@ -59,6 +63,9 @@ public class FlowableModelTestUtils {
protected final FlowableExcelMapper flowableExcelMapper; protected final FlowableExcelMapper flowableExcelMapper;
protected static final Logger logger = LoggerFactory.getLogger(FlowableModelTestUtils.class); protected static final Logger logger = LoggerFactory.getLogger(FlowableModelTestUtils.class);
protected final TestMailServer testMailServer; protected final TestMailServer testMailServer;
protected final TestRestServer testRestServer;
protected final TestRestClient testRestClient;
protected final ServiceRegistryRuntimeService serviceRegistryRuntimeService;
protected final AuditService auditService; protected final AuditService auditService;
protected final ObjectMapper objectMapper = new ObjectMapper(); protected final ObjectMapper objectMapper = new ObjectMapper();
@@ -79,6 +86,9 @@ public class FlowableModelTestUtils {
ManagementService managementService, ManagementService managementService,
FlowableExcelMapper flowableExcelMapper, FlowableExcelMapper flowableExcelMapper,
TestMailServer testMailServer, TestMailServer testMailServer,
TestRestServer testRestServer,
TestRestClient testRestClient,
ServiceRegistryRuntimeService serviceRegistryRuntimeService,
AuditService auditService) { AuditService auditService) {
this.processEngine = processEngine; this.processEngine = processEngine;
this.cmmnEngine = cmmnEngine; this.cmmnEngine = cmmnEngine;
@@ -87,6 +97,9 @@ public class FlowableModelTestUtils {
this.managementService = managementService; this.managementService = managementService;
this.flowableExcelMapper = flowableExcelMapper; this.flowableExcelMapper = flowableExcelMapper;
this.testMailServer = testMailServer; this.testMailServer = testMailServer;
this.testRestServer = testRestServer;
this.testRestClient = testRestClient;
this.serviceRegistryRuntimeService = serviceRegistryRuntimeService;
this.auditService = auditService; this.auditService = auditService;
// Enable ObjectMapper for handling Instant as string // Enable ObjectMapper for handling Instant as string
objectMapper.registerModule(new JavaTimeModule()); objectMapper.registerModule(new JavaTimeModule());
@@ -157,6 +170,25 @@ public class FlowableModelTestUtils {
.containsExactlyInAnyOrder(receivers.split("[,\\s]+")); .containsExactlyInAnyOrder(receivers.split("[,\\s]+"));
} }
public void checkAndAssertRestRequest(Map<String, Object> map, int restNumber, String hint, String method, String path) {
if (map != null) {
Object check = map.get("rest");
if (check instanceof Integer && (Integer) check > 0) {
assertRestRequest(restNumber, hint, method, path);
}
}
}
protected void assertRestRequest(int restNumber, String hint, String method, String path) {
if (restNumber <= 0) return;
List<RestRequestDto> restRequests = getRestRequestList();
Assertions.assertThat(restRequests.size()).as("%s: invalid rest request number %s", hint, restNumber).isGreaterThanOrEqualTo(restNumber);
RestRequestDto restRequest = restRequests.get(restNumber - 1);
Assertions.assertThat(restRequest.method()).as("%s: invalid method %s", hint, restRequest.method()).isEqualTo(method);
Assertions.assertThat(restRequest.path()).as("%s: invalid path %s", hint, restRequest.path()).isEqualTo(path);
}
public Map<String, Object> getHistoryCasePayload(String caseInstanceId) { public Map<String, Object> getHistoryCasePayload(String caseInstanceId) {
List<HistoricVariableInstance> historicVariableInstanceList = cmmnEngine.getCmmnHistoryService() List<HistoricVariableInstance> historicVariableInstanceList = cmmnEngine.getCmmnHistoryService()
.createHistoricVariableInstanceQuery() .createHistoricVariableInstanceQuery()
@@ -187,6 +219,15 @@ public class FlowableModelTestUtils {
.start(); .start();
} }
public CaseInstance getCaseInstance(String caseDefinitionKey) {
CaseInstance caseInstance = cmmnEngine.getCmmnRuntimeService()
.createCaseInstanceQuery()
.caseDefinitionKey(caseDefinitionKey)
.singleResult();
Assertions.assertThat(caseInstance).as("No case instance with definition key %s found", caseDefinitionKey).isNotNull();
return caseInstance;
}
public Task getOpenTask(String taskKey) { public Task getOpenTask(String taskKey) {
Task task = taskService.createTaskQuery().taskDefinitionKey(taskKey).singleResult(); Task task = taskService.createTaskQuery().taskDefinitionKey(taskKey).singleResult();
Assertions.assertThat(task).as("No open task with key %s found", taskKey).isNotNull(); Assertions.assertThat(task).as("No open task with key %s found", taskKey).isNotNull();
@@ -221,6 +262,31 @@ public class FlowableModelTestUtils {
}).collect(Collectors.toList()); }).collect(Collectors.toList());
} }
public List<RestRequestDto> getRestRequestList() {
return testRestServer.getRequests();
}
public void stubRestResponse(String method, String path, int status, String body) {
testRestServer.stubResponse(method, path, status, body);
}
public ResponseEntity<String> sendRestEvent(String channelKey, ObjectNode payload) {
return testRestClient.sendChannelEvent(channelKey, payload);
}
public ResponseEntity<String> sendRestCall(HttpMethod method, String path, Object body) {
return testRestClient.exchange(method, path, body);
}
public ServiceInvocationResultResponse invokeRestService(String serviceKey, String operationKey, Map<String, Object> serviceData) {
return serviceRegistryRuntimeService.createServiceInvocationBuilder()
.serviceKey(serviceKey)
.operationKey(operationKey)
.serviceData(serviceData)
.tenantId(TENANT_ID) // Must not be "default"
.invoke();
}
public boolean isSubset(JsonNode root, JsonNode test) { public boolean isSubset(JsonNode root, JsonNode test) {
// If test is null, it is always a subset of root // If test is null, it is always a subset of root
if (test == null || test.isNull()) { if (test == null || test.isNull()) {
@@ -326,9 +392,12 @@ public class FlowableModelTestUtils {
Integer emailCount = null; Integer emailCount = null;
JsonNode emailNode = row.get("email"); JsonNode emailNode = row.get("email");
if (emailNode != null) emailCount = emailNode.asInt(); if (emailNode != null) emailCount = emailNode.asInt();
Integer restCount = null;
JsonNode restNode = row.get("rest");
if (restNode != null) restCount = restNode.asInt();
logger.info("TestExcelRow file={} id={} rootParam={} inParam={} outParam={} timer={} test={} audit={} email={} ", logger.info("TestExcelRow file={} id={} rootParam={} inParam={} outParam={} timer={} test={} audit={} email={} rest={} ",
path, idParam, rootParam, inParam, outParam, timerCount, test, auditRecordCount, emailCount); path, idParam, rootParam, inParam, outParam, timerCount, test, auditRecordCount, emailCount, restCount);
Assertions.assertThat(idParam).as("Column 'id' missing in row of %s", path).isNotNull(); Assertions.assertThat(idParam).as("Column 'id' missing in row of %s", path).isNotNull();
ObjectNode processIds = createRootTestProcessInstance(idParam.asText(), convertJsonNodeToMap(vars)); ObjectNode processIds = createRootTestProcessInstance(idParam.asText(), convertJsonNodeToMap(vars));
for (int i = 0; i < timerCount; i++) { for (int i = 0; i < timerCount; i++) {
@@ -353,6 +422,11 @@ public class FlowableModelTestUtils {
Assertions.assertThat(emailList.size()).as("Invalid number of emails: %s", emailList.size()).isEqualTo(emailCount); Assertions.assertThat(emailList.size()).as("Invalid number of emails: %s", emailList.size()).isEqualTo(emailCount);
result.set("emails", objectMapper.valueToTree(Collections.singletonList(emailList))); result.set("emails", objectMapper.valueToTree(Collections.singletonList(emailList)));
} }
if (restCount != null) {
List<RestRequestDto> restRequests = getRestRequestList();
Assertions.assertThat(restRequests.size()).as("Invalid number of rest requests: %s", restRequests.size()).isEqualTo(restCount);
result.set("restRequests", objectMapper.valueToTree(Collections.singletonList(restRequests)));
}
return result; return result;
} }
@@ -381,9 +455,12 @@ public class FlowableModelTestUtils {
Integer emailCount = null; Integer emailCount = null;
Object emailNode = row.get("email"); Object emailNode = row.get("email");
if (emailNode != null) emailCount = (Integer) emailNode; if (emailNode != null) emailCount = (Integer) emailNode;
Integer restCount = null;
Object restNode = row.get("rest");
if (restNode != null) restCount = (Integer) restNode;
logger.info("TestExcelRow file={} id={} rootParam={} inParam={} outParam={} timer={} test={} audit={} email={} ", logger.info("TestExcelRow file={} id={} rootParam={} inParam={} outParam={} timer={} test={} audit={} email={} rest={} ",
path, idParam, rootParam, inParam, outParam, timerCount, test, auditRecordCount, emailCount); path, idParam, rootParam, inParam, outParam, timerCount, test, auditRecordCount, emailCount, restCount);
Assertions.assertThat(idParam).as("Column 'id' missing in row of %s", path).isNotNull(); Assertions.assertThat(idParam).as("Column 'id' missing in row of %s", path).isNotNull();
ObjectNode processIds = createRootTestProcessInstance(idParam, vars); ObjectNode processIds = createRootTestProcessInstance(idParam, vars);
for (int i = 0; i < timerCount; i++) { for (int i = 0; i < timerCount; i++) {
@@ -408,6 +485,11 @@ public class FlowableModelTestUtils {
Assertions.assertThat(emailList.size()).as("Invalid number of emails: %s", emailList.size()).isEqualTo(emailCount); Assertions.assertThat(emailList.size()).as("Invalid number of emails: %s", emailList.size()).isEqualTo(emailCount);
result.put("email", emailCount); result.put("email", emailCount);
} }
if (restCount != null) {
List<RestRequestDto> restRequests = getRestRequestList();
Assertions.assertThat(restRequests.size()).as("Invalid number of rest requests: %s", restRequests.size()).isEqualTo(restCount);
result.put("rest", restCount);
}
return result; return result;
} }
@@ -0,0 +1,7 @@
package com.customer.work.model;
import java.util.List;
import java.util.Map;
public record RestRequestDto(String method, String path, String query, Map<String, List<String>> headers, String body) {
}
@@ -0,0 +1,129 @@
package com.customer.work.model;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.Filter;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Test client simulating incoming REST calls which are processed in Flowable models. The
* requests are executed against the real Flowable REST API (including the security filter
* chain) but in-process via MockMvc, so they participate in the test transaction. Base URL
* and basic auth are taken from the application configuration (see 'test.rest.in.*').
*
* The Flowable REST APIs are served by their own dispatcher servlets (/platform-api/*,
* /process-api/*, ...) which are never initialized in the mock web environment. Requests
* are therefore routed by path prefix to the matching servlet registration, whose child
* application context is refreshed on first use.
*/
@Component
public class TestRestClient {
private final WebApplicationContext webApplicationContext;
private final Filter springSecurityFilterChain;
private final String baseUrl;
private final String basicAuthHeader;
private final Map<String, MockMvc> mockMvcByServletPath = new ConcurrentHashMap<>();
private final ObjectMapper objectMapper = new ObjectMapper();
public TestRestClient(WebApplicationContext webApplicationContext,
@Qualifier("springSecurityFilterChain") Filter springSecurityFilterChain,
@Value("${test.rest.in.base-url}") String baseUrl,
@Value("${test.rest.in.username}") String username,
@Value("${test.rest.in.password}") String password) {
this.webApplicationContext = webApplicationContext;
this.springSecurityFilterChain = springSecurityFilterChain;
this.baseUrl = baseUrl;
this.basicAuthHeader = "Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8));
}
public ResponseEntity<String> get(String path) {
return exchange(HttpMethod.GET, path, null);
}
public ResponseEntity<String> post(String path, Object body) {
return exchange(HttpMethod.POST, path, body);
}
public ResponseEntity<String> exchange(HttpMethod method, String path, Object body) {
try {
String servletPath = findServletPath(path);
MockMvc mockMvc = mockMvcByServletPath.computeIfAbsent(servletPath, this::createMockMvc);
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.request(method, URI.create(baseUrl + path))
.servletPath(servletPath)
.header(HttpHeaders.AUTHORIZATION, basicAuthHeader);
if (body != null) {
builder.contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsBytes(body));
}
MvcResult result = mockMvc.perform(builder).andReturn();
HttpHeaders headers = new HttpHeaders();
for (String name : result.getResponse().getHeaderNames()) {
headers.addAll(name, result.getResponse().getHeaders(name));
}
return ResponseEntity.status(result.getResponse().getStatus())
.headers(headers)
.body(new String(result.getResponse().getContentAsByteArray(), StandardCharsets.UTF_8));
} catch (Exception e) {
throw new RuntimeException("Cannot execute " + method + " " + path, e);
}
}
public ResponseEntity<String> sendChannelEvent(String channelKey, Object payload) {
return post("/platform-api/channel-definitions/key/" + channelKey + "/events", payload);
}
protected String findServletPath(String path) {
for (ServletRegistrationBean<?> registration : webApplicationContext.getBeansOfType(ServletRegistrationBean.class).values()) {
for (String urlMapping : registration.getUrlMappings()) {
if (urlMapping.endsWith("/*") && path.startsWith(urlMapping.substring(0, urlMapping.length() - 1))) {
return urlMapping.substring(0, urlMapping.length() - 2);
}
}
}
return "";
}
protected MockMvc createMockMvc(String servletPath) {
WebApplicationContext context = webApplicationContext;
if (!servletPath.isEmpty()) {
context = findServletContext(servletPath);
}
return MockMvcBuilders.webAppContextSetup(context)
.addFilters(springSecurityFilterChain)
.build();
}
protected WebApplicationContext findServletContext(String servletPath) {
for (ServletRegistrationBean<?> registration : webApplicationContext.getBeansOfType(ServletRegistrationBean.class).values()) {
if (registration.getUrlMappings().contains(servletPath + "/*")
&& registration.getServlet() instanceof DispatcherServlet dispatcherServlet) {
WebApplicationContext servletContext = dispatcherServlet.getWebApplicationContext();
if (servletContext instanceof ConfigurableWebApplicationContext configurableContext && !configurableContext.isActive()) {
configurableContext.setServletContext(webApplicationContext.getServletContext());
configurableContext.refresh();
}
return servletContext;
}
}
throw new IllegalStateException("No dispatcher servlet registered for " + servletPath);
}
}
@@ -0,0 +1,119 @@
package com.customer.work.model;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpServer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Test server receiving outgoing REST calls sent from Flowable models. The models resolve
* their base URL from the application configuration (see 'test.rest.out.base-url'), so all
* outgoing calls end up here, get recorded and are answered with stubbed responses.
* Requests must carry basic auth matching the configured credentials, otherwise 401 is returned.
*/
@Component
public class TestRestServer {
private HttpServer server;
private final List<RestRequestDto> requests = new CopyOnWriteArrayList<>();
private final List<StubbedResponse> stubs = new CopyOnWriteArrayList<>();
private final String baseUrl;
private final String username;
private final String password;
public TestRestServer(@Value("${test.rest.out.base-url}") String baseUrl,
@Value("${test.rest.out.username}") String username,
@Value("${test.rest.out.password}") String password) {
this.baseUrl = baseUrl;
this.username = username;
this.password = password;
}
public void setup() {
try {
URI uri = URI.create(baseUrl);
server = HttpServer.create(new InetSocketAddress(uri.getHost(), uri.getPort()), 0);
server.createContext("/", this::handle);
server.start();
} catch (IOException e) {
throw new UncheckedIOException("Cannot start TestRestServer on " + baseUrl, e);
}
}
public void stop() {
if (server != null) {
server.stop(0);
server = null;
}
requests.clear();
stubs.clear();
}
public void stubResponse(String method, String path, int status, String body) {
stubs.add(new StubbedResponse(method, path, status, body));
}
public List<RestRequestDto> getRequests() {
if (server == null) {
throw new IllegalStateException("TestRestServer is not started, is the TestRestServerExtension registered?");
}
return new ArrayList<>(requests);
}
protected void handle(HttpExchange exchange) throws IOException {
try {
String method = exchange.getRequestMethod();
URI uri = exchange.getRequestURI();
String body = new String(exchange.getRequestBody().readAllBytes(), StandardCharsets.UTF_8);
requests.add(new RestRequestDto(method, uri.getPath(), uri.getQuery(), new LinkedHashMap<>(exchange.getRequestHeaders()), body));
if (!isAuthorized(exchange)) {
send(exchange, 401, "{\"error\":\"unauthorized\"}");
return;
}
StubbedResponse stub = findStub(method, uri.getPath());
if (stub != null) {
send(exchange, stub.status(), stub.body());
} else {
send(exchange, 200, "{}");
}
} finally {
exchange.close();
}
}
protected boolean isAuthorized(HttpExchange exchange) {
if (username == null || username.isEmpty()) {
return true;
}
String expected = "Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8));
return expected.equals(exchange.getRequestHeaders().getFirst("Authorization"));
}
protected StubbedResponse findStub(String method, String path) {
return stubs.stream()
.filter(stub -> stub.method().equalsIgnoreCase(method) && path.startsWith(stub.path()))
.findFirst()
.orElse(null);
}
private void send(HttpExchange exchange, int status, String body) throws IOException {
byte[] bytes = body.getBytes(StandardCharsets.UTF_8);
exchange.getResponseHeaders().set("Content-Type", "application/json");
exchange.sendResponseHeaders(status, bytes.length);
exchange.getResponseBody().write(bytes);
}
public record StubbedResponse(String method, String path, int status, String body) {}
}
@@ -0,0 +1,23 @@
package com.customer.work.model;
import org.jspecify.annotations.NonNull;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.springframework.test.context.junit.jupiter.SpringExtension;
public class TestRestServerExtension implements BeforeEachCallback, AfterEachCallback {
@Override
public void beforeEach(@NonNull ExtensionContext context) {
getTestRestServer(context).setup();
}
@Override
public void afterEach(@NonNull ExtensionContext context) {
getTestRestServer(context).stop();
}
protected TestRestServer getTestRestServer(ExtensionContext context) {
return SpringExtension.getApplicationContext(context).getBean(TestRestServer.class);
}
}
@@ -3,8 +3,10 @@ package com.customer.work.model.test;
import com.customer.work.model.EmailDto; import com.customer.work.model.EmailDto;
import com.customer.work.model.FlowableModelTest; import com.customer.work.model.FlowableModelTest;
import com.customer.work.model.FlowableModelTestUtils; import com.customer.work.model.FlowableModelTestUtils;
import com.customer.work.model.RestRequestDto;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import com.flowable.serviceregistry.api.runtime.ServiceInvocationResultResponse;
import org.assertj.core.api.Assertions; import org.assertj.core.api.Assertions;
import org.flowable.cmmn.api.runtime.CaseInstance; import org.flowable.cmmn.api.runtime.CaseInstance;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
@@ -13,6 +15,7 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -76,6 +79,35 @@ public class ModelTest {
.hasMessageContaining("Implicit parameter 'param'"); .hasMessageContaining("Implicit parameter 'param'");
} }
@Test
public void c002RestInboundTest() {
// Simulate an incoming REST call to the inbound channel TST_CH001, processed by case TST_C002
ObjectNode payload = flowableModelTest.emptyNode();
payload.put("param", "my event payload");
ResponseEntity<String> response = flowableModelTest.sendRestEvent("TST_CH001", payload);
Assertions.assertThat(response.getStatusCode().is2xxSuccessful()).as("response: %s", response.getBody()).isTrue();
CaseInstance caseInstance = flowableModelTest.getCaseInstance("TST_C002");
Map<String, Object> casePayload = flowableModelTest.getRuntimeCasePayload(caseInstance.getId());
// param is mapped as full payload, so it contains the complete event body
Assertions.assertThat(String.valueOf(casePayload.get("param"))).contains("my event payload");
}
@Test
public void sv001RestOutgoingTest() {
// The outgoing REST call from service model TST_SV001 is answered by the test REST server
flowableModelTest.stubRestResponse("GET", "/idm-api/current-user", 200,
"{\"id\":\"admin\",\"firstName\":\"Test\",\"lastName\":\"Administrator\"}");
ServiceInvocationResultResponse result = flowableModelTest.invokeRestService("TST_SV001", "getCurrentUser", Map.of());
Assertions.assertThat(result.getValue("id")).isEqualTo("admin");
Assertions.assertThat(result.getValue("lastName")).isEqualTo("Administrator");
List<RestRequestDto> restRequests = flowableModelTest.getRestRequestList();
Assertions.assertThat(restRequests).hasSize(1);
Assertions.assertThat(restRequests.get(0).method()).isEqualTo("GET");
Assertions.assertThat(restRequests.get(0).path()).isEqualTo("/idm-api/current-user");
}
private Stream<Arguments> p002TestData() { private Stream<Arguments> p002TestData() {
return Stream.of( return Stream.of(
Arguments.of("model/test/P002/boolean1.json", true, false), Arguments.of("model/test/P002/boolean1.json", true, false),