summaryrefslogtreecommitdiff
path: root/opendc-web/opendc-web-server/src
diff options
context:
space:
mode:
Diffstat (limited to 'opendc-web/opendc-web-server/src')
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/SchedulerResource.java (renamed from opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/SchedulerResource.kt)36
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/TraceResource.java (renamed from opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/TraceService.java)39
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/MissingKotlinParameterExceptionMapper.java (renamed from opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/error/MissingKotlinParameterExceptionMapper.kt)29
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/WebApplicationExceptionMapper.java (renamed from opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/error/WebApplicationExceptionMapper.kt)36
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/runner/JobResource.java103
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioResource.java120
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioScenarioResource.java89
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ProjectResource.java118
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ScenarioResource.java94
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/TopologyResource.java146
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/UserResource.java (renamed from opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/UserService.java)33
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/JobService.java8
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/ScenarioService.java9
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/KotlinModuleCustomizer.java (renamed from opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/util/KotlinModuleCustomizer.kt)19
-rw-r--r--opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/OpenDCApplication.kt30
-rw-r--r--opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/TraceResource.kt54
-rw-r--r--opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/runner/JobResource.kt76
-rw-r--r--opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/user/PortfolioResource.kt83
-rw-r--r--opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/user/PortfolioScenarioResource.kt63
-rw-r--r--opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/user/ProjectResource.kt87
-rw-r--r--opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/user/ScenarioResource.kt64
-rw-r--r--opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/user/TopologyResource.kt94
-rw-r--r--opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/user/UserResource.kt45
-rw-r--r--opendc-web/opendc-web-server/src/main/resources/application-test.properties1
-rw-r--r--opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/SchedulerResourceTest.java (renamed from opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/SchedulerResourceTest.kt)29
-rw-r--r--opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/TraceResourceTest.java86
-rw-r--r--opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/runner/JobResourceTest.java194
-rw-r--r--opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioResourceTest.java240
-rw-r--r--opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioScenarioResourceTest.java218
-rw-r--r--opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ProjectResourceTest.java208
-rw-r--r--opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ScenarioResourceTest.java166
-rw-r--r--opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/TopologyResourceTest.java281
-rw-r--r--opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/UserResourceTest.java (renamed from opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/user/UserResourceTest.kt)56
-rw-r--r--opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/TraceResourceTest.kt100
-rw-r--r--opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/runner/JobResourceTest.kt203
-rw-r--r--opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/user/PortfolioResourceTest.kt265
-rw-r--r--opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/user/PortfolioScenarioResourceTest.kt222
-rw-r--r--opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/user/ProjectResourceTest.kt240
-rw-r--r--opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/user/ScenarioResourceTest.kt187
-rw-r--r--opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/user/TopologyResourceTest.kt304
40 files changed, 2236 insertions, 2239 deletions
diff --git a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/SchedulerResource.kt b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/SchedulerResource.java
index 919b25fc..0fd58182 100644
--- a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/SchedulerResource.kt
+++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/SchedulerResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 AtLarge Research
+ * Copyright (c) 2023 AtLarge Research
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -20,29 +20,33 @@
* SOFTWARE.
*/
-package org.opendc.web.server.rest
+package org.opendc.web.server.rest;
-import javax.ws.rs.GET
-import javax.ws.rs.Path
+import java.util.List;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
/**
* A resource representing the available schedulers that can be used during experiments.
*/
+@Produces("application/json")
@Path("/schedulers")
-class SchedulerResource {
+public final class SchedulerResource {
/**
* Obtain all available schedulers.
*/
@GET
- fun getAll() = listOf(
- "mem",
- "mem-inv",
- "core-mem",
- "core-mem-inv",
- "active-servers",
- "active-servers-inv",
- "provisioned-cores",
- "provisioned-cores-inv",
- "random"
- )
+ public List<String> getAll() {
+ return List.of(
+ "mem",
+ "mem-inv",
+ "core-mem",
+ "core-mem-inv",
+ "active-servers",
+ "active-servers-inv",
+ "provisioned-cores",
+ "provisioned-cores-inv",
+ "random");
+ }
}
diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/TraceService.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/TraceResource.java
index 94b8340b..2b1efb02 100644
--- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/TraceService.java
+++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/TraceResource.java
@@ -20,30 +20,45 @@
* SOFTWARE.
*/
-package org.opendc.web.server.service;
+package org.opendc.web.server.rest;
import java.util.List;
-import javax.enterprise.context.ApplicationScoped;
+import java.util.stream.Stream;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
import org.opendc.web.server.model.Trace;
/**
- * Service for managing {@link Trace}s.
+ * A resource representing the workload traces available in the OpenDC instance.
*/
-@ApplicationScoped
-public final class TraceService {
+@Produces("application/json")
+@Path("/traces")
+public final class TraceResource {
/**
- * Obtain all available workload traces.
+ * Obtain all available traces.
*/
- public List<org.opendc.web.proto.Trace> findAll() {
- List<Trace> entities = Trace.listAll();
- return entities.stream().map(TraceService::toUserDto).toList();
+ @GET
+ public List<org.opendc.web.proto.Trace> getAll() {
+ Stream<Trace> entities = Trace.streamAll();
+ return entities.map(TraceResource::toUserDto).toList();
}
/**
- * Obtain a workload trace by identifier.
+ * Obtain trace information by identifier.
*/
- public org.opendc.web.proto.Trace findById(String id) {
- return toUserDto(Trace.findById(id));
+ @GET
+ @Path("{id}")
+ public org.opendc.web.proto.Trace get(@PathParam("id") String id) {
+ Trace trace = Trace.findById(id);
+
+ if (trace == null) {
+ throw new WebApplicationException("Trace not found", 404);
+ }
+
+ return toUserDto(trace);
}
/**
diff --git a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/error/MissingKotlinParameterExceptionMapper.kt b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/MissingKotlinParameterExceptionMapper.java
index e50917aa..3b6be42e 100644
--- a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/error/MissingKotlinParameterExceptionMapper.kt
+++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/MissingKotlinParameterExceptionMapper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022 AtLarge Research
+ * Copyright (c) 2023 AtLarge Research
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -20,24 +20,27 @@
* SOFTWARE.
*/
-package org.opendc.web.server.rest.error
+package org.opendc.web.server.rest.error;
-import com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException
-import org.opendc.web.proto.ProtocolError
-import javax.ws.rs.core.MediaType
-import javax.ws.rs.core.Response
-import javax.ws.rs.ext.ExceptionMapper
-import javax.ws.rs.ext.Provider
+import com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+import org.opendc.web.proto.ProtocolError;
/**
* An [ExceptionMapper] for [MissingKotlinParameterException] thrown by Jackson.
*/
@Provider
-class MissingKotlinParameterExceptionMapper : ExceptionMapper<MissingKotlinParameterException> {
- override fun toResponse(exception: MissingKotlinParameterException): Response {
+public final class MissingKotlinParameterExceptionMapper implements ExceptionMapper<MissingKotlinParameterException> {
+ @Override
+ public Response toResponse(MissingKotlinParameterException exception) {
return Response.status(Response.Status.BAD_REQUEST)
- .entity(ProtocolError(Response.Status.BAD_REQUEST.statusCode, "Field '${exception.parameter.name}' is missing from body."))
- .type(MediaType.APPLICATION_JSON)
- .build()
+ .entity(new ProtocolError(
+ Response.Status.BAD_REQUEST.getStatusCode(),
+ "Field " + exception.getParameter().getName() + " is missing from body."))
+ .type(MediaType.APPLICATION_JSON)
+ .build();
}
}
diff --git a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/error/WebApplicationExceptionMapper.kt b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/WebApplicationExceptionMapper.java
index aa046abf..ad1bb05e 100644
--- a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/error/WebApplicationExceptionMapper.kt
+++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/WebApplicationExceptionMapper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022 AtLarge Research
+ * Copyright (c) 2023 AtLarge Research
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -20,26 +20,32 @@
* SOFTWARE.
*/
-package org.opendc.web.server.rest.error
+package org.opendc.web.server.rest.error;
-import org.opendc.web.proto.ProtocolError
-import javax.ws.rs.WebApplicationException
-import javax.ws.rs.core.MediaType
-import javax.ws.rs.core.Response
-import javax.ws.rs.ext.ExceptionMapper
-import javax.ws.rs.ext.Provider
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+import org.opendc.web.proto.ProtocolError;
/**
- * Helper class to transform a [WebApplicationException] into an JSON error response.
+ * Helper class to transform a {@link WebApplicationException} into an JSON error response.
*/
@Provider
-class WebApplicationExceptionMapper : ExceptionMapper<WebApplicationException> {
- override fun toResponse(exception: WebApplicationException): Response {
- val code = exception.response.status
+public final class WebApplicationExceptionMapper implements ExceptionMapper<WebApplicationException> {
+ @Override
+ public Response toResponse(WebApplicationException exception) {
+ int code = exception.getResponse().getStatus();
+
+ String message = exception.getMessage();
+ if (message == null) {
+ message = "Unknown error";
+ }
return Response.status(code)
- .entity(ProtocolError(code, exception.message ?: "Unknown error"))
- .type(MediaType.APPLICATION_JSON)
- .build()
+ .entity(new ProtocolError(code, message))
+ .type(MediaType.APPLICATION_JSON)
+ .build();
}
}
diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/runner/JobResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/runner/JobResource.java
new file mode 100644
index 00000000..134c6814
--- /dev/null
+++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/runner/JobResource.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2023 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.web.server.rest.runner;
+
+import java.util.List;
+import javax.annotation.security.RolesAllowed;
+import javax.transaction.Transactional;
+import javax.validation.Valid;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import org.opendc.web.server.service.JobService;
+
+/**
+ * A resource representing the available simulation jobs.
+ */
+@Produces("application/json")
+@Path("/jobs")
+@RolesAllowed("runner")
+public final class JobResource {
+ /**
+ * The {@link JobService} responsible for managing the jobs.
+ */
+ private final JobService jobService;
+
+ /**
+ * Construct a {@link JobResource} instance.
+ *
+ * @param jobService The {@link JobService} responsible for managing the jobs.
+ */
+ public JobResource(JobService jobService) {
+ this.jobService = jobService;
+ }
+
+ /**
+ * Obtain all pending simulation jobs.
+ */
+ @GET
+ public List<org.opendc.web.proto.runner.Job> queryPending() {
+ return jobService.listPending();
+ }
+
+ /**
+ * Get a job by identifier.
+ */
+ @GET
+ @Path("{job}")
+ public org.opendc.web.proto.runner.Job get(@PathParam("job") long id) {
+ org.opendc.web.proto.runner.Job job = jobService.findById(id);
+ if (job == null) {
+ throw new WebApplicationException("Job not found", 404);
+ }
+
+ return job;
+ }
+
+ /**
+ * Atomically update the state of a job.
+ */
+ @POST
+ @Path("{job}")
+ @Consumes("application/json")
+ @Transactional
+ public org.opendc.web.proto.runner.Job update(
+ @PathParam("job") long id, @Valid org.opendc.web.proto.runner.Job.Update update) {
+ try {
+ var job = jobService.updateState(id, update.getState(), update.getRuntime(), update.getResults());
+ if (job == null) {
+ throw new WebApplicationException("Job not found", 404);
+ }
+
+ return job;
+ } catch (IllegalArgumentException e) {
+ throw new WebApplicationException(e, 400);
+ } catch (IllegalStateException e) {
+ throw new WebApplicationException(e, 409);
+ }
+ }
+}
diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioResource.java
new file mode 100644
index 00000000..e8e05f97
--- /dev/null
+++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioResource.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2023 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.web.server.rest.user;
+
+import io.quarkus.security.identity.SecurityIdentity;
+import java.util.List;
+import javax.annotation.security.RolesAllowed;
+import javax.transaction.Transactional;
+import javax.validation.Valid;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import org.opendc.web.server.service.PortfolioService;
+
+/**
+ * A resource representing the portfolios of a project.
+ */
+@Produces("application/json")
+@Path("/projects/{project}/portfolios")
+@RolesAllowed("openid")
+public final class PortfolioResource {
+ /**
+ * The service for managing the user portfolios.
+ */
+ private final PortfolioService portfolioService;
+
+ /**
+ * The identity of the current user.
+ */
+ private final SecurityIdentity identity;
+
+ /**
+ * Construct a {@link PortfolioResource}.
+ *
+ * @param portfolioService The {@link PortfolioService} instance to use.
+ * @param identity The {@link SecurityIdentity} of the current user.
+ */
+ public PortfolioResource(PortfolioService portfolioService, SecurityIdentity identity) {
+ this.portfolioService = portfolioService;
+ this.identity = identity;
+ }
+
+ /**
+ * Get all portfolios that belong to the specified project.
+ */
+ @GET
+ public List<org.opendc.web.proto.user.Portfolio> getAll(@PathParam("project") long projectId) {
+ return portfolioService.findByUser(identity.getPrincipal().getName(), projectId);
+ }
+
+ /**
+ * Create a portfolio for this project.
+ */
+ @POST
+ @Transactional
+ public org.opendc.web.proto.user.Portfolio create(
+ @PathParam("project") long projectId, @Valid org.opendc.web.proto.user.Portfolio.Create request) {
+ var portfolio = portfolioService.create(identity.getPrincipal().getName(), projectId, request);
+ if (portfolio == null) {
+ throw new WebApplicationException("Project not found", 404);
+ }
+
+ return portfolio;
+ }
+
+ /**
+ * Obtain a portfolio by its identifier.
+ */
+ @GET
+ @Path("{portfolio}")
+ public org.opendc.web.proto.user.Portfolio get(
+ @PathParam("project") long projectId, @PathParam("portfolio") int number) {
+ var portfolio = portfolioService.findByUser(identity.getPrincipal().getName(), projectId, number);
+ if (portfolio == null) {
+ throw new WebApplicationException("Portfolio not found", 404);
+ }
+
+ return portfolio;
+ }
+
+ /**
+ * Delete a portfolio.
+ */
+ @DELETE
+ @Path("{portfolio}")
+ @Transactional
+ public org.opendc.web.proto.user.Portfolio delete(
+ @PathParam("project") long projectId, @PathParam("portfolio") int number) {
+ var portfolio = portfolioService.delete(identity.getPrincipal().getName(), projectId, number);
+ if (portfolio == null) {
+ throw new WebApplicationException("Portfolio not found", 404);
+ }
+
+ return portfolio;
+ }
+}
diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioScenarioResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioScenarioResource.java
new file mode 100644
index 00000000..a6db7c54
--- /dev/null
+++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioScenarioResource.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2023 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.web.server.rest.user;
+
+import io.quarkus.security.identity.SecurityIdentity;
+import java.util.List;
+import javax.annotation.security.RolesAllowed;
+import javax.transaction.Transactional;
+import javax.validation.Valid;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.WebApplicationException;
+import org.opendc.web.proto.user.Scenario;
+import org.opendc.web.server.service.ScenarioService;
+
+/**
+ * A resource representing the scenarios of a portfolio.
+ */
+@Path("/projects/{project}/portfolios/{portfolio}/scenarios")
+@RolesAllowed("openid")
+public final class PortfolioScenarioResource {
+ /**
+ * The service for managing the user scenarios.
+ */
+ private final ScenarioService scenarioService;
+
+ /**
+ * The identity of the current user.
+ */
+ private final SecurityIdentity identity;
+
+ /**
+ * Construct a {@link PortfolioScenarioResource}.
+ *
+ * @param scenarioService The {@link ScenarioService} instance to use.
+ * @param identity The {@link SecurityIdentity} of the current user.
+ */
+ public PortfolioScenarioResource(ScenarioService scenarioService, SecurityIdentity identity) {
+ this.scenarioService = scenarioService;
+ this.identity = identity;
+ }
+
+ /**
+ * Get all scenarios that belong to the specified portfolio.
+ */
+ @GET
+ public List<Scenario> get(@PathParam("project") long projectId, @PathParam("portfolio") int portfolioNumber) {
+ return scenarioService.findAll(identity.getPrincipal().getName(), projectId, portfolioNumber);
+ }
+
+ /**
+ * Create a scenario for this portfolio.
+ */
+ @POST
+ @Transactional
+ public org.opendc.web.proto.user.Scenario create(
+ @PathParam("project") long projectId,
+ @PathParam("portfolio") int portfolioNumber,
+ @Valid org.opendc.web.proto.user.Scenario.Create request) {
+ var scenario = scenarioService.create(identity.getPrincipal().getName(), projectId, portfolioNumber, request);
+ if (scenario == null) {
+ throw new WebApplicationException("Portfolio not found", 404);
+ }
+
+ return scenario;
+ }
+}
diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ProjectResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ProjectResource.java
new file mode 100644
index 00000000..b0b8eb4e
--- /dev/null
+++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ProjectResource.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2023 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.web.server.rest.user;
+
+import io.quarkus.security.identity.SecurityIdentity;
+import java.util.List;
+import javax.annotation.security.RolesAllowed;
+import javax.transaction.Transactional;
+import javax.validation.Valid;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import org.opendc.web.server.service.ProjectService;
+
+/**
+ * A resource representing the created projects.
+ */
+@Produces("application/json")
+@Path("/projects")
+@RolesAllowed("openid")
+public final class ProjectResource {
+ /**
+ * The service for managing the user projects.
+ */
+ private final ProjectService projectService;
+
+ /**
+ * The identity of the current user.
+ */
+ private final SecurityIdentity identity;
+
+ /**
+ * Construct a {@link ProjectResource}.
+ *
+ * @param projectService The {@link ProjectService} instance to use.
+ * @param identity The {@link SecurityIdentity} of the current user.
+ */
+ public ProjectResource(ProjectService projectService, SecurityIdentity identity) {
+ this.projectService = projectService;
+ this.identity = identity;
+ }
+
+ /**
+ * Obtain all the projects of the current user.
+ */
+ @GET
+ public List<org.opendc.web.proto.user.Project> getAll() {
+ return projectService.findByUser(identity.getPrincipal().getName());
+ }
+
+ /**
+ * Create a new project for the current user.
+ */
+ @POST
+ @Transactional
+ @Consumes("application/json")
+ public org.opendc.web.proto.user.Project create(@Valid org.opendc.web.proto.user.Project.Create request) {
+ return projectService.create(identity.getPrincipal().getName(), request.getName());
+ }
+
+ /**
+ * Obtain a single project by its identifier.
+ */
+ @GET
+ @Path("{project}")
+ public org.opendc.web.proto.user.Project get(@PathParam("project") long id) {
+ var project = projectService.findByUser(identity.getPrincipal().getName(), id);
+ if (project == null) {
+ throw new WebApplicationException("Project not found", 404);
+ }
+
+ return project;
+ }
+
+ /**
+ * Delete a project.
+ */
+ @DELETE
+ @Path("{project}")
+ @Transactional
+ public org.opendc.web.proto.user.Project delete(@PathParam("project") long id) {
+ try {
+ var project = projectService.delete(identity.getPrincipal().getName(), id);
+ if (project == null) {
+ throw new WebApplicationException("Project not found", 404);
+ }
+
+ return project;
+ } catch (IllegalArgumentException e) {
+ throw new WebApplicationException(e.getMessage(), 403);
+ }
+ }
+}
diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ScenarioResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ScenarioResource.java
new file mode 100644
index 00000000..a6838148
--- /dev/null
+++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ScenarioResource.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2023 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.web.server.rest.user;
+
+import io.quarkus.security.identity.SecurityIdentity;
+import javax.annotation.security.RolesAllowed;
+import javax.transaction.Transactional;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import org.opendc.web.server.service.ScenarioService;
+
+/**
+ * A resource representing the scenarios of a portfolio.
+ */
+@Produces("application/json")
+@Path("/projects/{project}/scenarios")
+@RolesAllowed("openid")
+public final class ScenarioResource {
+ /**
+ * The service for managing the user scenarios.
+ */
+ private final ScenarioService scenarioService;
+
+ /**
+ * The identity of the current user.
+ */
+ private final SecurityIdentity identity;
+
+ /**
+ * Construct a {@link ScenarioResource}.
+ *
+ * @param scenarioService The {@link ScenarioService} instance to use.
+ * @param identity The {@link SecurityIdentity} of the current user.
+ */
+ public ScenarioResource(ScenarioService scenarioService, SecurityIdentity identity) {
+ this.scenarioService = scenarioService;
+ this.identity = identity;
+ }
+
+ /**
+ * Obtain a scenario by its identifier.
+ */
+ @GET
+ @Path("{scenario}")
+ public org.opendc.web.proto.user.Scenario get(
+ @PathParam("project") long projectId, @PathParam("scenario") int number) {
+ var scenario = scenarioService.findOne(identity.getPrincipal().getName(), projectId, number);
+ if (scenario == null) {
+ throw new WebApplicationException("Scenario not found", 404);
+ }
+
+ return scenario;
+ }
+
+ /**
+ * Delete a scenario.
+ */
+ @DELETE
+ @Path("{scenario}")
+ @Transactional
+ public org.opendc.web.proto.user.Scenario delete(
+ @PathParam("project") long projectId, @PathParam("scenario") int number) {
+ var scenario = scenarioService.delete(identity.getPrincipal().getName(), projectId, number);
+ if (scenario == null) {
+ throw new WebApplicationException("Scenario not found", 404);
+ }
+
+ return scenario;
+ }
+}
diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/TopologyResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/TopologyResource.java
new file mode 100644
index 00000000..54afc1ce
--- /dev/null
+++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/TopologyResource.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2023 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.web.server.rest.user;
+
+import io.quarkus.security.identity.SecurityIdentity;
+import java.util.List;
+import javax.annotation.security.RolesAllowed;
+import javax.transaction.Transactional;
+import javax.validation.Valid;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import org.opendc.web.server.service.TopologyService;
+
+/**
+ * A resource representing the constructed datacenter topologies.
+ */
+@Produces("application/json")
+@Path("/projects/{project}/topologies")
+@RolesAllowed("openid")
+public final class TopologyResource {
+ /**
+ * The service for managing the user topologies.
+ */
+ private final TopologyService topologyService;
+
+ /**
+ * The identity of the current user.
+ */
+ private final SecurityIdentity identity;
+
+ /**
+ * Construct a {@link TopologyResource}.
+ *
+ * @param topologyService The {@link TopologyService} instance to use.
+ * @param identity The {@link SecurityIdentity} of the current user.
+ */
+ public TopologyResource(TopologyService topologyService, SecurityIdentity identity) {
+ this.topologyService = topologyService;
+ this.identity = identity;
+ }
+
+ /**
+ * Get all topologies that belong to the specified project.
+ */
+ @GET
+ public List<org.opendc.web.proto.user.Topology> getAll(@PathParam("project") long projectId) {
+ return topologyService.findAll(identity.getPrincipal().getName(), projectId);
+ }
+
+ /**
+ * Create a topology for this project.
+ */
+ @POST
+ @Consumes("application/json")
+ @Transactional
+ public org.opendc.web.proto.user.Topology create(
+ @PathParam("project") long projectId, @Valid org.opendc.web.proto.user.Topology.Create request) {
+ var topology = topologyService.create(identity.getPrincipal().getName(), projectId, request);
+
+ if (topology == null) {
+ throw new WebApplicationException("Topology not found", 404);
+ }
+
+ return topology;
+ }
+
+ /**
+ * Obtain a topology by its number.
+ */
+ @GET
+ @Path("{topology}")
+ public org.opendc.web.proto.user.Topology get(
+ @PathParam("project") long projectId, @PathParam("topology") int number) {
+ var topology = topologyService.findOne(identity.getPrincipal().getName(), projectId, number);
+
+ if (topology == null) {
+ throw new WebApplicationException("Topology not found", 404);
+ }
+
+ return topology;
+ }
+
+ /**
+ * Update the specified topology by its number.
+ */
+ @PUT
+ @Path("{topology}")
+ @Consumes("application/json")
+ @Transactional
+ public org.opendc.web.proto.user.Topology update(
+ @PathParam("project") long projectId,
+ @PathParam("topology") int number,
+ @Valid org.opendc.web.proto.user.Topology.Update request) {
+ var topology = topologyService.update(identity.getPrincipal().getName(), projectId, number, request);
+
+ if (topology == null) {
+ throw new WebApplicationException("Topology not found", 404);
+ }
+
+ return topology;
+ }
+
+ /**
+ * Delete the specified topology.
+ */
+ @Path("{topology}")
+ @DELETE
+ @Transactional
+ public org.opendc.web.proto.user.Topology delete(
+ @PathParam("project") long projectId, @PathParam("topology") int number) {
+ var topology = topologyService.delete(identity.getPrincipal().getName(), projectId, number);
+
+ if (topology == null) {
+ throw new WebApplicationException("Topology not found", 404);
+ }
+
+ return topology;
+ }
+}
diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/UserService.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/UserResource.java
index b46b799b..c3fb2866 100644
--- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/UserService.java
+++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/UserResource.java
@@ -20,36 +20,51 @@
* SOFTWARE.
*/
-package org.opendc.web.server.service;
+package org.opendc.web.server.rest.user;
import io.quarkus.security.identity.SecurityIdentity;
-import javax.enterprise.context.ApplicationScoped;
+import javax.annotation.security.RolesAllowed;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
import org.opendc.web.proto.user.User;
import org.opendc.web.proto.user.UserAccounting;
+import org.opendc.web.server.service.UserAccountingService;
/**
- * Service for managing {@link User}s.
+ * A resource representing the active user.
*/
-@ApplicationScoped
-public final class UserService {
+@Produces("application/json")
+@Path("/users")
+@RolesAllowed("openid")
+public final class UserResource {
/**
* The service for managing the user accounting.
*/
private final UserAccountingService accountingService;
/**
- * Construct a {@link UserService} instance.
+ * The identity of the current user.
+ */
+ private final SecurityIdentity identity;
+
+ /**
+ * Construct a {@link UserResource}.
*
* @param accountingService The {@link UserAccountingService} instance to use.
+ * @param identity The {@link SecurityIdentity} of the current user.
*/
- public UserService(UserAccountingService accountingService) {
+ public UserResource(UserAccountingService accountingService, SecurityIdentity identity) {
this.accountingService = accountingService;
+ this.identity = identity;
}
/**
- * Obtain the {@link User} object for the specified <code>identity</code>.
+ * Get the current active user data.
*/
- public User getUser(SecurityIdentity identity) {
+ @GET
+ @Path("me")
+ public User get() {
String userId = identity.getPrincipal().getName();
UserAccounting accounting = accountingService.getAccounting(userId);
diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/JobService.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/JobService.java
index eb0982ec..47f44d27 100644
--- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/JobService.java
+++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/JobService.java
@@ -61,7 +61,13 @@ public final class JobService {
* Find a job by its identifier.
*/
public org.opendc.web.proto.runner.Job findById(long id) {
- return toRunnerDto(Job.findById(id));
+ Job job = Job.findById(id);
+
+ if (job == null) {
+ return null;
+ }
+
+ return toRunnerDto(job);
}
/**
diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/ScenarioService.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/ScenarioService.java
index bf5206af..6a70db1e 100644
--- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/ScenarioService.java
+++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/ScenarioService.java
@@ -219,6 +219,13 @@ public final class ScenarioService {
* Convert a {@link Workload} entity into a DTO.
*/
public static org.opendc.web.proto.Workload toDto(Workload workload) {
- return new org.opendc.web.proto.Workload(TraceService.toUserDto(workload.trace), workload.samplingFraction);
+ return new org.opendc.web.proto.Workload(toDto(workload.trace), workload.samplingFraction);
+ }
+
+ /**
+ * Convert a {@link Trace] entity into a {@link org.opendc.web.proto.Trace} DTO.
+ */
+ public static org.opendc.web.proto.Trace toDto(Trace trace) {
+ return new org.opendc.web.proto.Trace(trace.id, trace.name, trace.type);
}
}
diff --git a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/util/KotlinModuleCustomizer.kt b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/KotlinModuleCustomizer.java
index 8634c8a4..c30edcbf 100644
--- a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/util/KotlinModuleCustomizer.kt
+++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/KotlinModuleCustomizer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022 AtLarge Research
+ * Copyright (c) 2023 AtLarge Research
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -20,19 +20,20 @@
* SOFTWARE.
*/
-package org.opendc.web.server.util
+package org.opendc.web.server.util;
-import com.fasterxml.jackson.databind.ObjectMapper
-import com.fasterxml.jackson.module.kotlin.KotlinModule
-import io.quarkus.jackson.ObjectMapperCustomizer
-import javax.inject.Singleton
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.module.kotlin.KotlinModule;
+import io.quarkus.jackson.ObjectMapperCustomizer;
+import javax.inject.Singleton;
/**
* Helper class to register the Kotlin Jackson module.
*/
@Singleton
-class KotlinModuleCustomizer : ObjectMapperCustomizer {
- override fun customize(objectMapper: ObjectMapper) {
- objectMapper.registerModule(KotlinModule.Builder().build())
+public final class KotlinModuleCustomizer implements ObjectMapperCustomizer {
+ @Override
+ public void customize(ObjectMapper objectMapper) {
+ objectMapper.registerModule(new KotlinModule.Builder().build());
}
}
diff --git a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/OpenDCApplication.kt b/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/OpenDCApplication.kt
deleted file mode 100644
index 1a426095..00000000
--- a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/OpenDCApplication.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (c) 2021 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.web.server
-
-import javax.ws.rs.core.Application
-
-/**
- * [Application] definition for the OpenDC web API.
- */
-class OpenDCApplication : Application()
diff --git a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/TraceResource.kt b/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/TraceResource.kt
deleted file mode 100644
index a33bd8f1..00000000
--- a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/TraceResource.kt
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (c) 2021 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.web.server.rest
-
-import org.opendc.web.proto.Trace
-import org.opendc.web.server.service.TraceService
-import javax.inject.Inject
-import javax.ws.rs.GET
-import javax.ws.rs.Path
-import javax.ws.rs.PathParam
-import javax.ws.rs.WebApplicationException
-
-/**
- * A resource representing the workload traces available in the OpenDC instance.
- */
-@Path("/traces")
-class TraceResource @Inject constructor(private val traceService: TraceService) {
- /**
- * Obtain all available traces.
- */
- @GET
- fun getAll(): List<Trace> {
- return traceService.findAll()
- }
-
- /**
- * Obtain trace information by identifier.
- */
- @GET
- @Path("{id}")
- fun get(@PathParam("id") id: String): Trace {
- return traceService.findById(id) ?: throw WebApplicationException("Trace not found", 404)
- }
-}
diff --git a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/runner/JobResource.kt b/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/runner/JobResource.kt
deleted file mode 100644
index 1e9abc14..00000000
--- a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/runner/JobResource.kt
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.web.server.rest.runner
-
-import org.opendc.web.proto.runner.Job
-import org.opendc.web.server.service.JobService
-import javax.annotation.security.RolesAllowed
-import javax.inject.Inject
-import javax.transaction.Transactional
-import javax.validation.Valid
-import javax.ws.rs.GET
-import javax.ws.rs.POST
-import javax.ws.rs.Path
-import javax.ws.rs.PathParam
-import javax.ws.rs.WebApplicationException
-
-/**
- * A resource representing the available simulation jobs.
- */
-@Path("/jobs")
-@RolesAllowed("runner")
-class JobResource @Inject constructor(private val jobService: JobService) {
- /**
- * Obtain all pending simulation jobs.
- */
- @GET
- fun queryPending(): List<Job> {
- return jobService.listPending()
- }
-
- /**
- * Get a job by identifier.
- */
- @GET
- @Path("{job}")
- fun get(@PathParam("job") id: Long): Job {
- return jobService.findById(id) ?: throw WebApplicationException("Job not found", 404)
- }
-
- /**
- * Atomically update the state of a job.
- */
- @POST
- @Path("{job}")
- @Transactional
- fun update(@PathParam("job") id: Long, @Valid update: Job.Update): Job {
- return try {
- jobService.updateState(id, update.state, update.runtime, update.results)
- ?: throw WebApplicationException("Job not found", 404)
- } catch (e: IllegalArgumentException) {
- throw WebApplicationException(e, 400)
- } catch (e: IllegalStateException) {
- throw WebApplicationException(e, 409)
- }
- }
-}
diff --git a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/user/PortfolioResource.kt b/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/user/PortfolioResource.kt
deleted file mode 100644
index 82843a5a..00000000
--- a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/user/PortfolioResource.kt
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.web.server.rest.user
-
-import io.quarkus.security.identity.SecurityIdentity
-import org.opendc.web.proto.user.Portfolio
-import org.opendc.web.server.service.PortfolioService
-import javax.annotation.security.RolesAllowed
-import javax.inject.Inject
-import javax.transaction.Transactional
-import javax.validation.Valid
-import javax.ws.rs.DELETE
-import javax.ws.rs.GET
-import javax.ws.rs.POST
-import javax.ws.rs.Path
-import javax.ws.rs.PathParam
-import javax.ws.rs.WebApplicationException
-
-/**
- * A resource representing the portfolios of a project.
- */
-@Path("/projects/{project}/portfolios")
-@RolesAllowed("openid")
-class PortfolioResource @Inject constructor(
- private val portfolioService: PortfolioService,
- private val identity: SecurityIdentity
-) {
- /**
- * Get all portfolios that belong to the specified project.
- */
- @GET
- fun getAll(@PathParam("project") projectId: Long): List<Portfolio> {
- return portfolioService.findByUser(identity.principal.name, projectId)
- }
-
- /**
- * Create a portfolio for this project.
- */
- @POST
- @Transactional
- fun create(@PathParam("project") projectId: Long, @Valid request: Portfolio.Create): Portfolio {
- return portfolioService.create(identity.principal.name, projectId, request) ?: throw WebApplicationException("Project not found", 404)
- }
-
- /**
- * Obtain a portfolio by its identifier.
- */
- @GET
- @Path("{portfolio}")
- fun get(@PathParam("project") projectId: Long, @PathParam("portfolio") number: Int): Portfolio {
- return portfolioService.findByUser(identity.principal.name, projectId, number) ?: throw WebApplicationException("Portfolio not found", 404)
- }
-
- /**
- * Delete a portfolio.
- */
- @DELETE
- @Path("{portfolio}")
- @Transactional
- fun delete(@PathParam("project") projectId: Long, @PathParam("portfolio") number: Int): Portfolio {
- return portfolioService.delete(identity.principal.name, projectId, number) ?: throw WebApplicationException("Portfolio not found", 404)
- }
-}
diff --git a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/user/PortfolioScenarioResource.kt b/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/user/PortfolioScenarioResource.kt
deleted file mode 100644
index 82f35127..00000000
--- a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/user/PortfolioScenarioResource.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.web.server.rest.user
-
-import io.quarkus.security.identity.SecurityIdentity
-import org.opendc.web.proto.user.Scenario
-import org.opendc.web.server.service.ScenarioService
-import javax.annotation.security.RolesAllowed
-import javax.inject.Inject
-import javax.transaction.Transactional
-import javax.validation.Valid
-import javax.ws.rs.GET
-import javax.ws.rs.POST
-import javax.ws.rs.Path
-import javax.ws.rs.PathParam
-import javax.ws.rs.WebApplicationException
-
-/**
- * A resource representing the scenarios of a portfolio.
- */
-@Path("/projects/{project}/portfolios/{portfolio}/scenarios")
-@RolesAllowed("openid")
-class PortfolioScenarioResource @Inject constructor(
- private val scenarioService: ScenarioService,
- private val identity: SecurityIdentity
-) {
- /**
- * Get all scenarios that belong to the specified portfolio.
- */
- @GET
- fun get(@PathParam("project") projectId: Long, @PathParam("portfolio") portfolioNumber: Int): List<Scenario> {
- return scenarioService.findAll(identity.principal.name, projectId, portfolioNumber)
- }
-
- /**
- * Create a scenario for this portfolio.
- */
- @POST
- @Transactional
- fun create(@PathParam("project") projectId: Long, @PathParam("portfolio") portfolioNumber: Int, @Valid request: Scenario.Create): Scenario {
- return scenarioService.create(identity.principal.name, projectId, portfolioNumber, request) ?: throw WebApplicationException("Portfolio not found", 404)
- }
-}
diff --git a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/user/ProjectResource.kt b/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/user/ProjectResource.kt
deleted file mode 100644
index d12fc690..00000000
--- a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/user/ProjectResource.kt
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.web.server.rest.user
-
-import io.quarkus.security.identity.SecurityIdentity
-import org.opendc.web.proto.user.Project
-import org.opendc.web.server.service.ProjectService
-import javax.annotation.security.RolesAllowed
-import javax.inject.Inject
-import javax.transaction.Transactional
-import javax.validation.Valid
-import javax.ws.rs.DELETE
-import javax.ws.rs.GET
-import javax.ws.rs.POST
-import javax.ws.rs.Path
-import javax.ws.rs.PathParam
-import javax.ws.rs.WebApplicationException
-
-/**
- * A resource representing the created projects.
- */
-@Path("/projects")
-@RolesAllowed("openid")
-class ProjectResource @Inject constructor(
- private val projectService: ProjectService,
- private val identity: SecurityIdentity
-) {
- /**
- * Obtain all the projects of the current user.
- */
- @GET
- fun getAll(): List<Project> {
- return projectService.findByUser(identity.principal.name)
- }
-
- /**
- * Create a new project for the current user.
- */
- @POST
- @Transactional
- fun create(@Valid request: Project.Create): Project {
- return projectService.create(identity.principal.name, request.name)
- }
-
- /**
- * Obtain a single project by its identifier.
- */
- @GET
- @Path("{project}")
- fun get(@PathParam("project") id: Long): Project {
- return projectService.findByUser(identity.principal.name, id) ?: throw WebApplicationException("Project not found", 404)
- }
-
- /**
- * Delete a project.
- */
- @DELETE
- @Path("{project}")
- @Transactional
- fun delete(@PathParam("project") id: Long): Project {
- try {
- return projectService.delete(identity.principal.name, id) ?: throw WebApplicationException("Project not found", 404)
- } catch (e: IllegalArgumentException) {
- throw WebApplicationException(e.message, 403)
- }
- }
-}
diff --git a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/user/ScenarioResource.kt b/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/user/ScenarioResource.kt
deleted file mode 100644
index 56bb4290..00000000
--- a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/user/ScenarioResource.kt
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.web.server.rest.user
-
-import io.quarkus.security.identity.SecurityIdentity
-import org.opendc.web.proto.user.Scenario
-import org.opendc.web.server.service.ScenarioService
-import javax.annotation.security.RolesAllowed
-import javax.inject.Inject
-import javax.transaction.Transactional
-import javax.ws.rs.DELETE
-import javax.ws.rs.GET
-import javax.ws.rs.Path
-import javax.ws.rs.PathParam
-import javax.ws.rs.WebApplicationException
-
-/**
- * A resource representing the scenarios of a portfolio.
- */
-@Path("/projects/{project}/scenarios")
-@RolesAllowed("openid")
-class ScenarioResource @Inject constructor(
- private val scenarioService: ScenarioService,
- private val identity: SecurityIdentity
-) {
- /**
- * Obtain a scenario by its identifier.
- */
- @GET
- @Path("{scenario}")
- fun get(@PathParam("project") projectId: Long, @PathParam("scenario") number: Int): Scenario {
- return scenarioService.findOne(identity.principal.name, projectId, number) ?: throw WebApplicationException("Scenario not found", 404)
- }
-
- /**
- * Delete a scenario.
- */
- @DELETE
- @Path("{scenario}")
- @Transactional
- fun delete(@PathParam("project") projectId: Long, @PathParam("scenario") number: Int): Scenario {
- return scenarioService.delete(identity.principal.name, projectId, number) ?: throw WebApplicationException("Scenario not found", 404)
- }
-}
diff --git a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/user/TopologyResource.kt b/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/user/TopologyResource.kt
deleted file mode 100644
index 8eef66c8..00000000
--- a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/user/TopologyResource.kt
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.web.server.rest.user
-
-import io.quarkus.security.identity.SecurityIdentity
-import org.opendc.web.proto.user.Topology
-import org.opendc.web.server.service.TopologyService
-import javax.annotation.security.RolesAllowed
-import javax.inject.Inject
-import javax.transaction.Transactional
-import javax.validation.Valid
-import javax.ws.rs.DELETE
-import javax.ws.rs.GET
-import javax.ws.rs.POST
-import javax.ws.rs.PUT
-import javax.ws.rs.Path
-import javax.ws.rs.PathParam
-import javax.ws.rs.WebApplicationException
-
-/**
- * A resource representing the constructed datacenter topologies.
- */
-@Path("/projects/{project}/topologies")
-@RolesAllowed("openid")
-class TopologyResource @Inject constructor(
- private val topologyService: TopologyService,
- private val identity: SecurityIdentity
-) {
- /**
- * Get all topologies that belong to the specified project.
- */
- @GET
- fun getAll(@PathParam("project") projectId: Long): List<Topology> {
- return topologyService.findAll(identity.principal.name, projectId)
- }
-
- /**
- * Create a topology for this project.
- */
- @POST
- @Transactional
- fun create(@PathParam("project") projectId: Long, @Valid request: Topology.Create): Topology {
- return topologyService.create(identity.principal.name, projectId, request) ?: throw WebApplicationException("Topology not found", 404)
- }
-
- /**
- * Obtain a topology by its number.
- */
- @GET
- @Path("{topology}")
- fun get(@PathParam("project") projectId: Long, @PathParam("topology") number: Int): Topology {
- return topologyService.findOne(identity.principal.name, projectId, number) ?: throw WebApplicationException("Topology not found", 404)
- }
-
- /**
- * Update the specified topology by its number.
- */
- @PUT
- @Path("{topology}")
- @Transactional
- fun update(@PathParam("project") projectId: Long, @PathParam("topology") number: Int, @Valid request: Topology.Update): Topology {
- return topologyService.update(identity.principal.name, projectId, number, request) ?: throw WebApplicationException("Topology not found", 404)
- }
-
- /**
- * Delete the specified topology.
- */
- @Path("{topology}")
- @DELETE
- @Transactional
- fun delete(@PathParam("project") projectId: Long, @PathParam("topology") number: Int): Topology {
- return topologyService.delete(identity.principal.name, projectId, number) ?: throw WebApplicationException("Topology not found", 404)
- }
-}
diff --git a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/user/UserResource.kt b/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/user/UserResource.kt
deleted file mode 100644
index d640cc08..00000000
--- a/opendc-web/opendc-web-server/src/main/kotlin/org/opendc/web/server/rest/user/UserResource.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.web.server.rest.user
-
-import io.quarkus.security.identity.SecurityIdentity
-import org.opendc.web.proto.user.User
-import org.opendc.web.server.service.UserService
-import javax.annotation.security.RolesAllowed
-import javax.inject.Inject
-import javax.ws.rs.GET
-import javax.ws.rs.Path
-
-/**
- * A resource representing the active user.
- */
-@Path("/users")
-@RolesAllowed("openid")
-class UserResource @Inject constructor(private val userService: UserService, private val identity: SecurityIdentity) {
- /**
- * Get the current active user data.
- */
- @GET
- @Path("me")
- fun get(): User = userService.getUser(identity)
-}
diff --git a/opendc-web/opendc-web-server/src/main/resources/application-test.properties b/opendc-web/opendc-web-server/src/main/resources/application-test.properties
index 338a00b9..17502b6c 100644
--- a/opendc-web/opendc-web-server/src/main/resources/application-test.properties
+++ b/opendc-web/opendc-web-server/src/main/resources/application-test.properties
@@ -23,6 +23,7 @@ quarkus.datasource.db-kind = h2
quarkus.datasource.jdbc.url=jdbc:h2:mem:default;DB_CLOSE_DELAY=-1;INIT=CREATE TYPE IF NOT EXISTS "JSONB" AS json;
quarkus.hibernate-orm.dialect=org.hibernate.dialect.H2Dialect
+quarkus.hibernate-orm.log.sql=true
quarkus.flyway.clean-at-start=true
# Disable security
diff --git a/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/SchedulerResourceTest.kt b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/SchedulerResourceTest.java
index c1460db9..feeac4d3 100644
--- a/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/SchedulerResourceTest.kt
+++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/SchedulerResourceTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 AtLarge Research
+ * Copyright (c) 2023 AtLarge Research
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -20,29 +20,26 @@
* SOFTWARE.
*/
-package org.opendc.web.server.rest
+package org.opendc.web.server.rest;
-import io.quarkus.test.junit.QuarkusTest
-import io.restassured.http.ContentType
-import io.restassured.module.kotlin.extensions.Then
-import io.restassured.module.kotlin.extensions.When
-import org.junit.jupiter.api.Test
+import static io.restassured.RestAssured.when;
+
+import io.quarkus.test.common.http.TestHTTPEndpoint;
+import io.quarkus.test.junit.QuarkusTest;
+import io.restassured.http.ContentType;
+import org.junit.jupiter.api.Test;
/**
- * Test suite for [SchedulerResource]
+ * Test suite for {@link SchedulerResource}.
*/
@QuarkusTest
-class SchedulerResourceTest {
+@TestHTTPEndpoint(SchedulerResource.class)
+public final class SchedulerResourceTest {
/**
* Test to verify whether we can obtain all schedulers.
*/
@Test
- fun testGetSchedulers() {
- When {
- get("/schedulers")
- } Then {
- statusCode(200)
- contentType(ContentType.JSON)
- }
+ public void testGetSchedulers() {
+ when().get().then().statusCode(200).contentType(ContentType.JSON);
}
}
diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/TraceResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/TraceResourceTest.java
new file mode 100644
index 00000000..ebef3945
--- /dev/null
+++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/TraceResourceTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2023 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.web.server.rest;
+
+import static io.restassured.RestAssured.when;
+import static org.hamcrest.Matchers.equalTo;
+
+import io.quarkus.panache.mock.PanacheMock;
+import io.quarkus.test.common.http.TestHTTPEndpoint;
+import io.quarkus.test.junit.QuarkusTest;
+import io.restassured.http.ContentType;
+import java.util.stream.Stream;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.opendc.web.server.model.Trace;
+
+/**
+ * Test suite for {@link TraceResource}.
+ */
+@QuarkusTest
+@TestHTTPEndpoint(TraceResource.class)
+public final class TraceResourceTest {
+ /**
+ * Set up the test environment.
+ */
+ @BeforeEach
+ public void setUp() {
+ PanacheMock.mock(Trace.class);
+ }
+
+ /**
+ * Test that tries to obtain all traces (empty response).
+ */
+ @Test
+ public void testGetAllEmpty() {
+ Mockito.when(Trace.streamAll()).thenReturn(Stream.of());
+
+ when().get().then().statusCode(200).contentType(ContentType.JSON).body("", Matchers.empty());
+ }
+
+ /**
+ * Test that tries to obtain a non-existent trace.
+ */
+ @Test
+ public void testGetNonExisting() {
+ Mockito.when(Trace.findById("bitbrains")).thenReturn(null);
+
+ when().get("/bitbrains").then().statusCode(404).contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to obtain an existing trace.
+ */
+ @Test
+ public void testGetExisting() {
+ Mockito.when(Trace.findById("bitbrains")).thenReturn(new Trace("bitbrains", "Bitbrains", "VM"));
+
+ when().get("/bitbrains")
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON)
+ .body("name", equalTo("Bitbrains"));
+ }
+}
diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/runner/JobResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/runner/JobResourceTest.java
new file mode 100644
index 00000000..a163cd29
--- /dev/null
+++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/runner/JobResourceTest.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2023 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.web.server.rest.runner;
+
+import static io.restassured.RestAssured.given;
+import static io.restassured.RestAssured.when;
+import static org.hamcrest.Matchers.equalTo;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+
+import io.quarkus.test.common.http.TestHTTPEndpoint;
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.mockito.InjectMock;
+import io.quarkus.test.security.TestSecurity;
+import io.restassured.http.ContentType;
+import java.time.Instant;
+import java.util.List;
+import java.util.Set;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.opendc.web.proto.JobState;
+import org.opendc.web.proto.OperationalPhenomena;
+import org.opendc.web.proto.Targets;
+import org.opendc.web.proto.Trace;
+import org.opendc.web.proto.Workload;
+import org.opendc.web.proto.runner.Job;
+import org.opendc.web.proto.runner.Portfolio;
+import org.opendc.web.proto.runner.Scenario;
+import org.opendc.web.proto.runner.Topology;
+import org.opendc.web.server.service.JobService;
+
+/**
+ * Test suite for {@link JobResource}.
+ */
+@QuarkusTest
+@TestHTTPEndpoint(JobResource.class)
+public final class JobResourceTest {
+ @InjectMock
+ private JobService jobService;
+
+ /**
+ * Dummy values
+ */
+ private final Portfolio dummyPortfolio = new Portfolio(1, 1, "test", new Targets(Set.of(), 1));
+
+ private final Topology dummyTopology = new Topology(1, 1, "test", List.of(), Instant.now(), Instant.now());
+ private final Trace dummyTrace = new Trace("bitbrains", "Bitbrains", "vm");
+ private final Scenario dummyScenario = new Scenario(
+ 1,
+ 1,
+ dummyPortfolio,
+ "test",
+ new Workload(dummyTrace, 1.0),
+ dummyTopology,
+ new OperationalPhenomena(false, false),
+ "test");
+ private final Job dummyJob = new Job(1, dummyScenario, JobState.PENDING, Instant.now(), Instant.now(), 0, null);
+
+ /**
+ * Test that tries to query the pending jobs without token.
+ */
+ @Test
+ public void testQueryWithoutToken() {
+ when().get().then().statusCode(401);
+ }
+
+ /**
+ * Test that tries to query the pending jobs for a user.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testQueryInvalidScope() {
+ when().get().then().statusCode(403);
+ }
+
+ /**
+ * Test that tries to query the pending jobs for a runner.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"runner"})
+ public void testQuery() {
+ Mockito.when(jobService.listPending()).thenReturn(List.of(dummyJob));
+
+ when().get().then().statusCode(200).contentType(ContentType.JSON).body("get(0).id", equalTo(1));
+ }
+
+ /**
+ * Test that tries to obtain a non-existent job.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"runner"})
+ public void testGetNonExisting() {
+ Mockito.when(jobService.findById(1)).thenReturn(null);
+
+ when().get("/1").then().statusCode(404).contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to obtain a job.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"runner"})
+ public void testGetExisting() {
+ Mockito.when(jobService.findById(1)).thenReturn(dummyJob);
+
+ when().get("/1").then().statusCode(200).contentType(ContentType.JSON).body("id", equalTo(1));
+ }
+
+ /**
+ * Test that tries to update a non-existent job.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"runner"})
+ public void testUpdateNonExistent() {
+ Mockito.when(jobService.updateState(eq(1L), any(), anyInt(), any())).thenReturn(null);
+
+ given().body(new Job.Update(JobState.PENDING, 0, null))
+ .contentType(ContentType.JSON)
+ .when()
+ .post("/1")
+ .then()
+ .statusCode(404)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to update a job.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"runner"})
+ public void testUpdateState() {
+ Mockito.when(jobService.updateState(eq(1L), any(), anyInt(), any()))
+ .thenReturn(new Job(1, dummyScenario, JobState.CLAIMED, Instant.now(), Instant.now(), 0, null));
+
+ given().body(new Job.Update(JobState.CLAIMED, 0, null))
+ .contentType(ContentType.JSON)
+ .when()
+ .post("/1")
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON)
+ .body("state", equalTo(JobState.CLAIMED.toString()));
+ }
+
+ /**
+ * Test that tries to update a job with invalid input.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"runner"})
+ public void testUpdateInvalidInput() {
+ given().body("{ \"test\": \"test\" }")
+ .contentType(ContentType.JSON)
+ .when()
+ .post("/1")
+ .then()
+ .statusCode(400)
+ .contentType(ContentType.JSON);
+ }
+}
diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioResourceTest.java
new file mode 100644
index 00000000..cc3ac978
--- /dev/null
+++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioResourceTest.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2023 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.web.server.rest.user;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.equalTo;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+
+import io.quarkus.test.common.http.TestHTTPEndpoint;
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.mockito.InjectMock;
+import io.quarkus.test.security.TestSecurity;
+import io.restassured.http.ContentType;
+import java.time.Instant;
+import java.util.List;
+import java.util.Set;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.opendc.web.proto.Targets;
+import org.opendc.web.proto.user.Portfolio;
+import org.opendc.web.proto.user.Project;
+import org.opendc.web.proto.user.ProjectRole;
+import org.opendc.web.server.service.PortfolioService;
+
+/**
+ * Test suite for {@link PortfolioResource}.
+ */
+@QuarkusTest
+@TestHTTPEndpoint(PortfolioResource.class)
+public final class PortfolioResourceTest {
+ @InjectMock
+ private PortfolioService portfolioService;
+
+ /**
+ * Dummy project and portfolio
+ */
+ private final Project dummyProject = new Project(1, "test", Instant.now(), Instant.now(), ProjectRole.OWNER);
+
+ private final Portfolio dummyPortfolio =
+ new Portfolio(1, 1, dummyProject, "test", new Targets(Set.of(), 1), List.of());
+
+ /**
+ * Test that tries to obtain the list of portfolios belonging to a project.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testGetForProject() {
+ Mockito.when(portfolioService.findByUser("testUser", 1)).thenReturn(List.of());
+
+ given().pathParam("project", 1).when().get().then().statusCode(200).contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to create a topology for a project.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testCreateNonExistent() {
+ Mockito.when(portfolioService.create(eq("testUser"), eq(1), any())).thenReturn(null);
+
+ given().pathParam("project", "1")
+ .body(new Portfolio.Create("test", new Targets(Set.of(), 1)))
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(404)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to create a portfolio for a scenario.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testCreate() {
+ Mockito.when(portfolioService.create(eq("testUser"), eq(1L), any())).thenReturn(dummyPortfolio);
+
+ given().pathParam("project", "1")
+ .body(new Portfolio.Create("test", new Targets(Set.of(), 1)))
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON)
+ .body("id", equalTo(1))
+ .body("name", equalTo("test"));
+ }
+
+ /**
+ * Test to create a portfolio with an empty body.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testCreateEmpty() {
+ given().pathParam("project", "1")
+ .body("{}")
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(400)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test to create a portfolio with a blank name.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testCreateBlankName() {
+ given().pathParam("project", "1")
+ .body(new Portfolio.Create("", new Targets(Set.of(), 1)))
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(400)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to obtain a portfolio without token.
+ */
+ @Test
+ public void testGetWithoutToken() {
+ given().pathParam("project", "1").when().get("/1").then().statusCode(401);
+ }
+
+ /**
+ * Test that tries to obtain a portfolio with an invalid scope.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"runner"})
+ public void testGetInvalidToken() {
+ given().pathParam("project", "1").when().get("/1").then().statusCode(403);
+ }
+
+ /**
+ * Test that tries to obtain a non-existent portfolio.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testGetNonExisting() {
+ Mockito.when(portfolioService.findByUser("testUser", 1, 1)).thenReturn(null);
+
+ given().pathParam("project", "1")
+ .when()
+ .get("/1")
+ .then()
+ .statusCode(404)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to obtain a portfolio.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testGetExisting() {
+ Mockito.when(portfolioService.findByUser("testUser", 1, 1)).thenReturn(dummyPortfolio);
+
+ given().pathParam("project", "1")
+ .when()
+ .get("/1")
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON)
+ .body("id", equalTo(1));
+ }
+
+ /**
+ * Test to delete a non-existent portfolio.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testDeleteNonExistent() {
+ Mockito.when(portfolioService.delete("testUser", 1, 1)).thenReturn(null);
+
+ given().pathParam("project", "1").when().delete("/1").then().statusCode(404);
+ }
+
+ /**
+ * Test to delete a portfolio.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testDelete() {
+ Mockito.when(portfolioService.delete("testUser", 1, 1)).thenReturn(dummyPortfolio);
+
+ given().pathParam("project", "1")
+ .when()
+ .delete("/1")
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON);
+ }
+}
diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioScenarioResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioScenarioResourceTest.java
new file mode 100644
index 00000000..8cb95a98
--- /dev/null
+++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioScenarioResourceTest.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2023 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.web.server.rest.user;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.equalTo;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+
+import io.quarkus.test.common.http.TestHTTPEndpoint;
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.mockito.InjectMock;
+import io.quarkus.test.security.TestSecurity;
+import io.restassured.http.ContentType;
+import java.time.Instant;
+import java.util.List;
+import java.util.Set;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.opendc.web.proto.JobState;
+import org.opendc.web.proto.OperationalPhenomena;
+import org.opendc.web.proto.Targets;
+import org.opendc.web.proto.Trace;
+import org.opendc.web.proto.Workload;
+import org.opendc.web.proto.user.Job;
+import org.opendc.web.proto.user.Portfolio;
+import org.opendc.web.proto.user.Project;
+import org.opendc.web.proto.user.ProjectRole;
+import org.opendc.web.proto.user.Scenario;
+import org.opendc.web.proto.user.Topology;
+import org.opendc.web.server.service.ScenarioService;
+
+/**
+ * Test suite for {@link PortfolioScenarioResource}.
+ */
+@QuarkusTest
+@TestHTTPEndpoint(PortfolioScenarioResource.class)
+public final class PortfolioScenarioResourceTest {
+ @InjectMock
+ private ScenarioService scenarioService;
+
+ /**
+ * Dummy values
+ */
+ private final Project dummyProject = new Project(0, "test", Instant.now(), Instant.now(), ProjectRole.OWNER);
+
+ private final Portfolio.Summary dummyPortfolio = new Portfolio.Summary(1, 1, "test", new Targets(Set.of(), 1));
+ private final Job dummyJob = new Job(1, JobState.PENDING, Instant.now(), Instant.now(), null);
+ private final Trace dummyTrace = new Trace("bitbrains", "Bitbrains", "vm");
+ private final Topology.Summary dummyTopology = new Topology.Summary(1, 1, "test", Instant.now(), Instant.now());
+ private final Scenario dummyScenario = new Scenario(
+ 1,
+ 1,
+ dummyProject,
+ dummyPortfolio,
+ "test",
+ new Workload(dummyTrace, 1.0),
+ dummyTopology,
+ new OperationalPhenomena(false, false),
+ "test",
+ dummyJob);
+
+ /**
+ * Test that tries to obtain a portfolio without token.
+ */
+ @Test
+ public void testGetWithoutToken() {
+ given().pathParam("project", "1")
+ .pathParam("portfolio", "1")
+ .when()
+ .get()
+ .then()
+ .statusCode(401);
+ }
+
+ /**
+ * Test that tries to obtain a portfolio with an invalid scope.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"runner"})
+ public void testGetInvalidToken() {
+ given().pathParam("project", "1")
+ .pathParam("portfolio", "1")
+ .when()
+ .get()
+ .then()
+ .statusCode(403);
+ }
+
+ /**
+ * Test that tries to obtain a non-existent portfolio.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testGet() {
+ Mockito.when(scenarioService.findAll("testUser", 1, 1)).thenReturn(List.of());
+
+ given().pathParam("project", "1")
+ .pathParam("portfolio", "1")
+ .when()
+ .get()
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to create a scenario for a portfolio.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testCreateNonExistent() {
+ Mockito.when(scenarioService.create(eq("testUser"), eq(1L), anyInt(), any()))
+ .thenReturn(null);
+
+ given().pathParam("project", "1")
+ .pathParam("portfolio", "1")
+ .body(new Scenario.Create(
+ "test", new Workload.Spec("test", 1.0), 1, new OperationalPhenomena(false, false), "test"))
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(404)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to create a scenario for a portfolio.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testCreate() {
+ Mockito.when(scenarioService.create(eq("testUser"), eq(1L), eq(1), any()))
+ .thenReturn(dummyScenario);
+
+ given().pathParam("project", "1")
+ .pathParam("portfolio", "1")
+ .body(new Scenario.Create(
+ "test", new Workload.Spec("test", 1.0), 1, new OperationalPhenomena(false, false), "test"))
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON)
+ .body("id", equalTo(1))
+ .body("name", equalTo("test"));
+ }
+
+ /**
+ * Test to create a project with an empty body.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testCreateEmpty() {
+ given().pathParam("project", "1")
+ .pathParam("portfolio", "1")
+ .body("{}")
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(400)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test to create a project with a blank name.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testCreateBlankName() {
+ given().pathParam("project", "1")
+ .pathParam("portfolio", "1")
+ .body(new Scenario.Create(
+ "", new Workload.Spec("test", 1.0), 1, new OperationalPhenomena(false, false), "test"))
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(400)
+ .contentType(ContentType.JSON);
+ }
+}
diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ProjectResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ProjectResourceTest.java
new file mode 100644
index 00000000..7ca314a6
--- /dev/null
+++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ProjectResourceTest.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2023 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.web.server.rest.user;
+
+import static io.restassured.RestAssured.given;
+import static io.restassured.RestAssured.when;
+import static org.hamcrest.Matchers.equalTo;
+
+import io.quarkus.test.common.http.TestHTTPEndpoint;
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.mockito.InjectMock;
+import io.quarkus.test.security.TestSecurity;
+import io.restassured.http.ContentType;
+import java.time.Instant;
+import java.util.List;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.opendc.web.proto.user.Project;
+import org.opendc.web.proto.user.ProjectRole;
+import org.opendc.web.server.service.ProjectService;
+
+/**
+ * Test suite for [ProjectResource].
+ */
+@QuarkusTest
+@TestHTTPEndpoint(ProjectResource.class)
+public final class ProjectResourceTest {
+ @InjectMock
+ private ProjectService projectService;
+
+ /**
+ * Dummy values.
+ */
+ private final Project dummyProject = new Project(0, "test", Instant.now(), Instant.now(), ProjectRole.OWNER);
+
+ /**
+ * Test that tries to obtain all projects without token.
+ */
+ @Test
+ public void testGetAllWithoutToken() {
+ when().get().then().statusCode(401);
+ }
+
+ /**
+ * Test that tries to obtain all projects with an invalid scope.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"runner"})
+ public void testGetAllWithInvalidScope() {
+ when().get().then().statusCode(403);
+ }
+
+ /**
+ * Test that tries to obtain all project for a user.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testGetAll() {
+ Mockito.when(projectService.findByUser("testUser")).thenReturn(List.of(dummyProject));
+
+ when().get().then().statusCode(200).contentType(ContentType.JSON).body("get(0).name", equalTo("test"));
+ }
+
+ /**
+ * Test that tries to obtain a non-existent project.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testGetNonExisting() {
+ Mockito.when(projectService.findByUser("testUser", 1)).thenReturn(null);
+
+ when().get("/1").then().statusCode(404).contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to obtain a job.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testGetExisting() {
+ Mockito.when(projectService.findByUser("testUser", 1)).thenReturn(dummyProject);
+
+ when().get("/1").then().statusCode(200).contentType(ContentType.JSON).body("id", equalTo(0));
+ }
+
+ /**
+ * Test that tries to create a project.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testCreate() {
+ Mockito.when(projectService.create("testUser", "test")).thenReturn(dummyProject);
+
+ given().body(new Project.Create("test"))
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON)
+ .body("id", equalTo(0))
+ .body("name", equalTo("test"));
+ }
+
+ /**
+ * Test to create a project with an empty body.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testCreateEmpty() {
+ given().body("{}")
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(400)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test to create a project with a blank name.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testCreateBlankName() {
+ given().body(new Project.Create(""))
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(400)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test to delete a non-existent project.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testDeleteNonExistent() {
+ Mockito.when(projectService.delete("testUser", 1)).thenReturn(null);
+
+ when().delete("/1").then().statusCode(404).contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test to delete a project.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testDelete() {
+ Mockito.when(projectService.delete("testUser", 1)).thenReturn(dummyProject);
+
+ when().delete("/1").then().statusCode(200).contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test to delete a project which the user does not own.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testDeleteNonOwner() {
+ Mockito.when(projectService.delete("testUser", 1))
+ .thenThrow(new IllegalArgumentException("User does not own project"));
+
+ when().delete("/1").then().statusCode(403).contentType(ContentType.JSON);
+ }
+}
diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ScenarioResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ScenarioResourceTest.java
new file mode 100644
index 00000000..850236d6
--- /dev/null
+++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ScenarioResourceTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2023 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.web.server.rest.user;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.equalTo;
+
+import io.quarkus.test.common.http.TestHTTPEndpoint;
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.mockito.InjectMock;
+import io.quarkus.test.security.TestSecurity;
+import io.restassured.http.ContentType;
+import java.time.Instant;
+import java.util.Set;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.opendc.web.proto.JobState;
+import org.opendc.web.proto.OperationalPhenomena;
+import org.opendc.web.proto.Targets;
+import org.opendc.web.proto.Trace;
+import org.opendc.web.proto.Workload;
+import org.opendc.web.proto.user.Job;
+import org.opendc.web.proto.user.Portfolio;
+import org.opendc.web.proto.user.Project;
+import org.opendc.web.proto.user.ProjectRole;
+import org.opendc.web.proto.user.Scenario;
+import org.opendc.web.proto.user.Topology;
+import org.opendc.web.server.service.ScenarioService;
+
+/**
+ * Test suite for [ScenarioResource].
+ */
+@QuarkusTest
+@TestHTTPEndpoint(ScenarioResource.class)
+public final class ScenarioResourceTest {
+ @InjectMock
+ private ScenarioService scenarioService;
+
+ /**
+ * Dummy values
+ */
+ private final Project dummyProject = new Project(0, "test", Instant.now(), Instant.now(), ProjectRole.OWNER);
+
+ private final Portfolio.Summary dummyPortfolio = new Portfolio.Summary(1, 1, "test", new Targets(Set.of(), 1));
+ private final Job dummyJob = new Job(1, JobState.PENDING, Instant.now(), Instant.now(), null);
+ private final Trace dummyTrace = new Trace("bitbrains", "Bitbrains", "vm");
+ private final Topology.Summary dummyTopology = new Topology.Summary(1, 1, "test", Instant.now(), Instant.now());
+ private final Scenario dummyScenario = new Scenario(
+ 1,
+ 1,
+ dummyProject,
+ dummyPortfolio,
+ "test",
+ new Workload(dummyTrace, 1.0),
+ dummyTopology,
+ new OperationalPhenomena(false, false),
+ "test",
+ dummyJob);
+
+ /**
+ * Test that tries to obtain a scenario without token.
+ */
+ @Test
+ public void testGetWithoutToken() {
+ given().pathParam("project", "1").when().get("/1").then().statusCode(401);
+ }
+
+ /**
+ * Test that tries to obtain a scenario with an invalid scope.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"runner"})
+ public void testGetInvalidToken() {
+ given().pathParam("project", "1").when().get("/1").then().statusCode(403);
+ }
+
+ /**
+ * Test that tries to obtain a non-existent scenario.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testGetNonExisting() {
+ Mockito.when(scenarioService.findOne("testUser", 1, 1)).thenReturn(null);
+
+ given().pathParam("project", "1")
+ .when()
+ .get("/1")
+ .then()
+ .statusCode(404)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to obtain a scenario.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testGetExisting() {
+ Mockito.when(scenarioService.findOne("testUser", 1, 1)).thenReturn(dummyScenario);
+
+ given().pathParam("project", "1")
+ .when()
+ .get("/1")
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON)
+ .body("id", equalTo(1));
+ }
+
+ /**
+ * Test to delete a non-existent scenario.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testDeleteNonExistent() {
+ Mockito.when(scenarioService.delete("testUser", 1, 1)).thenReturn(null);
+
+ given().pathParam("project", "1").when().delete("/1").then().statusCode(404);
+ }
+
+ /**
+ * Test to delete a scenario.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testDelete() {
+ Mockito.when(scenarioService.delete("testUser", 1, 1)).thenReturn(dummyScenario);
+
+ given().pathParam("project", "1")
+ .when()
+ .delete("/1")
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON);
+ }
+}
diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/TopologyResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/TopologyResourceTest.java
new file mode 100644
index 00000000..2cc6ea4b
--- /dev/null
+++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/TopologyResourceTest.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2023 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.web.server.rest.user;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.equalTo;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+
+import io.quarkus.test.common.http.TestHTTPEndpoint;
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.mockito.InjectMock;
+import io.quarkus.test.security.TestSecurity;
+import io.restassured.http.ContentType;
+import java.time.Instant;
+import java.util.List;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.opendc.web.proto.user.Project;
+import org.opendc.web.proto.user.ProjectRole;
+import org.opendc.web.proto.user.Topology;
+import org.opendc.web.server.service.TopologyService;
+
+/**
+ * Test suite for {@link TopologyResource}.
+ */
+@QuarkusTest
+@TestHTTPEndpoint(TopologyResource.class)
+public final class TopologyResourceTest {
+ @InjectMock
+ private TopologyService topologyService;
+
+ /**
+ * Dummy project and topology.
+ */
+ private final Project dummyProject = new Project(1, "test", Instant.now(), Instant.now(), ProjectRole.OWNER);
+
+ private final Topology dummyTopology =
+ new Topology(1, 1, dummyProject, "test", List.of(), Instant.now(), Instant.now());
+
+ /**
+ * Test that tries to obtain the list of topologies belonging to a project.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testGetForProject() {
+ Mockito.when(topologyService.findAll("testUser", 1)).thenReturn(List.of());
+
+ given().pathParam("project", "1").when().get().then().statusCode(200).contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to create a topology for a project.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testCreateNonExistent() {
+ Mockito.when(topologyService.create(eq("testUser"), eq(1L), any())).thenReturn(null);
+
+ given().pathParam("project", "1")
+ .body(new Topology.Create("test", List.of()))
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(404)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to create a topology for a project.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testCreate() {
+ Mockito.when(topologyService.create(eq("testUser"), eq(1L), any())).thenReturn(dummyTopology);
+
+ given().pathParam("project", "1")
+ .body(new Topology.Create("test", List.of()))
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON)
+ .body("id", equalTo(1))
+ .body("name", equalTo("test"));
+ }
+
+ /**
+ * Test to create a topology with an empty body.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testCreateEmpty() {
+ given().pathParam("project", "1")
+ .body("{}")
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(400)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test to create a topology with a blank name.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testCreateBlankName() {
+ given().pathParam("project", "1")
+ .body(new Topology.Create("", List.of()))
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(400)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to obtain a topology without token.
+ */
+ @Test
+ public void testGetWithoutToken() {
+ given().pathParam("project", "1").when().get("/1").then().statusCode(401);
+ }
+
+ /**
+ * Test that tries to obtain a topology with an invalid scope.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"runner"})
+ public void testGetInvalidToken() {
+ given().pathParam("project", "1").when().get("/1").then().statusCode(403);
+ }
+
+ /**
+ * Test that tries to obtain a non-existent topology.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testGetNonExisting() {
+ Mockito.when(topologyService.findOne("testUser", 1, 1)).thenReturn(null);
+
+ given().pathParam("project", "1")
+ .when()
+ .get("/1")
+ .then()
+ .statusCode(404)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to obtain a topology.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testGetExisting() {
+ Mockito.when(topologyService.findOne("testUser", 1, 1)).thenReturn(dummyTopology);
+
+ given().pathParam("project", "1")
+ .when()
+ .get("/1")
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON)
+ .body("id", equalTo(1));
+ }
+
+ /**
+ * Test to delete a non-existent topology.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testUpdateNonExistent() {
+ Mockito.when(topologyService.update(eq("testUser"), anyLong(), anyInt(), any()))
+ .thenReturn(null);
+
+ given().pathParam("project", "1")
+ .body(new Topology.Update(List.of()))
+ .contentType(ContentType.JSON)
+ .when()
+ .put("/1")
+ .then()
+ .statusCode(404);
+ }
+
+ /**
+ * Test to update a topology.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testUpdate() {
+ Mockito.when(topologyService.update(eq("testUser"), anyLong(), anyInt(), any()))
+ .thenReturn(dummyTopology);
+
+ given().pathParam("project", "1")
+ .body(new Topology.Update(List.of()))
+ .contentType(ContentType.JSON)
+ .when()
+ .put("/1")
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test to delete a non-existent topology.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testDeleteNonExistent() {
+ Mockito.when(topologyService.delete("testUser", 1, 1)).thenReturn(null);
+
+ given().pathParam("project", "1").when().delete("/1").then().statusCode(404);
+ }
+
+ /**
+ * Test to delete a topology.
+ */
+ @Test
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testDelete() {
+ Mockito.when(topologyService.delete("testUser", 1, 1)).thenReturn(dummyTopology);
+
+ given().pathParam("project", "1")
+ .when()
+ .delete("/1")
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON);
+ }
+}
diff --git a/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/user/UserResourceTest.kt b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/UserResourceTest.java
index 36af20f4..6dcb3b4d 100644
--- a/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/user/UserResourceTest.kt
+++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/UserResourceTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022 AtLarge Research
+ * Copyright (c) 2023 AtLarge Research
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -20,50 +20,46 @@
* SOFTWARE.
*/
-package org.opendc.web.server.rest.user
+package org.opendc.web.server.rest.user;
-import io.quarkus.test.common.http.TestHTTPEndpoint
-import io.quarkus.test.junit.QuarkusTest
-import io.quarkus.test.security.TestSecurity
-import io.restassured.http.ContentType
-import io.restassured.module.kotlin.extensions.Then
-import io.restassured.module.kotlin.extensions.When
-import org.hamcrest.Matchers
-import org.junit.jupiter.api.Test
+import static io.restassured.RestAssured.when;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.greaterThan;
+
+import io.quarkus.test.common.http.TestHTTPEndpoint;
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.security.TestSecurity;
+import io.restassured.http.ContentType;
+import org.junit.jupiter.api.Test;
/**
* Test suite for [UserResource].
*/
@QuarkusTest
-@TestHTTPEndpoint(UserResource::class)
-class UserResourceTest {
+@TestHTTPEndpoint(UserResource.class)
+public final class UserResourceTest {
/**
* Test that tries to obtain the profile of the active user.
*/
@Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testMe() {
- When {
- get("me")
- } Then {
- statusCode(200)
- contentType(ContentType.JSON)
-
- body("userId", Matchers.equalTo("testUser"))
- body("accounting.simulationTime", Matchers.equalTo(0))
- body("accounting.simulationTimeBudget", Matchers.greaterThan(0))
- }
+ @TestSecurity(
+ user = "testUser",
+ roles = {"openid"})
+ public void testMe() {
+ when().get("me")
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON)
+ .body("userId", equalTo("testUser"))
+ .body("accounting.simulationTime", equalTo(0))
+ .body("accounting.simulationTimeBudget", greaterThan(0));
}
/**
* Test that tries to obtain the profile of the active user without authorization.
*/
@Test
- fun testMeUnauthorized() {
- When {
- get("me")
- } Then {
- statusCode(401)
- }
+ public void testMeUnauthorized() {
+ when().get("me").then().statusCode(401);
}
}
diff --git a/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/TraceResourceTest.kt b/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/TraceResourceTest.kt
deleted file mode 100644
index 2490cf46..00000000
--- a/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/TraceResourceTest.kt
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.web.server.rest
-
-import io.mockk.every
-import io.quarkiverse.test.junit.mockk.InjectMock
-import io.quarkus.test.common.http.TestHTTPEndpoint
-import io.quarkus.test.junit.QuarkusMock
-import io.quarkus.test.junit.QuarkusTest
-import io.restassured.http.ContentType
-import io.restassured.module.kotlin.extensions.Then
-import io.restassured.module.kotlin.extensions.When
-import org.hamcrest.Matchers
-import org.hamcrest.Matchers.equalTo
-import org.junit.jupiter.api.BeforeEach
-import org.junit.jupiter.api.Test
-import org.opendc.web.proto.Trace
-import org.opendc.web.server.service.TraceService
-
-/**
- * Test suite for [TraceResource].
- */
-@QuarkusTest
-@TestHTTPEndpoint(TraceResource::class)
-class TraceResourceTest {
- @InjectMock
- private lateinit var traceService: TraceService
-
- @BeforeEach
- fun setUp() {
- QuarkusMock.installMockForType(traceService, TraceService::class.java)
- }
-
- /**
- * Test that tries to obtain all traces (empty response).
- */
- @Test
- fun testGetAllEmpy() {
- every { traceService.findAll() } returns emptyList()
-
- When {
- get()
- } Then {
- statusCode(200)
- contentType(ContentType.JSON)
- body("", Matchers.empty<String>())
- }
- }
-
- /**
- * Test that tries to obtain a non-existent trace.
- */
- @Test
- fun testGetNonExisting() {
- every { traceService.findById("bitbrains") } returns null
-
- When {
- get("/bitbrains")
- } Then {
- statusCode(404)
- contentType(ContentType.JSON)
- }
- }
-
- /**
- * Test that tries to obtain an existing trace.
- */
- @Test
- fun testGetExisting() {
- every { traceService.findById("bitbrains") } returns Trace("bitbrains", "Bitbrains", "VM")
-
- When {
- get("/bitbrains")
- } Then {
- statusCode(200)
- contentType(ContentType.JSON)
- body("name", equalTo("Bitbrains"))
- }
- }
-}
diff --git a/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/runner/JobResourceTest.kt b/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/runner/JobResourceTest.kt
deleted file mode 100644
index 753b9ac4..00000000
--- a/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/runner/JobResourceTest.kt
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.web.server.rest.runner
-
-import io.mockk.every
-import io.quarkiverse.test.junit.mockk.InjectMock
-import io.quarkus.test.common.http.TestHTTPEndpoint
-import io.quarkus.test.junit.QuarkusMock
-import io.quarkus.test.junit.QuarkusTest
-import io.quarkus.test.security.TestSecurity
-import io.restassured.http.ContentType
-import io.restassured.module.kotlin.extensions.Given
-import io.restassured.module.kotlin.extensions.Then
-import io.restassured.module.kotlin.extensions.When
-import org.hamcrest.Matchers.equalTo
-import org.junit.jupiter.api.BeforeEach
-import org.junit.jupiter.api.Test
-import org.opendc.web.proto.JobState
-import org.opendc.web.proto.OperationalPhenomena
-import org.opendc.web.proto.Targets
-import org.opendc.web.proto.Trace
-import org.opendc.web.proto.Workload
-import org.opendc.web.proto.runner.Job
-import org.opendc.web.proto.runner.Portfolio
-import org.opendc.web.proto.runner.Scenario
-import org.opendc.web.proto.runner.Topology
-import org.opendc.web.server.service.JobService
-import java.time.Instant
-
-/**
- * Test suite for [JobResource].
- */
-@QuarkusTest
-@TestHTTPEndpoint(JobResource::class)
-class JobResourceTest {
- @InjectMock
- private lateinit var jobService: JobService
-
- /**
- * Dummy values
- */
- private val dummyPortfolio = Portfolio(1, 1, "test", Targets(emptySet()))
- private val dummyTopology = Topology(1, 1, "test", emptyList(), Instant.now(), Instant.now())
- private val dummyTrace = Trace("bitbrains", "Bitbrains", "vm")
- private val dummyScenario = Scenario(1, 1, dummyPortfolio, "test", Workload(dummyTrace, 1.0), dummyTopology, OperationalPhenomena(false, false), "test")
- private val dummyJob = Job(1, dummyScenario, JobState.PENDING, Instant.now(), Instant.now(), 0)
-
- @BeforeEach
- fun setUp() {
- QuarkusMock.installMockForType(jobService, JobService::class.java)
- }
-
- /**
- * Test that tries to query the pending jobs without token.
- */
- @Test
- fun testQueryWithoutToken() {
- When {
- get()
- } Then {
- statusCode(401)
- }
- }
-
- /**
- * Test that tries to query the pending jobs for a user.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testQueryInvalidScope() {
- When {
- get()
- } Then {
- statusCode(403)
- }
- }
-
- /**
- * Test that tries to query the pending jobs for a runner.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["runner"])
- fun testQuery() {
- every { jobService.listPending() } returns listOf(dummyJob)
-
- When {
- get()
- } Then {
- statusCode(200)
- contentType(ContentType.JSON)
- body("get(0).id", equalTo(1))
- }
- }
-
- /**
- * Test that tries to obtain a non-existent job.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["runner"])
- fun testGetNonExisting() {
- every { jobService.findById(1) } returns null
-
- When {
- get("/1")
- } Then {
- statusCode(404)
- contentType(ContentType.JSON)
- }
- }
-
- /**
- * Test that tries to obtain a job.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["runner"])
- fun testGetExisting() {
- every { jobService.findById(1) } returns dummyJob
-
- When {
- get("/1")
- } Then {
- statusCode(200)
- contentType(ContentType.JSON)
- body("id", equalTo(1))
- }
- }
-
- /**
- * Test that tries to update a non-existent job.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["runner"])
- fun testUpdateNonExistent() {
- every { jobService.updateState(1, any(), any(), any()) } returns null
-
- Given {
- body(Job.Update(JobState.PENDING, 0))
- contentType(ContentType.JSON)
- } When {
- post("/1")
- } Then {
- statusCode(404)
- contentType(ContentType.JSON)
- }
- }
-
- /**
- * Test that tries to update a job.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["runner"])
- fun testUpdateState() {
- every { jobService.updateState(1, any(), any(), any()) } returns dummyJob.copy(state = JobState.CLAIMED)
-
- Given {
- body(Job.Update(JobState.CLAIMED, 0))
- contentType(ContentType.JSON)
- } When {
- post("/1")
- } Then {
- statusCode(200)
- contentType(ContentType.JSON)
- body("state", equalTo(JobState.CLAIMED.toString()))
- }
- }
-
- /**
- * Test that tries to update a job with invalid input.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["runner"])
- fun testUpdateInvalidInput() {
- Given {
- body("""{ "test": "test" }""")
- contentType(ContentType.JSON)
- } When {
- post("/1")
- } Then {
- statusCode(400)
- contentType(ContentType.JSON)
- }
- }
-}
diff --git a/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/user/PortfolioResourceTest.kt b/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/user/PortfolioResourceTest.kt
deleted file mode 100644
index 3ef63a51..00000000
--- a/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/user/PortfolioResourceTest.kt
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.web.server.rest.user
-
-import io.mockk.every
-import io.quarkiverse.test.junit.mockk.InjectMock
-import io.quarkus.test.common.http.TestHTTPEndpoint
-import io.quarkus.test.junit.QuarkusMock
-import io.quarkus.test.junit.QuarkusTest
-import io.quarkus.test.security.TestSecurity
-import io.restassured.http.ContentType
-import io.restassured.module.kotlin.extensions.Given
-import io.restassured.module.kotlin.extensions.Then
-import io.restassured.module.kotlin.extensions.When
-import org.hamcrest.Matchers
-import org.junit.jupiter.api.BeforeEach
-import org.junit.jupiter.api.Test
-import org.opendc.web.proto.Targets
-import org.opendc.web.proto.user.Portfolio
-import org.opendc.web.proto.user.Project
-import org.opendc.web.proto.user.ProjectRole
-import org.opendc.web.server.service.PortfolioService
-import java.time.Instant
-
-/**
- * Test suite for [PortfolioResource].
- */
-@QuarkusTest
-@TestHTTPEndpoint(PortfolioResource::class)
-class PortfolioResourceTest {
- @InjectMock
- private lateinit var portfolioService: PortfolioService
-
- /**
- * Dummy project and portfolio
- */
- private val dummyProject = Project(1, "test", Instant.now(), Instant.now(), ProjectRole.OWNER)
- private val dummyPortfolio = Portfolio(1, 1, dummyProject, "test", Targets(emptySet(), 1), emptyList())
-
- @BeforeEach
- fun setUp() {
- QuarkusMock.installMockForType(portfolioService, PortfolioService::class.java)
- }
-
- /**
- * Test that tries to obtain the list of portfolios belonging to a project.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testGetForProject() {
- every { portfolioService.findByUser("testUser", 1) } returns emptyList()
-
- Given {
- pathParam("project", "1")
- } When {
- get()
- } Then {
- statusCode(200)
- contentType(ContentType.JSON)
- }
- }
-
- /**
- * Test that tries to create a topology for a project.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testCreateNonExistent() {
- every { portfolioService.create("testUser", 1, any()) } returns null
-
- Given {
- pathParam("project", "1")
-
- body(Portfolio.Create("test", Targets(emptySet(), 1)))
- contentType(ContentType.JSON)
- } When {
- post()
- } Then {
- statusCode(404)
- contentType(ContentType.JSON)
- }
- }
-
- /**
- * Test that tries to create a portfolio for a scenario.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testCreate() {
- every { portfolioService.create("testUser", 1, any()) } returns dummyPortfolio
-
- Given {
- pathParam("project", "1")
-
- body(Portfolio.Create("test", Targets(emptySet(), 1)))
- contentType(ContentType.JSON)
- } When {
- post()
- } Then {
- statusCode(200)
- contentType(ContentType.JSON)
- body("id", Matchers.equalTo(1))
- body("name", Matchers.equalTo("test"))
- }
- }
-
- /**
- * Test to create a portfolio with an empty body.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testCreateEmpty() {
- Given {
- pathParam("project", "1")
-
- body("{}")
- contentType(ContentType.JSON)
- } When {
- post()
- } Then {
- statusCode(400)
- contentType(ContentType.JSON)
- }
- }
-
- /**
- * Test to create a portfolio with a blank name.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testCreateBlankName() {
- Given {
- pathParam("project", "1")
-
- body(Portfolio.Create("", Targets(emptySet(), 1)))
- contentType(ContentType.JSON)
- } When {
- post()
- } Then {
- statusCode(400)
- contentType(ContentType.JSON)
- }
- }
-
- /**
- * Test that tries to obtain a portfolio without token.
- */
- @Test
- fun testGetWithoutToken() {
- Given {
- pathParam("project", "1")
- } When {
- get("/1")
- } Then {
- statusCode(401)
- }
- }
-
- /**
- * Test that tries to obtain a portfolio with an invalid scope.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["runner"])
- fun testGetInvalidToken() {
- Given {
- pathParam("project", "1")
- } When {
- get("/1")
- } Then {
- statusCode(403)
- }
- }
-
- /**
- * Test that tries to obtain a non-existent portfolio.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testGetNonExisting() {
- every { portfolioService.findByUser("testUser", 1, 1) } returns null
-
- Given {
- pathParam("project", "1")
- } When {
- get("/1")
- } Then {
- statusCode(404)
- contentType(ContentType.JSON)
- }
- }
-
- /**
- * Test that tries to obtain a portfolio.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testGetExisting() {
- every { portfolioService.findByUser("testUser", 1, 1) } returns dummyPortfolio
-
- Given {
- pathParam("project", "1")
- } When {
- get("/1")
- } Then {
- statusCode(200)
- contentType(ContentType.JSON)
- body("id", Matchers.equalTo(1))
- }
- }
-
- /**
- * Test to delete a non-existent portfolio.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testDeleteNonExistent() {
- every { portfolioService.delete("testUser", 1, 1) } returns null
-
- Given {
- pathParam("project", "1")
- } When {
- delete("/1")
- } Then {
- statusCode(404)
- }
- }
-
- /**
- * Test to delete a portfolio.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testDelete() {
- every { portfolioService.delete("testUser", 1, 1) } returns dummyPortfolio
-
- Given {
- pathParam("project", "1")
- } When {
- delete("/1")
- } Then {
- statusCode(200)
- contentType(ContentType.JSON)
- }
- }
-}
diff --git a/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/user/PortfolioScenarioResourceTest.kt b/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/user/PortfolioScenarioResourceTest.kt
deleted file mode 100644
index 676a43dc..00000000
--- a/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/user/PortfolioScenarioResourceTest.kt
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.web.server.rest.user
-
-import io.mockk.every
-import io.quarkiverse.test.junit.mockk.InjectMock
-import io.quarkus.test.common.http.TestHTTPEndpoint
-import io.quarkus.test.junit.QuarkusMock
-import io.quarkus.test.junit.QuarkusTest
-import io.quarkus.test.security.TestSecurity
-import io.restassured.http.ContentType
-import io.restassured.module.kotlin.extensions.Given
-import io.restassured.module.kotlin.extensions.Then
-import io.restassured.module.kotlin.extensions.When
-import org.hamcrest.Matchers
-import org.junit.jupiter.api.BeforeEach
-import org.junit.jupiter.api.Test
-import org.opendc.web.proto.JobState
-import org.opendc.web.proto.OperationalPhenomena
-import org.opendc.web.proto.Targets
-import org.opendc.web.proto.Trace
-import org.opendc.web.proto.Workload
-import org.opendc.web.proto.user.Job
-import org.opendc.web.proto.user.Portfolio
-import org.opendc.web.proto.user.Project
-import org.opendc.web.proto.user.ProjectRole
-import org.opendc.web.proto.user.Scenario
-import org.opendc.web.proto.user.Topology
-import org.opendc.web.server.service.ScenarioService
-import java.time.Instant
-
-/**
- * Test suite for [PortfolioScenarioResource].
- */
-@QuarkusTest
-@TestHTTPEndpoint(PortfolioScenarioResource::class)
-class PortfolioScenarioResourceTest {
- @InjectMock
- private lateinit var scenarioService: ScenarioService
-
- /**
- * Dummy values
- */
- private val dummyProject = Project(0, "test", Instant.now(), Instant.now(), ProjectRole.OWNER)
- private val dummyPortfolio = Portfolio.Summary(1, 1, "test", Targets(emptySet()))
- private val dummyJob = Job(1, JobState.PENDING, Instant.now(), Instant.now(), null)
- private val dummyTrace = Trace("bitbrains", "Bitbrains", "vm")
- private val dummyTopology = Topology.Summary(1, 1, "test", Instant.now(), Instant.now())
- private val dummyScenario = Scenario(
- 1,
- 1,
- dummyProject,
- dummyPortfolio,
- "test",
- Workload(dummyTrace, 1.0),
- dummyTopology,
- OperationalPhenomena(false, false),
- "test",
- dummyJob
- )
-
- @BeforeEach
- fun setUp() {
- QuarkusMock.installMockForType(scenarioService, ScenarioService::class.java)
- }
-
- /**
- * Test that tries to obtain a portfolio without token.
- */
- @Test
- fun testGetWithoutToken() {
- Given {
- pathParam("project", "1")
- pathParam("portfolio", "1")
- } When {
- get()
- } Then {
- statusCode(401)
- }
- }
-
- /**
- * Test that tries to obtain a portfolio with an invalid scope.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["runner"])
- fun testGetInvalidToken() {
- Given {
- pathParam("project", "1")
- pathParam("portfolio", "1")
- } When {
- get()
- } Then {
- statusCode(403)
- }
- }
-
- /**
- * Test that tries to obtain a non-existent portfolio.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testGet() {
- every { scenarioService.findAll("testUser", 1, 1) } returns emptyList()
-
- Given {
- pathParam("project", "1")
- pathParam("portfolio", "1")
- } When {
- get()
- } Then {
- statusCode(200)
- contentType(ContentType.JSON)
- }
- }
-
- /**
- * Test that tries to create a scenario for a portfolio.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testCreateNonExistent() {
- every { scenarioService.create("testUser", 1, any(), any()) } returns null
-
- Given {
- pathParam("project", "1")
- pathParam("portfolio", "1")
-
- body(Scenario.Create("test", Workload.Spec("test", 1.0), 1, OperationalPhenomena(false, false), "test"))
- contentType(ContentType.JSON)
- } When {
- post()
- } Then {
- statusCode(404)
- contentType(ContentType.JSON)
- }
- }
-
- /**
- * Test that tries to create a scenario for a portfolio.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testCreate() {
- every { scenarioService.create("testUser", 1, 1, any()) } returns dummyScenario
-
- Given {
- pathParam("project", "1")
- pathParam("portfolio", "1")
-
- body(Scenario.Create("test", Workload.Spec("test", 1.0), 1, OperationalPhenomena(false, false), "test"))
- contentType(ContentType.JSON)
- } When {
- post()
- } Then {
- statusCode(200)
- contentType(ContentType.JSON)
- body("id", Matchers.equalTo(1))
- body("name", Matchers.equalTo("test"))
- }
- }
-
- /**
- * Test to create a project with an empty body.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testCreateEmpty() {
- Given {
- pathParam("project", "1")
- pathParam("portfolio", "1")
-
- body("{}")
- contentType(ContentType.JSON)
- } When {
- post()
- } Then {
- statusCode(400)
- contentType(ContentType.JSON)
- }
- }
-
- /**
- * Test to create a project with a blank name.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testCreateBlankName() {
- Given {
- pathParam("project", "1")
- pathParam("portfolio", "1")
-
- body(Scenario.Create("", Workload.Spec("test", 1.0), 1, OperationalPhenomena(false, false), "test"))
- contentType(ContentType.JSON)
- } When {
- post()
- } Then {
- statusCode(400)
- contentType(ContentType.JSON)
- }
- }
-}
diff --git a/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/user/ProjectResourceTest.kt b/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/user/ProjectResourceTest.kt
deleted file mode 100644
index 0be56c56..00000000
--- a/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/user/ProjectResourceTest.kt
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.web.server.rest.user
-
-import io.mockk.every
-import io.quarkiverse.test.junit.mockk.InjectMock
-import io.quarkus.test.common.http.TestHTTPEndpoint
-import io.quarkus.test.junit.QuarkusMock
-import io.quarkus.test.junit.QuarkusTest
-import io.quarkus.test.security.TestSecurity
-import io.restassured.http.ContentType
-import io.restassured.module.kotlin.extensions.Given
-import io.restassured.module.kotlin.extensions.Then
-import io.restassured.module.kotlin.extensions.When
-import org.hamcrest.Matchers.equalTo
-import org.junit.jupiter.api.BeforeEach
-import org.junit.jupiter.api.Test
-import org.opendc.web.proto.user.Project
-import org.opendc.web.proto.user.ProjectRole
-import org.opendc.web.server.service.ProjectService
-import java.time.Instant
-
-/**
- * Test suite for [ProjectResource].
- */
-@QuarkusTest
-@TestHTTPEndpoint(ProjectResource::class)
-class ProjectResourceTest {
- @InjectMock
- private lateinit var projectService: ProjectService
-
- /**
- * Dummy values.
- */
- private val dummyProject = Project(0, "test", Instant.now(), Instant.now(), ProjectRole.OWNER)
-
- @BeforeEach
- fun setUp() {
- QuarkusMock.installMockForType(projectService, ProjectService::class.java)
- }
-
- /**
- * Test that tries to obtain all projects without token.
- */
- @Test
- fun testGetAllWithoutToken() {
- When {
- get()
- } Then {
- statusCode(401)
- }
- }
-
- /**
- * Test that tries to obtain all projects with an invalid scope.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["runner"])
- fun testGetAllWithInvalidScope() {
- When {
- get()
- } Then {
- statusCode(403)
- }
- }
-
- /**
- * Test that tries to obtain all project for a user.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testGetAll() {
- val projects = listOf(dummyProject)
- every { projectService.findByUser("testUser") } returns projects
-
- When {
- get()
- } Then {
- statusCode(200)
- contentType(ContentType.JSON)
- body("get(0).name", equalTo("test"))
- }
- }
-
- /**
- * Test that tries to obtain a non-existent project.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testGetNonExisting() {
- every { projectService.findByUser("testUser", 1) } returns null
-
- When {
- get("/1")
- } Then {
- statusCode(404)
- contentType(ContentType.JSON)
- }
- }
-
- /**
- * Test that tries to obtain a job.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testGetExisting() {
- every { projectService.findByUser("testUser", 1) } returns dummyProject
-
- When {
- get("/1")
- } Then {
- statusCode(200)
- contentType(ContentType.JSON)
- body("id", equalTo(0))
- }
- }
-
- /**
- * Test that tries to create a project.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testCreate() {
- every { projectService.create("testUser", "test") } returns dummyProject
-
- Given {
- body(Project.Create("test"))
- contentType(ContentType.JSON)
- } When {
- post()
- } Then {
- statusCode(200)
- contentType(ContentType.JSON)
- body("id", equalTo(0))
- body("name", equalTo("test"))
- }
- }
-
- /**
- * Test to create a project with an empty body.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testCreateEmpty() {
- Given {
- body("{}")
- contentType(ContentType.JSON)
- } When {
- post()
- } Then {
- statusCode(400)
- contentType(ContentType.JSON)
- }
- }
-
- /**
- * Test to create a project with a blank name.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testCreateBlankName() {
- Given {
- body(Project.Create(""))
- contentType(ContentType.JSON)
- } When {
- post()
- } Then {
- statusCode(400)
- contentType(ContentType.JSON)
- }
- }
-
- /**
- * Test to delete a non-existent project.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testDeleteNonExistent() {
- every { projectService.delete("testUser", 1) } returns null
-
- When {
- delete("/1")
- } Then {
- statusCode(404)
- contentType(ContentType.JSON)
- }
- }
-
- /**
- * Test to delete a project.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testDelete() {
- every { projectService.delete("testUser", 1) } returns dummyProject
-
- When {
- delete("/1")
- } Then {
- statusCode(200)
- contentType(ContentType.JSON)
- }
- }
-
- /**
- * Test to delete a project which the user does not own.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testDeleteNonOwner() {
- every { projectService.delete("testUser", 1) } throws IllegalArgumentException("User does not own project")
-
- When {
- delete("/1")
- } Then {
- statusCode(403)
- contentType(ContentType.JSON)
- }
- }
-}
diff --git a/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/user/ScenarioResourceTest.kt b/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/user/ScenarioResourceTest.kt
deleted file mode 100644
index 2e080971..00000000
--- a/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/user/ScenarioResourceTest.kt
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.web.server.rest.user
-
-import io.mockk.every
-import io.quarkiverse.test.junit.mockk.InjectMock
-import io.quarkus.test.common.http.TestHTTPEndpoint
-import io.quarkus.test.junit.QuarkusMock
-import io.quarkus.test.junit.QuarkusTest
-import io.quarkus.test.security.TestSecurity
-import io.restassured.http.ContentType
-import io.restassured.module.kotlin.extensions.Given
-import io.restassured.module.kotlin.extensions.Then
-import io.restassured.module.kotlin.extensions.When
-import org.hamcrest.Matchers
-import org.junit.jupiter.api.BeforeEach
-import org.junit.jupiter.api.Test
-import org.opendc.web.proto.JobState
-import org.opendc.web.proto.OperationalPhenomena
-import org.opendc.web.proto.Targets
-import org.opendc.web.proto.Trace
-import org.opendc.web.proto.Workload
-import org.opendc.web.proto.user.Job
-import org.opendc.web.proto.user.Portfolio
-import org.opendc.web.proto.user.Project
-import org.opendc.web.proto.user.ProjectRole
-import org.opendc.web.proto.user.Scenario
-import org.opendc.web.proto.user.Topology
-import org.opendc.web.server.service.ScenarioService
-import java.time.Instant
-
-/**
- * Test suite for [ScenarioResource].
- */
-@QuarkusTest
-@TestHTTPEndpoint(ScenarioResource::class)
-class ScenarioResourceTest {
- @InjectMock
- private lateinit var scenarioService: ScenarioService
-
- /**
- * Dummy values
- */
- private val dummyProject = Project(0, "test", Instant.now(), Instant.now(), ProjectRole.OWNER)
- private val dummyPortfolio = Portfolio.Summary(1, 1, "test", Targets(emptySet()))
- private val dummyJob = Job(1, JobState.PENDING, Instant.now(), Instant.now(), null)
- private val dummyTrace = Trace("bitbrains", "Bitbrains", "vm")
- private val dummyTopology = Topology.Summary(1, 1, "test", Instant.now(), Instant.now())
- private val dummyScenario = Scenario(
- 1,
- 1,
- dummyProject,
- dummyPortfolio,
- "test",
- Workload(dummyTrace, 1.0),
- dummyTopology,
- OperationalPhenomena(false, false),
- "test",
- dummyJob
- )
-
- @BeforeEach
- fun setUp() {
- QuarkusMock.installMockForType(scenarioService, ScenarioService::class.java)
- }
-
- /**
- * Test that tries to obtain a scenario without token.
- */
- @Test
- fun testGetWithoutToken() {
- Given {
- pathParam("project", "1")
- } When {
- get("/1")
- } Then {
- statusCode(401)
- }
- }
-
- /**
- * Test that tries to obtain a scenario with an invalid scope.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["runner"])
- fun testGetInvalidToken() {
- Given {
- pathParam("project", "1")
- } When {
- get("/1")
- } Then {
- statusCode(403)
- }
- }
-
- /**
- * Test that tries to obtain a non-existent scenario.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testGetNonExisting() {
- every { scenarioService.findOne("testUser", 1, 1) } returns null
-
- Given {
- pathParam("project", "1")
- } When {
- get("/1")
- } Then {
- statusCode(404)
- contentType(ContentType.JSON)
- }
- }
-
- /**
- * Test that tries to obtain a scenario.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testGetExisting() {
- every { scenarioService.findOne("testUser", 1, 1) } returns dummyScenario
-
- Given {
- pathParam("project", "1")
- } When {
- get("/1")
- } Then {
- statusCode(200)
- contentType(ContentType.JSON)
- body("id", Matchers.equalTo(1))
- }
- }
-
- /**
- * Test to delete a non-existent scenario.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testDeleteNonExistent() {
- every { scenarioService.delete("testUser", 1, 1) } returns null
-
- Given {
- pathParam("project", "1")
- } When {
- delete("/1")
- } Then {
- statusCode(404)
- }
- }
-
- /**
- * Test to delete a scenario.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testDelete() {
- every { scenarioService.delete("testUser", 1, 1) } returns dummyScenario
-
- Given {
- pathParam("project", "1")
- } When {
- delete("/1")
- } Then {
- statusCode(200)
- contentType(ContentType.JSON)
- }
- }
-}
diff --git a/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/user/TopologyResourceTest.kt b/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/user/TopologyResourceTest.kt
deleted file mode 100644
index 8a542d33..00000000
--- a/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/rest/user/TopologyResourceTest.kt
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.web.server.rest.user
-
-import io.mockk.every
-import io.quarkiverse.test.junit.mockk.InjectMock
-import io.quarkus.test.common.http.TestHTTPEndpoint
-import io.quarkus.test.junit.QuarkusMock
-import io.quarkus.test.junit.QuarkusTest
-import io.quarkus.test.security.TestSecurity
-import io.restassured.http.ContentType
-import io.restassured.module.kotlin.extensions.Given
-import io.restassured.module.kotlin.extensions.Then
-import io.restassured.module.kotlin.extensions.When
-import org.hamcrest.Matchers
-import org.junit.jupiter.api.BeforeEach
-import org.junit.jupiter.api.Test
-import org.opendc.web.proto.user.Project
-import org.opendc.web.proto.user.ProjectRole
-import org.opendc.web.proto.user.Topology
-import org.opendc.web.server.service.TopologyService
-import java.time.Instant
-
-/**
- * Test suite for [TopologyResource].
- */
-@QuarkusTest
-@TestHTTPEndpoint(TopologyResource::class)
-class TopologyResourceTest {
- @InjectMock
- private lateinit var topologyService: TopologyService
-
- /**
- * Dummy project and topology.
- */
- private val dummyProject = Project(1, "test", Instant.now(), Instant.now(), ProjectRole.OWNER)
- private val dummyTopology = Topology(1, 1, dummyProject, "test", emptyList(), Instant.now(), Instant.now())
-
- @BeforeEach
- fun setUp() {
- QuarkusMock.installMockForType(topologyService, TopologyService::class.java)
- }
-
- /**
- * Test that tries to obtain the list of topologies belonging to a project.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testGetForProject() {
- every { topologyService.findAll("testUser", 1) } returns emptyList()
-
- Given {
- pathParam("project", "1")
- } When {
- get()
- } Then {
- statusCode(200)
- contentType(ContentType.JSON)
- }
- }
-
- /**
- * Test that tries to create a topology for a project.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testCreateNonExistent() {
- every { topologyService.create("testUser", 1, any()) } returns null
-
- Given {
- pathParam("project", "1")
-
- body(Topology.Create("test", emptyList()))
- contentType(ContentType.JSON)
- } When {
- post()
- } Then {
- statusCode(404)
- contentType(ContentType.JSON)
- }
- }
-
- /**
- * Test that tries to create a topology for a project.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testCreate() {
- every { topologyService.create("testUser", 1, any()) } returns dummyTopology
-
- Given {
- pathParam("project", "1")
-
- body(Topology.Create("test", emptyList()))
- contentType(ContentType.JSON)
- } When {
- post()
- } Then {
- statusCode(200)
- contentType(ContentType.JSON)
- body("id", Matchers.equalTo(1))
- body("name", Matchers.equalTo("test"))
- }
- }
-
- /**
- * Test to create a topology with an empty body.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testCreateEmpty() {
- Given {
- pathParam("project", "1")
-
- body("{}")
- contentType(ContentType.JSON)
- } When {
- post()
- } Then {
- statusCode(400)
- contentType(ContentType.JSON)
- }
- }
-
- /**
- * Test to create a topology with a blank name.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testCreateBlankName() {
- Given {
- pathParam("project", "1")
-
- body(Topology.Create("", emptyList()))
- contentType(ContentType.JSON)
- } When {
- post()
- } Then {
- statusCode(400)
- contentType(ContentType.JSON)
- }
- }
-
- /**
- * Test that tries to obtain a topology without token.
- */
- @Test
- fun testGetWithoutToken() {
- Given {
- pathParam("project", "1")
- } When {
- get("/1")
- } Then {
- statusCode(401)
- }
- }
-
- /**
- * Test that tries to obtain a topology with an invalid scope.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["runner"])
- fun testGetInvalidToken() {
- Given {
- pathParam("project", "1")
- } When {
- get("/1")
- } Then {
- statusCode(403)
- }
- }
-
- /**
- * Test that tries to obtain a non-existent topology.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testGetNonExisting() {
- every { topologyService.findOne("testUser", 1, 1) } returns null
-
- Given {
- pathParam("project", "1")
- } When {
- get("/1")
- } Then {
- statusCode(404)
- contentType(ContentType.JSON)
- }
- }
-
- /**
- * Test that tries to obtain a topology.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testGetExisting() {
- every { topologyService.findOne("testUser", 1, 1) } returns dummyTopology
-
- Given {
- pathParam("project", "1")
- } When {
- get("/1")
- } Then {
- statusCode(200)
- contentType(ContentType.JSON)
- body("id", Matchers.equalTo(1))
- println(extract().asPrettyString())
- }
- }
-
- /**
- * Test to delete a non-existent topology.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testUpdateNonExistent() {
- every { topologyService.update("testUser", any(), any(), any()) } returns null
-
- Given {
- pathParam("project", "1")
- body(Topology.Update(emptyList()))
- contentType(ContentType.JSON)
- } When {
- put("/1")
- } Then {
- statusCode(404)
- }
- }
-
- /**
- * Test to update a topology.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testUpdate() {
- every { topologyService.update("testUser", any(), any(), any()) } returns dummyTopology
-
- Given {
- pathParam("project", "1")
- body(Topology.Update(emptyList()))
- contentType(ContentType.JSON)
- } When {
- put("/1")
- } Then {
- statusCode(200)
- contentType(ContentType.JSON)
- }
- }
-
- /**
- * Test to delete a non-existent topology.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testDeleteNonExistent() {
- every { topologyService.delete("testUser", 1, 1) } returns null
-
- Given {
- pathParam("project", "1")
- } When {
- delete("/1")
- } Then {
- statusCode(404)
- }
- }
-
- /**
- * Test to delete a topology.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testDelete() {
- every { topologyService.delete("testUser", 1, 1) } returns dummyTopology
-
- Given {
- pathParam("project", "1")
- } When {
- delete("/1")
- } Then {
- statusCode(200)
- contentType(ContentType.JSON)
- }
- }
-}