Skip to content

Commit

Permalink
Add new e2eTests
Browse files Browse the repository at this point in the history
  • Loading branch information
Mythicaeda committed Mar 16, 2024
1 parent c6c1516 commit b2ee901
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 9 deletions.
55 changes: 46 additions & 9 deletions e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/BindingsTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import javax.json.Json;
import javax.json.JsonArray;
Expand All @@ -30,8 +33,10 @@
import java.io.StringReader;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Named.named;

/**
* Test the Action Bindings for the Merlin and Scheduler Servers
Expand Down Expand Up @@ -156,16 +161,17 @@ void invalidPlanId() {
// Returns a 404 if the PlanId is invalid
// message is "no such plan"
final String data = Json.createObjectBuilder()
.add("action", Json.createObjectBuilder().add("name", "simulate"))
.add("input", Json.createObjectBuilder().add("planId", -1))
.add("request_query", "")
.add("session_variables", admin.getSession())
.build()
.toString();
.add("action", Json.createObjectBuilder().add("name", "simulate"))
.add("input", Json.createObjectBuilder().add("planId", -1))
.add("request_query", "")
.add("session_variables", admin.getSession())
.build()
.toString();
final var response = request.post("/getSimulationResults", RequestOptions.create().setData(data));
assertEquals(404, response.status());
assertEquals("no such plan", getBody(response).getString("message"));
}

@Test
void unauthorized() {
// Returns a 403 if Unauthorized
Expand All @@ -178,10 +184,12 @@ void unauthorized() {
.toString();
final var response = request.post("/getSimulationResults", RequestOptions.create().setData(data));
assertEquals(403, response.status());
assertEquals("User '"+nonOwner.name()+"' with role 'user' cannot perform 'simulate' because they are not "
+ "a 'PLAN_OWNER_COLLABORATOR' for plan with id '"+planId+"'",
getBody(response).getString("message"));
assertEquals(
"User '" + nonOwner.name() + "' with role 'user' cannot perform 'simulate' because they are not "
+ "a 'PLAN_OWNER_COLLABORATOR' for plan with id '" + planId + "'",
getBody(response).getString("message"));
}

@Test
void valid() throws InterruptedException {
// Returns a 200 otherwise
Expand All @@ -199,6 +207,35 @@ void valid() throws InterruptedException {
// Delay 1s to allow any workers to finish with the request
Thread.sleep(1000);
}

static Stream<Arguments> forceArgs() {
return Stream.of(
Arguments.arguments(named("valid, force is NULL", JsonValue.NULL)),
Arguments.arguments(named("valid, force is TRUE", JsonValue.TRUE)),
Arguments.arguments(named("valid, force is FALSE", JsonValue.FALSE))
);
}

@ParameterizedTest
@MethodSource("forceArgs")
void validWithForce(JsonValue force) throws InterruptedException {
// Returns a 200 otherwise
// "status" is not "failed"
final String data = Json.createObjectBuilder()
.add("action", Json.createObjectBuilder().add("name", "simulate"))
.add(
"input",
Json.createObjectBuilder().add("planId", planId).add("force", force))
.add("request_query", "")
.add("session_variables", admin.getSession())
.build()
.toString();
final var response = request.post("/getSimulationResults", RequestOptions.create().setData(data));
assertEquals(200, response.status());
assertNotEquals("failed", getBody(response).getString("status"));
// Delay 1s to allow any workers to finish with the request
Thread.sleep(1000);
}
}

@Nested
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,4 +319,50 @@ void cancelingSimReturnsPartialResults() throws IOException {
assertEquals(startedActivities, results.activities().size());
}
}

@Nested
class ForceResimulation {
private int simulationDatasetId;
@BeforeEach
void beforeEach() throws IOException {
simulationDatasetId = hasura.awaitSimulation(planId).simDatasetId();
}
@Test
void noResimWhenNull() throws IOException {
assertEquals(simulationDatasetId, hasura.awaitSimulation(planId, null).simDatasetId());
}

@Test
void noResimWhenFalse() throws IOException {
assertEquals(simulationDatasetId, hasura.awaitSimulation(planId, false).simDatasetId());
}

@Test
void noResimWhenAbsent() throws IOException {
assertEquals(simulationDatasetId, hasura.awaitSimulation(planId).simDatasetId());
}

@Test
void resimOnlyUpdatesConfigRevision() throws IOException {
final int planRevision = hasura.getPlanRevision(planId);
final var simConfig = hasura.getSimConfig(planId);

// Assert forcibly resimming returned a new simulation dataset
final var newSimDatasetId = hasura.awaitSimulation(planId, true).simDatasetId();
assertNotEquals(simulationDatasetId, newSimDatasetId);

// Assert that the plan revision is unchanged
assertEquals(planRevision, hasura.getPlanRevision(planId));

// Assert that the simulation configuration has only had its revision updated
final var newSimConfig = hasura.getSimConfig(planId);
assertNotEquals(simConfig.revision(), newSimConfig.revision());
assertEquals(simConfig.id(), newSimConfig.id());
assertEquals(simConfig.planId(), newSimConfig.planId());
assertEquals(simConfig.simulationTemplateId(), newSimConfig.simulationTemplateId());
assertEquals(simConfig.arguments(), newSimConfig.arguments());
assertEquals(simConfig.simulationStartTime(), newSimConfig.simulationStartTime());
assertEquals(simConfig.simulationEndTime(), newSimConfig.simulationEndTime());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package gov.nasa.jpl.aerie.e2e.types;

import javax.json.JsonObject;
import java.util.Optional;

public record SimulationConfiguration(
int id,
int revision,
int planId,
Optional<Integer> simulationTemplateId,
JsonObject arguments,
String simulationStartTime,
String simulationEndTime
) {
public static SimulationConfiguration fromJSON(JsonObject json) {
return new SimulationConfiguration(
json.getInt("id"),
json.getInt("revision"),
json.getInt("plan_id"),
json.isNull("simulation_template_id") ? Optional.empty() : Optional.of(json.getInt("simulation_template_id")),
json.getJsonObject("arguments"),
json.getString("simulation_start_time"),
json.getString("simulation_end_time"));
}
}
20 changes: 20 additions & 0 deletions e2e-tests/src/test/java/gov/nasa/jpl/aerie/e2e/utils/GQL.java
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,18 @@ query GetSchedulingRequest($specificationId: Int!, $specificationRev: Int!) {
status
}
}"""),
GET_SIMULATION_CONFIGURATION("""
query GetSimConfig($planId: Int!) {
sim_config: simulation(where: {plan_id: {_eq:$planId}}) {
id
revision
plan_id
simulation_template_id
arguments
simulation_start_time
simulation_end_time
}
}"""),
GET_SIMULATION_DATASET("""
query GetSimulationDataset($id: Int!) {
simulationDataset: simulation_dataset_by_pk(id: $id) {
Expand Down Expand Up @@ -501,6 +513,14 @@ query Simulate($plan_id: Int!) {
simulationDatasetId
}
}"""),
SIMULATE_FORCE("""
query SimulateForce($plan_id: Int!, $force: Boolean) {
simulate(planId: $plan_id, force: $force){
status
reason
simulationDatasetId
}
}"""),
UPDATE_ACTIVITY_DIRECTIVE_ARGUMENTS("""
mutation updateActivityDirectiveArguments($id: Int!, $plan_id: Int!, $arguments: jsonb!) {
updateActivityDirectiveArguments: update_activity_directive_by_pk(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,16 @@ private SimulationResponse simulate(int planId) throws IOException {
return SimulationResponse.fromJSON(makeRequest(GQL.SIMULATE, variables).getJsonObject("simulate"));
}

private SimulationResponse simulateForce(int planId, Boolean force) throws IOException {
final var variables = Json.createObjectBuilder().add("plan_id", planId);
if (force == null) {
variables.add("force", JsonValue.NULL);
} else {
variables.add("force", force);
}
return SimulationResponse.fromJSON(makeRequest(GQL.SIMULATE_FORCE, variables.build()).getJsonObject("simulate"));
}

private SimulationDataset cancelSimulation(int simDatasetId, int timeout) throws IOException {
final var variables = Json.createObjectBuilder().add("id", simDatasetId).build();
makeRequest(GQL.CANCEL_SIMULATION, variables);
Expand Down Expand Up @@ -307,6 +317,47 @@ public SimulationResponse awaitSimulation(int planId, int timeout) throws IOExce
throw new TimeoutError("Simulation timed out after " + timeout + " seconds");
}

/**
* Simulate the specified plan, potentially forcibly, with a timeout of 30 seconds
* @param planId the plan to simulate
* @param force whether to forcibly resimulate in the event of an existing dataset.
*/
public SimulationResponse awaitSimulation(int planId, Boolean force) throws IOException {
return awaitSimulation(planId, force, 30);
}

/**
* Simulate the specified plan, potentially forcibly, with a set timeout
* @param planId the plan to simulate
* @param force whether to forcibly resimulate in the event of an existing dataset.
* @param timeout the length of the timeout, in seconds
*/
public SimulationResponse awaitSimulation(int planId, Boolean force, int timeout) throws IOException {
for (int i = 0; i < timeout; ++i) {
final SimulationResponse response;
// Only use force on the initial request to avoid an infinite loop of making new sim requests
if (i == 0) {
response = simulateForce(planId, force);
} else {
response = simulate(planId);
}
switch (response.status()) {
case "pending", "incomplete" -> {
try {
Thread.sleep(1000); // 1s
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
case "complete" -> {
return response;
}
default -> fail("Simulation returned bad status " + response.status() + " with reason " + response.reason());
}
}
throw new TimeoutError("Simulation timed out after " + timeout + " seconds");
}

/**
* Start and immediately cancel a simulation with a timeout of 30 seconds
* @param planId the plan to simulate
Expand Down Expand Up @@ -351,6 +402,13 @@ public int getSimulationId(int planId) throws IOException {
return makeRequest(GQL.GET_SIMULATION_ID, variables).getJsonArray("simulation").getJsonObject(0).getInt("id");
}

public SimulationConfiguration getSimConfig(int planId) throws IOException {
final var variables = Json.createObjectBuilder().add("planId", planId).build();
final var simConfig = makeRequest(GQL.GET_SIMULATION_CONFIGURATION, variables).getJsonArray("sim_config");
assertEquals(1, simConfig.size());
return SimulationConfiguration.fromJSON(simConfig.getJsonObject(0));
}

public int insertAndAssociateSimTemplate(int modelId, String description, JsonObject arguments, int simConfigId)
throws IOException
{
Expand Down

0 comments on commit b2ee901

Please sign in to comment.