Added ability to retrieve multiple jokes

This commit is contained in:
Erik C. Thauvin 2022-10-07 01:43:02 -07:00
parent 77b62fc6dc
commit 9435df289a
13 changed files with 336 additions and 106 deletions

View file

@ -21,7 +21,6 @@ A `Joke` class instance is returned:
```kotlin ```kotlin
data class Joke( data class Joke(
val error: Boolean,
val category: Category, val category: Category,
val type: Type, val type: Type,
val joke: List<String>, val joke: List<String>,
@ -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)... - 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: If an error occurs, a `JokeException` is thrown:
```kotlin ```kotlin
class JokeException( class JokeException(
val error: Boolean,
val internalError: Boolean, val internalError: Boolean,
val code: Int, val code: Int,
message: String, message: String,
@ -75,13 +86,13 @@ dependencies {
Instructions for using with Maven, Ivy, etc. can be found on Maven Central. 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: For example for YAML:
```kotlin ```kotlin
var joke = getRawJoke(format = Format.YAML, idRange = IdRange(22)) var joke = getRawJokes(format = Format.YAML, idRange = IdRange(22))
println(joke) println(joke)
``` ```
```yaml ```yaml

View file

@ -15,7 +15,7 @@ plugins {
kotlin("jvm") version "1.7.20" 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" group = "net.thauvin.erik"
version = "0.9-SNAPSHOT" version = "0.9-SNAPSHOT"

View file

@ -2,12 +2,12 @@
<SmellBaseline> <SmellBaseline>
<ManuallySuppressedIssues/> <ManuallySuppressedIssues/>
<CurrentIssues> <CurrentIssues>
<ID>ComplexMethod:JokeApi.kt$JokeApi.Companion$@JvmStatic @Throws(HttpErrorException::class, IOException::class) fun getRawJoke( categories: Set&lt;Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set&lt;Flag> = emptySet(), type: Type = Type.ALL, format: Format = Format.JSON, search: String = "", idRange: IdRange = IdRange(), amount: Int = 1, safe: Boolean = false, ): String</ID> <ID>ComplexMethod:JokeApi.kt$JokeApi.Companion$@JvmStatic @Throws(HttpErrorException::class, IOException::class, IllegalArgumentException::class) fun getRawJokes( categories: Set&lt;Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set&lt;Flag> = emptySet(), type: Type = Type.ALL, format: Format = Format.JSON, search: String = "", idRange: IdRange = IdRange(), amount: Int = 1, safe: Boolean = false, ): String</ID>
<ID>LongParameterList:JokeApi.kt$JokeApi.Companion$( amount: Int, categories: Set&lt;Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set&lt;Flag> = emptySet(), type: Type = Type.ALL, search: String = "", idRange: IdRange = IdRange(), safe: Boolean = false, splitNewLine: Boolean = false )</ID>
<ID>LongParameterList:JokeApi.kt$JokeApi.Companion$( categories: Set&lt;Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set&lt;Flag> = emptySet(), type: Type = Type.ALL, format: Format = Format.JSON, search: String = "", idRange: IdRange = IdRange(), amount: Int = 1, safe: Boolean = false, )</ID> <ID>LongParameterList:JokeApi.kt$JokeApi.Companion$( categories: Set&lt;Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set&lt;Flag> = emptySet(), type: Type = Type.ALL, format: Format = Format.JSON, search: String = "", idRange: IdRange = IdRange(), amount: Int = 1, safe: Boolean = false, )</ID>
<ID>LongParameterList:JokeApi.kt$JokeApi.Companion$( categories: Set&lt;Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set&lt;Flag> = emptySet(), type: Type = Type.ALL, search: String = "", idRange: IdRange = IdRange(), safe: Boolean = false, splitNewLine: Boolean = true )</ID> <ID>LongParameterList:JokeApi.kt$JokeApi.Companion$( categories: Set&lt;Category> = setOf(Category.ANY), language: Language = Language.ENGLISH, flags: Set&lt;Flag> = emptySet(), type: Type = Type.ALL, search: String = "", idRange: IdRange = IdRange(), safe: Boolean = false, splitNewLine: Boolean = false )</ID>
<ID>LongParameterList:JokeConfig.kt$JokeConfig$( val categories: Set&lt;Category>, val language: Language, val flags: Set&lt;Flag>, val type: Type, val format: Format, val search: String, val idRange: IdRange, val amount: Int = 1, val safe: Boolean, val splitNewLine: Boolean, )</ID> <ID>LongParameterList:JokeConfig.kt$JokeConfig$( val categories: Set&lt;Category>, val language: Language, val flags: Set&lt;Flag>, val type: Type, val format: Format, val search: String, val idRange: IdRange, val amount: Int = 1, val safe: Boolean, val splitNewLine: Boolean, )</ID>
<ID>LongParameterList:JokeException.kt$JokeException$( val error: Boolean, val internalError: Boolean, val code: Int, message: String, val causedBy: List&lt;String>, val additionalInfo: String, val timestamp: Long, cause: Throwable? = null )</ID> <ID>LongParameterList:JokeException.kt$JokeException$( val internalError: Boolean, val code: Int, message: String, val causedBy: List&lt;String>, val additionalInfo: String, val timestamp: Long, cause: Throwable? = null )</ID>
<ID>MagicNumber:JokeApi.kt$JokeApi.Companion$10</ID>
<ID>MagicNumber:JokeApi.kt$JokeApi.Companion$200</ID> <ID>MagicNumber:JokeApi.kt$JokeApi.Companion$200</ID>
<ID>MagicNumber:JokeApi.kt$JokeApi.Companion$399</ID> <ID>MagicNumber:JokeApi.kt$JokeApi.Companion$399</ID>
<ID>MagicNumber:JokeApi.kt$JokeApi.Companion$400</ID> <ID>MagicNumber:JokeApi.kt$JokeApi.Companion$400</ID>
@ -18,6 +18,7 @@
<ID>MagicNumber:JokeApi.kt$JokeApi.Companion$429</ID> <ID>MagicNumber:JokeApi.kt$JokeApi.Companion$429</ID>
<ID>MagicNumber:JokeApi.kt$JokeApi.Companion$500</ID> <ID>MagicNumber:JokeApi.kt$JokeApi.Companion$500</ID>
<ID>MagicNumber:JokeApi.kt$JokeApi.Companion$523</ID> <ID>MagicNumber:JokeApi.kt$JokeApi.Companion$523</ID>
<ID>TooManyFunctions:JokeApi.kt$JokeApi$Companion</ID>
<ID>TooManyFunctions:JokeConfig.kt$JokeConfig$Builder</ID> <ID>TooManyFunctions:JokeConfig.kt$JokeConfig$Builder</ID>
<ID>UtilityClassWithPublicConstructor:JokeApi.kt$JokeApi</ID> <ID>UtilityClassWithPublicConstructor:JokeApi.kt$JokeApi</ID>
</CurrentIssues> </CurrentIssues>

View file

@ -53,7 +53,7 @@ import java.util.logging.Logger
import java.util.stream.Collectors import java.util.stream.Collectors
/** /**
* Implements the [JokeAPI](https://jokeapi.dev/). * Implements the [Sv443's JokeAPI](https://jokeapi.dev/).
*/ */
class JokeApi { class JokeApi {
companion object { 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. * Sse the [JokeAPI Documentation](https://jokeapi.dev/#joke-endpoint) for more details.
* @see [getJoke] * @see [getJoke]
*/ */
@JvmStatic @JvmStatic
@Throws(HttpErrorException::class, IOException::class) @Throws(HttpErrorException::class, IOException::class, IllegalArgumentException::class)
fun getRawJoke( fun getRawJokes(
categories: Set<Category> = setOf(Category.ANY), categories: Set<Category> = setOf(Category.ANY),
language: Language = Language.ENGLISH, language: Language = Language.ENGLISH,
flags: Set<Flag> = emptySet(), flags: Set<Flag> = emptySet(),
@ -164,16 +164,16 @@ class JokeApi {
params[Parameter.RANGE] = idRange.start.toString() params[Parameter.RANGE] = idRange.start.toString()
} else if (idRange.end > idRange.start) { } else if (idRange.end > idRange.start) {
params[Parameter.RANGE] = "${idRange.start}-${idRange.end}" params[Parameter.RANGE] = "${idRange.start}-${idRange.end}"
} else if (logger.isLoggable(Level.WARNING)) { } else {
logger.warning("Invalid ID Range: ${idRange.start}, ${idRange.end}") throw IllegalArgumentException("Invalid ID Range: ${idRange.start}, ${idRange.end}")
} }
} }
// Amount // Amount
if (amount in 2..10) { if (amount > 1) {
params[Parameter.AMOUNT] = amount.toString() params[Parameter.AMOUNT] = amount.toString()
} else if (amount != 1 && logger.isLoggable(Level.WARNING)) { } else if (amount <= 0) {
logger.warning("Invalid Amount: $amount") throw IllegalArgumentException("Invalid Amount: $amount")
} }
// Safe // 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. * Sse the [JokeAPI Documentation](https://jokeapi.dev/#joke-endpoint) for more details.
*/ */
@JvmStatic @JvmStatic
@Throws(HttpErrorException::class, IOException::class) @Throws(HttpErrorException::class, IOException::class, IllegalArgumentException::class)
fun getRawJoke(config: JokeConfig): String { fun getRawJokes(config: JokeConfig): String {
return getRawJoke( return getRawJokes(
categories = config.categories, categories = config.categories,
language = config.language, language = config.language,
flags = config.flags, 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. * Sse the [JokeAPI Documentation](https://jokeapi.dev/#joke-endpoint) for more details.
* *
* @param splitNewLine Split newline within [Type.SINGLE] joke. * @param splitNewLine Split newline within [Type.SINGLE] joke.
* @see [getRawJoke] * @see [getRawJokes]
*/ */
@JvmStatic @JvmStatic
@Throws(JokeException::class, HttpErrorException::class, IOException::class) @Throws(JokeException::class, HttpErrorException::class, IOException::class, IllegalArgumentException::class)
fun getJoke( fun getJoke(
categories: Set<Category> = setOf(Category.ANY), categories: Set<Category> = setOf(Category.ANY),
language: Language = Language.ENGLISH, language: Language = Language.ENGLISH,
@ -302,65 +302,79 @@ class JokeApi {
search: String = "", search: String = "",
idRange: IdRange = IdRange(), idRange: IdRange = IdRange(),
safe: Boolean = false, safe: Boolean = false,
splitNewLine: Boolean = true splitNewLine: Boolean = false
): Joke { ): Joke {
val json = JSONObject( val json = JSONObject(
getRawJoke( getRawJokes(
categories, language, flags, type, search = search, idRange = idRange, safe = safe categories = categories,
language = language,
flags = flags,
type = type,
search = search,
idRange = idRange,
safe = safe
) )
) )
if (json.getBoolean("error")) { if (json.getBoolean("error")) {
val causedBy = json.getJSONArray("causedBy") throw parseError(json)
val causes = MutableList<String>(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")
)
} else { } else {
val jokes = mutableListOf<String>() return parseJoke(json, splitNewLine)
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<Flag>()
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())
)
} }
} }
/** /**
* 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<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
): Array<Joke> {
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. * Sse the [JokeAPI Documentation](https://jokeapi.dev/#joke-endpoint) for more details.
*/ */
@JvmStatic @JvmStatic
@Throws(JokeException::class, HttpErrorException::class, IOException::class) @Throws(JokeException::class, HttpErrorException::class, IOException::class, IllegalArgumentException::class)
fun getJoke(config: JokeConfig): Joke { fun getJoke(config: JokeConfig): Joke {
return getJoke( return getJoke(
categories = config.categories, categories = config.categories,
@ -373,5 +387,69 @@ class JokeApi {
splitNewLine = config.splitNewLine 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<Joke> {
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<String>(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<String>()
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<Flag>()
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())
)
}
} }
} }

View file

@ -32,6 +32,7 @@
package net.thauvin.erik.jokeapi 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.Category
import net.thauvin.erik.jokeapi.models.Flag import net.thauvin.erik.jokeapi.models.Flag
import net.thauvin.erik.jokeapi.models.Format import net.thauvin.erik.jokeapi.models.Format
@ -73,7 +74,7 @@ class JokeConfig private constructor(
var idRange: IdRange = IdRange(), var idRange: IdRange = IdRange(),
var amount: Int = 1, var amount: Int = 1,
var safe: Boolean = false, var safe: Boolean = false,
var splitNewLine: Boolean = true var splitNewLine: Boolean = false
) { ) {
fun categories(categories: Set<Category>) = apply { this.categories = categories } fun categories(categories: Set<Category>) = apply { this.categories = categories }
fun language(language: Language) = apply { this.language = language } fun language(language: Language) = apply { this.language = language }

View file

@ -38,7 +38,6 @@ package net.thauvin.erik.jokeapi.exceptions
* Sse the [JokeAPI Documentation](https://jokeapi.dev/#errors) for more details. * Sse the [JokeAPI Documentation](https://jokeapi.dev/#errors) for more details.
*/ */
class JokeException @JvmOverloads constructor( class JokeException @JvmOverloads constructor(
val error: Boolean,
val internalError: Boolean, val internalError: Boolean,
val code: Int, val code: Int,
message: String, message: String,
@ -52,7 +51,7 @@ class JokeException @JvmOverloads constructor(
} }
fun debug(): String { 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)" " causedBy=$causedBy, additionalInfo='$additionalInfo', timestamp=$timestamp)"
} }
} }

View file

@ -36,7 +36,6 @@ package net.thauvin.erik.jokeapi.models
* Stores a joke's data. * Stores a joke's data.
*/ */
data class Joke( data class Joke(
val error: Boolean,
val category: Category, val category: Category,
val type: Type, val type: Type,
val joke: List<String>, val joke: List<String>,

View file

@ -41,16 +41,16 @@ enum class Language(val value: String) {
CZECH("cs"), CZECH("cs"),
CS(CZECH.value), CS(CZECH.value),
GERMAN("de"), GERMAN("de"),
DE(GERMAN.value), DE(GERMAN.value),
SPANISH("es"), SPANISH("es"),
ES(SPANISH.value), ES(SPANISH.value),
FRENCH("fr"), FRENCH("fr"),
FR(FRENCH.value), FR(FRENCH.value),
PORTUGUESE("pt"), PORTUGUESE("pt"),
PT(PORTUGUESE.value) PT(PORTUGUESE.value)
} }

View file

@ -41,7 +41,6 @@ import assertk.assertions.isGreaterThan
import assertk.assertions.isNotEmpty import assertk.assertions.isNotEmpty
import assertk.assertions.isNotNull import assertk.assertions.isNotNull
import assertk.assertions.isNull import assertk.assertions.isNull
import assertk.assertions.isTrue
import assertk.assertions.prop import assertk.assertions.prop
import assertk.assertions.size import assertk.assertions.size
import assertk.assertions.startsWith import assertk.assertions.startsWith
@ -70,7 +69,6 @@ internal class ExceptionsTest {
logger.fine(e.debug()) logger.fine(e.debug())
assertThat(e, "getJoke(${Category.CHRISTMAS},foo)").all { assertThat(e, "getJoke(${Category.CHRISTMAS},foo)").all {
prop(JokeException::code).isEqualTo(106) prop(JokeException::code).isEqualTo(106)
prop(JokeException::error).isTrue()
prop(JokeException::internalError).isFalse() prop(JokeException::internalError).isFalse()
prop(JokeException::message).isEqualTo("No matching joke found") prop(JokeException::message).isEqualTo("No matching joke found")
prop(JokeException::causedBy).size().isEqualTo(1) prop(JokeException::causedBy).size().isEqualTo(1)

View file

@ -36,14 +36,16 @@ import assertk.all
import assertk.assertThat import assertk.assertThat
import assertk.assertions.any import assertk.assertions.any
import assertk.assertions.contains import assertk.assertions.contains
import assertk.assertions.containsNone
import assertk.assertions.each
import assertk.assertions.isBetween import assertk.assertions.isBetween
import assertk.assertions.isEmpty import assertk.assertions.isEmpty
import assertk.assertions.isEqualTo import assertk.assertions.isEqualTo
import assertk.assertions.isFalse
import assertk.assertions.isGreaterThan import assertk.assertions.isGreaterThan
import assertk.assertions.isGreaterThanOrEqualTo import assertk.assertions.isGreaterThanOrEqualTo
import assertk.assertions.isIn import assertk.assertions.isIn
import assertk.assertions.isNotEmpty import assertk.assertions.isNotEmpty
import assertk.assertions.isNotNull
import assertk.assertions.isTrue import assertk.assertions.isTrue
import assertk.assertions.prop import assertk.assertions.prop
import assertk.assertions.size import assertk.assertions.size
@ -68,7 +70,6 @@ internal class GetJokeTest {
val joke = getJoke() val joke = getJoke()
logger.fine(joke.toString()) logger.fine(joke.toString())
assertThat(joke, "getJoke()").all { assertThat(joke, "getJoke()").all {
prop(Joke::error).isFalse()
prop(Joke::joke).isNotEmpty() prop(Joke::joke).isNotEmpty()
prop(Joke::type).isIn(Type.SINGLE, Type.TWOPART) prop(Joke::type).isIn(Type.SINGLE, Type.TWOPART)
prop(Joke::id).isGreaterThanOrEqualTo(0) prop(Joke::id).isGreaterThanOrEqualTo(0)
@ -115,9 +116,8 @@ internal class GetJokeTest {
@Test @Test
fun `Get Joke with invalid ID Range`() { fun `Get Joke with invalid ID Range`() {
val idRange = IdRange(100, 1) val idRange = IdRange(100, 1)
val joke = getJoke(idRange = idRange) val e = assertThrows<IllegalArgumentException> { getJoke(idRange = idRange, language = Language.DE) }
logger.fine(joke.toString()) assertThat(e::message).isNotNull().contains("100, 1")
assertThat(joke::error).isFalse()
} }
@Test @Test
@ -125,7 +125,6 @@ internal class GetJokeTest {
val idRange = IdRange(1, 30000) val idRange = IdRange(1, 30000)
val e = assertThrows<JokeException> { getJoke(idRange = idRange) } val e = assertThrows<JokeException> { getJoke(idRange = idRange) }
assertThat(e, "getJoke{${idRange})").all { assertThat(e, "getJoke{${idRange})").all {
prop(JokeException::error).isTrue()
prop(JokeException::additionalInfo).contains("ID range") prop(JokeException::additionalInfo).contains("ID range")
} }
} }
@ -153,17 +152,22 @@ internal class GetJokeTest {
fun `Get Joke with each Languages`() { fun `Get Joke with each Languages`() {
Language.values().forEach { Language.values().forEach {
val joke = getJoke(language = it) val joke = getJoke(language = it)
logger.fine(joke.toString())
assertThat(joke::language, "getJoke($it)").prop(Language::value).isEqualTo(it.value) assertThat(joke::language, "getJoke($it)").prop(Language::value).isEqualTo(it.value)
} }
} }
@Test @Test
fun `Get Joke with Newline`() { fun `Get Joke with Split Newline`() {
val joke = getJoke( 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 { logger.fine(joke.toString())
it.contains("\n") assertThat(joke::joke, "getJoke(splitNewLine=true)").all {
size().isEqualTo(2)
each {
containsNone("\n")
}
} }
} }

View file

@ -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<IllegalArgumentException> { 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
}
}
}
}

View file

@ -37,7 +37,7 @@ import assertk.assertThat
import assertk.assertions.doesNotContain import assertk.assertions.doesNotContain
import assertk.assertions.isNotEmpty import assertk.assertions.isNotEmpty
import assertk.assertions.startsWith 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.JokeApi.Companion.logger
import net.thauvin.erik.jokeapi.models.Format import net.thauvin.erik.jokeapi.models.Format
import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.BeforeAll
@ -46,37 +46,31 @@ import java.util.logging.ConsoleHandler
import java.util.logging.Level import java.util.logging.Level
import kotlin.test.assertContains import kotlin.test.assertContains
internal class GetRawJokeTest { internal class GetRawJokesTest {
@Test @Test
fun `Get Raw Joke with TXT`() { fun `Get Raw Joke with TXT`() {
val response = getRawJoke(format = Format.TEXT) val response = getRawJokes(format = Format.TEXT)
assertThat(response, "getRawJoke(txt)").all { assertThat(response, "getRawJoke(txt)").all {
isNotEmpty() isNotEmpty()
doesNotContain("Error") doesNotContain("Error")
} }
} }
@Test
fun `Get Raw Joke with invalid Amount`() {
val response = getRawJoke(amount = 100)
assertThat(response, "getRawJoke(100)").doesNotContain("\"amount\":")
}
@Test @Test
fun `Get Raw Joke with XML`() { fun `Get Raw Joke with XML`() {
val response = getRawJoke(format = Format.XML) val response = getRawJokes(format = Format.XML)
assertThat(response, "getRawJoke(xml)").startsWith("<?xml version='1.0'?>\n<data>\n <error>false</error>") assertThat(response, "getRawJoke(xml)").startsWith("<?xml version='1.0'?>\n<data>\n <error>false</error>")
} }
@Test @Test
fun `Get Raw Joke with YAML`() { 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") assertThat(response, "getRawJoke(yaml)").startsWith("error: false")
} }
@Test @Test
fun `Get Raw Jokes`() { fun `Get Raw Jokes`() {
val response = getRawJoke(amount = 2) val response = getRawJokes(amount = 2)
assertContains(response, "\"amount\": 2", false, "getRawJoke(2)") assertContains(response, "\"amount\": 2", false, "getRawJoke(2)")
} }

View file

@ -34,13 +34,17 @@ package net.thauvin.erik.jokeapi
import assertk.all import assertk.all
import assertk.assertThat import assertk.assertThat
import assertk.assertions.each
import assertk.assertions.isBetween import assertk.assertions.isBetween
import assertk.assertions.isEmpty import assertk.assertions.isEmpty
import assertk.assertions.isEqualTo import assertk.assertions.isEqualTo
import assertk.assertions.isGreaterThanOrEqualTo
import assertk.assertions.isTrue
import assertk.assertions.prop import assertk.assertions.prop
import assertk.assertions.size import assertk.assertions.size
import net.thauvin.erik.jokeapi.JokeApi.Companion.getJoke 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.JokeApi.Companion.logger
import net.thauvin.erik.jokeapi.models.Category import net.thauvin.erik.jokeapi.models.Category
import net.thauvin.erik.jokeapi.models.Flag import net.thauvin.erik.jokeapi.models.Flag
@ -66,7 +70,6 @@ class JokeConfigTest {
type(Type.TWOPART) type(Type.TWOPART)
idRange(IdRange(id - 2, id + 2)) idRange(IdRange(id - 2, id + 2))
safe(true) safe(true)
splitNewLine(false)
}.build() }.build()
val joke = getJoke(config) val joke = getJoke(config)
logger.fine(joke.toString()) 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 @Test
fun `Get Raw Joke with Builder`() { fun `Get Raw Joke with Builder`() {
val config = JokeConfig.Builder().apply { val config = JokeConfig.Builder().apply {
@ -89,10 +108,30 @@ class JokeConfigTest {
amount(2) amount(2)
safe(true) safe(true)
}.build() }.build()
val joke = getRawJoke(config) val joke = getRawJokes(config)
assertContains(joke, "----------------------------------------------", false, "config.amount(2)") 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 { companion object {
@JvmStatic @JvmStatic
@BeforeAll @BeforeAll