Java tutorial
/* * Licensed to Laurent Broudoux (the "Author") under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. Author licenses this * file to you 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 * * http://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 io.github.microcks.service; import io.github.microcks.domain.*; import io.github.microcks.repository.*; import io.github.microcks.util.IdBuilder; import io.github.microcks.util.UsernamePasswordAuthenticator; import io.github.microcks.util.openapi.OpenAPITestRunner; import io.github.microcks.util.postman.PostmanTestStepsRunner; import io.github.microcks.util.soapui.SoapUITestStepsRunner; import io.github.microcks.util.test.AbstractTestRunner; import io.github.microcks.util.test.HttpTestRunner; import io.github.microcks.util.test.SoapHttpTestRunner; import io.github.microcks.util.test.TestReturn; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.http.HttpMethod; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.scheduling.annotation.Async; import java.io.FileOutputStream; import java.io.IOException; import java.net.Authenticator; import java.net.URISyntaxException; import java.net.URL; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; /** * @author laurent */ @org.springframework.stereotype.Service public class TestRunnerService { /** A simple logger for diagnostic messages. */ private static Logger log = LoggerFactory.getLogger(TestRunnerService.class); @Autowired private ResourceRepository resourceRepository; @Autowired private RequestRepository requestRepository; @Autowired private ResponseRepository responseRepository; @Autowired private TestResultRepository testResultRepository; @Autowired private ImportJobRepository jobRepository; @Value("${network.username}") private final String username = null; @Value("${network.password}") private final String password = null; @Value("${tests-callback.url}") private final String testsCallbackUrl = null; @Value("${postman-runner.url}") private final String postmanRunnerUrl = null; @Value("${validation.resourceUrl}") private final String validationResourceUrl = null; /** * * @param testResult TestResults to aggregate results within * @param service Service to test * @param runnerType Type of runner for launching the tests * @return A Future wrapping test results */ @Async public CompletableFuture<TestResult> launchTestsInternal(TestResult testResult, Service service, TestRunnerType runnerType) { // Found next build number for this test. List<TestResult> older = testResultRepository.findByServiceId(service.getId(), new PageRequest(0, 2, Sort.Direction.DESC, "testNumber")); if (older != null && !older.isEmpty() && older.get(0).getTestNumber() != null) { testResult.setTestNumber(older.get(0).getTestNumber() + 1L); } else { testResult.setTestNumber(1L); } for (Operation operation : service.getOperations()) { // Prepare result container for operation tests. TestCaseResult testCaseResult = new TestCaseResult(); testCaseResult.setOperationName(operation.getName()); String testCaseId = IdBuilder.buildTestCaseId(testResult, operation); testResult.getTestCaseResults().add(testCaseResult); testResultRepository.save(testResult); // Prepare collection of requests to launch. List<Request> requests = requestRepository .findByOperationId(IdBuilder.buildOperationId(service, operation)); requests = cloneRequestsForTestCase(requests, testCaseId); List<TestReturn> results = new ArrayList<TestReturn>(); AbstractTestRunner<HttpMethod> testRunner = retrieveRunner(runnerType, service.getId()); try { HttpMethod method = testRunner.buildMethod(operation.getMethod()); results = testRunner.runTest(service, operation, testResult, requests, testResult.getTestedEndpoint(), method); } catch (URISyntaxException use) { log.error("URISyntaxException on endpoint {}, aborting current tests", testResult.getTestedEndpoint(), use); // Set flags and add to results before exiting loop. testCaseResult.setSuccess(false); testCaseResult.setElapsedTime(0); testResultRepository.save(testResult); break; } catch (Throwable t) { log.error("Throwable while testing operation {}", operation.getName(), t); } // Update result if we got returns. If no returns, it means that there's no // sample request for that operation. if (results != null && !results.isEmpty()) { updateTestCaseResultWithReturns(testCaseResult, results, testCaseId); testResultRepository.save(testResult); } else { //testCaseResult.setSuccess(false); //testCaseResult.setElapsedTime(0); //testResultRepository.save(testResult); } } // Update success, progress indicators and total time before saving and returning. updateTestResult(testResult); return CompletableFuture.completedFuture(testResult); } /** * */ private void updateTestCaseResultWithReturns(TestCaseResult testCaseResult, List<TestReturn> testReturns, String testCaseId) { // Prepare a bunch of flag we're going to complete. boolean successFlag = true; long caseElapsedTime = 0; List<Response> responses = new ArrayList<Response>(); List<Request> actualRequests = new ArrayList<Request>(); for (TestReturn testReturn : testReturns) { // Deal with elapsed time and success flag. caseElapsedTime += testReturn.getElapsedTime(); TestStepResult testStepResult = testReturn.buildTestStepResult(); if (!testStepResult.isSuccess()) { successFlag = false; } // Extract, complete and store response and request. testReturn.getResponse().setTestCaseId(testCaseId); testReturn.getRequest().setTestCaseId(testCaseId); responses.add(testReturn.getResponse()); actualRequests.add(testReturn.getRequest()); testCaseResult.getTestStepResults().add(testStepResult); } // Save the responses into repository to get their ids. log.debug("Saving {} responses with testCaseId {}", responses.size(), testCaseId); responseRepository.save(responses); // Associate responses to requests before saving requests. for (int i = 0; i < actualRequests.size(); i++) { actualRequests.get(i).setResponseId(responses.get(i).getId()); } log.debug("Saving {} requests with testCaseId {}", responses.size(), testCaseId); requestRepository.save(actualRequests); // Update and save the completed TestCaseResult. // We cannot consider as success if we have no TestStepResults associated... if (testCaseResult.getTestStepResults().size() > 0) { testCaseResult.setSuccess(successFlag); } testCaseResult.setElapsedTime(caseElapsedTime); } /** * */ private void updateTestResult(TestResult testResult) { log.debug("Updating total testResult"); // Update success, progress indicators and total time before saving and returning. boolean globalSuccessFlag = true; boolean globalProgressFlag = false; long totalElapsedTime = 0; for (TestCaseResult testCaseResult : testResult.getTestCaseResults()) { totalElapsedTime += testCaseResult.getElapsedTime(); if (!testCaseResult.isSuccess()) { globalSuccessFlag = false; } // -1 is default elapsed time for testcase so its mean that still in // progress because not updated yet. if (testCaseResult.getElapsedTime() == -1) { log.debug("testCaseResult.elapsedTime is -1, set globalProgressFlag to true"); globalProgressFlag = true; } } // Update aggregated flags before saving whole testResult. testResult.setSuccess(globalSuccessFlag); testResult.setInProgress(globalProgressFlag); testResult.setElapsedTime(totalElapsedTime); testResultRepository.save(testResult); } /** Clone and prepare request for a test case usage. */ private List<Request> cloneRequestsForTestCase(List<Request> requests, String testCaseId) { List<Request> result = new ArrayList<Request>(); for (Request request : requests) { Request clone = new Request(); clone.setName(request.getName()); clone.setContent(request.getContent()); clone.setHeaders(request.getHeaders()); clone.setQueryParameters(request.getQueryParameters()); // Assign testCaseId. clone.setTestCaseId(testCaseId); result.add(clone); } return result; } /** Retrieve correct test runner according given type. */ private AbstractTestRunner<HttpMethod> retrieveRunner(TestRunnerType runnerType, String serviceId) { // TODO: remove this ugly initialization later. SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); factory.setConnectTimeout(200); factory.setReadTimeout(10000); switch (runnerType) { case SOAP_HTTP: SoapHttpTestRunner soapRunner = new SoapHttpTestRunner(); soapRunner.setClientHttpRequestFactory(factory); soapRunner.setResourceUrl(validationResourceUrl); return soapRunner; case OPEN_API_SCHEMA: OpenAPITestRunner openApiRunner = new OpenAPITestRunner(resourceRepository, responseRepository, false); openApiRunner.setClientHttpRequestFactory(factory); return openApiRunner; case SOAP_UI: // Handle local download of correct project file. List<ImportJob> jobs = jobRepository.findByServiceRefId(serviceId); if (jobs != null && !jobs.isEmpty()) { try { String projectFile = handleRemoteFileDownload(jobs.get(0).getRepositoryUrl()); SoapUITestStepsRunner soapUIRunner = new SoapUITestStepsRunner(projectFile); return soapUIRunner; } catch (IOException ioe) { log.error("IOException while downloading {}", jobs.get(0).getRepositoryUrl()); } } case POSTMAN: // Handle local download of correct project file. jobs = jobRepository.findByServiceRefId(serviceId); if (jobs != null && !jobs.isEmpty()) { try { String collectionFile = handleRemoteFileDownload(jobs.get(0).getRepositoryUrl()); PostmanTestStepsRunner postmanRunner = new PostmanTestStepsRunner(collectionFile); postmanRunner.setClientHttpRequestFactory(factory); postmanRunner.setTestsCallbackUrl(testsCallbackUrl); postmanRunner.setPostmanRunnerUrl(postmanRunnerUrl); return postmanRunner; } catch (IOException ioe) { log.error("IOException while downloading {}", jobs.get(0).getRepositoryUrl()); } } default: HttpTestRunner httpRunner = new HttpTestRunner(); httpRunner.setClientHttpRequestFactory(factory); return httpRunner; } } /** Download a remote HTTP URL into a temporary local file. */ private String handleRemoteFileDownload(String remoteUrl) throws IOException { // Build remote Url and local file. URL website = new URL(remoteUrl); String localFile = System.getProperty("java.io.tmpdir") + "/microcks-" + System.currentTimeMillis() + ".project"; // Set authenticator instance. Authenticator.setDefault(new UsernamePasswordAuthenticator(username, password)); ReadableByteChannel rbc = null; FileOutputStream fos = null; try { rbc = Channels.newChannel(website.openStream()); // Transfer file to local. fos = new FileOutputStream(localFile); fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); } finally { if (fos != null) fos.close(); if (rbc != null) rbc.close(); } return localFile; } }