diff --git a/src/accessibilityTest/kotlin/br/ufpe/liber/AccessibilityTest.kt b/src/accessibilityTest/kotlin/br/ufpe/liber/AccessibilityTest.kt index 61819c1..5f478c0 100644 --- a/src/accessibilityTest/kotlin/br/ufpe/liber/AccessibilityTest.kt +++ b/src/accessibilityTest/kotlin/br/ufpe/liber/AccessibilityTest.kt @@ -18,60 +18,58 @@ import org.openqa.selenium.chrome.ChromeDriver import org.openqa.selenium.chrome.ChromeOptions @MicronautTest -class AccessibilityTest( - private val server: EmbeddedServer, - private val context: ApplicationContext, -) : BehaviorSpec({ - val axeBuilder = AxeBuilder().withTags(listOf("wcag21a", "wcag21aa", "wcag22aa", "experimental")) - val driver = ChromeDriver( - ChromeOptions() - .addArguments("--no-sandbox") - .addArguments("--disable-dev-shm-usage") - .addArguments("--headless"), - ) +class AccessibilityTest(private val server: EmbeddedServer, private val context: ApplicationContext) : + BehaviorSpec({ + val axeBuilder = AxeBuilder().withTags(listOf("wcag21a", "wcag21aa", "wcag22aa", "experimental")) + val driver = ChromeDriver( + ChromeOptions() + .addArguments("--no-sandbox") + .addArguments("--disable-dev-shm-usage") + .addArguments("--headless"), + ) - val parameterlessRoutes = context.getBean() - .uriRoutes() - // filter in only routes that DO NOT require a parameter - .filter { it.httpMethod == HttpMethod.GET && it.uriMatchTemplate.variables.isEmpty() } - // filter in only UI routes - .filter { it.produces.contains(MediaType.TEXT_HTML_TYPE) } - // get only the paths - .map { it.uriMatchTemplate.toPathString() } - // More readable? - .sorted() - // Avoid possible duplications - .distinct() - // since `uriRoutes` returns a `Stream`. - .toList() + val parameterlessRoutes = context.getBean() + .uriRoutes() + // filter in only routes that DO NOT require a parameter + .filter { it.httpMethod == HttpMethod.GET && it.uriMatchTemplate.variables.isEmpty() } + // filter in only UI routes + .filter { it.produces.contains(MediaType.TEXT_HTML_TYPE) } + // get only the paths + .map { it.uriMatchTemplate.toPathString() } + // More readable? + .sorted() + // Avoid possible duplications + .distinct() + // since `uriRoutes` returns a `Stream`. + .toList() - beforeSpec { - context.getBean() - } + beforeSpec { + context.getBean() + } - // Called once per Spec, after all tests have completed for that spec. - finalizeSpec { - driver.close() - } + // Called once per Spec, after all tests have completed for that spec. + finalizeSpec { + driver.close() + } - fun url(path: String): String = "http://${server.host}:${server.port}$path" + fun url(path: String): String = "http://${server.host}:${server.port}$path" - fun checkAccessibility(path: String) { - driver.get(url(path)) - driver.title shouldNotContain "404" - driver.title shouldNotContain "500" - val results = axeBuilder.analyze(driver) - results.violations shouldBe emptyList() - } + fun checkAccessibility(path: String) { + driver.get(url(path)) + driver.title shouldNotContain "404" + driver.title shouldNotContain "500" + val results = axeBuilder.analyze(driver) + results.violations shouldBe emptyList() + } - given("The server is running") { - `when`("accessing the main pages") { - @Suppress("detekt:SpreadOperator") - forAll(*parameterlessRoutes.map(::row).toTypedArray()) { path -> - then("$path passes accessibility tests") { - checkAccessibility(path) + given("The server is running") { + `when`("accessing the main pages") { + @Suppress("detekt:SpreadOperator") + forAll(*parameterlessRoutes.map(::row).toTypedArray()) { path -> + then("$path passes accessibility tests") { + checkAccessibility(path) + } } } } - } -}) + }) diff --git a/src/main/kotlin/br/ufpe/liber/assets/Assets.kt b/src/main/kotlin/br/ufpe/liber/assets/Assets.kt index 94a5a35..849058a 100644 --- a/src/main/kotlin/br/ufpe/liber/assets/Assets.kt +++ b/src/main/kotlin/br/ufpe/liber/assets/Assets.kt @@ -119,7 +119,10 @@ data class Encoding(val http: String, val extension: String, val priority: Int) } @Suppress("IDENTIFIER_LENGTH") // it is okay to use `q` because it is part of the spec. -data class AcceptEncoding(val name: String, val q: Optional = Optional.empty()) : Comparable { +data class AcceptEncoding( + val name: String, + val q: Optional = Optional.empty(), +) : Comparable { @Suppress("MAGIC_NUMBER") val qualityValue = q.getOrDefault(1f) diff --git a/src/main/kotlin/br/ufpe/liber/controllers/AssetsController.kt b/src/main/kotlin/br/ufpe/liber/controllers/AssetsController.kt index acbfebd..3432974 100644 --- a/src/main/kotlin/br/ufpe/liber/controllers/AssetsController.kt +++ b/src/main/kotlin/br/ufpe/liber/controllers/AssetsController.kt @@ -16,10 +16,7 @@ import java.time.LocalDateTime import java.util.Optional @Controller("/static/{+path}") -class AssetsController( - private val assetsResolver: AssetsResolver, - private val resourceResolver: ResourceResolver, -) { +class AssetsController(private val assetsResolver: AssetsResolver, private val resourceResolver: ResourceResolver) { @Get fun asset( path: String, @@ -45,22 +42,20 @@ class AssetsController( .map { HttpResponse.notModified() } } - private fun httpResponseForAsset(asset: Asset, encoding: String): HttpResponse { - return asset - .preferredEncodedResource(encoding) - .flatMap { availableEncoding -> - resourceResolver - .getResourceAsStream(asset.classpath(availableEncoding.extension)) - .map { inputStream -> - HttpResponse - .ok(StreamedFile(inputStream, asset.mediaType())) - .contentEncoding(availableEncoding.http) - } - } - .or { tryPlainAsset(asset) } - .map { response -> setCacheHeaders(asset, response) } - .orElse(HttpResponse.notFound()) - } + private fun httpResponseForAsset(asset: Asset, encoding: String): HttpResponse = asset + .preferredEncodedResource(encoding) + .flatMap { availableEncoding -> + resourceResolver + .getResourceAsStream(asset.classpath(availableEncoding.extension)) + .map { inputStream -> + HttpResponse + .ok(StreamedFile(inputStream, asset.mediaType())) + .contentEncoding(availableEncoding.http) + } + } + .or { tryPlainAsset(asset) } + .map { response -> setCacheHeaders(asset, response) } + .orElse(HttpResponse.notFound()) private fun tryPlainAsset(asset: Asset): Optional> = resourceResolver .getResourceAsStream(asset.classpath()) diff --git a/src/main/kotlin/br/ufpe/liber/controllers/DefaultExceptionHandler.kt b/src/main/kotlin/br/ufpe/liber/controllers/DefaultExceptionHandler.kt index 6e40085..b7ecf3c 100644 --- a/src/main/kotlin/br/ufpe/liber/controllers/DefaultExceptionHandler.kt +++ b/src/main/kotlin/br/ufpe/liber/controllers/DefaultExceptionHandler.kt @@ -13,7 +13,8 @@ import jakarta.inject.Singleton @Produces(MediaType.TEXT_HTML) @Requires(classes = [Exception::class, ExceptionHandler::class]) class DefaultExceptionHandler(private val templates: Templates) : - ExceptionHandler>, KteController { + ExceptionHandler>, + KteController { override fun handle(request: HttpRequest<*>, exception: Exception): HttpResponse = serverError(templates.internalServerError(request.path, exception.message!!)) } diff --git a/src/main/kotlin/br/ufpe/liber/pagination/Pagination.kt b/src/main/kotlin/br/ufpe/liber/pagination/Pagination.kt index 531be54..0172a90 100644 --- a/src/main/kotlin/br/ufpe/liber/pagination/Pagination.kt +++ b/src/main/kotlin/br/ufpe/liber/pagination/Pagination.kt @@ -44,9 +44,7 @@ data class Pagination( return min(count, possibleFrom) } - private fun calculateTo(): Int { - return min(count, from + perPage - 1) - } + private fun calculateTo(): Int = min(count, from + perPage - 1) @Suppress("SAY_NO_TO_VAR") fun listPages(): List { diff --git a/src/test/kotlin/br/ufpe/liber/ApplicationTest.kt b/src/test/kotlin/br/ufpe/liber/ApplicationTest.kt index 1d2bb3d..37995b8 100644 --- a/src/test/kotlin/br/ufpe/liber/ApplicationTest.kt +++ b/src/test/kotlin/br/ufpe/liber/ApplicationTest.kt @@ -8,19 +8,17 @@ import io.micronaut.runtime.server.EmbeddedServer import io.micronaut.test.extensions.kotest5.annotation.MicronautTest @MicronautTest -class ApplicationTest( - private val server: EmbeddedServer, - private val context: ApplicationContext, -) : BehaviorSpec({ - given("Application") { - `when`("application starts") { - then("server is running") { server.isRunning shouldBe true } - } - `when`("has a health check") { - then("it should return UP") { - val client = context.createBean(HttpClient::class.java, server.url).toBlocking() - client.exchange("/health").status.code shouldBe 200 +class ApplicationTest(private val server: EmbeddedServer, private val context: ApplicationContext) : + BehaviorSpec({ + given("Application") { + `when`("application starts") { + then("server is running") { server.isRunning shouldBe true } + } + `when`("has a health check") { + then("it should return UP") { + val client = context.createBean(HttpClient::class.java, server.url).toBlocking() + client.exchange("/health").status.code shouldBe 200 + } } } - } -}) + }) diff --git a/src/test/kotlin/br/ufpe/liber/TemplatesFactoryTest.kt b/src/test/kotlin/br/ufpe/liber/TemplatesFactoryTest.kt index 07b97c9..ff412a3 100644 --- a/src/test/kotlin/br/ufpe/liber/TemplatesFactoryTest.kt +++ b/src/test/kotlin/br/ufpe/liber/TemplatesFactoryTest.kt @@ -8,34 +8,35 @@ import io.micronaut.context.env.Environment import io.mockk.every import io.mockk.mockk -class TemplatesFactoryTest : BehaviorSpec({ - val factory = TemplatesFactory() - given("createTemplate") { - `when`("it is dev environment") { - val environment = mockk() - every { environment.activeNames } answers { setOf("something", Environment.DEVELOPMENT) } - - then("it should use dynamic templates") { - val templates = factory.createTemplate(environment) - (templates is DynamicTemplates) shouldBe true - } - } - - forAll( - row(Environment.TEST), - row(Environment.CLI), - row(Environment.CLOUD), - row(Environment.BARE_METAL), - ) { envName -> - `when`("it is $envName environment") { +class TemplatesFactoryTest : + BehaviorSpec({ + val factory = TemplatesFactory() + given("createTemplate") { + `when`("it is dev environment") { val environment = mockk() - every { environment.activeNames } answers { setOf(envName) } + every { environment.activeNames } answers { setOf("something", Environment.DEVELOPMENT) } then("it should use dynamic templates") { val templates = factory.createTemplate(environment) - (templates is StaticTemplates) shouldBe true + (templates is DynamicTemplates) shouldBe true + } + } + + forAll( + row(Environment.TEST), + row(Environment.CLI), + row(Environment.CLOUD), + row(Environment.BARE_METAL), + ) { envName -> + `when`("it is $envName environment") { + val environment = mockk() + every { environment.activeNames } answers { setOf(envName) } + + then("it should use dynamic templates") { + val templates = factory.createTemplate(environment) + (templates is StaticTemplates) shouldBe true + } } } } - } -}) + }) diff --git a/src/test/kotlin/br/ufpe/liber/assets/AssetTest.kt b/src/test/kotlin/br/ufpe/liber/assets/AssetTest.kt index 9cef0bc..5babb15 100644 --- a/src/test/kotlin/br/ufpe/liber/assets/AssetTest.kt +++ b/src/test/kotlin/br/ufpe/liber/assets/AssetTest.kt @@ -13,127 +13,128 @@ import java.util.Optional // DO NOT EDIT: this file is automatically synced from the template repository // in https://github.com/Liber-UFPE/project-starter. -class AssetTest : BehaviorSpec({ - given("Asset") { - val brotli = Encoding("br", "br", 0) - val gzip = Encoding("gzip", "gz", 1) - val deflate = Encoding("deflate", "zz", 2) - val asset = Asset( - basename = "/javascripts/main", - source = "/javascripts/main.js", - filename = "/javascripts/main.K68FJD75.js", - hash = "K68FJD75", - integrity = "sha384-qWyHoR/uZ7x+UjVssG6ex4WUplfdMrwZMRmqQDXnn6uwCmlQUJkwhdifK4iY0EnX", - etag = "SzY4RkpENzUK", - lastModified = Instant.now().toEpochMilli(), - extension = "js", - mediaType = "text/javascript", - supportedEncodings = listOf(brotli, gzip, deflate), - ) - - `when`("#mediaType") { - then("should return an MediaType object") { - asset.mediaType() shouldBe MediaType("text/javascript") +class AssetTest : + BehaviorSpec({ + given("Asset") { + val brotli = Encoding("br", "br", 0) + val gzip = Encoding("gzip", "gz", 1) + val deflate = Encoding("deflate", "zz", 2) + val asset = Asset( + basename = "/javascripts/main", + source = "/javascripts/main.js", + filename = "/javascripts/main.K68FJD75.js", + hash = "K68FJD75", + integrity = "sha384-qWyHoR/uZ7x+UjVssG6ex4WUplfdMrwZMRmqQDXnn6uwCmlQUJkwhdifK4iY0EnX", + etag = "SzY4RkpENzUK", + lastModified = Instant.now().toEpochMilli(), + extension = "js", + mediaType = "text/javascript", + supportedEncodings = listOf(brotli, gzip, deflate), + ) + + `when`("#mediaType") { + then("should return an MediaType object") { + asset.mediaType() shouldBe MediaType("text/javascript") + } } - } - `when`("#fullpath") { - then("should return without prefix") { - asset.fullpath() shouldBe "javascripts/main.K68FJD75.js" - } + `when`("#fullpath") { + then("should return without prefix") { + asset.fullpath() shouldBe "javascripts/main.K68FJD75.js" + } - then("should return with prefix") { - asset.fullpath("/static") shouldBe "/static/javascripts/main.K68FJD75.js" + then("should return with prefix") { + asset.fullpath("/static") shouldBe "/static/javascripts/main.K68FJD75.js" + } } - } - `when`("#classpath") { - then("should return without encoding extension") { - asset.classpath() shouldBe "classpath:public/javascripts/main.K68FJD75.js" - } + `when`("#classpath") { + then("should return without encoding extension") { + asset.classpath() shouldBe "classpath:public/javascripts/main.K68FJD75.js" + } - then("should return with encoding extension") { - asset.classpath("br") shouldBe "classpath:public/javascripts/main.K68FJD75.js.br" + then("should return with encoding extension") { + asset.classpath("br") shouldBe "classpath:public/javascripts/main.K68FJD75.js.br" + } } - } - `when`("#variant") { - then("should return with new extension and prefix") { - asset.variant("ts", "/static") shouldBe "/static/javascripts/main.K68FJD75.ts" - } + `when`("#variant") { + then("should return with new extension and prefix") { + asset.variant("ts", "/static") shouldBe "/static/javascripts/main.K68FJD75.ts" + } - then("should return with new extension without prefix") { - asset.variant("ts") shouldBe "javascripts/main.K68FJD75.ts" + then("should return with new extension without prefix") { + asset.variant("ts") shouldBe "javascripts/main.K68FJD75.ts" + } } - } - `when`("#preferredEncodedResource") { - forAll( - row("gzip, deflate, br", Optional.of(brotli)), - row("gzip, deflate", Optional.of(gzip)), - row("deflate", Optional.of(deflate)), - row("", Optional.empty()), - // gzip has a higher quality value - row("gzip;q=1, deflate;q=0.5, br;q=0.1", Optional.of(gzip)), - // all zeroes - row("gzip;q=0, deflate;q=0, br;q=0", Optional.empty()), - ) { acceptEncoding, expectedResource -> - then("should handle $acceptEncoding header value") { - asset.preferredEncodedResource(acceptEncoding) shouldBe expectedResource + `when`("#preferredEncodedResource") { + forAll( + row("gzip, deflate, br", Optional.of(brotli)), + row("gzip, deflate", Optional.of(gzip)), + row("deflate", Optional.of(deflate)), + row("", Optional.empty()), + // gzip has a higher quality value + row("gzip;q=1, deflate;q=0.5, br;q=0.1", Optional.of(gzip)), + // all zeroes + row("gzip;q=0, deflate;q=0, br;q=0", Optional.empty()), + ) { acceptEncoding, expectedResource -> + then("should handle $acceptEncoding header value") { + asset.preferredEncodedResource(acceptEncoding) shouldBe expectedResource + } } } } - } - - given("AcceptEncoding") { - `when`(".compareTo") { - then("higher q values should come first") { - val acceptEncodings = listOf( - AcceptEncoding("gzip", Optional.of(0.2f)), - AcceptEncoding("br", Optional.of(0.4f)), - AcceptEncoding("zz", Optional.of(0.1f)), - ) - - acceptEncodings.sorted() shouldBe listOf( - AcceptEncoding("br", Optional.of(0.4f)), - AcceptEncoding("gzip", Optional.of(0.2f)), - AcceptEncoding("zz", Optional.of(0.1f)), - ) + + given("AcceptEncoding") { + `when`(".compareTo") { + then("higher q values should come first") { + val acceptEncodings = listOf( + AcceptEncoding("gzip", Optional.of(0.2f)), + AcceptEncoding("br", Optional.of(0.4f)), + AcceptEncoding("zz", Optional.of(0.1f)), + ) + + acceptEncodings.sorted() shouldBe listOf( + AcceptEncoding("br", Optional.of(0.4f)), + AcceptEncoding("gzip", Optional.of(0.2f)), + AcceptEncoding("zz", Optional.of(0.1f)), + ) + } } - } - @Suppress("IDENTIFIER_LENGTH") - fun ae(name: String, q: Float) = AcceptEncoding(name, Optional.of(q)) - - @Suppress("IDENTIFIER_LENGTH") - fun ae(name: String, q: Optional = Optional.empty()) = AcceptEncoding(name, q) - - `when`(".parseHeader") { - forAll( - table( - headers("Header value", "Expected accepted encodings"), - row("gzip", listOf(ae("gzip"))), - row("gzip, compress, br", listOf(ae("gzip"), ae("compress"), ae("br"))), - row("gzip;q=1.0, compress, br", listOf(ae("gzip", 1f), ae("compress"), ae("br"))), - // in the row below, gzip has a lower priority compared with the default (1) - row("gzip;q=0.5, br", listOf(ae("br"), ae("gzip", 0.5f))), - row("deflate, gzip;q=1.0, *;q=0.5", listOf(ae("deflate"), ae("gzip", 1.0f), ae("*", 0.5f))), - row( - "deflate;q=0.1, gzip;q=0.5, br;q=1.0", - listOf(ae("br", 1f), ae("gzip", 0.5f), ae("deflate", 0.1f)), + @Suppress("IDENTIFIER_LENGTH") + fun ae(name: String, q: Float) = AcceptEncoding(name, Optional.of(q)) + + @Suppress("IDENTIFIER_LENGTH") + fun ae(name: String, q: Optional = Optional.empty()) = AcceptEncoding(name, q) + + `when`(".parseHeader") { + forAll( + table( + headers("Header value", "Expected accepted encodings"), + row("gzip", listOf(ae("gzip"))), + row("gzip, compress, br", listOf(ae("gzip"), ae("compress"), ae("br"))), + row("gzip;q=1.0, compress, br", listOf(ae("gzip", 1f), ae("compress"), ae("br"))), + // in the row below, gzip has a lower priority compared with the default (1) + row("gzip;q=0.5, br", listOf(ae("br"), ae("gzip", 0.5f))), + row("deflate, gzip;q=1.0, *;q=0.5", listOf(ae("deflate"), ae("gzip", 1.0f), ae("*", 0.5f))), + row( + "deflate;q=0.1, gzip;q=0.5, br;q=1.0", + listOf(ae("br", 1f), ae("gzip", 0.5f), ae("deflate", 0.1f)), + ), + row("compress;q, br;q=0.5, gzip", listOf(ae("compress"), ae("gzip"), ae("br", 0.5f))), + // A sender of q value MUST NOT generate more than three digits after the decimal point + row("compress;q=1.12321313", listOf(ae("compress"))), + // a q value of 0 means "not acceptable" + row("gzip;q=0, br", listOf(ae("br"))), + row("gzip;q=0.000, br", listOf(ae("br"))), ), - row("compress;q, br;q=0.5, gzip", listOf(ae("compress"), ae("gzip"), ae("br", 0.5f))), - // A sender of q value MUST NOT generate more than three digits after the decimal point - row("compress;q=1.12321313", listOf(ae("compress"))), - // a q value of 0 means "not acceptable" - row("gzip;q=0, br", listOf(ae("br"))), - row("gzip;q=0.000, br", listOf(ae("br"))), - ), - ) { headerValue, expectedAcceptedEncodings -> - then("should parse $headerValue properly") { - AcceptEncoding.parseHeader(headerValue).sorted() shouldBe expectedAcceptedEncodings + ) { headerValue, expectedAcceptedEncodings -> + then("should parse $headerValue properly") { + AcceptEncoding.parseHeader(headerValue).sorted() shouldBe expectedAcceptedEncodings + } } } } - } -}) + }) diff --git a/src/test/kotlin/br/ufpe/liber/assets/AssetsResolverTest.kt b/src/test/kotlin/br/ufpe/liber/assets/AssetsResolverTest.kt index 68ae575..bf059c3 100644 --- a/src/test/kotlin/br/ufpe/liber/assets/AssetsResolverTest.kt +++ b/src/test/kotlin/br/ufpe/liber/assets/AssetsResolverTest.kt @@ -14,59 +14,60 @@ import java.util.Optional // DO NOT EDIT: this file is automatically synced from the template repository // in https://github.com/Liber-UFPE/project-starter. -class AssetsResolverTest : BehaviorSpec({ - given("AssetsResolver") { - val resourceResolver: ResourceResolver = mockk() - every { resourceResolver.getResource("classpath:public/assets-metadata.json") } answers { - Optional.of(File("src/test/resources/public/test-assets-metadata.json").toURI().toURL()) - } +class AssetsResolverTest : + BehaviorSpec({ + given("AssetsResolver") { + val resourceResolver: ResourceResolver = mockk() + every { resourceResolver.getResource("classpath:public/assets-metadata.json") } answers { + Optional.of(File("src/test/resources/public/test-assets-metadata.json").toURI().toURL()) + } - val assetsResolver = AssetsResolver(resourceResolver) + val assetsResolver = AssetsResolver(resourceResolver) - `when`("#at") { - then("should return hashed version of asset") { - forAll( - row("/javascripts/main.js", "/javascripts/main", "34UGRNNI", "js", "application/javascript"), - row("/stylesheets/main.css", "/stylesheets/main", "Y6PST7YS", "css", "text/css"), - ) { requested, original, expected, extension, mediaType -> - assetsResolver.at(requested) shouldBePresent { result -> - result.basename shouldBe original - result.hash shouldBe expected - result.extension shouldBe extension - result.mediaType shouldBe mediaType + `when`("#at") { + then("should return hashed version of asset") { + forAll( + row("/javascripts/main.js", "/javascripts/main", "34UGRNNI", "js", "application/javascript"), + row("/stylesheets/main.css", "/stylesheets/main", "Y6PST7YS", "css", "text/css"), + ) { requested, original, expected, extension, mediaType -> + assetsResolver.at(requested) shouldBePresent { result -> + result.basename shouldBe original + result.hash shouldBe expected + result.extension shouldBe extension + result.mediaType shouldBe mediaType + } } } - } - then("should return empty when there is not hashed version") { - assetsResolver.at("/javascripts/not-there.js") shouldBe Optional.empty() + then("should return empty when there is not hashed version") { + assetsResolver.at("/javascripts/not-there.js") shouldBe Optional.empty() + } } - } - `when`("#fromHashed") { - then("should find using hashed name") { - forAll( - row( - "/javascripts/main.34UGRNNI.js", - "/javascripts/main", - "34UGRNNI", - "js", - "application/javascript", - ), - row("/stylesheets/main.Y6PST7YS.css", "/stylesheets/main", "Y6PST7YS", "css", "text/css"), - ) { requested, original, expected, extension, mediaType -> - assetsResolver.fromHashed(requested) shouldBePresent { result -> - result.basename shouldBe original - result.hash shouldBe expected - result.extension shouldBe extension - result.mediaType shouldBe mediaType + `when`("#fromHashed") { + then("should find using hashed name") { + forAll( + row( + "/javascripts/main.34UGRNNI.js", + "/javascripts/main", + "34UGRNNI", + "js", + "application/javascript", + ), + row("/stylesheets/main.Y6PST7YS.css", "/stylesheets/main", "Y6PST7YS", "css", "text/css"), + ) { requested, original, expected, extension, mediaType -> + assetsResolver.fromHashed(requested) shouldBePresent { result -> + result.basename shouldBe original + result.hash shouldBe expected + result.extension shouldBe extension + result.mediaType shouldBe mediaType + } } } - } - then("should return empty when hashed filename does not exists") { - assetsResolver.at("/javascripts/not-there.Y6PST7YS.js") shouldBe Optional.empty() + then("should return empty when hashed filename does not exists") { + assetsResolver.at("/javascripts/not-there.Y6PST7YS.js") shouldBe Optional.empty() + } } } - } -}) + }) diff --git a/src/test/kotlin/br/ufpe/liber/assets/CacheControlTest.kt b/src/test/kotlin/br/ufpe/liber/assets/CacheControlTest.kt index 1697231..7c6e5ee 100644 --- a/src/test/kotlin/br/ufpe/liber/assets/CacheControlTest.kt +++ b/src/test/kotlin/br/ufpe/liber/assets/CacheControlTest.kt @@ -11,36 +11,46 @@ import java.time.Duration // DO NOT EDIT: this file is automatically synced from the template repository // in https://github.com/Liber-UFPE/project-starter. -class CacheControlTest : BehaviorSpec({ - given("CacheControl") { - `when`(".toString") { - forAll( - table( - headers("Cache Control Configuration", "Expected header value"), - row(CacheControl(Duration.ofSeconds(10)), "max-age=10"), - row(CacheControl(Duration.ofSeconds(10), noCache = true), "max-age=10, no-cache"), - row(CacheControl(Duration.ofSeconds(10), noStore = true), "max-age=10, no-store"), - row(CacheControl(Duration.ofSeconds(10), noTransform = true), "max-age=10, no-transform"), - row(CacheControl(Duration.ofSeconds(10), mustRevalidate = true), "max-age=10, must-revalidate"), - row(CacheControl(Duration.ofSeconds(10), proxyRevalidate = true), "max-age=10, proxy-revalidate"), - row(CacheControl(Duration.ofSeconds(10), mustUnderstand = true), "max-age=10, must-understand"), - row(CacheControl(Duration.ofSeconds(10), private = true), "max-age=10, private"), - row(CacheControl(Duration.ofSeconds(10), public = true), "max-age=10, public"), - row(CacheControl(Duration.ofSeconds(10), immutable = true), "max-age=10, immutable"), - row( - CacheControl(Duration.ofSeconds(10), staleWhileRevalidate = true), - "max-age=10, stale-while-revalidate", +class CacheControlTest : + BehaviorSpec({ + given("CacheControl") { + `when`(".toString") { + forAll( + table( + headers("Cache Control Configuration", "Expected header value"), + row(CacheControl(Duration.ofSeconds(10)), "max-age=10"), + row(CacheControl(Duration.ofSeconds(10), noCache = true), "max-age=10, no-cache"), + row(CacheControl(Duration.ofSeconds(10), noStore = true), "max-age=10, no-store"), + row(CacheControl(Duration.ofSeconds(10), noTransform = true), "max-age=10, no-transform"), + row( + CacheControl(Duration.ofSeconds(10), mustRevalidate = true), + "max-age=10, must-revalidate", + ), + row( + CacheControl(Duration.ofSeconds(10), proxyRevalidate = true), + "max-age=10, proxy-revalidate", + ), + row( + CacheControl(Duration.ofSeconds(10), mustUnderstand = true), + "max-age=10, must-understand", + ), + row(CacheControl(Duration.ofSeconds(10), private = true), "max-age=10, private"), + row(CacheControl(Duration.ofSeconds(10), public = true), "max-age=10, public"), + row(CacheControl(Duration.ofSeconds(10), immutable = true), "max-age=10, immutable"), + row( + CacheControl(Duration.ofSeconds(10), staleWhileRevalidate = true), + "max-age=10, stale-while-revalidate", + ), + row( + CacheControl(Duration.ofSeconds(10), public = true, immutable = true), + "max-age=10, public, immutable", + ), ), - row( - CacheControl(Duration.ofSeconds(10), public = true, immutable = true), - "max-age=10, public, immutable", - ), - ), - ) { configuration, expectedHeader -> - then("generate header $expectedHeader") { - configuration.toString() shouldBe expectedHeader + ) { configuration, expectedHeader -> + then("generate header $expectedHeader") { + configuration.toString() shouldBe expectedHeader + } } } } - } -}) + }) diff --git a/src/test/kotlin/br/ufpe/liber/controllers/AddHeadersFilterTest.kt b/src/test/kotlin/br/ufpe/liber/controllers/AddHeadersFilterTest.kt index 85525a0..590c29d 100644 --- a/src/test/kotlin/br/ufpe/liber/controllers/AddHeadersFilterTest.kt +++ b/src/test/kotlin/br/ufpe/liber/controllers/AddHeadersFilterTest.kt @@ -14,40 +14,38 @@ import io.micronaut.test.extensions.kotest5.annotation.MicronautTest @MicronautTest @Suppress("CLASS_NAME_INCORRECT") -class AddHeadersFilterTest( - private val server: EmbeddedServer, - private val context: ApplicationContext, -) : BehaviorSpec({ - val client = context - .createBean( - HttpClient::class.java, - server.url, - DefaultHttpClientConfiguration().apply { isExceptionOnErrorStatus = false }, - ) - .toBlocking() +class AddHeadersFilterTest(private val server: EmbeddedServer, private val context: ApplicationContext) : + BehaviorSpec({ + val client = context + .createBean( + HttpClient::class.java, + server.url, + DefaultHttpClientConfiguration().apply { isExceptionOnErrorStatus = false }, + ) + .toBlocking() - beforeSpec { - // AssetsResolver initializes a lateinit property used by the view helpers - context.getBean(AssetsResolver::class.java) - } + beforeSpec { + // AssetsResolver initializes a lateinit property used by the view helpers + context.getBean(AssetsResolver::class.java) + } - given("AddHeadersFilter") { - `when`("a request is made") { - val response = client.get("/") - then("add the X-Content-Type-Options header") { - response.header("X-Content-Type-Options") shouldBe "nosniff" - } + given("AddHeadersFilter") { + `when`("a request is made") { + val response = client.get("/") + then("add the X-Content-Type-Options header") { + response.header("X-Content-Type-Options") shouldBe "nosniff" + } - then("add Referrer-Policy header") { - response.header("Referrer-Policy") shouldBe "no-referrer, strict-origin-when-cross-origin" + then("add Referrer-Policy header") { + response.header("Referrer-Policy") shouldBe "no-referrer, strict-origin-when-cross-origin" + } } - } - `when`("ordering filters") { - then("it should be the last one") { - val filter = context.getBean() - filter.order shouldBe ServerFilterPhase.LAST.order() + `when`("ordering filters") { + then("it should be the last one") { + val filter = context.getBean() + filter.order shouldBe ServerFilterPhase.LAST.order() + } } } - } -}) + }) diff --git a/src/test/kotlin/br/ufpe/liber/controllers/AssetsControllerTest.kt b/src/test/kotlin/br/ufpe/liber/controllers/AssetsControllerTest.kt index 04b123a..9267a92 100644 --- a/src/test/kotlin/br/ufpe/liber/controllers/AssetsControllerTest.kt +++ b/src/test/kotlin/br/ufpe/liber/controllers/AssetsControllerTest.kt @@ -21,77 +21,78 @@ import java.util.Optional // DO NOT EDIT: this file is automatically synced from the template repository // in https://github.com/Liber-UFPE/project-starter. -class AssetsControllerTest : BehaviorSpec({ - given("#asset") { - val resourceResolver: ResourceResolver = mockk() - every { resourceResolver.getResource("classpath:public/assets-metadata.json") } answers { - Optional.of(File("src/test/resources/public/test-assets-metadata.json").toURI().toURL()) - } +class AssetsControllerTest : + BehaviorSpec({ + given("#asset") { + val resourceResolver: ResourceResolver = mockk() + every { resourceResolver.getResource("classpath:public/assets-metadata.json") } answers { + Optional.of(File("src/test/resources/public/test-assets-metadata.json").toURI().toURL()) + } - // mock assets and its encoded versions (br, gzip, etc.) - listOf("javascripts/main.34UGRNNI.js", "stylesheets/main.Y6PST7YS.css").flatMap { - listOf("public/$it", "public/$it.br", "public/$it.gz", "public/$it.zz") - }.forEach { mockedAsset -> - every { resourceResolver.getResourceAsStream("classpath:$mockedAsset") } answers { - Optional.of("some content".byteInputStream()) + // mock assets and its encoded versions (br, gzip, etc.) + listOf("javascripts/main.34UGRNNI.js", "stylesheets/main.Y6PST7YS.css").flatMap { + listOf("public/$it", "public/$it.br", "public/$it.gz", "public/$it.zz") + }.forEach { mockedAsset -> + every { resourceResolver.getResourceAsStream("classpath:$mockedAsset") } answers { + Optional.of("some content".byteInputStream()) + } } - } - val assetsResolver = AssetsResolver(resourceResolver) - val assetsController = AssetsController(assetsResolver, resourceResolver) + val assetsResolver = AssetsResolver(resourceResolver) + val assetsController = AssetsController(assetsResolver, resourceResolver) - then("return HTTP Not Found if asset does not exist") { - assetsController.asset("asset/not-there.js", "br, gzip").status() shouldBe HttpStatus.NOT_FOUND - } + then("return HTTP Not Found if asset does not exist") { + assetsController.asset("asset/not-there.js", "br, gzip").status() shouldBe HttpStatus.NOT_FOUND + } - then("return HTTP Not Modified when Etag matches") { - assetsController.asset( - "javascripts/main.34UGRNNI.js", - "br, gzip", - Optional.of("MzRVR1JOTkkK"), - ).status() shouldBe HttpStatus.NOT_MODIFIED - } + then("return HTTP Not Modified when Etag matches") { + assetsController.asset( + "javascripts/main.34UGRNNI.js", + "br, gzip", + Optional.of("MzRVR1JOTkkK"), + ).status() shouldBe HttpStatus.NOT_MODIFIED + } - then("return HTTP OK when Etag does NOT matches") { - assetsController.asset( - "javascripts/main.34UGRNNI.js", - "br, gzip", - Optional.of("not-a-matching-etag"), - ).status() shouldBe HttpStatus.OK - } + then("return HTTP OK when Etag does NOT matches") { + assetsController.asset( + "javascripts/main.34UGRNNI.js", + "br, gzip", + Optional.of("not-a-matching-etag"), + ).status() shouldBe HttpStatus.OK + } - forAll( - row("javascripts/main.34UGRNNI.js", "br, gzip, deflate", HttpStatus.OK, Optional.of("br")), - row("javascripts/main.34UGRNNI.js", "gzip, deflate", HttpStatus.OK, Optional.of("gzip")), - row("javascripts/main.34UGRNNI.js", "deflate", HttpStatus.OK, Optional.of("deflate")), - row("javascripts/main.34UGRNNI.js", "", HttpStatus.OK, Optional.empty()), - row("stylesheets/main.Y6PST7YS.css", "br, gzip, deflate", HttpStatus.OK, Optional.of("br")), - ) { requested, acceptEncoding, expectedStatus, expectedEncoding -> - `when`("requesting $requested with Accept-Encoding $acceptEncoding") { - then("should return HTTP $expectedStatus") { - assetsController.asset(requested, acceptEncoding).status() shouldBe expectedStatus - } + forAll( + row("javascripts/main.34UGRNNI.js", "br, gzip, deflate", HttpStatus.OK, Optional.of("br")), + row("javascripts/main.34UGRNNI.js", "gzip, deflate", HttpStatus.OK, Optional.of("gzip")), + row("javascripts/main.34UGRNNI.js", "deflate", HttpStatus.OK, Optional.of("deflate")), + row("javascripts/main.34UGRNNI.js", "", HttpStatus.OK, Optional.empty()), + row("stylesheets/main.Y6PST7YS.css", "br, gzip, deflate", HttpStatus.OK, Optional.of("br")), + ) { requested, acceptEncoding, expectedStatus, expectedEncoding -> + `when`("requesting $requested with Accept-Encoding $acceptEncoding") { + then("should return HTTP $expectedStatus") { + assetsController.asset(requested, acceptEncoding).status() shouldBe expectedStatus + } - then("should return the encoded version $expectedEncoding") { - val response = assetsController.asset(requested, acceptEncoding) - Optional.ofNullable(response.header(CONTENT_ENCODING)) shouldBe expectedEncoding - } + then("should return the encoded version $expectedEncoding") { + val response = assetsController.asset(requested, acceptEncoding) + Optional.ofNullable(response.header(CONTENT_ENCODING)) shouldBe expectedEncoding + } - then("should set Cache-Control header") { - val response = assetsController.asset(requested, acceptEncoding) - response.header(CACHE_CONTROL) shouldStartWith "max-age=31536000, public, immutable" - } + then("should set Cache-Control header") { + val response = assetsController.asset(requested, acceptEncoding) + response.header(CACHE_CONTROL) shouldStartWith "max-age=31536000, public, immutable" + } - then("should set Last-Modified header") { - val response = assetsController.asset(requested, acceptEncoding) - response.header(LAST_MODIFIED) shouldBe "Thu, 25 Jan 2024 16:46:37 GMT" - } + then("should set Last-Modified header") { + val response = assetsController.asset(requested, acceptEncoding) + response.header(LAST_MODIFIED) shouldBe "Thu, 25 Jan 2024 16:46:37 GMT" + } - then("should set Etag header") { - val response = assetsController.asset(requested, acceptEncoding) - response.header(ETAG) shouldBeIn arrayOf("MzRVR1JOTkkK", "WTZQU1Q3WVMK") + then("should set Etag header") { + val response = assetsController.asset(requested, acceptEncoding) + response.header(ETAG) shouldBeIn arrayOf("MzRVR1JOTkkK", "WTZQU1Q3WVMK") + } } } } - } -}) + }) diff --git a/src/test/kotlin/br/ufpe/liber/controllers/DefaultExceptionHandlerTest.kt b/src/test/kotlin/br/ufpe/liber/controllers/DefaultExceptionHandlerTest.kt index 414561d..b74953d 100644 --- a/src/test/kotlin/br/ufpe/liber/controllers/DefaultExceptionHandlerTest.kt +++ b/src/test/kotlin/br/ufpe/liber/controllers/DefaultExceptionHandlerTest.kt @@ -13,21 +13,20 @@ import io.mockk.mockk // in https://github.com/Liber-UFPE/project-starter. @MicronautTest -class DefaultExceptionHandlerTest( - private val exceptionHandler: DefaultExceptionHandler, -) : BehaviorSpec({ - given("Default ExceptionHandler") { - `when`("there is a server error") { - then("it should return 500 Internal Server Error") { - val request: HttpRequest = mockk() - val exception = Exception("The error message") +class DefaultExceptionHandlerTest(private val exceptionHandler: DefaultExceptionHandler) : + BehaviorSpec({ + given("Default ExceptionHandler") { + `when`("there is a server error") { + then("it should return 500 Internal Server Error") { + val request: HttpRequest = mockk() + val exception = Exception("The error message") - every { request.path } answers { "/testing-path" } + every { request.path } answers { "/testing-path" } - val response = exceptionHandler.handle(request, exception) - response.status.shouldBe(HttpStatus.INTERNAL_SERVER_ERROR) - response.header(HttpHeaders.CONTENT_TYPE) shouldBe "text/html; charset=utf-8" + val response = exceptionHandler.handle(request, exception) + response.status.shouldBe(HttpStatus.INTERNAL_SERVER_ERROR) + response.header(HttpHeaders.CONTENT_TYPE) shouldBe "text/html; charset=utf-8" + } } } - } -}) + }) diff --git a/src/test/kotlin/br/ufpe/liber/controllers/ErrorsControllerTest.kt b/src/test/kotlin/br/ufpe/liber/controllers/ErrorsControllerTest.kt index 94f64ab..fef61e8 100644 --- a/src/test/kotlin/br/ufpe/liber/controllers/ErrorsControllerTest.kt +++ b/src/test/kotlin/br/ufpe/liber/controllers/ErrorsControllerTest.kt @@ -13,18 +13,19 @@ import io.mockk.mockk // in https://github.com/Liber-UFPE/project-starter. @MicronautTest -class ErrorsControllerTest(private val errorsController: ErrorsController) : BehaviorSpec({ - given("ErrorsController") { - `when`(".defaultNotFound") { - then("return 404 Not Found") { - val request: HttpRequest = mockk() +class ErrorsControllerTest(private val errorsController: ErrorsController) : + BehaviorSpec({ + given("ErrorsController") { + `when`(".defaultNotFound") { + then("return 404 Not Found") { + val request: HttpRequest = mockk() - every { request.path } answers { "/testing-path" } + every { request.path } answers { "/testing-path" } - val response = errorsController.defaultNotFound(request) - response.status.shouldBe(HttpStatus.NOT_FOUND) - response.header(HttpHeaders.CONTENT_TYPE) shouldBe "text/html; charset=utf-8" + val response = errorsController.defaultNotFound(request) + response.status.shouldBe(HttpStatus.NOT_FOUND) + response.header(HttpHeaders.CONTENT_TYPE) shouldBe "text/html; charset=utf-8" + } } } - } -}) + }) diff --git a/src/test/kotlin/br/ufpe/liber/controllers/IndexControllerTest.kt b/src/test/kotlin/br/ufpe/liber/controllers/IndexControllerTest.kt index 7e9a8e0..9023dbc 100644 --- a/src/test/kotlin/br/ufpe/liber/controllers/IndexControllerTest.kt +++ b/src/test/kotlin/br/ufpe/liber/controllers/IndexControllerTest.kt @@ -15,38 +15,36 @@ import io.micronaut.runtime.server.EmbeddedServer import io.micronaut.test.extensions.kotest5.annotation.MicronautTest @MicronautTest -class IndexControllerTest( - private val server: EmbeddedServer, - private val context: ApplicationContext, -) : BehaviorSpec({ - val client = context - .createBean( - HttpClient::class.java, - server.url, - DefaultHttpClientConfiguration().apply { isExceptionOnErrorStatus = false }, - ) - .toBlocking() +class IndexControllerTest(private val server: EmbeddedServer, private val context: ApplicationContext) : + BehaviorSpec({ + val client = context + .createBean( + HttpClient::class.java, + server.url, + DefaultHttpClientConfiguration().apply { isExceptionOnErrorStatus = false }, + ) + .toBlocking() - beforeSpec { - // AssetsResolver initializes a lateinit property used by the view helpers - context.getBean(AssetsResolver::class.java) - } + beforeSpec { + // AssetsResolver initializes a lateinit property used by the view helpers + context.getBean(AssetsResolver::class.java) + } - given("IndexController") { - `when`("navigating to pages") { - forAll( - row("/", HttpStatus.OK), - row("/does-not-exists", HttpStatus.NOT_FOUND), - ) { path, expectedStatus -> - then("GET $path should return $expectedStatus") { - val request: HttpRequest = HttpRequest.GET(path) - client.exchange( - request, - Argument.of(KteWriteable::class.java), - Argument.of(KteWriteable::class.java), - ).status.shouldBe(expectedStatus) + given("IndexController") { + `when`("navigating to pages") { + forAll( + row("/", HttpStatus.OK), + row("/does-not-exists", HttpStatus.NOT_FOUND), + ) { path, expectedStatus -> + then("GET $path should return $expectedStatus") { + val request: HttpRequest = HttpRequest.GET(path) + client.exchange( + request, + Argument.of(KteWriteable::class.java), + Argument.of(KteWriteable::class.java), + ).status.shouldBe(expectedStatus) + } } } } - } -}) + }) diff --git a/src/test/kotlin/br/ufpe/liber/pagination/PaginationTest.kt b/src/test/kotlin/br/ufpe/liber/pagination/PaginationTest.kt index 74bf460..56f4f87 100644 --- a/src/test/kotlin/br/ufpe/liber/pagination/PaginationTest.kt +++ b/src/test/kotlin/br/ufpe/liber/pagination/PaginationTest.kt @@ -10,130 +10,131 @@ import io.kotest.matchers.shouldBe // DO NOT EDIT: this file is automatically synced from the template repository // in https://github.com/Liber-UFPE/project-starter. -class PaginationTest : BehaviorSpec({ - given(".pages") { - forAll( - table( - headers("count", "page", "perPage", "expected amount of pages"), - row(103, 1, 20, 6), - row(99, 1, 20, 5), - row(1, 1, 20, 1), - row(20, 1, 20, 1), - row(21, 1, 20, 2), - row(20, 1, 1, 20), - row(21, 1, 1, 21), - ), - ) { count, page, perPage, expectedAmountOfPages -> - `when`("$count items, and $perPage per page") { - then("should have $expectedAmountOfPages pages") { - Pagination(count, page, perPage).pages shouldBe expectedAmountOfPages +class PaginationTest : + BehaviorSpec({ + given(".pages") { + forAll( + table( + headers("count", "page", "perPage", "expected amount of pages"), + row(103, 1, 20, 6), + row(99, 1, 20, 5), + row(1, 1, 20, 1), + row(20, 1, 20, 1), + row(21, 1, 20, 2), + row(20, 1, 1, 20), + row(21, 1, 1, 21), + ), + ) { count, page, perPage, expectedAmountOfPages -> + `when`("$count items, and $perPage per page") { + then("should have $expectedAmountOfPages pages") { + Pagination(count, page, perPage).pages shouldBe expectedAmountOfPages + } } } } - } - given(".page") { - forAll( - table( - headers("count", "current page", "perPage", "expected page"), - row(103, 1, 20, 1), - row(103, 3, 20, 3), - row(103, -5, 20, 1), - row(103, 60, 20, 6), - ), - ) { count, currentPage, perPage, page -> - `when`("$count items, and current page is $page") { - then("page should be $page") { - Pagination(count, currentPage, perPage).page shouldBe page + given(".page") { + forAll( + table( + headers("count", "current page", "perPage", "expected page"), + row(103, 1, 20, 1), + row(103, 3, 20, 3), + row(103, -5, 20, 1), + row(103, 60, 20, 6), + ), + ) { count, currentPage, perPage, page -> + `when`("$count items, and current page is $page") { + then("page should be $page") { + Pagination(count, currentPage, perPage).page shouldBe page + } } } } - } - given(".next") { - forAll( - table( - headers("count", "page", "perPage", "expected next page"), - row(103, 1, 20, 2), - row(103, 3, 20, 4), - row(103, 5, 20, 6), - row(103, 6, 20, null), - ), - ) { count, page, perPage, next -> - `when`("$count items, and current page is $page") { - then("next page should be $next") { - Pagination(count, page, perPage).next shouldBe next + given(".next") { + forAll( + table( + headers("count", "page", "perPage", "expected next page"), + row(103, 1, 20, 2), + row(103, 3, 20, 4), + row(103, 5, 20, 6), + row(103, 6, 20, null), + ), + ) { count, page, perPage, next -> + `when`("$count items, and current page is $page") { + then("next page should be $next") { + Pagination(count, page, perPage).next shouldBe next + } } } } - } - given(".prev") { - forAll( - table( - headers("count", "page", "perPage", "expected prev page"), - row(103, 1, 20, null), - row(103, 3, 20, 2), - row(103, 5, 20, 4), - row(103, 6, 20, 5), - ), - ) { count, page, perPage, prev -> - `when`("$count items, and current page is $page") { - then("prev page should be $prev") { - Pagination(count, page, perPage).prev shouldBe prev + given(".prev") { + forAll( + table( + headers("count", "page", "perPage", "expected prev page"), + row(103, 1, 20, null), + row(103, 3, 20, 2), + row(103, 5, 20, 4), + row(103, 6, 20, 5), + ), + ) { count, page, perPage, prev -> + `when`("$count items, and current page is $page") { + then("prev page should be $prev") { + Pagination(count, page, perPage).prev shouldBe prev + } } } } - } - given(".from and .to") { - forAll( - table( - headers("count", "page", "perPage", "expected from", "expected to"), - row(103, 1, 20, 1, 20), - row(103, 3, 20, 41, 60), - row(103, 5, 20, 81, 100), - row(103, 6, 20, 101, 103), - ), - ) { count, page, perPage, expectedFrom, expectedTo -> - `when`("$count items, and current page is $page") { - then("`from` should be $expectedFrom") { - Pagination(count, page, perPage).from shouldBe expectedFrom - } + given(".from and .to") { + forAll( + table( + headers("count", "page", "perPage", "expected from", "expected to"), + row(103, 1, 20, 1, 20), + row(103, 3, 20, 41, 60), + row(103, 5, 20, 81, 100), + row(103, 6, 20, 101, 103), + ), + ) { count, page, perPage, expectedFrom, expectedTo -> + `when`("$count items, and current page is $page") { + then("`from` should be $expectedFrom") { + Pagination(count, page, perPage).from shouldBe expectedFrom + } - then("`to` should be $expectedTo") { - Pagination(count, page, perPage).to shouldBe expectedTo + then("`to` should be $expectedTo") { + Pagination(count, page, perPage).to shouldBe expectedTo + } } } } - } - given(".listPages") { - forAll( - row(103, 2, listOf("1", "2", "3", "4", "5", "6")), - row(503, 1, listOf("1", "2", "3", "...", "25", "26")), - row(503, 2, listOf("1", "2", "3", "4", "...", "25", "26")), - row(503, 3, listOf("1", "2", "3", "4", "5", "...", "25", "26")), - row(503, 4, listOf("1", "2", "3", "4", "5", "6", "...", "25", "26")), - row(503, 12, listOf("1", "2", "...", "10", "11", "12", "13", "14", "...", "25", "26")), - row(503, 23, listOf("1", "2", "...", "21", "22", "23", "24", "25", "26")), - row(503, 24, listOf("1", "2", "...", "22", "23", "24", "25", "26")), - row(503, 25, listOf("1", "2", "...", "23", "24", "25", "26")), - row(503, 26, listOf("1", "2", "...", "24", "25", "26")), - ) { count, currentPage, expectedSeries -> - `when`("count = $count, current page = $currentPage") { - then("show pages $expectedSeries") { - val pagination = Pagination(count, currentPage) - pagination.listPages().map { it.toString() } shouldBe expectedSeries - } + given(".listPages") { + forAll( + row(103, 2, listOf("1", "2", "3", "4", "5", "6")), + row(503, 1, listOf("1", "2", "3", "...", "25", "26")), + row(503, 2, listOf("1", "2", "3", "4", "...", "25", "26")), + row(503, 3, listOf("1", "2", "3", "4", "5", "...", "25", "26")), + row(503, 4, listOf("1", "2", "3", "4", "5", "6", "...", "25", "26")), + row(503, 12, listOf("1", "2", "...", "10", "11", "12", "13", "14", "...", "25", "26")), + row(503, 23, listOf("1", "2", "...", "21", "22", "23", "24", "25", "26")), + row(503, 24, listOf("1", "2", "...", "22", "23", "24", "25", "26")), + row(503, 25, listOf("1", "2", "...", "23", "24", "25", "26")), + row(503, 26, listOf("1", "2", "...", "24", "25", "26")), + ) { count, currentPage, expectedSeries -> + `when`("count = $count, current page = $currentPage") { + then("show pages $expectedSeries") { + val pagination = Pagination(count, currentPage) + pagination.listPages().map { it.toString() } shouldBe expectedSeries + } - then("correctly tag current page") { - val pagination = Pagination(103, 2) - val expectedCurrentPage = - pagination.listPages().first { page -> page is SinglePage && page.current } as SinglePage - expectedCurrentPage.number shouldBe 2 + then("correctly tag current page") { + val pagination = Pagination(103, 2) + val expectedCurrentPage = + pagination.listPages().first { page -> page is SinglePage && page.current } as SinglePage + expectedCurrentPage.number shouldBe 2 + } } } } - } -}) + }) diff --git a/src/test/kotlin/br/ufpe/liber/views/LinksHelperTest.kt b/src/test/kotlin/br/ufpe/liber/views/LinksHelperTest.kt index 554450d..308167a 100644 --- a/src/test/kotlin/br/ufpe/liber/views/LinksHelperTest.kt +++ b/src/test/kotlin/br/ufpe/liber/views/LinksHelperTest.kt @@ -9,78 +9,79 @@ import io.kotest.extensions.system.withEnvironment import io.kotest.matchers.equals.shouldBeEqual import io.kotest.matchers.shouldBe -class LinksHelperTest : BehaviorSpec({ - given("LinksHelper") { - `when`(".Liber.link") { - then("should create a link that is a path of Liber website") { - LinksHelper.Liber.link("some-path") shouldBe "http://www.liber.ufpe.br/some-path" - } +class LinksHelperTest : + BehaviorSpec({ + given("LinksHelper") { + `when`(".Liber.link") { + then("should create a link that is a path of Liber website") { + LinksHelper.Liber.link("some-path") shouldBe "http://www.liber.ufpe.br/some-path" + } - then("should return Liber website URL when path is empty") { - LinksHelper.Liber.link("") shouldBe "http://www.liber.ufpe.br/" + then("should return Liber website URL when path is empty") { + LinksHelper.Liber.link("") shouldBe "http://www.liber.ufpe.br/" + } } } - } - given("LinksHelper.linkTo") { - `when`("""when base url is "/" """) { - withEnvironment("PROJECT_STARTER_PATH", "/") { - forAll( - table( - headers("path", "expectedResult"), - row("/some-path", "/some-path"), - row("some-path", "/some-path"), - row("/some-path/", "/some-path/"), - row("some-path/", "/some-path/"), - row("/", "/"), - row("", "/"), - ), - ) { path, expectedResult -> - then("generate correct link for $path") { - LinksHelper.linkTo(path) shouldBeEqual expectedResult + given("LinksHelper.linkTo") { + `when`("""when base url is "/" """) { + withEnvironment("PROJECT_STARTER_PATH", "/") { + forAll( + table( + headers("path", "expectedResult"), + row("/some-path", "/some-path"), + row("some-path", "/some-path"), + row("/some-path/", "/some-path/"), + row("some-path/", "/some-path/"), + row("/", "/"), + row("", "/"), + ), + ) { path, expectedResult -> + then("generate correct link for $path") { + LinksHelper.linkTo(path) shouldBeEqual expectedResult + } } } } - } - `when`("""when base url is "/base" """) { - withEnvironment("PROJECT_STARTER_PATH", "/base") { - forAll( - table( - headers("path", "expectedResult"), - row("/some-path", "/base/some-path"), - row("some-path", "/base/some-path"), - row("/some-path/", "/base/some-path/"), - row("some-path/", "/base/some-path/"), - row("/", "/base"), - row("", "/base"), - ), - ) { path, expectedResult -> - then("generate correct link for $path") { - LinksHelper.linkTo(path) shouldBeEqual expectedResult + `when`("""when base url is "/base" """) { + withEnvironment("PROJECT_STARTER_PATH", "/base") { + forAll( + table( + headers("path", "expectedResult"), + row("/some-path", "/base/some-path"), + row("some-path", "/base/some-path"), + row("/some-path/", "/base/some-path/"), + row("some-path/", "/base/some-path/"), + row("/", "/base"), + row("", "/base"), + ), + ) { path, expectedResult -> + then("generate correct link for $path") { + LinksHelper.linkTo(path) shouldBeEqual expectedResult + } } } } - } - `when`("""when base url is "/base/" """) { - withEnvironment("PROJECT_STARTER_PATH", "/base/") { - forAll( - table( - headers("path", "expectedResult"), - row("/some-path", "/base/some-path"), - row("some-path", "/base/some-path"), - row("/some-path/", "/base/some-path/"), - row("some-path/", "/base/some-path/"), - row("/", "/base/"), - row("", "/base/"), - ), - ) { path, expectedResult -> - then("generate correct link for $path") { - LinksHelper.linkTo(path) shouldBeEqual expectedResult + `when`("""when base url is "/base/" """) { + withEnvironment("PROJECT_STARTER_PATH", "/base/") { + forAll( + table( + headers("path", "expectedResult"), + row("/some-path", "/base/some-path"), + row("some-path", "/base/some-path"), + row("/some-path/", "/base/some-path/"), + row("some-path/", "/base/some-path/"), + row("/", "/base/"), + row("", "/base/"), + ), + ) { path, expectedResult -> + then("generate correct link for $path") { + LinksHelper.linkTo(path) shouldBeEqual expectedResult + } } } } } - } -}) + }) diff --git a/src/test/kotlin/br/ufpe/liber/views/ViewsHelperTest.kt b/src/test/kotlin/br/ufpe/liber/views/ViewsHelperTest.kt index 313a9c1..ca99dfb 100644 --- a/src/test/kotlin/br/ufpe/liber/views/ViewsHelperTest.kt +++ b/src/test/kotlin/br/ufpe/liber/views/ViewsHelperTest.kt @@ -16,81 +16,82 @@ import java.util.Optional // DO NOT EDIT: this file is automatically synced from the template repository // in https://github.com/Liber-UFPE/project-starter. -class ViewsHelperTest : BehaviorSpec({ - fun setupCurrentRequest(request: Optional>) { - mockkStatic(ServerRequestContext::class) - every { ServerRequestContext.currentRequest() } answers { request } - } +class ViewsHelperTest : + BehaviorSpec({ + fun setupCurrentRequest(request: Optional>) { + mockkStatic(ServerRequestContext::class) + every { ServerRequestContext.currentRequest() } answers { request } + } - fun setupCurrentRequest(block: (req: HttpRequest) -> Unit) { - val request: HttpRequest = mockk() - block(request) - setupCurrentRequest(Optional.of(request)) - } + fun setupCurrentRequest(block: (req: HttpRequest) -> Unit) { + val request: HttpRequest = mockk() + block(request) + setupCurrentRequest(Optional.of(request)) + } - given("ViewHelpers") { - `when`("HX-Request header value is \"true\"") { - beforeTest { - setupCurrentRequest { req -> - val headers: HttpHeaders = mockk() - every { headers[HtmxRequestHeaders.HX_REQUEST] } answers { "true" } - every { req.headers } answers { headers } + given("ViewHelpers") { + `when`("HX-Request header value is \"true\"") { + beforeTest { + setupCurrentRequest { req -> + val headers: HttpHeaders = mockk() + every { headers[HtmxRequestHeaders.HX_REQUEST] } answers { "true" } + every { req.headers } answers { headers } + } } - } - afterTest { - clearStaticMockk(ServerRequestContext::class) - } + afterTest { + clearStaticMockk(ServerRequestContext::class) + } - then("should be a htmx request") { - ViewsHelper.isHtmxRequest() shouldBe true - ViewsHelper.notHtmxRequest() shouldBe false + then("should be a htmx request") { + ViewsHelper.isHtmxRequest() shouldBe true + ViewsHelper.notHtmxRequest() shouldBe false + } } - } - `when`("HX-Request header is NOT present") { - beforeTest { - setupCurrentRequest { req -> - val headers: HttpHeaders = mockk() - every { headers[HtmxRequestHeaders.HX_REQUEST] } answers { null } - every { req.headers } answers { headers } + `when`("HX-Request header is NOT present") { + beforeTest { + setupCurrentRequest { req -> + val headers: HttpHeaders = mockk() + every { headers[HtmxRequestHeaders.HX_REQUEST] } answers { null } + every { req.headers } answers { headers } + } } - } - then("it should not be a htmx request") { - ViewsHelper.isHtmxRequest() shouldBe false - ViewsHelper.notHtmxRequest() shouldBe true + then("it should not be a htmx request") { + ViewsHelper.isHtmxRequest() shouldBe false + ViewsHelper.notHtmxRequest() shouldBe true + } } - } - `when`(".isActive") { - beforeTest { - setupCurrentRequest { req -> - every { req.path } answers { "/contact" } + `when`(".isActive") { + beforeTest { + setupCurrentRequest { req -> + every { req.path } answers { "/contact" } + } } - } - afterTest { - clearStaticMockk(ServerRequestContext::class) - } + afterTest { + clearStaticMockk(ServerRequestContext::class) + } - then("it should be marked as active when path matches") { - ViewsHelper.isActive("/contact") shouldBe true - } + then("it should be marked as active when path matches") { + ViewsHelper.isActive("/contact") shouldBe true + } - then("it should not be marked as active when path does NOT match") { - ViewsHelper.isActive("/about") shouldBe false + then("it should not be marked as active when path does NOT match") { + ViewsHelper.isActive("/about") shouldBe false + } } - } - `when`(".emptyContent") { - then("there is nothing to render") { - ViewsHelper.emptyContent().asString() shouldBe "" - } + `when`(".emptyContent") { + then("there is nothing to render") { + ViewsHelper.emptyContent().asString() shouldBe "" + } - then("it should be marked as emptyContent") { - ViewsHelper.emptyContent().isEmptyContent shouldBe true + then("it should be marked as emptyContent") { + ViewsHelper.emptyContent().isEmptyContent shouldBe true + } } } - } -}) + })