summaryrefslogtreecommitdiff
path: root/opendc-web/opendc-web-server/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'opendc-web/opendc-web-server/src/test')
-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.java66
-rw-r--r--opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/runner/JobResourceTest.java145
-rw-r--r--opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioResourceTest.java287
-rw-r--r--opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioScenarioResourceTest.java273
-rw-r--r--opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ProjectResourceTest.java187
-rw-r--r--opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ScenarioResourceTest.java203
-rw-r--r--opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/TopologyResourceTest.java358
-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/java/org/opendc/web/server/service/JobServiceTest.java124
-rw-r--r--opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/service/UserAccountingServiceTest.java213
-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
-rw-r--r--opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/service/UserAccountingServiceTest.kt203
19 files changed, 1895 insertions, 1770 deletions
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..5c5976db
--- /dev/null
+++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/TraceResourceTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.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 TraceResource}.
+ */
+@QuarkusTest
+@TestHTTPEndpoint(TraceResource.class)
+public final class TraceResourceTest {
+ /**
+ * Test that tries to obtain all traces.
+ */
+ @Test
+ public void testGetAllEmpty() {
+ when().get().then().statusCode(200).contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to obtain a non-existent trace.
+ */
+ @Test
+ public void testGetNonExisting() {
+ when().get("/unknown").then().statusCode(404).contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to obtain an existing trace.
+ */
+ @Test
+ public void testGetExisting() {
+ when().get("/bitbrains-small")
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON)
+ .body("name", equalTo("Bitbrains Small"));
+ }
+}
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..94b2cef0
--- /dev/null
+++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/runner/JobResourceTest.java
@@ -0,0 +1,145 @@
+/*
+ * 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 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;
+import org.opendc.web.proto.JobState;
+
+/**
+ * Test suite for {@link JobResource}.
+ */
+@QuarkusTest
+@TestHTTPEndpoint(JobResource.class)
+public final class JobResourceTest {
+ /**
+ * 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 = "test",
+ roles = {"openid"})
+ public void testQueryInvalidScope() {
+ when().get().then().statusCode(403);
+ }
+
+ /**
+ * Test that tries to query the pending jobs for a runner.
+ */
+ @Test
+ @TestSecurity(
+ user = "test",
+ roles = {"runner"})
+ public void testQuery() {
+ when().get().then().statusCode(200).contentType(ContentType.JSON).body("get(0).state", equalTo("PENDING"));
+ }
+
+ /**
+ * Test that tries to obtain a non-existent job.
+ */
+ @Test
+ @TestSecurity(
+ user = "test",
+ roles = {"runner"})
+ public void testGetNonExisting() {
+ when().get("/0").then().statusCode(404).contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to obtain a job.
+ */
+ @Test
+ @TestSecurity(
+ user = "test",
+ roles = {"runner"})
+ public void testGetExisting() {
+ 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 = "test",
+ roles = {"runner"})
+ public void testUpdateNonExistent() {
+ given().body(new org.opendc.web.proto.runner.Job.Update(JobState.PENDING, 0, null))
+ .contentType(ContentType.JSON)
+ .when()
+ .post("/0")
+ .then()
+ .statusCode(404)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to update a job.
+ */
+ @Test
+ @TestSecurity(
+ user = "test",
+ roles = {"runner"})
+ public void testUpdateState() {
+ given().body(new org.opendc.web.proto.runner.Job.Update(JobState.CLAIMED, 0, null))
+ .contentType(ContentType.JSON)
+ .when()
+ .post("/2")
+ .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 = "test",
+ 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..a952d83f
--- /dev/null
+++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioResourceTest.java
@@ -0,0 +1,287 @@
+/*
+ * 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.security.TestSecurity;
+import io.restassured.http.ContentType;
+import java.util.Set;
+import org.junit.jupiter.api.Test;
+import org.opendc.web.proto.Targets;
+
+/**
+ * Test suite for {@link PortfolioResource}.
+ */
+@QuarkusTest
+@TestHTTPEndpoint(PortfolioResource.class)
+public final class PortfolioResourceTest {
+ /**
+ * Test that tries to obtain the list of portfolios belonging to a project.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ roles = {"openid"})
+ public void testGetForProject() {
+ given().pathParam("project", 1).when().get().then().statusCode(200).contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to obtain the list of portfolios belonging to a project without authorization.
+ */
+ @Test
+ @TestSecurity(
+ user = "unknown",
+ roles = {"openid"})
+ public void testGetForProjectNoAuthorization() {
+ 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 = "owner",
+ roles = {"openid"})
+ public void testCreateNonExistent() {
+ given().pathParam("project", "0")
+ .body(new org.opendc.web.proto.user.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 topology for a project.
+ */
+ @Test
+ @TestSecurity(
+ user = "viewer",
+ roles = {"openid"})
+ public void testCreateNotPermitted() {
+ given().pathParam("project", "1")
+ .body(new org.opendc.web.proto.user.Portfolio.Create("test", new Targets(Set.of(), 1)))
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(403)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to create a portfolio for a project.
+ */
+ @Test
+ @TestSecurity(
+ user = "editor",
+ roles = {"openid"})
+ public void testCreate() {
+ given().pathParam("project", "1")
+ .body(new org.opendc.web.proto.user.Portfolio.Create("test", new Targets(Set.of(), 1)))
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON)
+ .body("name", equalTo("test"));
+ }
+
+ /**
+ * Test to create a portfolio with an empty body.
+ */
+ @Test
+ @TestSecurity(
+ user = "editor",
+ 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 = "editor",
+ roles = {"openid"})
+ public void testCreateBlankName() {
+ given().pathParam("project", "1")
+ .body(new org.opendc.web.proto.user.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 = "owner",
+ 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 = "owner",
+ roles = {"openid"})
+ public void testGetNonExisting() {
+ given().pathParam("project", "1")
+ .when()
+ .get("/0")
+ .then()
+ .statusCode(404)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to obtain a portfolio for a non-existent project.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ roles = {"openid"})
+ public void testGetNonExistingProject() {
+ given().pathParam("project", "0")
+ .when()
+ .get("/1")
+ .then()
+ .statusCode(404)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to obtain a portfolio.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ roles = {"openid"})
+ public void testGetExisting() {
+ 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 = "owner",
+ roles = {"openid"})
+ public void testDeleteNonExistent() {
+ given().pathParam("project", "1").when().delete("/0").then().statusCode(404);
+ }
+
+ /**
+ * Test to delete a portfolio on a non-existent project.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ roles = {"openid"})
+ public void testDeleteNonExistentProject() {
+ given().pathParam("project", "0").when().delete("/1").then().statusCode(404);
+ }
+
+ /**
+ * Test to delete a portfolio.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ roles = {"openid"})
+ public void testDelete() {
+ int number = given().pathParam("project", "1")
+ .body(new org.opendc.web.proto.user.Portfolio.Create("Delete Portfolio", new Targets(Set.of(), 1)))
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON)
+ .extract()
+ .path("number");
+
+ given().pathParam("project", "1")
+ .when()
+ .delete("/" + number)
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test to delete a portfolio as a viewer.
+ */
+ @Test
+ @TestSecurity(
+ user = "viewer",
+ roles = {"openid"})
+ public void testDeleteAsViewer() {
+ given().pathParam("project", "1")
+ .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/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..4f8d412c
--- /dev/null
+++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioScenarioResourceTest.java
@@ -0,0 +1,273 @@
+/*
+ * 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.security.TestSecurity;
+import io.restassured.http.ContentType;
+import org.junit.jupiter.api.Test;
+import org.opendc.web.proto.OperationalPhenomena;
+import org.opendc.web.proto.Workload;
+import org.opendc.web.proto.user.Scenario;
+
+/**
+ * Test suite for {@link PortfolioScenarioResource}.
+ */
+@QuarkusTest
+@TestHTTPEndpoint(PortfolioScenarioResource.class)
+public final class PortfolioScenarioResourceTest {
+ /**
+ * 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 = "owner",
+ roles = {"runner"})
+ public void testGetInvalidToken() {
+ given().pathParam("project", "1")
+ .pathParam("portfolio", "1")
+ .when()
+ .get()
+ .then()
+ .statusCode(403);
+ }
+
+ /**
+ * Test that tries to obtain a scenario without authorization.
+ */
+ @Test
+ @TestSecurity(
+ user = "unknown",
+ roles = {"openid"})
+ public void testGetUnauthorized() {
+ given().pathParam("project", "1")
+ .pathParam("portfolio", "1")
+ .when()
+ .get()
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to obtain a scenario.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ roles = {"openid"})
+ public void testGet() {
+ 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 = "owner",
+ roles = {"openid"})
+ public void testCreateNonExistent() {
+ given().pathParam("project", "1")
+ .pathParam("portfolio", "0")
+ .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 without authorization.
+ */
+ @Test
+ @TestSecurity(
+ user = "unknown",
+ roles = {"openid"})
+ public void testCreateUnauthorized() {
+ given().pathParam("project", "1")
+ .pathParam("portfolio", "0")
+ .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 as a viewer.
+ */
+ @Test
+ @TestSecurity(
+ user = "viewer",
+ roles = {"openid"})
+ public void testCreateAsViewer() {
+ given().pathParam("project", "1")
+ .pathParam("portfolio", "0")
+ .body(new Scenario.Create(
+ "test", new Workload.Spec("test", 1.0), 1, new OperationalPhenomena(false, false), "test"))
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(403)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to create a scenario for a portfolio.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ roles = {"openid"})
+ public void testCreate() {
+ given().pathParam("project", "1")
+ .pathParam("portfolio", "1")
+ .body(new Scenario.Create(
+ "test",
+ new Workload.Spec("bitbrains-small", 1.0),
+ 1,
+ new OperationalPhenomena(false, false),
+ "test"))
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON)
+ .body("name", equalTo("test"));
+ }
+
+ /**
+ * Test to create a project with an empty body.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ 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 = "owner",
+ 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);
+ }
+
+ /**
+ * Test that tries to create a scenario for a portfolio.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ roles = {"openid"})
+ public void testCreateUnknownTopology() {
+ given().pathParam("project", "1")
+ .pathParam("portfolio", "1")
+ .body(new Scenario.Create(
+ "test",
+ new Workload.Spec("bitbrains-small", 1.0),
+ -1,
+ new OperationalPhenomena(false, false),
+ "test"))
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(400)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to create a scenario for a portfolio.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ roles = {"openid"})
+ public void testCreateUnknownTrace() {
+ given().pathParam("project", "1")
+ .pathParam("portfolio", "1")
+ .body(new Scenario.Create(
+ "test", new Workload.Spec("unknown", 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..8bd60808
--- /dev/null
+++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ProjectResourceTest.java
@@ -0,0 +1,187 @@
+/*
+ * 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.security.TestSecurity;
+import io.restassured.http.ContentType;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test suite for [ProjectResource].
+ */
+@QuarkusTest
+@TestHTTPEndpoint(ProjectResource.class)
+public final class ProjectResourceTest {
+ /**
+ * 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 = "owner",
+ roles = {"runner"})
+ public void testGetAllWithInvalidScope() {
+ when().get().then().statusCode(403);
+ }
+
+ /**
+ * Test that tries to obtain all project for a user.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ roles = {"openid"})
+ public void testGetAll() {
+ when().get().then().statusCode(200).contentType(ContentType.JSON).body("get(0).name", equalTo("Test Project"));
+ }
+
+ /**
+ * Test that tries to obtain a non-existent project.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ roles = {"openid"})
+ public void testGetNonExisting() {
+ when().get("/0").then().statusCode(404).contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to obtain a project.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ roles = {"openid"})
+ public void testGetExisting() {
+ when().get("/1").then().statusCode(200).contentType(ContentType.JSON).body("id", equalTo(1));
+ }
+
+ /**
+ * Test that tries to create a project.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ roles = {"openid"})
+ public void testCreate() {
+ given().body(new org.opendc.web.proto.user.Project.Create("test"))
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON)
+ .body("name", equalTo("test"));
+ }
+
+ /**
+ * Test to create a project with an empty body.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ 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 = "owner",
+ roles = {"openid"})
+ public void testCreateBlankName() {
+ given().body(new org.opendc.web.proto.user.Project.Create(""))
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(400)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test to delete a non-existent project.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ roles = {"openid"})
+ public void testDeleteNonExistent() {
+ when().delete("/0").then().statusCode(404).contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test to delete a project.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ roles = {"openid"})
+ public void testDelete() {
+ int id = given().body(new org.opendc.web.proto.user.Project.Create("Delete Project"))
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON)
+ .extract()
+ .path("id");
+
+ when().delete("/" + id).then().statusCode(200).contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test to delete a project which the user does not own.
+ */
+ @Test
+ @TestSecurity(
+ user = "viewer",
+ roles = {"openid"})
+ public void testDeleteNonOwner() {
+ 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..a980e4e2
--- /dev/null
+++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ScenarioResourceTest.java
@@ -0,0 +1,203 @@
+/*
+ * 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.security.TestSecurity;
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import org.junit.jupiter.api.Test;
+import org.opendc.web.proto.OperationalPhenomena;
+import org.opendc.web.proto.Workload;
+import org.opendc.web.proto.user.Scenario;
+
+/**
+ * Test suite for {@link ScenarioResource}.
+ */
+@QuarkusTest
+@TestHTTPEndpoint(ScenarioResource.class)
+public final class ScenarioResourceTest {
+ /**
+ * Test that tries to obtain all scenarios belonging to a project without authorization.
+ */
+ @Test
+ @TestSecurity(
+ user = "unknown",
+ roles = {"openid"})
+ public void testGetAllUnauthorized() {
+ given().pathParam("project", "1").when().get().then().statusCode(404).contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to obtain all scenarios belonging to a project.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ roles = {"openid"})
+ public void testGetAll() {
+ given().pathParam("project", "1").when().get().then().statusCode(200).contentType(ContentType.JSON);
+ }
+
+ /**
+ * 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 = "owner",
+ 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 = "owner",
+ roles = {"openid"})
+ public void testGetNonExisting() {
+ given().pathParam("project", "1")
+ .when()
+ .get("/0")
+ .then()
+ .statusCode(404)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to obtain a scenario.
+ */
+ @Test
+ @TestSecurity(
+ user = "unknown",
+ roles = {"openid"})
+ public void testGetExistingUnauthorized() {
+ given().pathParam("project", "1")
+ .when()
+ .get("/1")
+ .then()
+ .statusCode(404)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to obtain a scenario.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ roles = {"openid"})
+ public void testGetExisting() {
+ 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 = "owner",
+ roles = {"openid"})
+ public void testDeleteNonExistent() {
+ given().pathParam("project", "1").when().delete("/0").then().statusCode(404);
+ }
+
+ /**
+ * Test to delete a scenario without authorization.
+ */
+ @Test
+ @TestSecurity(
+ user = "unknown",
+ roles = {"openid"})
+ public void testDeleteUnauthorized() {
+ given().pathParam("project", "1").when().delete("/1").then().statusCode(404);
+ }
+
+ /**
+ * Test to delete a scenario as a viewer.
+ */
+ @Test
+ @TestSecurity(
+ user = "viewer",
+ roles = {"openid"})
+ public void testDeleteAsViewer() {
+ given().pathParam("project", "1").when().delete("/1").then().statusCode(403);
+ }
+
+ /**
+ * Test to delete a scenario.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ roles = {"openid"})
+ public void testDelete() {
+ RequestSpecification spec = new RequestSpecBuilder()
+ .setBasePath("/projects/1/portfolios/1/scenarios")
+ .build();
+
+ int number = given(spec)
+ .body(new Scenario.Create(
+ "test",
+ new Workload.Spec("bitbrains-small", 1.0),
+ 1,
+ new OperationalPhenomena(false, false),
+ "test"))
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON)
+ .extract()
+ .path("number");
+
+ given().pathParam("project", "1")
+ .when()
+ .delete("/" + number)
+ .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..21e35b09
--- /dev/null
+++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/TopologyResourceTest.java
@@ -0,0 +1,358 @@
+/*
+ * 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.security.TestSecurity;
+import io.restassured.http.ContentType;
+import java.util.List;
+import org.junit.jupiter.api.Test;
+import org.opendc.web.proto.user.Topology;
+
+/**
+ * Test suite for {@link TopologyResource}.
+ */
+@QuarkusTest
+@TestHTTPEndpoint(TopologyResource.class)
+public final class TopologyResourceTest {
+ /**
+ * Test that tries to obtain the list of topologies of a project without proper authorization.
+ */
+ @Test
+ @TestSecurity(
+ user = "unknown",
+ roles = {"openid"})
+ public void testGetAllWithoutAuth() {
+ given().pathParam("project", "1")
+ .when()
+ .get()
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON)
+ .body(equalTo("[]"));
+ }
+
+ /**
+ * Test that tries to obtain the list of topologies belonging to a project.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ roles = {"openid"})
+ public void testGetAll() {
+ 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 = "owner",
+ roles = {"openid"})
+ public void testCreateNonExistent() {
+ given().pathParam("project", "0")
+ .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 as viewer.
+ */
+ @Test
+ @TestSecurity(
+ user = "viewer",
+ roles = {"openid"})
+ public void testCreateUnauthorized() {
+ given().pathParam("project", "1")
+ .body(new Topology.Create("test", List.of()))
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(403)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to create a topology for a project.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ roles = {"openid"})
+ public void testCreate() {
+ given().pathParam("project", "1")
+ .body(new Topology.Create("test", List.of()))
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON)
+ .body("name", equalTo("test"));
+ }
+
+ /**
+ * Test to create a topology with an empty body.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ 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 = "owner",
+ 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 = "owner",
+ 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 = "owner",
+ roles = {"openid"})
+ public void testGetNonExisting() {
+ given().pathParam("project", "1")
+ .when()
+ .get("/0")
+ .then()
+ .statusCode(404)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to obtain a topology without authorization.
+ */
+ @Test
+ @TestSecurity(
+ user = "unknown",
+ roles = {"openid"})
+ public void testGetUnauthorized() {
+ given().pathParam("project", "1")
+ .when()
+ .get("/1")
+ .then()
+ .statusCode(404)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test that tries to obtain a topology.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ roles = {"openid"})
+ public void testGetExisting() {
+ 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 = "owner",
+ roles = {"openid"})
+ public void testUpdateNonExistent() {
+ given().pathParam("project", "1")
+ .body(new Topology.Update(List.of()))
+ .contentType(ContentType.JSON)
+ .when()
+ .put("/0")
+ .then()
+ .statusCode(404);
+ }
+
+ /**
+ * Test to delete a topology without authorization.
+ */
+ @Test
+ @TestSecurity(
+ user = "unknown",
+ roles = {"openid"})
+ public void testUpdateUnauthorized() {
+ given().pathParam("project", "1")
+ .body(new Topology.Update(List.of()))
+ .contentType(ContentType.JSON)
+ .when()
+ .put("/1")
+ .then()
+ .statusCode(404);
+ }
+
+ /**
+ * Test to update a topology as a viewer.
+ */
+ @Test
+ @TestSecurity(
+ user = "viewer",
+ roles = {"openid"})
+ public void testUpdateAsViewer() {
+ given().pathParam("project", "1")
+ .body(new Topology.Update(List.of()))
+ .contentType(ContentType.JSON)
+ .when()
+ .put("/1")
+ .then()
+ .statusCode(403)
+ .contentType(ContentType.JSON);
+ }
+
+ /**
+ * Test to update a topology.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ roles = {"openid"})
+ public void testUpdate() {
+ 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 = "owner",
+ roles = {"openid"})
+ public void testDeleteNonExistent() {
+ given().pathParam("project", "1").when().delete("/0").then().statusCode(404);
+ }
+
+ /**
+ * Test to delete a topology without authorization.
+ */
+ @Test
+ @TestSecurity(
+ user = "unknown",
+ roles = {"openid"})
+ public void testDeleteUnauthorized() {
+ given().pathParam("project", "1").when().delete("/1").then().statusCode(404);
+ }
+
+ /**
+ * Test to delete a topology as a viewer.
+ */
+ @Test
+ @TestSecurity(
+ user = "viewer",
+ roles = {"openid"})
+ public void testDeleteAsViewer() {
+ given().pathParam("project", "1").when().delete("/1").then().statusCode(403);
+ }
+
+ /**
+ * Test to delete a topology.
+ */
+ @Test
+ @TestSecurity(
+ user = "owner",
+ roles = {"openid"})
+ public void testDelete() {
+ int number = given().pathParam("project", "1")
+ .body(new Topology.Create("Delete Topology", List.of()))
+ .contentType(ContentType.JSON)
+ .when()
+ .post()
+ .then()
+ .statusCode(200)
+ .contentType(ContentType.JSON)
+ .extract()
+ .path("number");
+
+ given().pathParam("project", "1")
+ .when()
+ .delete("/" + number)
+ .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/java/org/opendc/web/server/service/JobServiceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/service/JobServiceTest.java
new file mode 100644
index 00000000..f6d871c0
--- /dev/null
+++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/service/JobServiceTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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.service;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+
+import io.quarkus.test.junit.QuarkusTest;
+import java.time.Instant;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.opendc.web.proto.JobState;
+import org.opendc.web.server.model.Job;
+
+/**
+ * Test suite for the {@link JobService}.
+ */
+@QuarkusTest
+public class JobServiceTest {
+ /**
+ * The {@link JobService} instance under test.
+ */
+ private JobService service;
+
+ /**
+ * The mock {@link UserAccountingService}.
+ */
+ private UserAccountingService mockAccountingService;
+
+ @BeforeEach
+ public void setUp() {
+ mockAccountingService = Mockito.mock(UserAccountingService.class);
+ service = new JobService(mockAccountingService);
+ }
+
+ @Test
+ public void testUpdateInvalidTransition() {
+ Job job = new Job(null, "test", Instant.now(), 1);
+ job.state = JobState.RUNNING;
+
+ assertThrows(IllegalArgumentException.class, () -> service.updateJob(job, JobState.CLAIMED, 0, null));
+
+ Mockito.verifyNoInteractions(mockAccountingService);
+ }
+
+ @Test
+ public void testUpdateNoBudget() {
+ Job job = Mockito.spy(new Job(null, "test", Instant.now(), 1));
+ job.state = JobState.RUNNING;
+
+ Mockito.when(mockAccountingService.consumeSimulationBudget(any(), anyInt()))
+ .thenReturn(true);
+ Mockito.doReturn(true).when(job).updateAtomically(any(), any(), anyInt(), any());
+
+ service.updateJob(job, JobState.RUNNING, 0, null);
+
+ Mockito.verify(job).updateAtomically(eq(JobState.FAILED), any(), anyInt(), any());
+ }
+
+ @Test
+ public void testUpdateNoBudgetWhenFinishing() {
+ Job job = Mockito.spy(new Job(null, "test", Instant.now(), 1));
+ job.state = JobState.RUNNING;
+
+ Mockito.when(mockAccountingService.consumeSimulationBudget(any(), anyInt()))
+ .thenReturn(true);
+ Mockito.doReturn(true).when(job).updateAtomically(any(), any(), anyInt(), any());
+
+ service.updateJob(job, JobState.FINISHED, 0, null);
+
+ Mockito.verify(job).updateAtomically(eq(JobState.FINISHED), any(), anyInt(), any());
+ }
+
+ @Test
+ public void testUpdateSuccess() {
+ Job job = Mockito.spy(new Job(null, "test", Instant.now(), 1));
+ job.state = JobState.RUNNING;
+
+ Mockito.when(mockAccountingService.consumeSimulationBudget(any(), anyInt()))
+ .thenReturn(false);
+ Mockito.doReturn(true).when(job).updateAtomically(any(), any(), anyInt(), any());
+
+ service.updateJob(job, JobState.FINISHED, 0, null);
+
+ Mockito.verify(job).updateAtomically(eq(JobState.FINISHED), any(), anyInt(), any());
+ }
+
+ @Test
+ public void testUpdateConflict() {
+ Job job = Mockito.spy(new Job(null, "test", Instant.now(), 1));
+ job.state = JobState.RUNNING;
+
+ Mockito.when(mockAccountingService.consumeSimulationBudget(any(), anyInt()))
+ .thenReturn(false);
+ Mockito.doReturn(false).when(job).updateAtomically(any(), any(), anyInt(), any());
+
+ assertThrows(IllegalStateException.class, () -> service.updateJob(job, JobState.FINISHED, 0, null));
+
+ Mockito.verify(job).updateAtomically(eq(JobState.FINISHED), any(), anyInt(), any());
+ }
+}
diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/service/UserAccountingServiceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/service/UserAccountingServiceTest.java
new file mode 100644
index 00000000..d1d82097
--- /dev/null
+++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/service/UserAccountingServiceTest.java
@@ -0,0 +1,213 @@
+/*
+ * 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.service;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+
+import io.quarkus.panache.mock.PanacheMock;
+import io.quarkus.test.junit.QuarkusTest;
+import java.time.Duration;
+import java.time.LocalDate;
+import javax.persistence.EntityExistsException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.opendc.web.server.model.UserAccounting;
+
+/**
+ * Test suite for the {@link UserAccountingService}.
+ */
+@QuarkusTest
+public class UserAccountingServiceTest {
+ /**
+ * The {@link UserAccountingService} instance under test.
+ */
+ private UserAccountingService service;
+
+ /**
+ * The user id to test with
+ */
+ private final String userId = "test";
+
+ @BeforeEach
+ public void setUp() {
+ PanacheMock.mock(UserAccounting.class);
+ service = new UserAccountingService(Duration.ofHours(1));
+ }
+
+ @Test
+ public void testGetUserDoesNotExist() {
+ Mockito.when(UserAccounting.findByUser(userId)).thenReturn(null);
+
+ var accounting = service.getAccounting(userId);
+
+ assertTrue(accounting.getPeriodEnd().isAfter(LocalDate.now()));
+ assertEquals(0, accounting.getSimulationTime());
+ }
+
+ @Test
+ public void testGetUserDoesExist() {
+ var now = LocalDate.now();
+ var periodEnd = now.plusMonths(1);
+
+ var mockAccounting = new UserAccounting(userId, periodEnd, 3600);
+ mockAccounting.simulationTime = 32;
+
+ Mockito.when(UserAccounting.findByUser(userId)).thenReturn(mockAccounting);
+
+ var accounting = service.getAccounting(userId);
+
+ assertAll(
+ () -> assertEquals(periodEnd, accounting.getPeriodEnd()),
+ () -> assertEquals(32, accounting.getSimulationTime()),
+ () -> assertEquals(3600, accounting.getSimulationTimeBudget()));
+ }
+
+ @Test
+ public void testHasBudgetUserDoesNotExist() {
+ Mockito.when(UserAccounting.findByUser(userId)).thenReturn(null);
+
+ assertTrue(service.hasSimulationBudget(userId));
+ }
+
+ @Test
+ public void testHasBudget() {
+ var periodEnd = LocalDate.now().plusMonths(2);
+
+ var mockAccounting = new UserAccounting(userId, periodEnd, 3600);
+ Mockito.when(UserAccounting.findByUser(userId)).thenReturn(mockAccounting);
+
+ assertTrue(service.hasSimulationBudget(userId));
+ }
+
+ @Test
+ public void testHasBudgetExceededButPeriodExpired() {
+ var periodEnd = LocalDate.now().minusMonths(2);
+
+ var mockAccounting = new UserAccounting(userId, periodEnd, 3600);
+ mockAccounting.simulationTime = 3900;
+ Mockito.when(UserAccounting.findByUser(userId)).thenReturn(mockAccounting);
+
+ assertTrue(service.hasSimulationBudget(userId));
+ }
+
+ @Test
+ public void testHasBudgetPeriodExpired() {
+ var periodEnd = LocalDate.now().minusMonths(2);
+
+ var mockAccounting = new UserAccounting(userId, periodEnd, 3600);
+ Mockito.when(UserAccounting.findByUser(userId)).thenReturn(mockAccounting);
+
+ assertTrue(service.hasSimulationBudget(userId));
+ }
+
+ @Test
+ public void testHasBudgetExceeded() {
+ var periodEnd = LocalDate.now().plusMonths(1);
+
+ var mockAccounting = new UserAccounting(userId, periodEnd, 3600);
+ mockAccounting.simulationTime = 3900;
+ Mockito.when(UserAccounting.findByUser(userId)).thenReturn(mockAccounting);
+
+ assertFalse(service.hasSimulationBudget(userId));
+ }
+
+ @Test
+ public void testConsumeBudgetNewUser() {
+ Mockito.when(UserAccounting.findByUser(userId)).thenReturn(null);
+ Mockito.when(UserAccounting.create(anyString(), any(), anyInt(), anyInt()))
+ .thenAnswer((i) -> {
+ var accounting = new UserAccounting(i.getArgument(0), i.getArgument(1), i.getArgument(2));
+ accounting.simulationTime = i.getArgument(3);
+ return accounting;
+ });
+
+ assertFalse(service.consumeSimulationBudget(userId, 10));
+ }
+
+ @Test
+ public void testConsumeBudgetNewUserExceeded() {
+ Mockito.when(UserAccounting.findByUser(userId)).thenReturn(null);
+ Mockito.when(UserAccounting.create(anyString(), any(), anyInt(), anyInt()))
+ .thenAnswer((i) -> {
+ var accounting = new UserAccounting(i.getArgument(0), i.getArgument(1), i.getArgument(2));
+ accounting.simulationTime = i.getArgument(3);
+ return accounting;
+ });
+
+ assertTrue(service.consumeSimulationBudget(userId, 4000));
+ }
+
+ @Test
+ public void testConsumeBudgetNewUserConflict() {
+ var periodEnd = LocalDate.now().plusMonths(1);
+ var accountingMock = Mockito.spy(new UserAccounting(userId, periodEnd, 3600));
+
+ Mockito.when(UserAccounting.findByUser(userId)).thenReturn(null).thenReturn(accountingMock);
+ Mockito.when(UserAccounting.create(anyString(), any(), anyInt(), anyInt()))
+ .thenThrow(new EntityExistsException());
+ Mockito.when(accountingMock.consumeBudget(anyInt())).thenAnswer((i) -> {
+ accountingMock.simulationTime += i.<Integer>getArgument(0);
+ return true;
+ });
+
+ assertFalse(service.consumeSimulationBudget(userId, 10));
+ }
+
+ @Test
+ public void testConsumeBudgetResetSuccess() {
+ var periodEnd = LocalDate.now().minusMonths(2);
+ var accountingMock = Mockito.spy(new UserAccounting(userId, periodEnd, 3600));
+ accountingMock.simulationTime = 3900;
+
+ Mockito.when(UserAccounting.findByUser(userId)).thenReturn(accountingMock);
+ Mockito.when(accountingMock.resetBudget(any(), anyInt())).thenAnswer((i) -> {
+ accountingMock.periodEnd = i.getArgument(0);
+ accountingMock.simulationTime += i.<Integer>getArgument(1);
+ return true;
+ });
+
+ assertTrue(service.consumeSimulationBudget(userId, 4000));
+ }
+
+ @Test
+ public void testInfiniteConflict() {
+ var periodEnd = LocalDate.now().plusMonths(1);
+ var accountingMock = Mockito.spy(new UserAccounting(userId, periodEnd, 3600));
+
+ Mockito.when(UserAccounting.findByUser(userId)).thenReturn(accountingMock);
+ Mockito.when(accountingMock.consumeBudget(anyInt())).thenAnswer((i) -> {
+ accountingMock.simulationTime += i.<Integer>getArgument(0);
+ return false;
+ });
+
+ assertThrows(IllegalStateException.class, () -> service.consumeSimulationBudget(userId, 10));
+ }
+}
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 4a86c928..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.queryPending() } 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 5798d2e7..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.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 { 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.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 portfolio.
- */
- @Test
- @TestSecurity(user = "testUser", roles = ["openid"])
- fun testGetExisting() {
- every { portfolioService.findOne("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 fec8759c..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.findWithUser("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.findWithUser("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.findWithUser("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.createForUser("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.deleteWithUser("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.deleteWithUser("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.deleteWithUser("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)
- }
- }
-}
diff --git a/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/service/UserAccountingServiceTest.kt b/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/service/UserAccountingServiceTest.kt
deleted file mode 100644
index fdf04787..00000000
--- a/opendc-web/opendc-web-server/src/test/kotlin/org/opendc/web/server/service/UserAccountingServiceTest.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.service
-
-import io.mockk.every
-import io.mockk.mockk
-import io.quarkus.test.junit.QuarkusTest
-import org.junit.jupiter.api.Assertions.assertAll
-import org.junit.jupiter.api.Assertions.assertEquals
-import org.junit.jupiter.api.Assertions.assertFalse
-import org.junit.jupiter.api.Assertions.assertTrue
-import org.junit.jupiter.api.Test
-import org.junit.jupiter.api.assertThrows
-import org.opendc.web.server.model.UserAccounting
-import org.opendc.web.server.repository.UserAccountingRepository
-import java.time.Duration
-import java.time.LocalDate
-import javax.persistence.EntityExistsException
-
-/**
- * Test suite for the [UserAccountingService].
- */
-@QuarkusTest
-class UserAccountingServiceTest {
- /**
- * The [UserAccountingRepository] that is mocked.
- */
- private val repository: UserAccountingRepository = mockk()
-
- /**
- * The [UserAccountingService] instance under test.
- */
- private val service: UserAccountingService = UserAccountingService(repository, Duration.ofHours(1))
-
- @Test
- fun testGetUserDoesNotExist() {
- val userId = "test"
-
- every { repository.findForUser(userId) } returns null
-
- val accounting = service.getAccounting(userId)
-
- assertTrue(accounting.periodEnd.isAfter(LocalDate.now()))
- assertEquals(0, accounting.simulationTime)
- }
-
- @Test
- fun testGetUserDoesExist() {
- val userId = "test"
-
- val now = LocalDate.now()
- val periodEnd = now.plusMonths(1)
-
- every { repository.findForUser(userId) } returns UserAccounting(userId, periodEnd, 3600).also { it.simulationTime = 32 }
-
- val accounting = service.getAccounting(userId)
-
- assertAll(
- { assertEquals(periodEnd, accounting.periodEnd) },
- { assertEquals(32, accounting.simulationTime) },
- { assertEquals(3600, accounting.simulationTimeBudget) }
- )
- }
-
- @Test
- fun testHasBudgetUserDoesNotExist() {
- val userId = "test"
-
- every { repository.findForUser(userId) } returns null
-
- assertTrue(service.hasSimulationBudget(userId))
- }
-
- @Test
- fun testHasBudget() {
- val userId = "test"
- val periodEnd = LocalDate.now().plusMonths(2)
-
- every { repository.findForUser(userId) } returns UserAccounting(userId, periodEnd, 3600)
-
- assertTrue(service.hasSimulationBudget(userId))
- }
-
- @Test
- fun testHasBudgetExceededButPeriodExpired() {
- val userId = "test"
- val periodEnd = LocalDate.now().minusMonths(2)
-
- every { repository.findForUser(userId) } returns UserAccounting(userId, periodEnd, 3600).also { it.simulationTime = 3900 }
-
- assertTrue(service.hasSimulationBudget(userId))
- }
-
- @Test
- fun testHasBudgetPeriodExpired() {
- val userId = "test"
- val periodEnd = LocalDate.now().minusMonths(2)
-
- every { repository.findForUser(userId) } returns UserAccounting(userId, periodEnd, 3600)
-
- assertTrue(service.hasSimulationBudget(userId))
- }
-
- @Test
- fun testHasBudgetExceeded() {
- val userId = "test"
- val periodEnd = LocalDate.now().plusMonths(1)
-
- every { repository.findForUser(userId) } returns UserAccounting(userId, periodEnd, 3600).also { it.simulationTime = 3900 }
-
- assertFalse(service.hasSimulationBudget(userId))
- }
-
- @Test
- fun testConsumeBudgetNewUser() {
- val userId = "test"
-
- every { repository.findForUser(userId) } returns null
- every { repository.save(any()) } returns Unit
-
- assertFalse(service.consumeSimulationBudget(userId, 10))
- }
-
- @Test
- fun testConsumeBudgetNewUserExceeded() {
- val userId = "test"
-
- every { repository.findForUser(userId) } returns null
- every { repository.save(any()) } returns Unit
-
- assertTrue(service.consumeSimulationBudget(userId, 4000))
- }
-
- @Test
- fun testConsumeBudgetNewUserConflict() {
- val userId = "test"
-
- val periodEnd = LocalDate.now().plusMonths(1)
-
- every { repository.findForUser(userId) } returns null andThen UserAccounting(userId, periodEnd, 3600)
- every { repository.save(any()) } throws EntityExistsException()
- every { repository.consumeBudget(any(), any()) } answers {
- val accounting = it.invocation.args[0] as UserAccounting
- accounting.simulationTime -= it.invocation.args[1] as Int
- true
- }
-
- assertFalse(service.consumeSimulationBudget(userId, 10))
- }
-
- @Test
- fun testConsumeBudgetResetSuccess() {
- val userId = "test"
-
- val periodEnd = LocalDate.now().minusMonths(2)
-
- every { repository.findForUser(userId) } returns UserAccounting(userId, periodEnd, 3600).also { it.simulationTime = 3900 }
- every { repository.resetBudget(any(), any(), any()) } answers {
- val accounting = it.invocation.args[0] as UserAccounting
- accounting.periodEnd = it.invocation.args[1] as LocalDate
- accounting.simulationTime = it.invocation.args[2] as Int
- true
- }
-
- assertTrue(service.consumeSimulationBudget(userId, 4000))
- }
-
- @Test
- fun testInfiniteConflict() {
- val userId = "test"
-
- val periodEnd = LocalDate.now().plusMonths(1)
-
- every { repository.findForUser(userId) } returns UserAccounting(userId, periodEnd, 3600)
- every { repository.consumeBudget(any(), any()) } answers {
- val accounting = it.invocation.args[0] as UserAccounting
- accounting.simulationTime -= it.invocation.args[1] as Int
- false
- }
-
- assertThrows<IllegalStateException> { service.consumeSimulationBudget(userId, 10) }
- }
-}