diff --git a/src/main/kotlin/recce/server/api/DatasetRecRunController.kt b/src/main/kotlin/recce/server/api/DatasetRecRunController.kt index c5138cc0..21eee798 100644 --- a/src/main/kotlin/recce/server/api/DatasetRecRunController.kt +++ b/src/main/kotlin/recce/server/api/DatasetRecRunController.kt @@ -1,5 +1,6 @@ package recce.server.api +import com.fasterxml.jackson.annotation.JsonProperty import io.micronaut.core.annotation.Introspected import io.micronaut.http.annotation.Body import io.micronaut.http.annotation.Controller @@ -10,6 +11,9 @@ import mu.KotlinLogging import reactor.core.publisher.Mono import recce.server.dataset.DatasetRecRunner import recce.server.dataset.RecRun +import recce.server.dataset.RecRunResults +import java.time.Duration +import java.time.Instant import javax.validation.Valid import javax.validation.constraints.NotBlank @@ -19,11 +23,32 @@ private val logger = KotlinLogging.logger {} @Controller("/runs") class DatasetRecRunController(@Inject private val runner: DatasetRecRunner) { @Post - fun create(@Body @Valid params: RunCreationParams): Mono { + fun create(@Body @Valid params: RunCreationParams): Mono { logger.info { "Received request to create run for $params" } - return runner.runFor(params.datasetId) + return runner.runFor(params.datasetId).map { CompletedRun(it) } } @Introspected data class RunCreationParams(@field:NotBlank val datasetId: String) + + @Introspected + data class CompletedRun( + val id: Int, + val datasetId: String, + val createdTime: Instant, + val completedTime: Instant, + val results: RecRunResults, + ) { + constructor(run: RecRun) : this( + id = run.id!!, + datasetId = run.datasetId, + createdTime = run.createdTime!!, + completedTime = run.completedTime!!, + results = run.results!! + ) + + @get:JsonProperty("completedDurationSeconds") + val completedDuration: Duration + get() = Duration.between(createdTime, completedTime) + } } diff --git a/src/test/kotlin/recce/server/TestConfig.kt b/src/test/kotlin/recce/server/TestConfig.kt new file mode 100644 index 00000000..f1004234 --- /dev/null +++ b/src/test/kotlin/recce/server/TestConfig.kt @@ -0,0 +1,28 @@ +package recce.server + +import io.micronaut.context.annotation.Factory +import io.micronaut.runtime.server.EmbeddedServer +import io.restassured.RestAssured +import io.restassured.builder.RequestSpecBuilder +import io.restassured.config.JsonConfig +import io.restassured.filter.log.ResponseLoggingFilter +import io.restassured.http.ContentType +import io.restassured.path.json.config.JsonPathConfig +import io.restassured.specification.RequestSpecification +import jakarta.inject.Singleton + +@Factory +class TestConfig { + @Singleton + fun restAssuredSpec(server: EmbeddedServer): RequestSpecification { + // Yes, this is static configuration; seems no other way to do it with RestAssured + RestAssured.config = RestAssured.config() + .jsonConfig(JsonConfig.jsonConfig().numberReturnType(JsonPathConfig.NumberReturnType.DOUBLE)) + + return RequestSpecBuilder() + .setContentType(ContentType.JSON) + .setBaseUri(server.uri) + .addFilter(ResponseLoggingFilter()) + .build() + } +} diff --git a/src/test/kotlin/recce/server/api/DatasetRecRunControllerTest.kt b/src/test/kotlin/recce/server/api/DatasetRecRunControllerTest.kt index 1e63a5e0..d56b2d51 100644 --- a/src/test/kotlin/recce/server/api/DatasetRecRunControllerTest.kt +++ b/src/test/kotlin/recce/server/api/DatasetRecRunControllerTest.kt @@ -1,11 +1,7 @@ package recce.server.api -import io.micronaut.runtime.server.EmbeddedServer import io.micronaut.test.annotation.MockBean import io.micronaut.test.extensions.junit5.annotation.MicronautTest -import io.restassured.builder.RequestSpecBuilder -import io.restassured.filter.log.ResponseLoggingFilter -import io.restassured.http.ContentType import io.restassured.module.kotlin.extensions.Extract import io.restassured.module.kotlin.extensions.Given import io.restassured.module.kotlin.extensions.Then @@ -14,8 +10,8 @@ import io.restassured.specification.RequestSpecification import jakarta.inject.Inject import org.apache.http.HttpStatus import org.assertj.core.api.Assertions.assertThat +import org.hamcrest.Matchers.closeTo import org.hamcrest.Matchers.equalTo -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq @@ -26,16 +22,26 @@ import recce.server.dataset.DatasetRecRunner import recce.server.dataset.DatasetRecService import recce.server.dataset.RecRun import recce.server.dataset.RecRunResults +import java.time.Duration import java.time.LocalDateTime import java.time.ZoneOffset import java.time.format.DateTimeFormatter -internal class DatasetRecRunControllerTest { - - private val testDataset = "testDataset" +private val testDataset = "testDataset" +private val testCompletedDuration = Duration.ofMinutes(3).plusNanos(234) +private val testResults = RecRun( + id = 12, + datasetId = testDataset, + createdTime = LocalDateTime.of(2021, 10, 25, 16, 16, 16).toInstant(ZoneOffset.UTC), +).apply { + completedTime = createdTime?.plusNanos(testCompletedDuration.toNanos()) + updatedTime = completedTime?.plusSeconds(10) + results = RecRunResults(2000, 3000) +} +internal class DatasetRecRunControllerTest { private val service = mock { - on { runFor(eq(testDataset)) } doReturn Mono.just(RecRun(testDataset)) + on { runFor(eq(testDataset)) } doReturn Mono.just(testResults) } private val controller = DatasetRecRunController(service) @@ -43,37 +49,24 @@ internal class DatasetRecRunControllerTest { @Test fun `controller should delegate to service`() { StepVerifier.create(controller.create(DatasetRecRunController.RunCreationParams(eq(testDataset)))) - .expectNext(RecRun(testDataset)) + .assertNext { + assertThat(it.id).isEqualTo(testResults.id) + assertThat(it.datasetId).isEqualTo(testResults.datasetId) + assertThat(it.createdTime).isEqualTo(testResults.createdTime) + assertThat(it.completedTime).isEqualTo(testResults.completedTime) + assertThat(it.completedDuration).isEqualTo(testCompletedDuration) + assertThat(it.results).isEqualTo(testResults.results) + } .verifyComplete() } } @MicronautTest internal class DatasetRecRunControllerApiTest { - private val testDataset = "testDataset" - private val testResults = RecRun( - id = 12, - datasetId = testDataset, - createdTime = LocalDateTime.of(2021, 10, 25, 16, 16, 16).toInstant(ZoneOffset.UTC), - ).apply { - completedTime = createdTime?.plusSeconds(180) - updatedTime = completedTime?.plusSeconds(10) - results = RecRunResults(2000, 3000) - } @Inject - lateinit var server: EmbeddedServer lateinit var spec: RequestSpecification - @BeforeEach - fun setUp() { - spec = RequestSpecBuilder() - .setContentType(ContentType.JSON) - .setBaseUri(server.uri) - .addFilter(ResponseLoggingFilter()) - .build() - } - @Test fun `controller should delegate to service`() { Given { @@ -87,7 +80,7 @@ internal class DatasetRecRunControllerApiTest { body("id", equalTo(testResults.id)) body("createdTime", equalTo(DateTimeFormatter.ISO_INSTANT.format(testResults.createdTime))) body("completedTime", equalTo(DateTimeFormatter.ISO_INSTANT.format(testResults.completedTime))) - body("updatedTime", equalTo(DateTimeFormatter.ISO_INSTANT.format(testResults.updatedTime))) + body("completedDurationSeconds", closeTo(testCompletedDuration.toSeconds().toDouble(), 0.00001)) body("results.sourceRows", equalTo(testResults.results?.sourceRows?.toInt())) body("results.targetRows", equalTo(testResults.results?.targetRows?.toInt())) }