summaryrefslogtreecommitdiff
path: root/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server
diff options
context:
space:
mode:
Diffstat (limited to 'opendc-web/opendc-web-server/src/test/java/org/opendc/web/server')
-rw-r--r--opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/SchedulerResourceTest.java45
-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.java65
9 files changed, 1503 insertions, 0 deletions
diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/SchedulerResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/SchedulerResourceTest.java
new file mode 100644
index 00000000..feeac4d3
--- /dev/null
+++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/SchedulerResourceTest.java
@@ -0,0 +1,45 @@
+/*
+ * 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 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 {@link SchedulerResource}.
+ */
+@QuarkusTest
+@TestHTTPEndpoint(SchedulerResource.class)
+public final class SchedulerResourceTest {
+ /**
+ * Test to verify whether we can obtain all schedulers.
+ */
+ @Test
+ 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/java/org/opendc/web/server/rest/user/UserResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/UserResourceTest.java
new file mode 100644
index 00000000..6dcb3b4d
--- /dev/null
+++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/UserResourceTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.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)
+public final class UserResourceTest {
+ /**
+ * Test that tries to obtain the profile of the active user.
+ */
+ @Test
+ @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
+ public void testMeUnauthorized() {
+ when().get("me").then().statusCode(401);
+ }
+}