Skip to content
Snippets Groups Projects
Verified Commit 186f7426 authored by Martin Weise's avatar Martin Weise
Browse files

Updated endpoint observability tests

parent bc835f2f
No related branches found
No related tags found
4 merge requests!231CI: Remove build for log-service,!228Better error message handling in the frontend,!223Release of version 1.4.0,!213Resolve "Some bugs regarding data"
......@@ -151,10 +151,20 @@
<version>${c3p0-hibernate.version}</version>
</dependency>
<!-- Monitoring -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<scope>runtime</scope>
<version>${micrometer.version}</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-observation-test</artifactId>
<version>${micrometer.version}</version>
<scope>test</scope>
</dependency>
<!-- Authentication -->
<dependency>
......
......@@ -10,6 +10,7 @@ import at.tuwien.mapper.DatabaseMapper;
import at.tuwien.service.AccessService;
import at.tuwien.utils.PrincipalUtil;
import at.tuwien.utils.UserUtil;
import io.micrometer.observation.annotation.Observed;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
......@@ -46,6 +47,7 @@ public class AccessEndpoint {
@PostMapping("/{userId}")
@Transactional
@Observed(name = "dbr_access_give")
@PreAuthorize("hasAuthority('create-database-access')")
@Operation(summary = "Give access to some database", security = @SecurityRequirement(name = "bearerAuth"))
@ApiResponses(value = {
......@@ -89,6 +91,7 @@ public class AccessEndpoint {
@PutMapping("/{userId}")
@Transactional
@Observed(name = "dbr_access_modify")
@PreAuthorize("hasAuthority('update-database-access')")
@Operation(summary = "Modify access to some database", security = @SecurityRequirement(name = "bearerAuth"))
@ApiResponses(value = {
......@@ -126,6 +129,7 @@ public class AccessEndpoint {
@GetMapping
@Transactional
@Observed(name = "dbr_access_check")
@PreAuthorize("hasAuthority('check-database-access')")
@Operation(summary = "Check access to some database", security = @SecurityRequirement(name = "bearerAuth"))
@ApiResponses(value = {
......@@ -157,6 +161,7 @@ public class AccessEndpoint {
@DeleteMapping("/{userId}")
@Transactional
@Observed(name = "dbr_access_delete")
@PreAuthorize("hasAuthority('delete-database-access')")
@Operation(summary = "Revoke access to some database", security = @SecurityRequirement(name = "bearerAuth"))
@ApiResponses(value = {
......
......@@ -92,17 +92,37 @@ public class EndpointValidator {
.filter(c -> needSize.contains(c.getType()))
.findFirst();
if (optional0.isPresent()) {
log.error("Validation failed: column {} needs size parameter", optional0.get().getName() );
throw new TableMalformedException("Validation failed: column " + optional0.get().getName() + " needs size parameter");
}
/* check size and d */
final Optional<ColumnCreateDto> optional1 = data.getColumns()
.stream()
.filter(c -> Objects.isNull(c.getSize()) || Objects.isNull(c.getD()))
.filter(c -> needSizeAndD.contains(c.getType()))
.filter(c -> Objects.isNull(c.getSize()) || Objects.isNull(c.getD()))
.findFirst();
if (optional1.isPresent()) {
log.error("Validation failed: column {} needs size and d parameter", optional1.get().getName());
throw new TableMalformedException("Validation failed: column " + optional1.get().getName() + " needs size and d parameter");
}
final Optional<ColumnCreateDto> optional1a = data.getColumns()
.stream()
.filter(c -> needSizeAndD.contains(c.getType()))
.filter(c -> c.getSize() > 65 || c.getD() > 38)
.findFirst();
if (optional1a.isPresent()) {
log.error("Validation failed: column {} needs size (max 65) and d (max 30)", optional1a.get().getName());
throw new TableMalformedException("Validation failed: column " + optional1a.get().getName() + " needs size (max 65) and d (max 30)");
}
final Optional<ColumnCreateDto> optional1b = data.getColumns()
.stream()
.filter(c -> needSizeAndD.contains(c.getType()))
.filter(c -> c.getSize() < c.getD())
.findFirst();
if (optional1b.isPresent()) {
log.error("Validation failed: column {} needs size >= d", optional1b.get().getName());
throw new TableMalformedException("Validation failed: column " + optional1b.get().getName() + " needs size >= d");
}
/* check enum */
final Optional<ColumnCreateDto> optional2 = data.getColumns()
.stream()
......@@ -110,6 +130,7 @@ public class EndpointValidator {
.filter(c -> c.getEnums() == null || c.getEnums().isEmpty())
.findFirst();
if (optional2.isPresent()) {
log.error("Validation failed: column {} needs at least 1 allowed enum value", optional2.get().getName());
throw new TableMalformedException("Validation failed: column " + optional2.get().getName() + " needs at least 1 allowed enum value");
}
/* check set */
......@@ -119,6 +140,7 @@ public class EndpointValidator {
.filter(c -> c.getEnums() == null || c.getSets().isEmpty())
.findFirst();
if (optional3.isPresent()) {
log.error("Validation failed: column {} needs at least 1 allowed set value", optional3.get().getName());
throw new TableMalformedException("Validation failed: column " + optional3.get().getName() + " needs at least 1 allowed set value");
}
/* check date */
......@@ -128,6 +150,7 @@ public class EndpointValidator {
.filter(c -> Objects.isNull(c.getDfid()))
.findFirst();
if (optional4.isPresent()) {
log.error("Validation failed: column {} needs a format", optional4.get().getName());
throw new TableMalformedException("Validation failed: column " + optional4.get().getName() + " needs a format");
}
}
......
......@@ -6,6 +6,8 @@ import at.tuwien.annotations.MockOpensearch;
import at.tuwien.api.database.table.TableBriefDto;
import at.tuwien.api.database.table.TableCreateDto;
import at.tuwien.api.database.table.TableDto;
import at.tuwien.api.database.table.columns.ColumnCreateDto;
import at.tuwien.api.database.table.columns.ColumnTypeDto;
import at.tuwien.entities.database.Database;
import at.tuwien.entities.database.DatabaseAccess;
import at.tuwien.entities.database.table.Table;
......@@ -155,6 +157,109 @@ public class TableEndpointUnitTest extends BaseUnitTest {
});
}
@Test
@WithMockUser(username = USER_3_USERNAME, authorities = {"create-table"})
public void create_publicDecimalColumnSizeMissing_fails() {
final TableCreateDto request = TableCreateDto.builder()
.name("Some Table")
.description("Some Description")
.columns(List.of(ColumnCreateDto.builder()
.name("ID")
.type(ColumnTypeDto.DECIMAL)
.build()))
.constraints(null)
.build();
/* test */
assertThrows(TableMalformedException.class, () -> {
generic_create(DATABASE_3_ID, DATABASE_3, request, USER_1_ID, USER_1_PRINCIPAL, DATABASE_3_USER_1_WRITE_OWN_ACCESS);
});
}
@Test
@WithMockUser(username = USER_3_USERNAME, authorities = {"create-table"})
public void create_publicDecimalColumnSizeTooSmall_fails() {
final TableCreateDto request = TableCreateDto.builder()
.name("Some Table")
.description("Some Description")
.columns(List.of(ColumnCreateDto.builder()
.name("ID")
.type(ColumnTypeDto.DECIMAL)
.size(-1)
.d(0)
.build()))
.constraints(null)
.build();
/* test */
assertThrows(TableMalformedException.class, () -> {
generic_create(DATABASE_3_ID, DATABASE_3, request, USER_1_ID, USER_1_PRINCIPAL, DATABASE_3_USER_1_WRITE_OWN_ACCESS);
});
}
@Test
@WithMockUser(username = USER_3_USERNAME, authorities = {"create-table"})
public void create_publicDecimalColumnSizeTooBig_fails() {
final TableCreateDto request = TableCreateDto.builder()
.name("Some Table")
.description("Some Description")
.columns(List.of(ColumnCreateDto.builder()
.name("ID")
.type(ColumnTypeDto.DECIMAL)
.size(66)
.d(0)
.build()))
.constraints(null)
.build();
/* test */
assertThrows(TableMalformedException.class, () -> {
generic_create(DATABASE_3_ID, DATABASE_3, request, USER_1_ID, USER_1_PRINCIPAL, DATABASE_3_USER_1_WRITE_OWN_ACCESS);
});
}
@Test
@WithMockUser(username = USER_3_USERNAME, authorities = {"create-table"})
public void create_publicDecimalColumnDTooBig_fails() {
final TableCreateDto request = TableCreateDto.builder()
.name("Some Table")
.description("Some Description")
.columns(List.of(ColumnCreateDto.builder()
.name("ID")
.type(ColumnTypeDto.DECIMAL)
.size(0)
.d(39)
.build()))
.constraints(null)
.build();
/* test */
assertThrows(TableMalformedException.class, () -> {
generic_create(DATABASE_3_ID, DATABASE_3, request, USER_1_ID, USER_1_PRINCIPAL, DATABASE_3_USER_1_WRITE_OWN_ACCESS);
});
}
@Test
@WithMockUser(username = USER_3_USERNAME, authorities = {"create-table"})
public void create_publicDecimalColumnDBiggerSize_fails() {
final TableCreateDto request = TableCreateDto.builder()
.name("Some Table")
.description("Some Description")
.columns(List.of(ColumnCreateDto.builder()
.name("ID")
.type(ColumnTypeDto.DECIMAL)
.size(9)
.d(10)
.build()))
.constraints(null)
.build();
/* test */
assertThrows(TableMalformedException.class, () -> {
generic_create(DATABASE_3_ID, DATABASE_3, request, USER_1_ID, USER_1_PRINCIPAL, DATABASE_3_USER_1_WRITE_OWN_ACCESS);
});
}
@Test
@WithAnonymousUser
public void findById_publicAnonymous_succeeds() throws DatabaseNotFoundException, TableNotFoundException,
......
package at.tuwien.mvc;
import at.tuwien.BaseUnitTest;
import at.tuwien.annotations.MockAmqp;
import at.tuwien.annotations.MockOpensearch;
import lombok.extern.log4j.Log4j2;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.actuate.observability.AutoConfigureObservability;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@Log4j2
@ExtendWith(SpringExtension.class)
@AutoConfigureMockMvc
@SpringBootTest
@AutoConfigureObservability
@MockAmqp
@MockOpensearch
public class ActuatorEndpointMvcTest extends BaseUnitTest {
@Autowired
private MockMvc mockMvc;
@Test
public void actuatorInfo_succeeds() throws Exception {
/* test */
this.mockMvc.perform(get("/actuator/info"))
.andDo(print())
.andExpect(status().isOk());
}
@Test
public void actuatorPrometheus_succeeds() throws Exception {
/* test */
this.mockMvc.perform(get("/actuator/prometheus"))
.andDo(print())
.andExpect(status().isOk());
}
}
package at.tuwien.mvc;
import at.tuwien.BaseUnitTest;
import at.tuwien.annotations.MockAmqp;
import at.tuwien.annotations.MockOpensearch;
import at.tuwien.api.database.AccessTypeDto;
import at.tuwien.api.database.DatabaseGiveAccessDto;
import at.tuwien.api.database.DatabaseModifyAccessDto;
import at.tuwien.config.MetricsConfig;
import at.tuwien.endpoints.AccessEndpoint;
import io.micrometer.observation.tck.TestObservationRegistry;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.actuate.observability.AutoConfigureObservability;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import java.util.List;
import static io.micrometer.observation.tck.TestObservationRegistryAssert.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@Log4j2
@ExtendWith(SpringExtension.class)
@AutoConfigureMockMvc
@SpringBootTest
@Import(MetricsConfig.class)
@AutoConfigureObservability
@MockAmqp
@MockOpensearch
public class PrometheusEndpointMvcTest extends BaseUnitTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private TestObservationRegistry registry;
@Autowired
private AccessEndpoint accessEndpoint;
@TestConfiguration
static class ObservationTestConfiguration {
@Bean
public TestObservationRegistry observationRegistry() {
return TestObservationRegistry.create();
}
}
@Test
public void prometheus_succeeds() throws Exception {
/* test */
this.mockMvc.perform(get("/actuator/prometheus"))
.andDo(print())
.andExpect(status().isOk());
}
@Test
@WithMockUser(username = USER_1_USERNAME, authorities = {"create-database-access", "update-database-access", "check-database-access", "delete-database-access"})
public void prometheusAccessEndpoint_succeeds() throws Exception {
/* mock */
try {
accessEndpoint.create(DATABASE_1_ID, USER_1_ID, DatabaseGiveAccessDto.builder().type(AccessTypeDto.READ).build(), USER_1_PRINCIPAL);
} catch (Exception e) {
/* ignore */
}
try {
accessEndpoint.update(DATABASE_1_ID, USER_1_ID, DatabaseModifyAccessDto.builder().type(AccessTypeDto.READ).build(), USER_1_PRINCIPAL);
} catch (Exception e) {
/* ignore */
}
try {
accessEndpoint.find(DATABASE_1_ID, USER_1_PRINCIPAL);
} catch (Exception e) {
/* ignore */
}
try {
accessEndpoint.revoke(DATABASE_1_ID, USER_1_ID, USER_1_PRINCIPAL);
} catch (Exception e) {
/* ignore */
}
this.mockMvc.perform(get("/actuator/prometheus"))
.andDo(print())
.andExpect(status().isOk());
/* test */
for (String metric : List.of("dbr_access_give", "dbr_access_modify", "dbr_access_check", "dbr_access_delete")) {
assertThat(registry)
.hasObservationWithNameEqualTo(metric);
}
}
}
package at.tuwien.config;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.observation.aop.ObservedAspect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MetricsConfig {
@Bean
public ObservedAspect observedAspect(ObservationRegistry observationRegistry) {
return new ObservedAspect(observationRegistry);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment