diff --git a/it/testing-server/pom.xml b/it/testing-server/pom.xml
index 23b03d94db..d4cba3fc79 100644
--- a/it/testing-server/pom.xml
+++ b/it/testing-server/pom.xml
@@ -35,6 +35,10 @@
+
+ com.walmartlabs.concord
+ concord-agent
+
org.testcontainers
postgresql
@@ -43,5 +47,16 @@
com.typesafe
config
+
+
+ com.walmartlabs.concord
+ concord-client2
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
diff --git a/it/testing-server/src/main/java/com/walmartlabs/concord/it/testingserver/TestingConcordAgent.java b/it/testing-server/src/main/java/com/walmartlabs/concord/it/testingserver/TestingConcordAgent.java
new file mode 100644
index 0000000000..d2cd4531e3
--- /dev/null
+++ b/it/testing-server/src/main/java/com/walmartlabs/concord/it/testingserver/TestingConcordAgent.java
@@ -0,0 +1,92 @@
+package com.walmartlabs.concord.it.testingserver;
+
+/*-
+ * *****
+ * Concord
+ * -----
+ * Copyright (C) 2017 - 2023 Walmart Inc.
+ * -----
+ * 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
+ *
+ * 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.
+ * =====
+ */
+
+import com.google.inject.Guice;
+import com.google.inject.Module;
+import com.typesafe.config.Config;
+import com.typesafe.config.ConfigFactory;
+import com.typesafe.config.ConfigParseOptions;
+import com.typesafe.config.ConfigResolveOptions;
+import com.walmartlabs.concord.agent.Agent;
+import com.walmartlabs.concord.agent.AgentModule;
+
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+/**
+ * A helper class for running concord-agent.
+ * The agent runs in the same JVM as TestingConcordAgent.
+ */
+public class TestingConcordAgent implements AutoCloseable {
+
+ private final Map extraConfiguration;
+ private final List> extraModules;
+ private final String agentApiKey;
+
+ private Agent agent;
+
+ public TestingConcordAgent(String agentApiKey) {
+ this(agentApiKey, Map.of(), List.of());
+ }
+
+ public TestingConcordAgent(String agentApiKey, Map extraConfiguration, List> extraModules) {
+ this.agentApiKey = agentApiKey;
+ this.extraConfiguration = extraConfiguration;
+ this.extraModules = extraModules;
+ }
+
+ public synchronized void start() {
+ var config = prepareConfig();
+ var system = new AgentModule(config);
+ var allModules = Stream.concat(extraModules.stream().map(f -> f.apply(config)), Stream.of(system)).toList();
+ var injector = Guice.createInjector(allModules);
+ agent = injector.getInstance(Agent.class);
+ agent.start();
+ }
+
+ public synchronized void stop() {
+ if (agent != null) {
+ agent.stop();
+ agent = null;
+ }
+ }
+
+ @Override
+ public void close() {
+ this.stop();
+ }
+
+ private Config prepareConfig() {
+ var extraConfig = ConfigFactory.parseMap(this.extraConfiguration);
+
+ var testConfig = ConfigFactory.parseMap(Map.of(
+ "server.apiKey", agentApiKey
+ ));
+
+ var defaultConfig = ConfigFactory.load("concord-agent.conf", ConfigParseOptions.defaults(), ConfigResolveOptions.defaults().setAllowUnresolved(true))
+ .getConfig("concord-agent");
+
+ return extraConfig.withFallback(testConfig.withFallback(defaultConfig)).resolve();
+ }
+}
diff --git a/it/testing-server/src/main/java/com/walmartlabs/concord/it/testingserver/TestingConcordServer.java b/it/testing-server/src/main/java/com/walmartlabs/concord/it/testingserver/TestingConcordServer.java
index bcca549988..7bbfc6de86 100644
--- a/it/testing-server/src/main/java/com/walmartlabs/concord/it/testingserver/TestingConcordServer.java
+++ b/it/testing-server/src/main/java/com/walmartlabs/concord/it/testingserver/TestingConcordServer.java
@@ -38,21 +38,29 @@
import static java.util.Objects.requireNonNull;
+/**
+ * A helper class for running concord-server. It runs PostgreSQL in Docker
+ * and the server runs in the same JVM as TestingConcordServer.
+ */
public class TestingConcordServer implements AutoCloseable {
private final Map extraConfiguration;
private final List> extraModules;
+ private final String adminApiKey;
+ private final String agentApiKey;
private PostgreSQLContainer> db;
private ConcordServer server;
+ public TestingConcordServer() {
+ this(Map.of(), List.of());
+ }
+
public TestingConcordServer(Map extraConfiguration, List> extraModules) {
this.extraConfiguration = requireNonNull(extraConfiguration);
this.extraModules = requireNonNull(extraModules);
- }
-
- public TestingConcordServer() {
- this(Map.of(), List.of());
+ this.adminApiKey = randomString(8);
+ this.agentApiKey = randomString(16);
}
public synchronized TestingConcordServer start() throws Exception {
@@ -68,12 +76,7 @@ public synchronized TestingConcordServer start() throws Exception {
return this;
}
- @Override
- public synchronized void close() throws Exception {
- this.stop();
- }
-
- public void stop() throws Exception {
+ public synchronized void stop() throws Exception {
if (server != null) {
server.stop();
server = null;
@@ -85,6 +88,11 @@ public void stop() throws Exception {
}
}
+ @Override
+ public void close() throws Exception {
+ this.stop();
+ }
+
public ConcordServer getServer() {
return server;
}
@@ -93,6 +101,14 @@ public PostgreSQLContainer> getDb() {
return db;
}
+ public String getAdminApiKey() {
+ return adminApiKey;
+ }
+
+ public String getAgentApiKey() {
+ return agentApiKey;
+ }
+
private Config prepareConfig(PostgreSQLContainer> db) {
var extraConfig = ConfigFactory.parseMap(this.extraConfiguration);
@@ -102,10 +118,11 @@ private Config prepareConfig(PostgreSQLContainer> db) {
"db.appPassword", db.getPassword(),
"db.inventoryUsername", db.getUsername(),
"db.inventoryPassword", db.getPassword(),
- "db.changeLogParameters.defaultAdminToken", "foobar",
- "secretStore.serverPassword", randomString(),
- "secretStore.secretStoreSalt", randomString(),
- "secretStore.projectSecretSalt", randomString()
+ "db.changeLogParameters.defaultAdminToken", adminApiKey,
+ "db.changeLogParameters.defaultAgentToken", agentApiKey,
+ "secretStore.serverPassword", randomString(64),
+ "secretStore.secretStoreSalt", randomString(64),
+ "secretStore.projectSecretSalt", randomString(64)
));
var defaultConfig = ConfigFactory.load("concord-server.conf", ConfigParseOptions.defaults(), ConfigResolveOptions.defaults().setAllowUnresolved(true))
@@ -114,8 +131,8 @@ private Config prepareConfig(PostgreSQLContainer> db) {
return extraConfig.withFallback(testConfig.withFallback(defaultConfig)).resolve();
}
- private static String randomString() {
- byte[] ab = new byte[64];
+ private static String randomString(int minLength) {
+ byte[] ab = new byte[minLength];
new SecureRandom().nextBytes(ab);
return Base64.getEncoder().encodeToString(ab);
}
@@ -124,18 +141,26 @@ private static String randomString() {
* Just an example.
*/
public static void main(String[] args) throws Exception {
- try (TestingConcordServer server = new TestingConcordServer(Map.of("process.watchdogPeriod", "10 seconds"), List.of())) {
+ try (var server = new TestingConcordServer(Map.of("process.watchdogPeriod", "10 seconds"), List.of())) {
server.start();
- System.out.println("""
- ==============================================================
+ var db = server.getDb();
+ System.out.printf("""
+ ==============================================================
+
+ UI: http://localhost:8001/
+ DB:
+ JDBC URL: %s
+ username: %s
+ password: %s
- UI: http://localhost:8001/
- DB:
- JDBC URL: %s
- username: %s
- password: %s
- """.formatted(server.getDb().getJdbcUrl(), server.getDb().getUsername(), server.getDb().getPassword()));
+ admin API key: %s
+ agent API key: %s
+ %n""", db.getJdbcUrl(),
+ db.getUsername(),
+ db.getPassword(),
+ server.getAdminApiKey(),
+ server.getAgentApiKey());
Thread.currentThread().join();
}
diff --git a/it/testing-server/src/test/java/com/walmartlabs/concord/it/testingserver/TestingConcordIT.java b/it/testing-server/src/test/java/com/walmartlabs/concord/it/testingserver/TestingConcordIT.java
new file mode 100644
index 0000000000..2fe1d7f1e3
--- /dev/null
+++ b/it/testing-server/src/test/java/com/walmartlabs/concord/it/testingserver/TestingConcordIT.java
@@ -0,0 +1,85 @@
+package com.walmartlabs.concord.it.testingserver;
+
+/*-
+ * *****
+ * Concord
+ * -----
+ * Copyright (C) 2017 - 2023 Walmart Inc.
+ * -----
+ * 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
+ *
+ * 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.
+ * =====
+ */
+
+import com.walmartlabs.concord.client2.ApiClientConfiguration;
+import com.walmartlabs.concord.client2.DefaultApiClientFactory;
+import com.walmartlabs.concord.client2.ProcessApi;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.time.Duration;
+import java.util.Map;
+
+import static com.walmartlabs.concord.client2.ProcessEntry.StatusEnum.FINISHED;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+/**
+ * A test that tests TestingConcordServer and TestingConcordAgent can run together in the same JVM.
+ */
+public class TestingConcordIT {
+
+ private TestingConcordServer concordServer;
+ private TestingConcordAgent concordAgent;
+
+ @Before
+ public void setUp() throws Exception {
+ concordServer = new TestingConcordServer();
+ concordServer.start();
+
+ concordAgent = new TestingConcordAgent(concordServer.getAgentApiKey());
+ concordAgent.start();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (concordAgent != null) {
+ concordAgent.close();
+ concordAgent = null;
+ }
+
+ if (concordServer != null) {
+ concordServer.close();
+ concordServer = null;
+ }
+ }
+
+ @Test(timeout = 120_000)
+ public void testRunningSimpleProcess() throws Exception {
+ var client = new DefaultApiClientFactory("http://localhost:8001")
+ .create(ApiClientConfiguration.builder()
+ .apiKey(concordServer.getAdminApiKey())
+ .build());
+
+ var processApi = new ProcessApi(client);
+ var response = processApi.startProcess(Map.of("concord.yml", """
+ flows:
+ default:
+ - log: "Hello!"
+ """.getBytes()));
+ assertNotNull(response.getInstanceId());
+
+ var process = processApi.waitForCompletion(response.getInstanceId(), Duration.ofSeconds(60).toMillis());
+ assertEquals(FINISHED, process.getStatus());
+ }
+}