diff --git a/src/main/java/com/marklogic/appdeployer/command/CommandContext.java b/src/main/java/com/marklogic/appdeployer/command/CommandContext.java index 0934bea3..95b44dfa 100644 --- a/src/main/java/com/marklogic/appdeployer/command/CommandContext.java +++ b/src/main/java/com/marklogic/appdeployer/command/CommandContext.java @@ -120,6 +120,11 @@ public Map getContextMap() { return contextMap; } + /** + * @param contextMap + * @deprecated since 4.6.0, will be removed in 5.0.0; the contextMap is not intended to be replaced. + */ + @Deprecated public void setContextMap(Map contextMap) { this.contextMap = contextMap; } diff --git a/src/main/java/com/marklogic/appdeployer/command/TestConnectionsCommand.java b/src/main/java/com/marklogic/appdeployer/command/TestConnectionsCommand.java new file mode 100644 index 00000000..b8c297ba --- /dev/null +++ b/src/main/java/com/marklogic/appdeployer/command/TestConnectionsCommand.java @@ -0,0 +1,328 @@ +package com.marklogic.appdeployer.command; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.marklogic.appdeployer.AppConfig; +import com.marklogic.client.DatabaseClient; +import com.marklogic.client.ext.SecurityContextType; +import com.marklogic.mgmt.ManageClient; +import com.marklogic.mgmt.admin.AdminManager; +import com.marklogic.mgmt.cma.ConfigurationManager; +import com.marklogic.mgmt.resource.clusters.ClusterManager; +import com.marklogic.rest.util.RestConfig; +import org.springframework.web.client.DefaultResponseErrorHandler; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.ResponseErrorHandler; + +import javax.net.ssl.SSLContext; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; +import java.util.stream.Stream; + +/** + * Command for testing each of the connections that can be made to MarkLogic based on the configuration in a + * {@code CommandContext}. + * + * @since 4.6.0 + */ +public class TestConnectionsCommand extends AbstractCommand { + + /** + * If run in a deployment process, this should run immediately so as to fail fast. + */ + public TestConnectionsCommand() { + setExecuteSortOrder(0); + } + + /** + * Can be included in a deployment process so that the deployment fails if any of the connections fail. + * + * @param context + */ + @Override + public void execute(CommandContext context) { + TestResults results = testConnections(context); + if (results.anyTestFailed()) { + throw new RuntimeException(results.toString()); + } + logger.info(results.toString()); + } + + /** + * Intended for execution outside a deployment process, where the client wants access to the test results and + * will choose how to present those to a user. + * + * @param context + * @return + */ + public TestResults testConnections(CommandContext context) { + try { + TestResult manageResult = testManageAppServer(context.getManageClient()); + TestResult adminResult = testAdminAppServer(context.getAdminManager()); + + TestResult appServicesResult = null; + TestResult restResult = null; + TestResult testRestResult = null; + if (manageResult.isSucceeded()) { + List serverPorts = getAppServerPorts(context.getManageClient()); + appServicesResult = testAppServicesAppServer(context.getAppConfig(), serverPorts); + restResult = testRestAppServer(context.getAppConfig(), serverPorts); + testRestResult = testTestRestAppServer(context.getAppConfig(), serverPorts); + } + + return new TestResults(manageResult, adminResult, appServicesResult, restResult, testRestResult); + } catch (Exception ex) { + // We don't expect any exceptions above, as each connection test has its own try/catch block. + // This is simply to pretty up the error a bit. + throw new RuntimeException("Unable to test connections; cause: " + ex.getMessage(), ex); + } + } + + private List getAppServerPorts(ManageClient manageClient) { + JsonNode json = new ConfigurationManager(manageClient).getResourcesAsJson("server").getBody(); + ArrayNode servers = (ArrayNode) json.get("config").get(0).get("server"); + List ports = new ArrayList<>(); + servers.forEach(server -> { + if (server.has("port")) { + ports.add(server.get("port").asInt()); + } + }); + return ports; + } + + private TestResult testAppServicesAppServer(AppConfig appConfig, List serverPorts) { + if (appConfig.getAppServicesPort() != null && serverPorts.contains(appConfig.getAppServicesPort())) { + return testWithDatabaseClient(appConfig.getHost(), appConfig.getAppServicesPort(), + appConfig.getAppServicesSslContext(), appConfig.getAppServicesSecurityContextType(), + appConfig.getAppServicesUsername(), () -> appConfig.newAppServicesDatabaseClient(null)); + } + return null; + } + + private TestResult testRestAppServer(AppConfig appConfig, List serverPorts) { + if (appConfig.getRestPort() != null && serverPorts.contains(appConfig.getRestPort())) { + return testWithDatabaseClient(appConfig.getHost(), appConfig.getRestPort(), + appConfig.getRestSslContext(), appConfig.getRestSecurityContextType(), + appConfig.getRestAdminUsername(), appConfig::newDatabaseClient); + } + return null; + } + + private TestResult testTestRestAppServer(AppConfig appConfig, List serverPorts) { + if (appConfig.getTestRestPort() != null && serverPorts.contains(appConfig.getTestRestPort())) { + return testWithDatabaseClient(appConfig.getHost(), appConfig.getTestRestPort(), + appConfig.getRestSslContext(), appConfig.getRestSecurityContextType(), + appConfig.getRestAdminUsername(), appConfig::newTestDatabaseClient); + } + return null; + } + + public static class TestResults { + private TestResult manageTestResult; + private TestResult adminTestResult; + private TestResult appServicesTestResult; + private TestResult restServerTestResult; + private TestResult testRestServerTestResult; + + public TestResults(TestResult manageTestResult, TestResult adminTestResult, + TestResult appServicesTestResult, TestResult restServerTestResult, TestResult testRestServerTestResult) { + this.manageTestResult = manageTestResult; + this.adminTestResult = adminTestResult; + this.appServicesTestResult = appServicesTestResult; + this.restServerTestResult = restServerTestResult; + this.testRestServerTestResult = testRestServerTestResult; + } + + public boolean anyTestFailed() { + return Stream.of(manageTestResult, adminTestResult, appServicesTestResult, restServerTestResult, testRestServerTestResult) + .anyMatch(test -> test != null && !test.isSucceeded()); + } + + public TestResult getManageTestResult() { + return manageTestResult; + } + + public TestResult getAdminTestResult() { + return adminTestResult; + } + + public TestResult getAppServicesTestResult() { + return appServicesTestResult; + } + + public TestResult getRestServerTestResult() { + return restServerTestResult; + } + + public TestResult getTestRestServerTestResult() { + return testRestServerTestResult; + } + + /** + * @return a multi-line summary of all the non-null test results. This is intended to provide a simple + * rendering of the test result data, suitable for use in ml-gradle. + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Manage App Server\n").append(getManageTestResult()) + .append("\n\nAdmin App Server\n").append(getAdminTestResult()); + if (getManageTestResult().isSucceeded()) { + if (getAppServicesTestResult() != null) { + sb.append("\n\nApp-Services App Server\n").append(getAppServicesTestResult()); + } else { + sb.append("\n\nNo test run for the App-Services App Server as either a port is not configured for it or it has not been deployed yet"); + } + if (getRestServerTestResult() != null) { + sb.append("\n\nREST API App Server\n").append(getRestServerTestResult()); + } else { + sb.append("\n\nNo test run for a REST API App Server as either a port is not configured for it or it has not been deployed yet."); + } + if (getTestRestServerTestResult() != null) { + sb.append("\n\nTest REST API App Server\n").append(getTestRestServerTestResult()); + } else { + sb.append("\n\nNo test run for a Test REST API App Server as either a port is not configured for it or it has not been deployed yet."); + } + } else { + sb.append("\n\nCould not test connections against the App-Services or REST API App Servers " + + "due to the Manage App Server connection failing."); + } + return sb.toString(); + } + } + + public static class TestResult { + private String host; + private int port; + private String scheme; + private String authType; + private String username; + private boolean succeeded; + private String message; + + public TestResult(RestConfig restConfig, boolean succeeded, String message) { + this(restConfig.getHost(), restConfig.getPort(), restConfig.getScheme(), restConfig.getAuthType(), + restConfig.getUsername(), succeeded, message); + } + + public TestResult(RestConfig restConfig, Exception ex) { + this(restConfig, false, ex.getMessage()); + } + + public TestResult(String host, int port, String scheme, String authType, String username, DatabaseClient.ConnectionResult result) { + this.host = host; + this.port = port; + this.scheme = scheme; + this.authType = authType; + this.username = username; + this.succeeded = result.isConnected(); + if (!result.isConnected()) { + this.message = String.format("Received %d: %s", result.getStatusCode(), result.getErrorMessage()); + } + } + + public TestResult(String host, int port, String scheme, String authType, String username, boolean succeeded, String message) { + this.host = host; + this.port = port; + this.scheme = scheme; + this.authType = authType; + this.username = username; + this.succeeded = succeeded; + this.message = message; + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + public String getScheme() { + return scheme; + } + + public boolean isSucceeded() { + return succeeded; + } + + public String getMessage() { + return message; + } + + public String getAuthType() { + return authType; + } + + public String getUsername() { + return username; + } + + /** + * @return a multi-line representation of the test result. This is intended to provide a simple + * rendering of the test result data, suitable for use in ml-gradle. + */ + @Override + public String toString() { + String result = String.format("Configured to connect to %s://%s:%d using '%s' authentication", + getScheme(), getHost(), getPort(), getAuthType()); + if (getUsername() != null) { + result += String.format(" and username of '%s'", getUsername()); + } + if (isSucceeded()) { + result += "\nConnected successfully"; + return getMessage() != null ? result + "; " + getMessage() : result; + } + return result + "\nFAILED TO CONNECT; cause: " + message; + } + } + + private TestResult testManageAppServer(ManageClient client) { + ResponseErrorHandler originalErrorHandler = client.getRestTemplate().getErrorHandler(); + client.getRestTemplate().setErrorHandler(new DefaultResponseErrorHandler()); + try { + String version = new ClusterManager(client).getVersion(); + return new TestResult(client.getManageConfig(), true, "MarkLogic version: " + version); + } catch (Exception ex) { + if (ex instanceof HttpClientErrorException && ((HttpClientErrorException) ex).getRawStatusCode() == 404) { + return new TestResult(client.getManageConfig(), false, + "Unable to access /manage/v2; received 404; unexpected response: " + ex.getMessage()); + } else { + return new TestResult(client.getManageConfig(), ex); + } + } finally { + client.getRestTemplate().setErrorHandler(originalErrorHandler); + } + } + + private TestResult testAdminAppServer(AdminManager adminManager) { + try { + String timestamp = adminManager.getServerTimestamp(); + return new TestResult(adminManager.getAdminConfig(), true, "MarkLogic server timestamp: " + timestamp); + } catch (Exception ex) { + return new TestResult(adminManager.getAdminConfig(), ex); + } + } + + private TestResult testWithDatabaseClient(String host, Integer port, SSLContext sslContext, + SecurityContextType securityContextType, String username, Supplier supplier) { + if (port == null) { + return null; + } + final String scheme = sslContext != null ? "https" : "http"; + final String authType = securityContextType != null ? securityContextType.name().toLowerCase() : "unknown"; + DatabaseClient client = null; + try { + client = supplier.get(); + return new TestResult(host, port, scheme, authType, username, client.checkConnection()); + } catch (Exception ex) { + return new TestResult(host, port, scheme, authType, username, false, ex.getMessage()); + } finally { + if (client != null) { + client.release(); + } + } + } +} diff --git a/src/main/java/com/marklogic/mgmt/admin/AdminManager.java b/src/main/java/com/marklogic/mgmt/admin/AdminManager.java index 6c2ebe8d..570f4ad2 100644 --- a/src/main/java/com/marklogic/mgmt/admin/AdminManager.java +++ b/src/main/java/com/marklogic/mgmt/admin/AdminManager.java @@ -236,6 +236,15 @@ public String getServerVersion() { return getServerConfig().getElementValue("/m:host/m:version"); } + /** + * + * @return + * @since 4.6.0 + */ + public String getServerTimestamp() { + return getServerConfig().getElementValue("/m:host/m:timestamp"); + } + public void setWaitForRestartCheckInterval(int waitForRestartCheckInterval) { this.waitForRestartCheckInterval = waitForRestartCheckInterval; } diff --git a/src/main/java/com/marklogic/mgmt/cma/ConfigurationManager.java b/src/main/java/com/marklogic/mgmt/cma/ConfigurationManager.java index 4017cb2b..62429dc7 100644 --- a/src/main/java/com/marklogic/mgmt/cma/ConfigurationManager.java +++ b/src/main/java/com/marklogic/mgmt/cma/ConfigurationManager.java @@ -15,17 +15,20 @@ */ package com.marklogic.mgmt.cma; +import com.fasterxml.jackson.databind.JsonNode; import com.marklogic.mgmt.AbstractManager; import com.marklogic.mgmt.ManageClient; import com.marklogic.mgmt.SaveReceipt; import com.marklogic.rest.util.MgmtResponseErrorHandler; +import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.util.UriComponentsBuilder; /** * This doesn't extend AbstractResourceManager because a configuration isn't really a resource, it's a collection of * resources. - * + *

* Currently only supports JSON and XML configuration payloads. Not clear yet from the docs on what the format of a * zip should be. The docs also mention a bunch of request parameters, but the examples don't show what the purpose * of those are, so those aren't supported yet either. @@ -48,6 +51,7 @@ protected boolean useSecurityUser() { /** * Returns true if the CMA endpoint exists. This temporarily disables logging in MgmtResponseErrorHandler so that * a client doesn't see the 404 error being logged, which could be mistakenly perceived as a real error. + * * @return */ public boolean endpointExists() { @@ -96,4 +100,19 @@ public SaveReceipt submit(String payload) { return new SaveReceipt(null, payload, PATH, response); } + /** + * @param resourceType + * @return a JSON response containing details on each resource of the given type + * @since 4.6.0 + */ + public ResponseEntity getResourcesAsJson(String resourceType) { + String uri = UriComponentsBuilder + .fromUri(manageClient.buildUri("/manage/v3")) + .queryParam("format", "json") + .queryParam("resource-type", resourceType) + .encode() + .toUriString(); + + return manageClient.getRestTemplate().exchange(uri, HttpMethod.GET, null, JsonNode.class); + } } diff --git a/src/main/java/com/marklogic/mgmt/resource/forests/ForestManager.java b/src/main/java/com/marklogic/mgmt/resource/forests/ForestManager.java index c957ac0a..b05f79c9 100644 --- a/src/main/java/com/marklogic/mgmt/resource/forests/ForestManager.java +++ b/src/main/java/com/marklogic/mgmt/resource/forests/ForestManager.java @@ -20,6 +20,7 @@ import com.marklogic.mgmt.ManageClient; import com.marklogic.mgmt.api.API; import com.marklogic.mgmt.api.forest.Forest; +import com.marklogic.mgmt.cma.ConfigurationManager; import com.marklogic.mgmt.mapper.DefaultResourceMapper; import com.marklogic.mgmt.mapper.ResourceMapper; import com.marklogic.mgmt.resource.AbstractResourceManager; @@ -67,11 +68,8 @@ public ForestManager(ManageClient client) { * @since 4.5.3 */ public Map> getMapOfPrimaryForests() { - String uri = UriComponentsBuilder.fromUri(getManageClient().buildUri("/manage/v3")) - .queryParam("format", "json").queryParam("resource-type", "forest") - .encode().toUriString(); + JsonNode json = new ConfigurationManager(getManageClient()).getResourcesAsJson("forest").getBody(); - JsonNode json = getManageClient().getRestTemplate().exchange(uri, HttpMethod.GET, null, JsonNode.class).getBody(); // Config is an array of objects, and it will have a single object based on our request. ArrayNode allPrimaryForests = (ArrayNode) json.get("config").get(0).get("forest"); ResourceMapper mapper = new DefaultResourceMapper(new API(getManageClient())); diff --git a/src/test/java/com/marklogic/appdeployer/AbstractAppDeployerTest.java b/src/test/java/com/marklogic/appdeployer/AbstractAppDeployerTest.java index 74674978..5da4508e 100644 --- a/src/test/java/com/marklogic/appdeployer/AbstractAppDeployerTest.java +++ b/src/test/java/com/marklogic/appdeployer/AbstractAppDeployerTest.java @@ -15,12 +15,17 @@ */ package com.marklogic.appdeployer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.marklogic.appdeployer.command.Command; +import com.marklogic.appdeployer.command.CommandContext; import com.marklogic.appdeployer.command.modules.DefaultModulesLoaderFactory; import com.marklogic.appdeployer.command.modules.LoadModulesCommand; +import com.marklogic.appdeployer.command.security.GenerateTemporaryCertificateCommand; import com.marklogic.appdeployer.impl.SimpleAppDeployer; import com.marklogic.client.ext.modulesloader.impl.DefaultModulesLoader; import com.marklogic.mgmt.AbstractMgmtTest; +import com.marklogic.mgmt.resource.appservers.ServerManager; import com.marklogic.xcc.template.XccTemplate; import org.junit.jupiter.api.BeforeEach; @@ -108,4 +113,35 @@ protected LoadModulesCommand buildLoadModulesCommand() { protected void setConfigBaseDir(String path) { appConfig.getFirstConfigDir().setBaseDir(new File("src/test/resources/" + path)); } + + /** + * Intended to simplify testing app servers that require SSL. + */ + protected final void configureRestServersToRequireSSL() { + GenerateTemporaryCertificateCommand gtcc = new GenerateTemporaryCertificateCommand(); + gtcc.setTemplateIdOrName("sample-app-template"); + gtcc.execute(new CommandContext(appConfig, manageClient, adminManager)); + + ObjectNode payload = new ObjectMapper().createObjectNode() + .put("server-name", SAMPLE_APP_NAME) + .put("group-name", "Default") + .put("ssl-certificate-template", "sample-app-template"); + + ServerManager mgr = new ServerManager(manageClient); + mgr.save(payload.toString()); + payload.put("server-name", SAMPLE_APP_NAME + "-test"); + mgr.save(payload.toString()); + } + + protected final void configureRestServersToNotRequireSSL() { + ObjectNode payload = new ObjectMapper().createObjectNode() + .put("server-name", SAMPLE_APP_NAME) + .put("group-name", "Default") + .put("ssl-certificate-template", ""); + + ServerManager mgr = new ServerManager(manageClient); + mgr.save(payload.toString()); + payload.put("server-name", SAMPLE_APP_NAME + "-test"); + mgr.save(payload.toString()); + } } diff --git a/src/test/java/com/marklogic/appdeployer/command/TestConnectionsTest.java b/src/test/java/com/marklogic/appdeployer/command/TestConnectionsTest.java new file mode 100644 index 00000000..fd59e050 --- /dev/null +++ b/src/test/java/com/marklogic/appdeployer/command/TestConnectionsTest.java @@ -0,0 +1,208 @@ +package com.marklogic.appdeployer.command; + +import com.marklogic.appdeployer.AbstractAppDeployerTest; +import com.marklogic.appdeployer.command.restapis.DeployRestApiServersCommand; +import com.marklogic.appdeployer.command.security.DeployCertificateTemplatesCommand; +import com.marklogic.mgmt.ManageClient; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class TestConnectionsTest extends AbstractAppDeployerTest { + + private TestConnectionsCommand command = new TestConnectionsCommand(); + + @AfterEach + void afterEach() { + undeploySampleApp(); + } + + /** + * Multiple scenarios are tested here as they can all use the same deployed app. + */ + @Test + void allConnectionsSucceed() { + appConfig.setTestRestPort(SAMPLE_APP_TEST_REST_PORT); + initializeAppDeployer(new DeployRestApiServersCommand(), new DeployCertificateTemplatesCommand()); + deploySampleApp(); + + verifyAllConnectionsSucceed(); + verifyConnectionsSucceedWhenRestServersRequireSSL(); + verifyResultsWhenManageConnectionFails(); + } + + /** + * In this scenario, the 3 REST API app servers don't exist yet - which is fine, no error should be thrown, + * we just don't get any test results for them. + */ + @Test + void restServersDontExist() { + appConfig.setAppServicesPort(SAMPLE_APP_REST_PORT); + appConfig.setTestRestPort(SAMPLE_APP_TEST_REST_PORT); + + CommandContext context = new CommandContext(appConfig, manageClient, adminManager); + TestConnectionsCommand command = new TestConnectionsCommand(); + // Smoke test, just expecting logging + command.execute(context); + + TestConnectionsCommand.TestResults results = command.testConnections(context); + assertFalse(results.anyTestFailed()); + + assertTrue(results.getManageTestResult().isSucceeded()); + assertTrue(results.getAdminTestResult().isSucceeded()); + assertNull(results.getAppServicesTestResult()); + assertNull(results.getRestServerTestResult()); + assertNull(results.getTestRestServerTestResult()); + } + + /** + * In this scenario, the Manage test fails, meaning we can't test any of the REST API app servers since we can't + * check to see if they exist or not via the Manage app server. + */ + private void verifyResultsWhenManageConnectionFails() { + final String validPassword = manageConfig.getPassword(); + try { + manageConfig.setPassword("Wrong password"); + TestConnectionsCommand.TestResults results = + command.testConnections(new CommandContext(appConfig, new ManageClient(manageConfig), adminManager)); + + assertTrue(results.anyTestFailed()); + assertFalse(results.getManageTestResult().isSucceeded()); + assertTrue(results.getAdminTestResult().isSucceeded()); + assertNull(results.getAppServicesTestResult()); + assertNull(results.getRestServerTestResult()); + assertNull(results.getTestRestServerTestResult()); + } finally { + manageConfig.setPassword(validPassword); + } + } + + private void verifyConnectionsSucceedWhenRestServersRequireSSL() { + appConfig.setSimpleSslConfig(); + configureRestServersToRequireSSL(); + try { + TestConnectionsCommand.TestResults results = + command.testConnections(new CommandContext(appConfig, manageClient, adminManager)); + assertFalse(results.anyTestFailed()); + + TestConnectionsCommand.TestResult restResult = results.getRestServerTestResult(); + assertEquals("https", restResult.getScheme()); + assertTrue(restResult.toString().startsWith( + "Configured to connect to https://localhost:8004 using 'digest' authentication and username of 'admin'"), + "Unexpected message: " + restResult); + + TestConnectionsCommand.TestResult testRestResult = results.getTestRestServerTestResult(); + assertEquals("https", testRestResult.getScheme()); + assertTrue(testRestResult.toString().startsWith( + "Configured to connect to https://localhost:8005 using 'digest' authentication and username of 'admin'"), + "Unexpected message: " + testRestResult); + + // Disable SSL on the client side and verify we get errors + appConfig.setRestSslContext(null); + appConfig.setRestTrustManager(null); + appConfig.setRestSslHostnameVerifier(null); + + results = command.testConnections(new CommandContext(appConfig, manageClient, adminManager)); + restResult = results.getRestServerTestResult(); + assertFalse(restResult.isSucceeded()); + assertEquals("Received 403: Forbidden", restResult.getMessage(), "Unfortunately, the Java Client receives " + + "nothing indicating an SSL issue; the request doesn't even show up in the app server's AccessLog. " + + "All the user gets is a 403 back when SSL is required by the client is not using it."); + testRestResult = results.getTestRestServerTestResult(); + assertFalse(testRestResult.isSucceeded()); + assertEquals("Received 403: Forbidden", testRestResult.getMessage()); + } finally { + configureRestServersToNotRequireSSL(); + appConfig.setRestSslContext(null); + appConfig.setRestTrustManager(null); + appConfig.setRestSslHostnameVerifier(null); + } + } + + private void verifyAllConnectionsSucceed() { + CommandContext context = new CommandContext(appConfig, manageClient, adminManager); + + // Smoke test - this just logs text on success. + command.execute(context); + TestConnectionsCommand.TestResults results = command.testConnections(context); + assertFalse(results.anyTestFailed()); + + final String host = manageConfig.getHost(); + + TestConnectionsCommand.TestResult manageResult = results.getManageTestResult(); + assertEquals(host, manageResult.getHost()); + assertEquals(manageConfig.getPort(), manageResult.getPort()); + assertEquals("http", manageResult.getScheme()); + assertEquals(manageConfig.getUsername(), manageResult.getUsername()); + assertEquals(manageConfig.getAuthType(), manageResult.getAuthType()); + assertTrue(manageResult.isSucceeded()); + assertTrue(manageResult.getMessage().startsWith("MarkLogic version:"), + "Unexpected message; the MarkLogic version should be shown as a quick confirmation of the version of " + + "MarkLogic that the user is connecting to; actual message: " + manageResult.getMessage()); + assertTrue( + manageResult.toString().startsWith("Configured to connect to http://localhost:8002 using 'digest' authentication and username of 'admin'"), + "Unexpected toString content: " + manageResult + ); + + TestConnectionsCommand.TestResult adminResult = results.getAdminTestResult(); + assertEquals(host, adminResult.getHost()); + assertEquals(adminConfig.getPort(), adminResult.getPort()); + assertEquals("http", adminResult.getScheme()); + assertEquals(adminConfig.getUsername(), adminResult.getUsername()); + assertEquals(adminConfig.getAuthType(), adminResult.getAuthType()); + assertTrue(adminResult.isSucceeded()); + assertTrue(adminResult.getMessage().startsWith("MarkLogic server timestamp:"), + "Unexpected message; the server timestamp should be shown as a quick confirmation of the timezone that " + + "the MarkLogic instance is running in; actual message: " + adminResult.getMessage()); + assertTrue( + adminResult.toString().startsWith("Configured to connect to http://localhost:8001 using 'digest' authentication and username of 'admin'"), + "Unexpected toString content: " + adminResult + ); + + TestConnectionsCommand.TestResult appServicesResult = results.getAppServicesTestResult(); + assertEquals(host, appServicesResult.getHost()); + assertEquals(appConfig.getAppServicesPort(), appServicesResult.getPort()); + assertEquals("http", appServicesResult.getScheme()); + assertEquals(appConfig.getAppServicesUsername(), appServicesResult.getUsername()); + assertEquals(appConfig.getAppServicesSecurityContextType().name().toLowerCase(), appServicesResult.getAuthType()); + assertTrue(appServicesResult.isSucceeded()); + assertNull(appServicesResult.getMessage(), "The Java Client doesn't provide any success message when " + + "checkConnection succeeds."); + assertTrue( + appServicesResult.toString().startsWith("Configured to connect to http://localhost:8000 using 'digest' authentication and username of 'admin'"), + "Unexpected toString content: " + appServicesResult + ); + + TestConnectionsCommand.TestResult restResult = results.getRestServerTestResult(); + assertEquals(host, restResult.getHost()); + assertEquals(appConfig.getRestPort(), restResult.getPort()); + assertEquals("http", restResult.getScheme()); + assertEquals(appConfig.getRestAdminUsername(), restResult.getUsername()); + assertEquals(appConfig.getRestSecurityContextType().name().toLowerCase(), restResult.getAuthType()); + assertTrue(restResult.isSucceeded()); + assertNull(restResult.getMessage(), "The Java Client doesn't provide any success message when " + + "checkConnection succeeds."); + assertTrue( + restResult.toString().startsWith("Configured to connect to http://localhost:8004 using 'digest' authentication and username of 'admin'"), + "Unexpected toString content: " + restResult + ); + + TestConnectionsCommand.TestResult testRestResult = results.getTestRestServerTestResult(); + assertEquals(host, testRestResult.getHost()); + assertEquals(appConfig.getTestRestPort(), testRestResult.getPort()); + assertEquals("http", testRestResult.getScheme()); + assertEquals(appConfig.getRestAdminUsername(), testRestResult.getUsername()); + assertEquals(appConfig.getRestSecurityContextType().name().toLowerCase(), testRestResult.getAuthType()); + assertTrue(testRestResult.isSucceeded()); + assertNull(testRestResult.getMessage(), "The Java Client doesn't provide any success message when " + + "checkConnection succeeds."); + assertTrue( + testRestResult.toString().startsWith("Configured to connect to http://localhost:8005 using 'digest' authentication and username of 'admin'"), + "Unexpected toString content: " + testRestResult + ); + } +}