From a66ed8e59f2560fe67f240977444d9409f804b12 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 19 Aug 2022 10:11:19 +0200 Subject: [PATCH] add jacoco test code coverage --- README.md | 23 ++++ build.gradle | 97 ++++++++++++++- lombok.config | 1 + .../RestResponseEntityExceptionHandler.java | 20 +-- ...esponseEntityExceptionHandlerUnitTest.java | 90 ++++++++++++++ .../CustomerControllerRestTest.java | 117 +++++++++--------- .../RbacGrantRepositoryIntegrationTest.java | 106 ++++++++-------- 7 files changed, 334 insertions(+), 120 deletions(-) create mode 100644 lombok.config create mode 100644 src/test/java/net/hostsharing/hsadminng/errors/RestResponseEntityExceptionHandlerUnitTest.java diff --git a/README.md b/README.md index 4cba2443..d2763bb8 100644 --- a/README.md +++ b/README.md @@ -369,6 +369,29 @@ To apply formatting rules, use: gw spotlessApply ``` +### JaCoCo Test Code Coverage Check + +This project uses the JaCoCo test code coverage report with limit checks. +It can be executed with: + +```shell +gw jacocoTestReport +``` + +This task is also automatically run after `gw test`. +It is configured in [build.gradle](build.gradle). + +A report is generated under [build/reports/jacoco/tests/test/index.html](./build/reports/jacoco/test/html/index.html). + +Additionally, quality limits are checked via: + +```shell +gw jacocoTestCoverageVerification +``` + +This task is also executed as part of `gw check`. + + ### OWASP Security Vulnerability Check An OWASP security vulnerability is configured and can be utilized by running: diff --git a/build.gradle b/build.gradle index 164133ea..5855d2fa 100644 --- a/build.gradle +++ b/build.gradle @@ -6,6 +6,7 @@ plugins { id 'com.github.jk1.dependency-license-report' version '2.1' id "org.owasp.dependencycheck" version "7.1.1" id "com.diffplug.spotless" version "6.9.0" + id 'jacoco' } group = 'net.hostsharing' @@ -71,14 +72,19 @@ dependencyManagement { } } +// Java Compiler Options tasks.withType(JavaCompile) { - options.compilerArgs += ["-parameters"] + options.compilerArgs += [ + "-parameters" // keep parameter names => no need for @Param for SpringData + ] } +// Use JUnit Jupiter tasks.named('test') { useJUnitPlatform() } +// OpenAPI Source Code Generation openapiProcessor { spring { processor 'io.openapiprocessor:openapi-processor-spring:2022.4' @@ -93,6 +99,7 @@ sourceSets.main.java.srcDir 'build/generated/sources/openapi' project.tasks.processResources.dependsOn('processSpring') project.tasks.compileJava.dependsOn('processSpring') +// Spotless Code Formatting spotless { java { // removeUnusedImports() TODO: reactivate once it can deal with multi-line-strings @@ -108,6 +115,7 @@ spotless { } project.tasks.check.dependsOn(spotlessCheck) +// OWASP Dependency Security Test dependencyCheck { cveValidForHours=4 format = 'ALL' @@ -117,8 +125,95 @@ dependencyCheck { } project.tasks.check.dependsOn(dependencyCheckAnalyze) +// License Check licenseReport { excludeBoms = true allowedLicensesFile = new File("$projectDir/etc/allowed-licenses.json") } project.tasks.check.dependsOn(checkLicense) + +// JaCoCo Test Code Coverage +jacoco { + toolVersion = "0.8.8" +} +test { + finalizedBy jacocoTestReport // generate report after tests + excludes = [ + 'net.hostsharing.hsadminng.generated.**', + ] +} +jacocoTestReport { + dependsOn test + afterEvaluate { + classDirectories.setFrom(files(classDirectories.files.collect { + fileTree(dir: it, exclude: [ + "net/hostsharing/hsadminng/generated/**/*.class", + + // TODO: improve test code coverage for these classes: + "net/hostsharing/hsadminng/rbac/rbacuser/UserController.class", + "net/hostsharing/hsadminng/rbac/rbacgrant/GrantController.class", + "net/hostsharing/hsadminng/hs/hscustomer/CustomerController.class" + ]) + })) + } + doLast { + println "HTML Jacoco Test Code Coverage Report: file://${reports.html.outputLocation.get()}/index.html" + } +} +project.tasks.check.dependsOn(jacocoTestCoverageVerification) +jacocoTestCoverageVerification { + violationRules { + rule { + excludes = ['net.hostsharing.hsadminng.generated.**'] + limit { + minimum = 0.7 // TODO: increase to 0.9 + } + } + + // element: PACKAGE, BUNDLE, CLASS, SOURCEFILE or METHOD + // counter: INSTRUCTION, BRANCH, LINE, COMPLEXITY, METHOD, or CLASS + // value: TOTALCOUNT, COVEREDCOUNT, MISSEDCOUNT, COVEREDRATIO or MISSEDRATIO + + rule { + element = 'CLASS' + excludes = [ + 'net.hostsharing.hsadminng.generated.**', + 'net.hostsharing.hsadminng.HsadminNgApplication', + 'net.hostsharing.hsadminng.TestController', + + // TODO: improve test code coverage: + 'net.hostsharing.hsadminng.rbac.rbacuser.UserController', + 'net.hostsharing.hsadminng.hs.hscustomer.CustomerController' + ] + + limit { + counter = 'LINE' + value = 'COVEREDRATIO' + minimum = 0.7 + } + } + rule { + element = 'METHOD' + excludes = [ + 'net.hostsharing.hsadminng.generated.**', + 'net.hostsharing.hsadminng.HsadminNgApplication.*', + + // TODO: improve test code coverage: + 'net.hostsharing.hsadminng.rbac.rbacuser.RbacUserController.listUsers(*)', + 'net.hostsharing.hsadminng.rbac.rbacuser.RbacUserController.listUserPermissions(*)', + 'net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantController.listUserGrants(*)', + 'net.hostsharing.hsadminng.hs.hscustomer.CustomerController.addCustomer(java.lang.String, java.lang.String, net.hostsharing.hsadminng.generated.api.v1.model.CustomerResource)' + ] + + limit { + counter = 'BRANCH' + value = 'COVEREDRATIO' + minimum = 0.5 // TODO: increase test code coverage + } + } + } +} + + + + diff --git a/lombok.config b/lombok.config new file mode 100644 index 00000000..7a21e880 --- /dev/null +++ b/lombok.config @@ -0,0 +1 @@ +lombok.addLombokGeneratedAnnotation = true diff --git a/src/main/java/net/hostsharing/hsadminng/errors/RestResponseEntityExceptionHandler.java b/src/main/java/net/hostsharing/hsadminng/errors/RestResponseEntityExceptionHandler.java index 3adeafba..d8602d1f 100644 --- a/src/main/java/net/hostsharing/hsadminng/errors/RestResponseEntityExceptionHandler.java +++ b/src/main/java/net/hostsharing/hsadminng/errors/RestResponseEntityExceptionHandler.java @@ -17,11 +17,11 @@ import java.util.Optional; @ControllerAdvice public class RestResponseEntityExceptionHandler - extends ResponseEntityExceptionHandler { + extends ResponseEntityExceptionHandler { @ExceptionHandler(DataIntegrityViolationException.class) protected ResponseEntity handleConflict( - final RuntimeException exc, final WebRequest request) { + final RuntimeException exc, final WebRequest request) { final var message = firstLine(NestedExceptionUtils.getMostSpecificCause(exc).getMessage()); return errorResponse(request, HttpStatus.CONFLICT, message); @@ -29,16 +29,16 @@ public class RestResponseEntityExceptionHandler @ExceptionHandler(JpaSystemException.class) protected ResponseEntity handleJpaExceptions( - final RuntimeException exc, final WebRequest request) { + final RuntimeException exc, final WebRequest request) { final var message = firstLine(NestedExceptionUtils.getMostSpecificCause(exc).getMessage()); - return errorResponse(request, httpStatus(message).orElse(HttpStatus.FORBIDDEN), message); + return errorResponse(request, httpStatus(message).orElse(HttpStatus.INTERNAL_SERVER_ERROR), message); } @ExceptionHandler(Throwable.class) protected ResponseEntity handleOtherExceptions( - final RuntimeException exc, final WebRequest request) { + final Throwable exc, final WebRequest request) { final var message = firstLine(NestedExceptionUtils.getMostSpecificCause(exc).getMessage()); - return errorResponse(request, httpStatus(message).orElse(HttpStatus.FORBIDDEN), message); + return errorResponse(request, httpStatus(message).orElse(HttpStatus.INTERNAL_SERVER_ERROR), message); } private Optional httpStatus(final String message) { @@ -54,11 +54,11 @@ public class RestResponseEntityExceptionHandler } private static ResponseEntity errorResponse( - final WebRequest request, - final HttpStatus httpStatus, - final String message) { + final WebRequest request, + final HttpStatus httpStatus, + final String message) { return new ResponseEntity<>( - new CustomErrorResponse(request.getContextPath(), httpStatus, message), httpStatus); + new CustomErrorResponse(request.getContextPath(), httpStatus, message), httpStatus); } private String firstLine(final String message) { diff --git a/src/test/java/net/hostsharing/hsadminng/errors/RestResponseEntityExceptionHandlerUnitTest.java b/src/test/java/net/hostsharing/hsadminng/errors/RestResponseEntityExceptionHandlerUnitTest.java new file mode 100644 index 00000000..6e4a8ebd --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/errors/RestResponseEntityExceptionHandlerUnitTest.java @@ -0,0 +1,90 @@ +package net.hostsharing.hsadminng.errors; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.orm.jpa.JpaSystemException; +import org.springframework.web.context.request.WebRequest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +@ExtendWith(MockitoExtension.class) +class RestResponseEntityExceptionHandlerUnitTest { + + final RestResponseEntityExceptionHandler exceptionHandler = new RestResponseEntityExceptionHandler(); + + @Test + void handleConflict() { + // given + final var givenException = new DataIntegrityViolationException("First Line\nSecond Line\nThird Line"); + final var givenWebRequest = mock(WebRequest.class); + + // when + final var errorResponse = exceptionHandler.handleConflict(givenException, givenWebRequest); + + // then + assertThat(errorResponse.getStatusCodeValue()).isEqualTo(409); + assertThat(errorResponse.getBody().getMessage()).isEqualTo("First Line"); + } + + @Test + void jpaExceptionWithKnownErrorCode() { + // given + final var givenException = new JpaSystemException(new RuntimeException( + "ERROR: [401] First Line\nSecond Line\nThird Line")); + final var givenWebRequest = mock(WebRequest.class); + + // when + final var errorResponse = exceptionHandler.handleJpaExceptions(givenException, givenWebRequest); + + // then + assertThat(errorResponse.getStatusCodeValue()).isEqualTo(401); + assertThat(errorResponse.getBody().getMessage()).isEqualTo("ERROR: [401] First Line"); + } + + @Test + void jpaExceptionWithUnknownErrorCode() { + // given + final var givenException = new JpaSystemException(new RuntimeException( + "ERROR: [999] First Line\nSecond Line\nThird Line")); + final var givenWebRequest = mock(WebRequest.class); + + // when + final var errorResponse = exceptionHandler.handleJpaExceptions(givenException, givenWebRequest); + + // then + assertThat(errorResponse.getStatusCodeValue()).isEqualTo(500); + assertThat(errorResponse.getBody().getMessage()).isEqualTo("ERROR: [999] First Line"); + } + + @Test + void handleOtherExceptionsWithoutErrorCode() { + // given + final var givenThrowable = new Error("First Line\nSecond Line\nThird Line"); + final var givenWebRequest = mock(WebRequest.class); + + // when + final var errorResponse = exceptionHandler.handleOtherExceptions(givenThrowable, givenWebRequest); + + // then + assertThat(errorResponse.getStatusCodeValue()).isEqualTo(500); + assertThat(errorResponse.getBody().getMessage()).isEqualTo("First Line"); + } + + @Test + void handleOtherExceptionsWithErrorCode() { + // given + final var givenThrowable = new Error("ERROR: [418] First Line\nSecond Line\nThird Line"); + final var givenWebRequest = mock(WebRequest.class); + + // when + final var errorResponse = exceptionHandler.handleOtherExceptions(givenThrowable, givenWebRequest); + + // then + assertThat(errorResponse.getStatusCodeValue()).isEqualTo(418); + assertThat(errorResponse.getBody().getMessage()).isEqualTo("ERROR: [418] First Line"); + } + +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerRestTest.java index ed80dd01..8f200632 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerRestTest.java @@ -1,6 +1,7 @@ package net.hostsharing.hsadminng.hs.hscustomer; import net.hostsharing.hsadminng.context.Context; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; @@ -28,78 +29,82 @@ class CustomerControllerRestTest { @MockBean CustomerRepository customerRepositoryMock; - @Test - void listCustomersWillReturnAllCustomersFromRepositoryIfNoCriteriaGiven() throws Exception { + @Nested + class ListCustomers { - // given - when(customerRepositoryMock.findCustomerByOptionalPrefixLike(null)).thenReturn(List.of( - TestCustomer.xxx, - TestCustomer.yyy)); + @Test + void listCustomersWillReturnAllCustomersFromRepositoryIfNoCriteriaGiven() throws Exception { - // when - mockMvc.perform(MockMvcRequestBuilders - .get("/api/customers") - .header("current-user", "mike@hostsharing.net") - .accept(MediaType.APPLICATION_JSON)) + // given + when(customerRepositoryMock.findCustomerByOptionalPrefixLike(null)).thenReturn(List.of( + TestCustomer.xxx, + TestCustomer.yyy)); + + // when + mockMvc.perform(MockMvcRequestBuilders + .get("/api/customers") + .header("current-user", "mike@hostsharing.net") + .accept(MediaType.APPLICATION_JSON)) + + // then + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(2))) + .andExpect(jsonPath("$[0].prefix", is(TestCustomer.xxx.getPrefix()))) + .andExpect(jsonPath("$[1].reference", is(TestCustomer.yyy.getReference())) + ); // then - .andExpect(status().isOk()) - .andExpect(jsonPath("$", hasSize(2))) - .andExpect(jsonPath("$[0].prefix", is(TestCustomer.xxx.getPrefix()))) - .andExpect(jsonPath("$[1].reference", is(TestCustomer.yyy.getReference())) - ); + verify(contextMock).setCurrentUser("mike@hostsharing.net"); + verify(contextMock, never()).assumeRoles(anyString()); + } - // then - verify(contextMock).setCurrentUser("mike@hostsharing.net"); - verify(contextMock, never()).assumeRoles(anyString()); - } + @Test + void listCustomersWillReturnMatchingCustomersFromRepositoryIfCriteriaGiven() throws Exception { - @Test - void listCustomersWillReturnMatchingCustomersFromRepositoryIfCriteriaGiven() throws Exception { + // given + when(customerRepositoryMock.findCustomerByOptionalPrefixLike("x")).thenReturn(List.of(TestCustomer.xxx)); - // given - when(customerRepositoryMock.findCustomerByOptionalPrefixLike("x")).thenReturn(List.of(TestCustomer.xxx)); + // when + mockMvc.perform(MockMvcRequestBuilders + .get("/api/customers") + .header("current-user", "mike@hostsharing.net") + .param("prefix", "x") + .accept(MediaType.APPLICATION_JSON)) - // when - mockMvc.perform(MockMvcRequestBuilders - .get("/api/customers") - .header("current-user", "mike@hostsharing.net") - .param("prefix", "x") - .accept(MediaType.APPLICATION_JSON)) + // then + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(1))) + .andExpect(jsonPath("$[0].prefix", is(TestCustomer.xxx.getPrefix())) + ); // then - .andExpect(status().isOk()) - .andExpect(jsonPath("$", hasSize(1))) - .andExpect(jsonPath("$[0].prefix", is(TestCustomer.xxx.getPrefix())) - ); + verify(contextMock).setCurrentUser("mike@hostsharing.net"); + verify(contextMock, never()).assumeRoles(anyString()); + } - // then - verify(contextMock).setCurrentUser("mike@hostsharing.net"); - verify(contextMock, never()).assumeRoles(anyString()); - } + @Test + void listCustomersWillReturnAllCustomersForGivenAssumedRoles() throws Exception { - @Test - void listCustomersWillReturnAllCustomersForGivenAssumedRoles() throws Exception { + // given + when(customerRepositoryMock.findCustomerByOptionalPrefixLike(null)).thenReturn(List.of(TestCustomer.yyy)); - // given - when(customerRepositoryMock.findCustomerByOptionalPrefixLike(null)).thenReturn(List.of(TestCustomer.yyy)); + // when + mockMvc.perform(MockMvcRequestBuilders + .get("/api/customers") + .header("current-user", "mike@hostsharing.net") + .header("assumed-roles", "admin@yyy.example.com") + .accept(MediaType.APPLICATION_JSON)) - // when - mockMvc.perform(MockMvcRequestBuilders - .get("/api/customers") - .header("current-user", "mike@hostsharing.net") - .header("assumed-roles", "admin@yyy.example.com") - .accept(MediaType.APPLICATION_JSON)) + // then + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(1))) + .andExpect(jsonPath("$[0].prefix", is(TestCustomer.yyy.getPrefix())) + ); // then - .andExpect(status().isOk()) - .andExpect(jsonPath("$", hasSize(1))) - .andExpect(jsonPath("$[0].prefix", is(TestCustomer.yyy.getPrefix())) - ); - - // then - verify(contextMock).setCurrentUser("mike@hostsharing.net"); - verify(contextMock).assumeRoles("admin@yyy.example.com"); + verify(contextMock).setCurrentUser("mike@hostsharing.net"); + verify(contextMock).assumeRoles("admin@yyy.example.com"); + } } } diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantRepositoryIntegrationTest.java index 6350c7fd..5364d1b1 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantRepositoryIntegrationTest.java @@ -17,7 +17,6 @@ import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import javax.persistence.EntityManager; -import javax.persistence.PersistenceException; import java.util.List; import java.util.UUID; @@ -62,8 +61,8 @@ class RbacGrantRepositoryIntegrationTest { // then exactlyTheseRbacGrantsAreReturned( - result, - "{ grant assumed role package#aaa00.admin to user aaa00@aaa.example.com by role customer#aaa.admin }"); + result, + "{ grant assumed role package#aaa00.admin to user aaa00@aaa.example.com by role customer#aaa.admin }"); } @Test @@ -77,11 +76,11 @@ class RbacGrantRepositoryIntegrationTest { // then exactlyTheseRbacGrantsAreReturned( - result, - "{ grant assumed role customer#aaa.admin to user admin@aaa.example.com by role global#hostsharing.admin }", - "{ grant assumed role package#aaa00.admin to user aaa00@aaa.example.com by role customer#aaa.admin }", - "{ grant assumed role package#aaa01.admin to user aaa01@aaa.example.com by role customer#aaa.admin }", - "{ grant assumed role package#aaa02.admin to user aaa02@aaa.example.com by role customer#aaa.admin }"); + result, + "{ grant assumed role customer#aaa.admin to user admin@aaa.example.com by role global#hostsharing.admin }", + "{ grant assumed role package#aaa00.admin to user aaa00@aaa.example.com by role customer#aaa.admin }", + "{ grant assumed role package#aaa01.admin to user aaa01@aaa.example.com by role customer#aaa.admin }", + "{ grant assumed role package#aaa02.admin to user aaa02@aaa.example.com by role customer#aaa.admin }"); } @Test @@ -96,8 +95,8 @@ class RbacGrantRepositoryIntegrationTest { // then exactlyTheseRbacGrantsAreReturned( - result, - "{ grant assumed role package#aaa00.admin to user aaa00@aaa.example.com by role customer#aaa.admin }"); + result, + "{ grant assumed role package#aaa00.admin to user aaa00@aaa.example.com by role customer#aaa.admin }"); } } @@ -114,18 +113,19 @@ class RbacGrantRepositoryIntegrationTest { // when final var grant = RbacGrantEntity.builder() - .granteeUserUuid(givenArbitraryUserUuid).grantedRoleUuid(givenOwnPackageRoleUuid) - .assumed(true) - .build(); + .granteeUserUuid(givenArbitraryUserUuid).grantedRoleUuid(givenOwnPackageRoleUuid) + .assumed(true) + .build(); final var attempt = attempt(em, () -> - rbacGrantRepository.save(grant) + rbacGrantRepository.save(grant) ); // then assertThat(attempt.caughtException()).isNull(); assertThat(rbacGrantRepository.findAll()) - .extracting(RbacGrantEntity::toDisplay) - .contains("{ grant assumed role package#aaa00.admin to user aac00@aac.example.com by role customer#aaa.admin }"); + .extracting(RbacGrantEntity::toDisplay) + .contains( + "{ grant assumed role package#aaa00.admin to user aac00@aac.example.com by role customer#aaa.admin }"); } @Test @@ -139,8 +139,8 @@ class RbacGrantRepositoryIntegrationTest { // to find the uuids of we need to have access rights to these currentUser("admin@aaa.example.com"); return new Given( - createNewUser(), - rbacRoleRepository.findByRoleName("package#aaa00.owner").getUuid() + createNewUser(), + rbacRoleRepository.findByRoleName("package#aaa00.owner").getUuid() ); }).returnedValue(); @@ -150,24 +150,24 @@ class RbacGrantRepositoryIntegrationTest { currentUser("aaa00@aaa.example.com"); assumedRoles("package#aaa00.admin"); final var grant = RbacGrantEntity.builder() - .granteeUserUuid(given.arbitraryUser.getUuid()) - .grantedRoleUuid(given.packageOwnerRoleUuid) - .assumed(true) - .build(); + .granteeUserUuid(given.arbitraryUser.getUuid()) + .grantedRoleUuid(given.packageOwnerRoleUuid) + .assumed(true) + .build(); rbacGrantRepository.save(grant); }); // then attempt.assertExceptionWithRootCauseMessage( - JpaSystemException.class, - "ERROR: [403] Access to granted role " + given.packageOwnerRoleUuid - + " forbidden for {package#aaa00.admin}"); + JpaSystemException.class, + "ERROR: [403] Access to granted role " + given.packageOwnerRoleUuid + + " forbidden for {package#aaa00.admin}"); jpaAttempt.transacted(() -> { // finally, we use the new user to make sure, no roles were granted currentUser(given.arbitraryUser.getName()); assertThat(rbacGrantRepository.findAll()) - .extracting(RbacGrantEntity::toDisplay) - .hasSize(0); + .extracting(RbacGrantEntity::toDisplay) + .hasSize(0); }); } } @@ -179,8 +179,8 @@ class RbacGrantRepositoryIntegrationTest { public void customerAdmin_canRevokeSelfGrantedPackageAdminRole() { // given final var grant = create(grant() - .byUser("admin@aaa.example.com").withAssumedRole("customer#aaa.admin") - .grantingRole("package#aaa00.admin").toUser("aac00@aac.example.com")); + .byUser("admin@aaa.example.com").withAssumedRole("customer#aaa.admin") + .grantingRole("package#aaa00.admin").toUser("aac00@aac.example.com")); // when currentUser("admin@aaa.example.com"); @@ -194,16 +194,16 @@ class RbacGrantRepositoryIntegrationTest { assumedRoles("customer#aaa.admin"); assertThat(revokeAttempt.caughtExceptionsRootCause()).isNull(); assertThat(rbacGrantRepository.findAll()) - .extracting(RbacGrantEntity::getGranteeUserName) - .doesNotContain("aac00@aac.example.com"); + .extracting(RbacGrantEntity::getGranteeUserName) + .doesNotContain("aac00@aac.example.com"); } @Test public void packageAdmin_canRevokeOwnPackageAdminRoleGrantedByAnotherAdminOfThatPackage() { // given final var grant = create(grant() - .byUser("admin@aaa.example.com").withAssumedRole("package#aaa00.admin") - .grantingRole("package#aaa00.admin").toUser(createNewUser().getName())); + .byUser("admin@aaa.example.com").withAssumedRole("package#aaa00.admin") + .grantingRole("package#aaa00.admin").toUser(createNewUser().getName())); // when currentUser("aaa00@aaa.example.com"); @@ -217,16 +217,16 @@ class RbacGrantRepositoryIntegrationTest { currentUser("admin@aaa.example.com"); assumedRoles("customer#aaa.admin"); assertThat(rbacGrantRepository.findAll()) - .extracting(RbacGrantEntity::getGranteeUserName) - .doesNotContain("aac00@aac.example.com"); + .extracting(RbacGrantEntity::getGranteeUserName) + .doesNotContain("aac00@aac.example.com"); } @Test public void packageAdmin_canNotRevokeOwnPackageAdminRoleGrantedByOwnerRoleOfThatPackage() { // given final var grant = create(grant() - .byUser("admin@aaa.example.com").withAssumedRole("package#aaa00.owner") - .grantingRole("package#aaa00.admin").toUser("aac00@aac.example.com")); + .byUser("admin@aaa.example.com").withAssumedRole("package#aaa00.owner") + .grantingRole("package#aaa00.admin").toUser("aac00@aac.example.com")); final var grantedByRole = rbacRoleRepository.findByRoleName("package#aaa00.owner"); // when @@ -238,10 +238,10 @@ class RbacGrantRepositoryIntegrationTest { // then revokeAttempt.assertExceptionWithRootCauseMessage( - PersistenceException.class, - "ERROR: [403] Revoking role created by %s is forbidden for {package#aaa00.admin}." .formatted( - grantedByRole.getUuid() - )); + JpaSystemException.class, + "ERROR: [403] Revoking role created by %s is forbidden for {package#aaa00.admin}.".formatted( + grantedByRole.getUuid() + )); } private RbacGrantEntity create(GrantBuilder with) { @@ -251,19 +251,19 @@ class RbacGrantRepositoryIntegrationTest { final var givenOwnPackageRoleUuid = rbacRoleRepository.findByRoleName(with.grantedRole).getUuid(); final var grant = RbacGrantEntity.builder() - .granteeUserUuid(givenArbitraryUserUuid).grantedRoleUuid(givenOwnPackageRoleUuid) - .assumed(true) - .build(); + .granteeUserUuid(givenArbitraryUserUuid).grantedRoleUuid(givenOwnPackageRoleUuid) + .assumed(true) + .build(); final var grantAttempt = attempt(em, () -> - rbacGrantRepository.save(grant) + rbacGrantRepository.save(grant) ); assumeThat(grantAttempt.caughtException()).isNull(); assumeThat(rbacGrantRepository.findAll()) - .extracting(RbacGrantEntity::toDisplay) - .contains("{ grant assumed role %s to user %s by role %s }" .formatted( - with.grantedRole, with.granteeUserName, with.assumedRole - )); + .extracting(RbacGrantEntity::toDisplay) + .contains("{ grant assumed role %s to user %s by role %s }".formatted( + with.grantedRole, with.granteeUserName, with.assumedRole + )); return grant; } @@ -303,7 +303,7 @@ class RbacGrantRepositoryIntegrationTest { private RbacUserEntity createNewUser() { return rbacUserRepository.create( - new RbacUserEntity(null, "test-user-" + System.currentTimeMillis() + "@example.com")); + new RbacUserEntity(null, "test-user-" + System.currentTimeMillis() + "@example.com")); } void currentUser(final String currentUser) { @@ -318,9 +318,9 @@ class RbacGrantRepositoryIntegrationTest { void exactlyTheseRbacGrantsAreReturned(final List actualResult, final String... expectedGrant) { assertThat(actualResult) - .filteredOn(g -> !g.getGranteeUserName().startsWith("test-user-")) // ignore test-users created by other tests - .extracting(RbacGrantEntity::toDisplay) - .containsExactlyInAnyOrder(expectedGrant); + .filteredOn(g -> !g.getGranteeUserName().startsWith("test-user-")) // ignore test-users created by other tests + .extracting(RbacGrantEntity::toDisplay) + .containsExactlyInAnyOrder(expectedGrant); } }