From 9435df289a678ef28bce36ba59b4eb95b46339da Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 7 Oct 2022 01:43:02 -0700 Subject: [PATCH] Added ability to retrieve multiple jokes --- README.md | 21 +- build.gradle.kts | 2 +- detekt-baseline.xml | 9 +- .../net/thauvin/erik/jokeapi/JokeApi.kt | 200 ++++++++++++------ .../net/thauvin/erik/jokeapi/JokeConfig.kt | 3 +- .../erik/jokeapi/exceptions/JokeException.kt | 3 +- .../net/thauvin/erik/jokeapi/models/Joke.kt | 1 - .../thauvin/erik/jokeapi/models/Language.kt | 8 +- .../thauvin/erik/jokeapi/ExceptionsTest.kt | 2 - .../net/thauvin/erik/jokeapi/GetJokeTest.kt | 24 ++- .../net/thauvin/erik/jokeapi/GetJokesTest.kt | 106 ++++++++++ .../{GetRawJokeTest.kt => GetRawJokesTest.kt} | 18 +- .../thauvin/erik/jokeapi/JokeConfigTest.kt | 45 +++- 13 files changed, 336 insertions(+), 106 deletions(-) create mode 100644 src/test/kotlin/net/thauvin/erik/jokeapi/GetJokesTest.kt rename src/test/kotlin/net/thauvin/erik/jokeapi/{GetRawJokeTest.kt => GetRawJokesTest.kt} (85%) diff --git a/README.md b/README.md index cb01051..50626c2 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,6 @@ A `Joke` class instance is returned: ```kotlin data class Joke( - val error: Boolean, val category: Category, val type: Type, val joke: List, @@ -33,11 +32,23 @@ data class Joke( ``` - View more [examples](https://github.com/ethauvin/jokeapi/blob/master/src/test/kotlin/net/thauvin/erik/jokeapi/GetJokeTest.kt)... +To retrieve multiple jokes: + +```kotlin +val frenchJokes = getJokes(amount = 2, type = Type.TWOPART, language = Language.FR) +frenchJokes.forEach { + println(it.joke.joinToString("\n")) + println("-".repeat(46)) +} +``` + +- View more [examples](https://github.com/ethauvin/jokeapi/blob/master/src/test/kotlin/net/thauvin/erik/jokeapi/GetJokesTest.kt)... + + If an error occurs, a `JokeException` is thrown: ```kotlin class JokeException( - val error: Boolean, val internalError: Boolean, val code: Int, message: String, @@ -75,13 +86,13 @@ dependencies { Instructions for using with Maven, Ivy, etc. can be found on Maven Central. -## Raw Joke +## Raw Jokes -You can also retrieve a raw joke in all [supported formats](https://jokeapi.dev/#format-param). +You can also retrieve one or more raw (unprocessed) jokes in all [supported formats](https://jokeapi.dev/#format-param). For example for YAML: ```kotlin -var joke = getRawJoke(format = Format.YAML, idRange = IdRange(22)) +var joke = getRawJokes(format = Format.YAML, idRange = IdRange(22)) println(joke) ``` ```yaml diff --git a/build.gradle.kts b/build.gradle.kts index 3064cd2..d59cc39 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,7 +15,7 @@ plugins { kotlin("jvm") version "1.7.20" } -description = "Kotlin/Java Wrapper for Sv443's JokeApi" +description = "Kotlin/Java Wrapper for Sv443's JokeAPI" group = "net.thauvin.erik" version = "0.9-SNAPSHOT" diff --git a/detekt-baseline.xml b/detekt-baseline.xml index 19fd5a3..8f4c3a4 100644 --- a/detekt-baseline.xml +++ b/detekt-baseline.xml @@ -2,12 +2,12 @@ - ComplexMethod:JokeApi.kt$JokeApi.Companion$@JvmStatic @Throws(HttpErrorException::class, IOException::class) fun getRawJoke( categories: Set<Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set<Flag> = emptySet(), type: Type = Type.ALL, format: Format = Format.JSON, search: String = "", idRange: IdRange = IdRange(), amount: Int = 1, safe: Boolean = false, ): String + ComplexMethod:JokeApi.kt$JokeApi.Companion$@JvmStatic @Throws(HttpErrorException::class, IOException::class, IllegalArgumentException::class) fun getRawJokes( categories: Set<Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set<Flag> = emptySet(), type: Type = Type.ALL, format: Format = Format.JSON, search: String = "", idRange: IdRange = IdRange(), amount: Int = 1, safe: Boolean = false, ): String + LongParameterList:JokeApi.kt$JokeApi.Companion$( amount: Int, categories: Set<Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set<Flag> = emptySet(), type: Type = Type.ALL, search: String = "", idRange: IdRange = IdRange(), safe: Boolean = false, splitNewLine: Boolean = false ) LongParameterList:JokeApi.kt$JokeApi.Companion$( categories: Set<Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set<Flag> = emptySet(), type: Type = Type.ALL, format: Format = Format.JSON, search: String = "", idRange: IdRange = IdRange(), amount: Int = 1, safe: Boolean = false, ) - LongParameterList:JokeApi.kt$JokeApi.Companion$( categories: Set<Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set<Flag> = emptySet(), type: Type = Type.ALL, search: String = "", idRange: IdRange = IdRange(), safe: Boolean = false, splitNewLine: Boolean = true ) + LongParameterList:JokeApi.kt$JokeApi.Companion$( categories: Set<Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set<Flag> = emptySet(), type: Type = Type.ALL, search: String = "", idRange: IdRange = IdRange(), safe: Boolean = false, splitNewLine: Boolean = false ) LongParameterList:JokeConfig.kt$JokeConfig$( val categories: Set<Category>, val language: Language, val flags: Set<Flag>, val type: Type, val format: Format, val search: String, val idRange: IdRange, val amount: Int = 1, val safe: Boolean, val splitNewLine: Boolean, ) - LongParameterList:JokeException.kt$JokeException$( val error: Boolean, val internalError: Boolean, val code: Int, message: String, val causedBy: List<String>, val additionalInfo: String, val timestamp: Long, cause: Throwable? = null ) - MagicNumber:JokeApi.kt$JokeApi.Companion$10 + LongParameterList:JokeException.kt$JokeException$( val internalError: Boolean, val code: Int, message: String, val causedBy: List<String>, val additionalInfo: String, val timestamp: Long, cause: Throwable? = null ) MagicNumber:JokeApi.kt$JokeApi.Companion$200 MagicNumber:JokeApi.kt$JokeApi.Companion$399 MagicNumber:JokeApi.kt$JokeApi.Companion$400 @@ -18,6 +18,7 @@ MagicNumber:JokeApi.kt$JokeApi.Companion$429 MagicNumber:JokeApi.kt$JokeApi.Companion$500 MagicNumber:JokeApi.kt$JokeApi.Companion$523 + TooManyFunctions:JokeApi.kt$JokeApi$Companion TooManyFunctions:JokeConfig.kt$JokeConfig$Builder UtilityClassWithPublicConstructor:JokeApi.kt$JokeApi diff --git a/src/main/kotlin/net/thauvin/erik/jokeapi/JokeApi.kt b/src/main/kotlin/net/thauvin/erik/jokeapi/JokeApi.kt index 559682a..be136c0 100644 --- a/src/main/kotlin/net/thauvin/erik/jokeapi/JokeApi.kt +++ b/src/main/kotlin/net/thauvin/erik/jokeapi/JokeApi.kt @@ -53,7 +53,7 @@ import java.util.logging.Logger import java.util.stream.Collectors /** - * Implements the [JokeAPI](https://jokeapi.dev/). + * Implements the [Sv443's JokeAPI](https://jokeapi.dev/). */ class JokeApi { companion object { @@ -102,14 +102,14 @@ class JokeApi { } /** - * Retrieves one or more jokes. + * Returns one or more jokes. * * Sse the [JokeAPI Documentation](https://jokeapi.dev/#joke-endpoint) for more details. * @see [getJoke] */ @JvmStatic - @Throws(HttpErrorException::class, IOException::class) - fun getRawJoke( + @Throws(HttpErrorException::class, IOException::class, IllegalArgumentException::class) + fun getRawJokes( categories: Set = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set = emptySet(), @@ -164,16 +164,16 @@ class JokeApi { params[Parameter.RANGE] = idRange.start.toString() } else if (idRange.end > idRange.start) { params[Parameter.RANGE] = "${idRange.start}-${idRange.end}" - } else if (logger.isLoggable(Level.WARNING)) { - logger.warning("Invalid ID Range: ${idRange.start}, ${idRange.end}") + } else { + throw IllegalArgumentException("Invalid ID Range: ${idRange.start}, ${idRange.end}") } } // Amount - if (amount in 2..10) { + if (amount > 1) { params[Parameter.AMOUNT] = amount.toString() - } else if (amount != 1 && logger.isLoggable(Level.WARNING)) { - logger.warning("Invalid Amount: $amount") + } else if (amount <= 0) { + throw IllegalArgumentException("Invalid Amount: $amount") } // Safe @@ -185,14 +185,14 @@ class JokeApi { } /** - * Retrieves ond or more jokes using a [configuration][JokeConfig]. + * Returns one or more jokes using a [configuration][JokeConfig]. * * Sse the [JokeAPI Documentation](https://jokeapi.dev/#joke-endpoint) for more details. */ @JvmStatic - @Throws(HttpErrorException::class, IOException::class) - fun getRawJoke(config: JokeConfig): String { - return getRawJoke( + @Throws(HttpErrorException::class, IOException::class, IllegalArgumentException::class) + fun getRawJokes(config: JokeConfig): String { + return getRawJokes( categories = config.categories, language = config.language, flags = config.flags, @@ -285,15 +285,15 @@ class JokeApi { } /** - * Retrieves a [Joke] instance. + * Returns a [Joke] instance. * * Sse the [JokeAPI Documentation](https://jokeapi.dev/#joke-endpoint) for more details. * * @param splitNewLine Split newline within [Type.SINGLE] joke. - * @see [getRawJoke] + * @see [getRawJokes] */ @JvmStatic - @Throws(JokeException::class, HttpErrorException::class, IOException::class) + @Throws(JokeException::class, HttpErrorException::class, IOException::class, IllegalArgumentException::class) fun getJoke( categories: Set = setOf(Category.ANY), language: Language = Language.ENGLISH, @@ -302,65 +302,79 @@ class JokeApi { search: String = "", idRange: IdRange = IdRange(), safe: Boolean = false, - splitNewLine: Boolean = true + splitNewLine: Boolean = false ): Joke { val json = JSONObject( - getRawJoke( - categories, language, flags, type, search = search, idRange = idRange, safe = safe + getRawJokes( + categories = categories, + language = language, + flags = flags, + type = type, + search = search, + idRange = idRange, + safe = safe ) ) if (json.getBoolean("error")) { - val causedBy = json.getJSONArray("causedBy") - val causes = MutableList(causedBy.length()) { i -> causedBy.getString(i) } - - throw JokeException( - error = true, - internalError = json.getBoolean("internalError"), - code = json.getInt("code"), - message = json.getString("message"), - causedBy = causes, - additionalInfo = json.getString("additionalInfo"), - timestamp = json.getLong("timestamp") - ) + throw parseError(json) } else { - val jokes = mutableListOf() - if (json.has("setup")) { - jokes.add(json.getString("setup")) - jokes.add(json.getString(("delivery"))) - } else { - if (splitNewLine) { - jokes.addAll(json.getString("joke").split("\n")) - } else { - jokes.add(json.getString("joke")) - } - } - val enabledFlags = mutableSetOf() - val jsonFlags = json.getJSONObject("flags") - Flag.values().filter { it != Flag.ALL }.forEach { - if (jsonFlags.has(it.value) && jsonFlags.getBoolean(it.value)) { - enabledFlags.add(it) - } - } - return Joke( - error = false, - category = Category.valueOf(json.getString("category").uppercase()), - type = Type.valueOf(json.getString(Parameter.TYPE).uppercase()), - joke = jokes, - flags = enabledFlags, - safe = json.getBoolean("safe"), - id = json.getInt("id"), - language = Language.valueOf(json.getString(Parameter.LANG).uppercase()) - ) + return parseJoke(json, splitNewLine) } } /** - * Retrieves a [Joke] instance using a [configuration][JokeConfig]. + * Returns an array of [Joke] instances. + * + * Sse the [JokeAPI Documentation](https://jokeapi.dev/#joke-endpoint) for more details. + * + * @param amount The required amount of jokes to return. + * @param splitNewLine Split newline within [Type.SINGLE] joke. + * @see [getRawJokes] + */ + @JvmStatic + @Throws(JokeException::class, HttpErrorException::class, IOException::class, IllegalArgumentException::class) + fun getJokes( + amount: Int, + categories: Set = setOf(Category.ANY), + language: Language = Language.ENGLISH, + flags: Set = emptySet(), + type: Type = Type.ALL, + search: String = "", + idRange: IdRange = IdRange(), + safe: Boolean = false, + splitNewLine: Boolean = false + ): Array { + val json = JSONObject( + getRawJokes( + categories = categories, + language = language, + flags = flags, + type = type, + search = search, + idRange = idRange, + amount = amount, + safe = safe + ) + ) + if (json.getBoolean("error")) { + throw parseError(json) + } else { + return if (json.has("amount")) { + val jokes = json.getJSONArray("jokes") + Array(jokes.length()) { i -> parseJoke(jokes.getJSONObject(i), splitNewLine) } + } else { + arrayOf(parseJoke(json, splitNewLine)) + } + } + } + + /** + * Retrieve a [Joke] instance using a [configuration][JokeConfig]. * * Sse the [JokeAPI Documentation](https://jokeapi.dev/#joke-endpoint) for more details. */ @JvmStatic - @Throws(JokeException::class, HttpErrorException::class, IOException::class) + @Throws(JokeException::class, HttpErrorException::class, IOException::class, IllegalArgumentException::class) fun getJoke(config: JokeConfig): Joke { return getJoke( categories = config.categories, @@ -373,5 +387,69 @@ class JokeApi { splitNewLine = config.splitNewLine ) } + + /** + * Returns an array of [Joke] instances using a [configuration][JokeConfig]. + * + * Sse the [JokeAPI Documentation](https://jokeapi.dev/#joke-endpoint) for more details. + */ + @JvmStatic + @Throws(JokeException::class, HttpErrorException::class, IOException::class, IllegalArgumentException::class) + fun getJokes(config: JokeConfig): Array { + return getJokes( + categories = config.categories, + language = config.language, + flags = config.flags, + type = config.type, + search = config.search, + idRange = config.idRange, + amount = config.amount, + safe = config.safe, + splitNewLine = config.splitNewLine + ) + } + + private fun parseError(json: JSONObject): JokeException { + val causedBy = json.getJSONArray("causedBy") + val causes = List(causedBy.length()) { i -> causedBy.getString(i) } + return JokeException( + internalError = json.getBoolean("internalError"), + code = json.getInt("code"), + message = json.getString("message"), + causedBy = causes, + additionalInfo = json.getString("additionalInfo"), + timestamp = json.getLong("timestamp") + ) + } + + private fun parseJoke(json: JSONObject, splitNewLine: Boolean): Joke { + val jokes = mutableListOf() + if (json.has("setup")) { + jokes.add(json.getString("setup")) + jokes.add(json.getString(("delivery"))) + } else { + if (splitNewLine) { + jokes.addAll(json.getString("joke").split("\n")) + } else { + jokes.add(json.getString("joke")) + } + } + val enabledFlags = mutableSetOf() + val jsonFlags = json.getJSONObject("flags") + Flag.values().filter { it != Flag.ALL }.forEach { + if (jsonFlags.has(it.value) && jsonFlags.getBoolean(it.value)) { + enabledFlags.add(it) + } + } + return Joke( + category = Category.valueOf(json.getString("category").uppercase()), + type = Type.valueOf(json.getString(Parameter.TYPE).uppercase()), + joke = jokes, + flags = enabledFlags, + safe = json.getBoolean("safe"), + id = json.getInt("id"), + language = Language.valueOf(json.getString(Parameter.LANG).uppercase()) + ) + } } } diff --git a/src/main/kotlin/net/thauvin/erik/jokeapi/JokeConfig.kt b/src/main/kotlin/net/thauvin/erik/jokeapi/JokeConfig.kt index b64f502..f27132e 100644 --- a/src/main/kotlin/net/thauvin/erik/jokeapi/JokeConfig.kt +++ b/src/main/kotlin/net/thauvin/erik/jokeapi/JokeConfig.kt @@ -32,6 +32,7 @@ package net.thauvin.erik.jokeapi +import net.thauvin.erik.jokeapi.JokeConfig.Builder import net.thauvin.erik.jokeapi.models.Category import net.thauvin.erik.jokeapi.models.Flag import net.thauvin.erik.jokeapi.models.Format @@ -73,7 +74,7 @@ class JokeConfig private constructor( var idRange: IdRange = IdRange(), var amount: Int = 1, var safe: Boolean = false, - var splitNewLine: Boolean = true + var splitNewLine: Boolean = false ) { fun categories(categories: Set) = apply { this.categories = categories } fun language(language: Language) = apply { this.language = language } diff --git a/src/main/kotlin/net/thauvin/erik/jokeapi/exceptions/JokeException.kt b/src/main/kotlin/net/thauvin/erik/jokeapi/exceptions/JokeException.kt index 93bea1f..29d7849 100644 --- a/src/main/kotlin/net/thauvin/erik/jokeapi/exceptions/JokeException.kt +++ b/src/main/kotlin/net/thauvin/erik/jokeapi/exceptions/JokeException.kt @@ -38,7 +38,6 @@ package net.thauvin.erik.jokeapi.exceptions * Sse the [JokeAPI Documentation](https://jokeapi.dev/#errors) for more details. */ class JokeException @JvmOverloads constructor( - val error: Boolean, val internalError: Boolean, val code: Int, message: String, @@ -52,7 +51,7 @@ class JokeException @JvmOverloads constructor( } fun debug(): String { - return "JokeException(message=$message, error=$error, internalError=$internalError, code=$code," + + return "JokeException(message=$message, internalError=$internalError, code=$code," + " causedBy=$causedBy, additionalInfo='$additionalInfo', timestamp=$timestamp)" } } diff --git a/src/main/kotlin/net/thauvin/erik/jokeapi/models/Joke.kt b/src/main/kotlin/net/thauvin/erik/jokeapi/models/Joke.kt index 3a5e1e1..e9df053 100644 --- a/src/main/kotlin/net/thauvin/erik/jokeapi/models/Joke.kt +++ b/src/main/kotlin/net/thauvin/erik/jokeapi/models/Joke.kt @@ -36,7 +36,6 @@ package net.thauvin.erik.jokeapi.models * Stores a joke's data. */ data class Joke( - val error: Boolean, val category: Category, val type: Type, val joke: List, diff --git a/src/main/kotlin/net/thauvin/erik/jokeapi/models/Language.kt b/src/main/kotlin/net/thauvin/erik/jokeapi/models/Language.kt index 1fce7a7..d45b368 100644 --- a/src/main/kotlin/net/thauvin/erik/jokeapi/models/Language.kt +++ b/src/main/kotlin/net/thauvin/erik/jokeapi/models/Language.kt @@ -41,16 +41,16 @@ enum class Language(val value: String) { CZECH("cs"), CS(CZECH.value), - + GERMAN("de"), DE(GERMAN.value), - + SPANISH("es"), ES(SPANISH.value), - + FRENCH("fr"), FR(FRENCH.value), - + PORTUGUESE("pt"), PT(PORTUGUESE.value) } diff --git a/src/test/kotlin/net/thauvin/erik/jokeapi/ExceptionsTest.kt b/src/test/kotlin/net/thauvin/erik/jokeapi/ExceptionsTest.kt index b23a5b2..e0a68d3 100644 --- a/src/test/kotlin/net/thauvin/erik/jokeapi/ExceptionsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/jokeapi/ExceptionsTest.kt @@ -41,7 +41,6 @@ import assertk.assertions.isGreaterThan import assertk.assertions.isNotEmpty import assertk.assertions.isNotNull import assertk.assertions.isNull -import assertk.assertions.isTrue import assertk.assertions.prop import assertk.assertions.size import assertk.assertions.startsWith @@ -70,7 +69,6 @@ internal class ExceptionsTest { logger.fine(e.debug()) assertThat(e, "getJoke(${Category.CHRISTMAS},foo)").all { prop(JokeException::code).isEqualTo(106) - prop(JokeException::error).isTrue() prop(JokeException::internalError).isFalse() prop(JokeException::message).isEqualTo("No matching joke found") prop(JokeException::causedBy).size().isEqualTo(1) diff --git a/src/test/kotlin/net/thauvin/erik/jokeapi/GetJokeTest.kt b/src/test/kotlin/net/thauvin/erik/jokeapi/GetJokeTest.kt index deda3a7..ea0a740 100644 --- a/src/test/kotlin/net/thauvin/erik/jokeapi/GetJokeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/jokeapi/GetJokeTest.kt @@ -36,14 +36,16 @@ import assertk.all import assertk.assertThat import assertk.assertions.any import assertk.assertions.contains +import assertk.assertions.containsNone +import assertk.assertions.each import assertk.assertions.isBetween import assertk.assertions.isEmpty import assertk.assertions.isEqualTo -import assertk.assertions.isFalse import assertk.assertions.isGreaterThan import assertk.assertions.isGreaterThanOrEqualTo import assertk.assertions.isIn import assertk.assertions.isNotEmpty +import assertk.assertions.isNotNull import assertk.assertions.isTrue import assertk.assertions.prop import assertk.assertions.size @@ -68,7 +70,6 @@ internal class GetJokeTest { val joke = getJoke() logger.fine(joke.toString()) assertThat(joke, "getJoke()").all { - prop(Joke::error).isFalse() prop(Joke::joke).isNotEmpty() prop(Joke::type).isIn(Type.SINGLE, Type.TWOPART) prop(Joke::id).isGreaterThanOrEqualTo(0) @@ -115,9 +116,8 @@ internal class GetJokeTest { @Test fun `Get Joke with invalid ID Range`() { val idRange = IdRange(100, 1) - val joke = getJoke(idRange = idRange) - logger.fine(joke.toString()) - assertThat(joke::error).isFalse() + val e = assertThrows { getJoke(idRange = idRange, language = Language.DE) } + assertThat(e::message).isNotNull().contains("100, 1") } @Test @@ -125,7 +125,6 @@ internal class GetJokeTest { val idRange = IdRange(1, 30000) val e = assertThrows { getJoke(idRange = idRange) } assertThat(e, "getJoke{${idRange})").all { - prop(JokeException::error).isTrue() prop(JokeException::additionalInfo).contains("ID range") } } @@ -153,17 +152,22 @@ internal class GetJokeTest { fun `Get Joke with each Languages`() { Language.values().forEach { val joke = getJoke(language = it) + logger.fine(joke.toString()) assertThat(joke::language, "getJoke($it)").prop(Language::value).isEqualTo(it.value) } } @Test - fun `Get Joke with Newline`() { + fun `Get Joke with Split Newline`() { val joke = getJoke( - categories = setOf(Category.DARK), type = Type.SINGLE, idRange = IdRange(178), splitNewLine = false + categories = setOf(Category.DARK), type = Type.SINGLE, idRange = IdRange(178), splitNewLine = true ) - assertThat(joke::joke, "getJoke(splitNewLine=false)").any { - it.contains("\n") + logger.fine(joke.toString()) + assertThat(joke::joke, "getJoke(splitNewLine=true)").all { + size().isEqualTo(2) + each { + containsNone("\n") + } } } diff --git a/src/test/kotlin/net/thauvin/erik/jokeapi/GetJokesTest.kt b/src/test/kotlin/net/thauvin/erik/jokeapi/GetJokesTest.kt new file mode 100644 index 0000000..489aa74 --- /dev/null +++ b/src/test/kotlin/net/thauvin/erik/jokeapi/GetJokesTest.kt @@ -0,0 +1,106 @@ +/* + * GetJokeTest.kt + * + * Copyright (c) 2022, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.jokeapi + +import assertk.all +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.each +import assertk.assertions.index +import assertk.assertions.isEmpty +import assertk.assertions.isEqualTo +import assertk.assertions.isGreaterThanOrEqualTo +import assertk.assertions.isNotNull +import assertk.assertions.isTrue +import assertk.assertions.prop +import assertk.assertions.size +import net.thauvin.erik.jokeapi.JokeApi.Companion.getJokes +import net.thauvin.erik.jokeapi.JokeApi.Companion.logger +import net.thauvin.erik.jokeapi.models.Joke +import net.thauvin.erik.jokeapi.models.Language +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import java.util.logging.ConsoleHandler +import java.util.logging.Level + +internal class GetJokesTest { + @Test + fun `Get Multiple Jokes`() { + val amount = 2 + val jokes = getJokes(amount = amount, safe = true, language = Language.FR) + assertThat(jokes, "jokes").all { + size().isEqualTo(amount) + each { + it.prop(Joke::id).isGreaterThanOrEqualTo(0) + it.prop(Joke::safe).isTrue() + it.prop(Joke::flags).isEmpty() + it.prop(Joke::language).isEqualTo(Language.FR) + } + } + } + + @Test + fun `Get Jokes with Invalid Amount`() { + val e = assertThrows { getJokes(amount = -1) } + assertThat(e::message).isNotNull().contains("-1") + } + + @Test + fun `Get One Joke as Multiple`() { + val jokes = getJokes(amount = 1, safe = true) + jokes.forEach { + println(it.joke.joinToString("\n")) + println("-".repeat(46)) + } + assertThat(jokes, "jokes").all { + size().isEqualTo(1) + index(0).all { + prop(Joke::id).isGreaterThanOrEqualTo(0) + prop(Joke::flags).isEmpty() + prop(Joke::safe).isTrue() + } + } + } + + companion object { + @JvmStatic + @BeforeAll + fun beforeAll() { + with(logger) { + addHandler(ConsoleHandler().apply { level = Level.FINE }) + level = Level.FINE + } + } + } +} diff --git a/src/test/kotlin/net/thauvin/erik/jokeapi/GetRawJokeTest.kt b/src/test/kotlin/net/thauvin/erik/jokeapi/GetRawJokesTest.kt similarity index 85% rename from src/test/kotlin/net/thauvin/erik/jokeapi/GetRawJokeTest.kt rename to src/test/kotlin/net/thauvin/erik/jokeapi/GetRawJokesTest.kt index 9ec9f0d..c0f07e1 100644 --- a/src/test/kotlin/net/thauvin/erik/jokeapi/GetRawJokeTest.kt +++ b/src/test/kotlin/net/thauvin/erik/jokeapi/GetRawJokesTest.kt @@ -37,7 +37,7 @@ import assertk.assertThat import assertk.assertions.doesNotContain import assertk.assertions.isNotEmpty import assertk.assertions.startsWith -import net.thauvin.erik.jokeapi.JokeApi.Companion.getRawJoke +import net.thauvin.erik.jokeapi.JokeApi.Companion.getRawJokes import net.thauvin.erik.jokeapi.JokeApi.Companion.logger import net.thauvin.erik.jokeapi.models.Format import org.junit.jupiter.api.BeforeAll @@ -46,37 +46,31 @@ import java.util.logging.ConsoleHandler import java.util.logging.Level import kotlin.test.assertContains -internal class GetRawJokeTest { +internal class GetRawJokesTest { @Test fun `Get Raw Joke with TXT`() { - val response = getRawJoke(format = Format.TEXT) + val response = getRawJokes(format = Format.TEXT) assertThat(response, "getRawJoke(txt)").all { isNotEmpty() doesNotContain("Error") } } - @Test - fun `Get Raw Joke with invalid Amount`() { - val response = getRawJoke(amount = 100) - assertThat(response, "getRawJoke(100)").doesNotContain("\"amount\":") - } - @Test fun `Get Raw Joke with XML`() { - val response = getRawJoke(format = Format.XML) + val response = getRawJokes(format = Format.XML) assertThat(response, "getRawJoke(xml)").startsWith("\n\n false") } @Test fun `Get Raw Joke with YAML`() { - val response = getRawJoke(format = Format.YAML) + val response = getRawJokes(format = Format.YAML) assertThat(response, "getRawJoke(yaml)").startsWith("error: false") } @Test fun `Get Raw Jokes`() { - val response = getRawJoke(amount = 2) + val response = getRawJokes(amount = 2) assertContains(response, "\"amount\": 2", false, "getRawJoke(2)") } diff --git a/src/test/kotlin/net/thauvin/erik/jokeapi/JokeConfigTest.kt b/src/test/kotlin/net/thauvin/erik/jokeapi/JokeConfigTest.kt index ef1109e..2ab52ca 100644 --- a/src/test/kotlin/net/thauvin/erik/jokeapi/JokeConfigTest.kt +++ b/src/test/kotlin/net/thauvin/erik/jokeapi/JokeConfigTest.kt @@ -34,13 +34,17 @@ package net.thauvin.erik.jokeapi import assertk.all import assertk.assertThat +import assertk.assertions.each import assertk.assertions.isBetween import assertk.assertions.isEmpty import assertk.assertions.isEqualTo +import assertk.assertions.isGreaterThanOrEqualTo +import assertk.assertions.isTrue import assertk.assertions.prop import assertk.assertions.size import net.thauvin.erik.jokeapi.JokeApi.Companion.getJoke -import net.thauvin.erik.jokeapi.JokeApi.Companion.getRawJoke +import net.thauvin.erik.jokeapi.JokeApi.Companion.getJokes +import net.thauvin.erik.jokeapi.JokeApi.Companion.getRawJokes import net.thauvin.erik.jokeapi.JokeApi.Companion.logger import net.thauvin.erik.jokeapi.models.Category import net.thauvin.erik.jokeapi.models.Flag @@ -66,7 +70,6 @@ class JokeConfigTest { type(Type.TWOPART) idRange(IdRange(id - 2, id + 2)) safe(true) - splitNewLine(false) }.build() val joke = getJoke(config) logger.fine(joke.toString()) @@ -80,6 +83,22 @@ class JokeConfigTest { } } + @Test + fun `Get joke with Builder and Split Newline`() { + val id = 5 + val config = JokeConfig.Builder().apply { + categories(setOf(Category.PROGRAMMING)) + idRange(IdRange(id)) + splitNewLine(true) + }.build() + val joke = getJoke(config) + logger.fine(joke.toString()) + assertThat(joke, "config").all { + prop(Joke::id).isEqualTo(id) + prop(Joke::joke).size().isEqualTo(2) + } + } + @Test fun `Get Raw Joke with Builder`() { val config = JokeConfig.Builder().apply { @@ -89,10 +108,30 @@ class JokeConfigTest { amount(2) safe(true) }.build() - val joke = getRawJoke(config) + val joke = getRawJokes(config) assertContains(joke, "----------------------------------------------", false, "config.amount(2)") } + @Test + fun `Get Multiple Jokes with Builder`() { + val amount = 2 + val config = JokeConfig.Builder().apply { + amount(amount) + safe(true) + language(Language.FR) + }.build() + val jokes = getJokes(config) + assertThat(jokes, "jokes").all { + size().isEqualTo(amount) + each { + it.prop(Joke::id).isGreaterThanOrEqualTo(0) + it.prop(Joke::safe).isTrue() + it.prop(Joke::flags).isEmpty() + it.prop(Joke::language).isEqualTo(Language.FR) + } + } + } + companion object { @JvmStatic @BeforeAll