Add mock tests
This commit is contained in:
parent
1ec08e732b
commit
1ad447d661
34 changed files with 816 additions and 226 deletions
|
@ -84,6 +84,8 @@
|
|||
<ID>TooManyFunctions:Tell.kt$Tell : AbstractCommand</ID>
|
||||
<ID>UtilityClassWithPublicConstructor:LocalProperties.kt$LocalProperties</ID>
|
||||
<ID>WildcardImport:AddonsTest.kt$import net.thauvin.erik.mobibot.modules.*</ID>
|
||||
<ID>WildcardImport:CryptoPricesTest.kt$import assertk.assertions.*</ID>
|
||||
<ID>WildcardImport:CurrencyConverterTest.kt$import assertk.assertions.*</ID>
|
||||
<ID>WildcardImport:EntryLinkTest.kt$import assertk.assertions.*</ID>
|
||||
<ID>WildcardImport:FeedMgrTest.kt$import assertk.assertions.*</ID>
|
||||
<ID>WildcardImport:FeedReaderTest.kt$import assertk.assertions.*</ID>
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -4,7 +4,7 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>net.thauvin.erik.mobibot</groupId>
|
||||
<artifactId>mobibot</artifactId>
|
||||
<version>0.8.0-rc+20250506091211</version>
|
||||
<version>0.8.0-rc+20250509073816</version>
|
||||
<name>mobibot</name>
|
||||
<description></description>
|
||||
<url></url>
|
||||
|
|
|
@ -125,7 +125,12 @@ public class MobibotBuild extends Project {
|
|||
.include(dependency("net.thauvin.erik", "pinboard-poster", "1.2.1-SNAPSHOT"))
|
||||
.include(dependency("net.thauvin.erik.urlencoder", "urlencoder-lib-jvm", "1.6.0"));
|
||||
scope(test)
|
||||
// Mockito
|
||||
.include(dependency("net.bytebuddy", "byte-buddy", version(1, 17, 5)))
|
||||
.include(dependency("org.mockito.kotlin", "mockito-kotlin", version(5, 4, 0)))
|
||||
// AssertK
|
||||
.include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 28, 1)))
|
||||
// JUnit
|
||||
.include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", kotlin))
|
||||
.include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 12, 2)))
|
||||
.include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 12, 2)))
|
||||
|
|
|
@ -14,12 +14,12 @@ import java.time.ZoneId
|
|||
*/
|
||||
object ReleaseInfo {
|
||||
const val PROJECT = "mobibot"
|
||||
const val VERSION = "0.8.0-rc+20250507140607"
|
||||
const val VERSION = "0.8.0-rc+20250509075545"
|
||||
|
||||
@JvmField
|
||||
@Suppress("MagicNumber")
|
||||
val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant(
|
||||
Instant.ofEpochMilli(1746651967388L), ZoneId.systemDefault()
|
||||
Instant.ofEpochMilli(1746802545281L), ZoneId.systemDefault()
|
||||
)
|
||||
|
||||
const val WEBSITE = "https://mobitopia.org/mobibot/"
|
||||
|
|
|
@ -73,11 +73,11 @@ class Calc : AbstractModule() {
|
|||
if (args.isNotBlank()) {
|
||||
try {
|
||||
event.respond(calculate(args))
|
||||
} catch (e: IllegalArgumentException) {
|
||||
if (logger.isWarnEnabled) logger.warn("Failed to calculate: $args", e)
|
||||
event.respond("No idea. This is the kind of math I don't get.")
|
||||
} catch (e: UnknownFunctionOrVariableException) {
|
||||
if (logger.isWarnEnabled) logger.warn("Unable to calculate: $args", e)
|
||||
event.respond("No idea. This is the kind of math I don't get.")
|
||||
} catch (e: IllegalArgumentException) {
|
||||
if (logger.isWarnEnabled) logger.warn("Failed to calculate: $args", e)
|
||||
event.respond("No idea. I must've some form of Dyscalculia.")
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -50,47 +50,6 @@ class CryptoPrices : AbstractModule() {
|
|||
|
||||
override val name = "CryptoPrices"
|
||||
|
||||
/**
|
||||
* Returns the cryptocurrency market price from
|
||||
* [Coinbase](https://docs.cdp.coinbase.com/coinbase-app/docs/api-prices#get-spot-price).
|
||||
*/
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
if (CURRENCIES.isEmpty()) {
|
||||
try {
|
||||
loadCurrencies()
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||
}
|
||||
}
|
||||
|
||||
val debugMessage = "crypto($cmd $args)"
|
||||
if (args == CODES_KEYWORD) {
|
||||
event.sendMessage("The supported currencies are:")
|
||||
event.sendList(ArrayList(CURRENCIES.keys), 10, isIndent = true)
|
||||
} else if (args.matches("\\w+( [a-zA-Z]{3}+)?".toRegex())) {
|
||||
try {
|
||||
val price = currentPrice(args.split(' '))
|
||||
val amount = try {
|
||||
price.toCurrency()
|
||||
} catch (_: IllegalArgumentException) {
|
||||
price.amount
|
||||
}
|
||||
event.respond("${price.base} current price is $amount [${CURRENCIES[price.currency]}]")
|
||||
} catch (e: CryptoException) {
|
||||
if (logger.isWarnEnabled) logger.warn("$debugMessage => ${e.statusCode}", e)
|
||||
e.message?.let {
|
||||
event.respond(it)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
if (logger.isErrorEnabled) logger.error(debugMessage, e)
|
||||
event.respond("An IO error has occurred while retrieving the cryptocurrency market price.")
|
||||
}
|
||||
} else {
|
||||
helpResponse(event)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Crypto command
|
||||
private const val CRYPTO_CMD = "crypto"
|
||||
|
@ -101,6 +60,9 @@ class CryptoPrices : AbstractModule() {
|
|||
// Currency codes keyword
|
||||
private const val CODES_KEYWORD = "codes"
|
||||
|
||||
// Default error message
|
||||
const val DEFAULT_ERROR_MESSAGE = "An error has occurred while retrieving the cryptocurrency market price"
|
||||
|
||||
/**
|
||||
* Get the current market price.
|
||||
*/
|
||||
|
@ -156,4 +118,47 @@ class CryptoPrices : AbstractModule() {
|
|||
}
|
||||
loadCurrencies()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cryptocurrency market price from
|
||||
* [Coinbase](https://docs.cdp.coinbase.com/coinbase-app/docs/api-prices#get-spot-price).
|
||||
*/
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
if (CURRENCIES.isEmpty()) {
|
||||
try {
|
||||
loadCurrencies()
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||
}
|
||||
}
|
||||
|
||||
val debugMessage = "crypto($cmd $args)"
|
||||
if (args == CODES_KEYWORD) {
|
||||
event.sendMessage("The supported currencies are:")
|
||||
event.sendList(ArrayList(CURRENCIES.keys), 10, isIndent = true)
|
||||
} else if (args.matches("\\w+( [a-zA-Z]{3}+)?".toRegex())) {
|
||||
try {
|
||||
val price = currentPrice(args.split(' '))
|
||||
val amount = try {
|
||||
price.toCurrency()
|
||||
} catch (_: IllegalArgumentException) {
|
||||
price.amount
|
||||
}
|
||||
event.respond("${price.base} current price is $amount [${CURRENCIES[price.currency]}]")
|
||||
} catch (e: CryptoException) {
|
||||
if (logger.isWarnEnabled) logger.warn("$debugMessage => ${e.statusCode}", e)
|
||||
if (e.message != null) {
|
||||
event.respond("$DEFAULT_ERROR_MESSAGE: ${e.message}")
|
||||
} else {
|
||||
event.respond("$DEFAULT_ERROR_MESSAGE.")
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
if (logger.isErrorEnabled) logger.error(debugMessage, e)
|
||||
event.respond("$DEFAULT_ERROR_MESSAGE: ${e.message}")
|
||||
}
|
||||
} else {
|
||||
helpResponse(event)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,6 @@ import java.net.URL
|
|||
import java.text.DecimalFormat
|
||||
import java.util.*
|
||||
|
||||
|
||||
/**
|
||||
* Converts between currencies.
|
||||
*/
|
||||
|
@ -79,13 +78,19 @@ class CurrencyConverter : AbstractModule() {
|
|||
// Currency symbols
|
||||
private val SYMBOLS: TreeMap<String, String> = TreeMap()
|
||||
|
||||
/**
|
||||
* No API key error message.
|
||||
*/
|
||||
const val ERROR_MESSAGE_NO_API_KEY = "No Exchange Rate API key specified."
|
||||
|
||||
|
||||
/**
|
||||
* Converts from a currency to another.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun convertCurrency(apiKey: String?, query: String): Message {
|
||||
if (apiKey.isNullOrEmpty()) {
|
||||
throw ModuleException("${CURRENCY_CMD}($query)", "No Exchange Rate API key specified.")
|
||||
throw ModuleException("${CURRENCY_CMD}($query)", ERROR_MESSAGE_NO_API_KEY)
|
||||
}
|
||||
|
||||
val cmds = query.split(" ")
|
||||
|
@ -175,17 +180,22 @@ class CurrencyConverter : AbstractModule() {
|
|||
*/
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
reload(properties[API_KEY_PROP])
|
||||
|
||||
when {
|
||||
SYMBOLS.isEmpty() -> {
|
||||
event.respond(EMPTY_SYMBOLS_TABLE)
|
||||
}
|
||||
|
||||
args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ (to|in) [a-zA-Z]{3}+".toRegex()) -> {
|
||||
val msg = convertCurrency(properties[API_KEY_PROP], args)
|
||||
event.respond(msg.msg)
|
||||
if (msg.isError) {
|
||||
helpResponse(event)
|
||||
try {
|
||||
val msg = convertCurrency(properties[API_KEY_PROP], args)
|
||||
if (msg.isError) {
|
||||
helpResponse(event)
|
||||
} else {
|
||||
event.respond(msg.msg)
|
||||
}
|
||||
} catch (e: ModuleException) {
|
||||
if (LOGGER.isWarnEnabled) LOGGER.warn(e.debugMessage, e)
|
||||
event.respond(e.message)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -130,6 +130,9 @@ class Gemini2 : AbstractModule() {
|
|||
e.message?.let {
|
||||
event.respond(it)
|
||||
}
|
||||
} catch (e: NumberFormatException) {
|
||||
if (logger.isErrorEnabled) logger.error("Invalid $MAX_TOKENS_PROP property.", e)
|
||||
event.respond("The $name module is misconfigured.")
|
||||
}
|
||||
} else {
|
||||
helpResponse(event)
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.ReleaseInfo
|
||||
import net.thauvin.erik.mobibot.Utils.capitalize
|
||||
import net.thauvin.erik.mobibot.Utils.colorize
|
||||
import net.thauvin.erik.mobibot.Utils.encodeUrl
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
|
@ -56,15 +55,24 @@ import java.net.URL
|
|||
class GoogleSearch : AbstractModule() {
|
||||
private val logger: Logger = LoggerFactory.getLogger(GoogleSearch::class.java)
|
||||
|
||||
override val name = "GoogleSearch"
|
||||
override val name = SERVICE_NAME
|
||||
|
||||
companion object {
|
||||
// Google API Key property
|
||||
/**
|
||||
* API Key property
|
||||
*/
|
||||
const val API_KEY_PROP = "google-api-key"
|
||||
|
||||
// Google Custom Search Engine ID property
|
||||
/**
|
||||
* Google Custom Search Engine ID property
|
||||
*/
|
||||
const val CSE_KEY_PROP = "google-cse-cx"
|
||||
|
||||
/**
|
||||
* The service name
|
||||
*/
|
||||
const val SERVICE_NAME = "GoogleSearch"
|
||||
|
||||
// Google command
|
||||
private const val GOOGLE_CMD = "google"
|
||||
|
||||
|
@ -82,7 +90,7 @@ class GoogleSearch : AbstractModule() {
|
|||
if (apiKey.isNullOrBlank() || cseKey.isNullOrBlank()) {
|
||||
throw ModuleException(
|
||||
"${GoogleSearch::class.java.name} is disabled.",
|
||||
"${GOOGLE_CMD.capitalize()} is disabled. The API keys are missing."
|
||||
"$SERVICE_NAME is disabled. The API keys are missing."
|
||||
)
|
||||
}
|
||||
val results = mutableListOf<Message>()
|
||||
|
|
|
@ -89,16 +89,14 @@ class Joke : AbstractModule() {
|
|||
* Returns a random joke from [JokeAPI](https://v2.jokeapi.dev/).
|
||||
*/
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
with(event.bot()) {
|
||||
try {
|
||||
randomJoke().forEach {
|
||||
sendIRC().notice(channel, it.msg.colorize(it.color))
|
||||
}
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||
e.message?.let {
|
||||
event.respond(it)
|
||||
}
|
||||
try {
|
||||
randomJoke().forEach {
|
||||
event.bot().sendIRC().notice(channel, it.msg.colorize(it.color))
|
||||
}
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||
e.message?.let {
|
||||
event.respond(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,11 +74,18 @@ class Mastodon : SocialModule() {
|
|||
*/
|
||||
@JvmStatic
|
||||
@Throws(ModuleException::class)
|
||||
fun toot(apiKey: String?, instance: String?, handle: String?, message: String, isDm: Boolean): String {
|
||||
fun toot(accessToken: String?, instance: String?, handle: String?, message: String, isDm: Boolean): String {
|
||||
if (accessToken.isNullOrBlank()) {
|
||||
throw ModuleException("Missing access token", "The access token is missing.")
|
||||
} else if (instance.isNullOrBlank()) {
|
||||
throw ModuleException("Missing instance", "The Mastodon instance is missing.")
|
||||
} else if (isDm && handle.isNullOrBlank()) {
|
||||
throw ModuleException("Missing handle", "The Mastodon handle is missing.")
|
||||
}
|
||||
val request = HttpRequest.newBuilder()
|
||||
.uri(URI.create("https://$instance/api/v1/statuses"))
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Authorization", "Bearer $apiKey")
|
||||
.header("Authorization", "Bearer $accessToken")
|
||||
.POST(
|
||||
HttpRequest.BodyPublishers.ofString(
|
||||
JSONWriter.valueToString(
|
||||
|
@ -89,8 +96,7 @@ class Mastodon : SocialModule() {
|
|||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
.build()
|
||||
).build()
|
||||
try {
|
||||
val response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString())
|
||||
if (response.statusCode() == 200) {
|
||||
|
@ -105,7 +111,7 @@ class Mastodon : SocialModule() {
|
|||
throw ModuleException("mastodonPost($message)", "A JSON error has occurred: ${e.message}", e)
|
||||
}
|
||||
} else {
|
||||
throw IOException("Status Code: " + response.statusCode())
|
||||
throw IOException("HTTP Status Code: " + response.statusCode())
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw ModuleException("mastodonPost($message)", "An IO error has occurred: ${e.message}", e)
|
||||
|
@ -142,7 +148,7 @@ class Mastodon : SocialModule() {
|
|||
@Throws(ModuleException::class)
|
||||
override fun post(message: String, isDm: Boolean): String {
|
||||
return toot(
|
||||
apiKey = properties[ACCESS_TOKEN_PROP],
|
||||
accessToken = properties[ACCESS_TOKEN_PROP],
|
||||
instance = properties[INSTANCE_PROP],
|
||||
handle = handle,
|
||||
message = message,
|
||||
|
|
|
@ -96,7 +96,7 @@ class RockPaperScissors : AbstractModule() {
|
|||
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
val hand = Hands.valueOf(cmd.uppercase())
|
||||
val botHand = Hands.entries[(0..Hands.entries.size).random()]
|
||||
val botHand = Hands.entries[(0..Hands.entries.size - 1).random()]
|
||||
when {
|
||||
hand == botHand -> {
|
||||
event.respond("${hand.name} vs. ${botHand.name} » You ${"tie".bold()}.")
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import net.thauvin.erik.mobibot.Utils.capitalize
|
||||
import net.thauvin.erik.mobibot.Utils.encodeUrl
|
||||
import net.thauvin.erik.mobibot.Utils.helpFormat
|
||||
import net.thauvin.erik.mobibot.Utils.reader
|
||||
|
@ -54,7 +53,7 @@ import java.net.URL
|
|||
class StockQuote : AbstractModule() {
|
||||
private val logger: Logger = LoggerFactory.getLogger(StockQuote::class.java)
|
||||
|
||||
override val name = "StockQuote"
|
||||
override val name = SERVICE_NAME
|
||||
|
||||
companion object {
|
||||
/**
|
||||
|
@ -67,6 +66,11 @@ class StockQuote : AbstractModule() {
|
|||
*/
|
||||
const val INVALID_SYMBOL = "Invalid symbol."
|
||||
|
||||
/**
|
||||
* The service name.
|
||||
*/
|
||||
const val SERVICE_NAME = "StockQuote"
|
||||
|
||||
// API URL
|
||||
private const val API_URL = "https://www.alphavantage.co/query?function="
|
||||
|
||||
|
@ -111,8 +115,8 @@ class StockQuote : AbstractModule() {
|
|||
fun getQuote(symbol: String, apiKey: String?): List<Message> {
|
||||
if (apiKey.isNullOrBlank()) {
|
||||
throw ModuleException(
|
||||
"${StockQuote::class.java.name} is disabled.",
|
||||
"${STOCK_CMD.capitalize()} is disabled. The API key is missing."
|
||||
"$SERVICE_NAME is disabled.",
|
||||
"$SERVICE_NAME is disabled. The API key is missing."
|
||||
)
|
||||
}
|
||||
val messages = mutableListOf<Message>()
|
||||
|
|
|
@ -56,7 +56,7 @@ import kotlin.math.roundToInt
|
|||
class Weather2 : AbstractModule() {
|
||||
private val logger: Logger = LoggerFactory.getLogger(Weather2::class.java)
|
||||
|
||||
override val name = "Weather"
|
||||
override val name = WEATHER_NAME
|
||||
|
||||
companion object {
|
||||
/**
|
||||
|
@ -64,6 +64,11 @@ class Weather2 : AbstractModule() {
|
|||
*/
|
||||
const val API_KEY_PROP = "owm-api-key"
|
||||
|
||||
/**
|
||||
* The service name.
|
||||
*/
|
||||
const val WEATHER_NAME = "Weather"
|
||||
|
||||
// Weather command
|
||||
private const val WEATHER_CMD = "weather"
|
||||
|
||||
|
|
|
@ -42,25 +42,40 @@ import org.slf4j.LoggerFactory
|
|||
import java.io.IOException
|
||||
import java.net.URL
|
||||
|
||||
/**
|
||||
* Allows user to query Wolfram Alpha.
|
||||
*/
|
||||
class WolframAlpha : AbstractModule() {
|
||||
private val logger: Logger = LoggerFactory.getLogger(WolframAlpha::class.java)
|
||||
|
||||
override val name = "WolframAlpha"
|
||||
override val name = SERVICE_NAME
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* The Wolfram Alpha API Key property.
|
||||
* The Wolfram Alpha AppID property.
|
||||
*/
|
||||
const val APPID_KEY_PROP = "wolfram-appid"
|
||||
|
||||
/**
|
||||
* Metric unit
|
||||
*/
|
||||
const val METRIC = "metric"
|
||||
|
||||
/**
|
||||
* Imperial unit
|
||||
*/
|
||||
const val IMPERIAL = "imperial"
|
||||
|
||||
/**
|
||||
* The service name.
|
||||
*/
|
||||
const val SERVICE_NAME = "WolframAlpha"
|
||||
|
||||
/**
|
||||
* The Wolfram units properties
|
||||
*/
|
||||
const val UNITS_PROP = "wolfram-units"
|
||||
|
||||
const val METRIC = "metric"
|
||||
const val IMPERIAL = "imperial"
|
||||
|
||||
// Wolfram command
|
||||
private const val WOLFRAM_CMD = "wolfram"
|
||||
|
||||
|
@ -79,17 +94,17 @@ class WolframAlpha : AbstractModule() {
|
|||
throw ModuleException(
|
||||
"wolfram($query): ${urlReader.responseCode} : ${urlReader.body} ",
|
||||
urlReader.body.ifEmpty {
|
||||
"Looks like Wolfram Alpha isn't able to answer that. (${urlReader.responseCode})"
|
||||
"Looks like $SERVICE_NAME isn't able to answer that. (${urlReader.responseCode})"
|
||||
}
|
||||
)
|
||||
}
|
||||
} catch (ioe: IOException) {
|
||||
throw ModuleException(
|
||||
"wolfram($query): IOE", "An IO Error occurred while querying Wolfram Alpha.", ioe
|
||||
"wolfram($query): IOE", "An IO Error occurred while querying $SERVICE_NAME.", ioe
|
||||
)
|
||||
}
|
||||
} else {
|
||||
throw ModuleException("wolfram($query): No API Key", "No Wolfram Alpha API key specified.")
|
||||
throw ModuleException("wolfram($query): No API Key", "No $SERVICE_NAME AppID specified.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,9 @@ class PinboardTest : LocalProperties() {
|
|||
private val pinboard = Pinboard().apply { setApiToken(apiToken) }
|
||||
|
||||
private fun newEntry(): EntryLink {
|
||||
return EntryLink(randomUrl(), "Test Example", "ErikT", "", "#mobitopia", listOf("test"))
|
||||
return EntryLink(
|
||||
randomUrl(), "Test Example", "ErikT", "", "#mobitopia", listOf("test")
|
||||
)
|
||||
}
|
||||
|
||||
private fun randomUrl(): String {
|
||||
|
@ -55,7 +57,9 @@ class PinboardTest : LocalProperties() {
|
|||
|
||||
private fun validatePin(apiToken: String, url: String, vararg matches: String): Boolean {
|
||||
val response =
|
||||
URL("https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" + url.encodeUrl()).reader().body
|
||||
URL(
|
||||
"https://api.pinboard.in/v1/posts/get?auth_token=${apiToken}&tag=test&" + url.encodeUrl()
|
||||
).reader().body
|
||||
|
||||
matches.forEach {
|
||||
if (!response.contains(it)) {
|
||||
|
|
|
@ -162,6 +162,7 @@ class UtilsTest {
|
|||
assertThat(p.getIntProperty("one", 9), "getIntProperty(one)").isEqualTo(1)
|
||||
assertThat(p.getIntProperty("two", 2), "getIntProperty(two)").isEqualTo(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Convert property to int using default value`() {
|
||||
assertThat(p.getIntProperty("foo", 3), "getIntProperty(foo)").isEqualTo(3)
|
||||
|
@ -177,6 +178,7 @@ class UtilsTest {
|
|||
val two = listOf("1", "2")
|
||||
assertThat(two.lastOrEmpty(), "lastOrEmpty(1,2)").isEqualTo("2")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Return empty if list only has one item`() {
|
||||
val one = listOf("1")
|
||||
|
@ -258,6 +260,7 @@ class UtilsTest {
|
|||
fun `Convert string to int`() {
|
||||
assertThat("10".toIntOrDefault(1), "toIntOrDefault(10, 1)").isEqualTo(10)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Convert string to int using default value`() {
|
||||
assertThat("a".toIntOrDefault(2), "toIntOrDefault(a, 2)").isEqualTo(2)
|
||||
|
@ -314,11 +317,13 @@ class UtilsTest {
|
|||
fun `Replace occurrences not found in string`() {
|
||||
assertThat(test.replaceEach(search, replace), "replaceEach(nothing)").isEqualTo(test)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Replace and remove occurrences in string`() {
|
||||
assertThat(test.replaceEach(arrayOf("t", "e"), arrayOf("", "E")), "replaceEach($test)")
|
||||
.isEqualTo(test.replace("t", "").replace("e", "E"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Replace empty occurrences in string`() {
|
||||
assertThat(test.replaceEach(search, emptyArray()), "replaceEach(search, empty)")
|
||||
|
|
|
@ -37,37 +37,70 @@ import assertk.assertions.isInstanceOf
|
|||
import net.objecthunter.exp4j.tokenizer.UnknownFunctionOrVariableException
|
||||
import net.thauvin.erik.mobibot.Utils.bold
|
||||
import net.thauvin.erik.mobibot.modules.Calc.Companion.calculate
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.mockito.Mockito
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import kotlin.test.Test
|
||||
|
||||
class CalcTest {
|
||||
@Test
|
||||
fun `Calculate basic addition`() {
|
||||
assertThat(calculate("1 + 1"), "calculate(1+1)").isEqualTo("1+1 = ${2.bold()}")
|
||||
@Nested
|
||||
@DisplayName("Calculate Tests")
|
||||
inner class CalculateTests {
|
||||
@Test
|
||||
fun `Calculate basic addition`() {
|
||||
assertThat(calculate("1 + 1"), "calculate(1+1)").isEqualTo("1+1 = ${2.bold()}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Calculate basic subtraction`() {
|
||||
assertThat(calculate("1 -3"), "calculate(1-3)").isEqualTo("1-3 = ${(-2).bold()}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Calculate mathematical constants`() {
|
||||
assertThat(calculate("pi+π+e+φ"), "calculate(pi+π+e+φ)").isEqualTo("pi+π+e+φ = ${"10.62".bold()}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Calculate scientific notations`() {
|
||||
assertThat(calculate("3e2 - 3e1"), "calculate(3e2-3e1 )").isEqualTo("3e2-3e1 = ${"270".bold()}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Calculate trigonometric functions`() {
|
||||
assertThat(calculate("3*sin(10)-cos(2)"), "calculate(3*sin(10)-cos(2)")
|
||||
.isEqualTo("3*sin(10)-cos(2) = ${"-1.22".bold()}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Empty calculation should throw exception`() {
|
||||
assertFailure { calculate(" ") }.isInstanceOf(IllegalArgumentException::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Invalid calculation should throw exception`() {
|
||||
assertFailure { calculate("a + b = c") }.isInstanceOf(UnknownFunctionOrVariableException::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Calculate basic subtraction`() {
|
||||
assertThat(calculate("1 -3"), "calculate(1-3)").isEqualTo("1-3 = ${(-2).bold()}")
|
||||
}
|
||||
@Nested
|
||||
@DisplayName("Command Response Tests")
|
||||
inner class CommandResponseTests {
|
||||
@Test
|
||||
fun `Basic calculation`() {
|
||||
val calc = Calc()
|
||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||
calc.commandResponse("channel", "calc", "1 + 1 * 2", event)
|
||||
Mockito.verify(event, Mockito.times(1)).respond("1+1*2 = ${"3".bold()}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Calculate mathematical constants`() {
|
||||
assertThat(calculate("pi+π+e+φ"), "calculate(pi+π+e+φ)").isEqualTo("pi+π+e+φ = ${"10.62".bold()}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Calculate scientific notations`() {
|
||||
assertThat(calculate("3e2 - 3e1"), "calculate(3e2-3e1 )").isEqualTo("3e2-3e1 = ${"270".bold()}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Calculate trigonometric functions`() {
|
||||
assertThat(calculate("3*sin(10)-cos(2)"), "calculate(3*sin(10)-cos(2)")
|
||||
.isEqualTo("3*sin(10)-cos(2) = ${"-1.22".bold()}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Invalid calculation show throw exception`() {
|
||||
assertFailure { calculate("one + one") }.isInstanceOf(UnknownFunctionOrVariableException::class.java)
|
||||
@Test
|
||||
fun `Invalid calculation`() {
|
||||
val calc = Calc()
|
||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||
calc.commandResponse("channel", "calc", "two + two", event)
|
||||
Mockito.verify(event, Mockito.times(1)).respond("No idea. This is the kind of math I don't get.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,26 +32,45 @@ package net.thauvin.erik.mobibot.modules
|
|||
|
||||
import assertk.assertFailure
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.hasNoCause
|
||||
import assertk.assertions.isInstanceOf
|
||||
import assertk.assertions.contains
|
||||
import assertk.assertions.hasNoCause
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isInstanceOf
|
||||
import net.thauvin.erik.mobibot.DisableOnCi
|
||||
import net.thauvin.erik.mobibot.LocalProperties
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mockito
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import kotlin.test.Test
|
||||
|
||||
class ChatGpt2Test : LocalProperties() {
|
||||
@Test
|
||||
fun apiKey() {
|
||||
assertFailure { ChatGpt2.chat("1 gallon to liter", "", 0) }
|
||||
.isInstanceOf(ModuleException::class.java)
|
||||
.hasNoCause()
|
||||
@Nested
|
||||
@DisplayName("Command Response Tests")
|
||||
inner class CommandResponseTests {
|
||||
@Test
|
||||
fun moduleMisconfigured() {
|
||||
val chatGpt2 = ChatGpt2()
|
||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||
val captor = ArgumentCaptor.forClass(String::class.java)
|
||||
|
||||
chatGpt2.commandResponse("channel", "chatgpt", "1 liter to gallon", event)
|
||||
|
||||
Mockito.verify(event, Mockito.atLeastOnce()).respond(captor.capture())
|
||||
assertThat(captor.value).isEqualTo("The ${ChatGpt2.CHATGPT_NAME} module is misconfigured.")
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Chat Tests")
|
||||
inner class ChatTests {
|
||||
@Test
|
||||
fun apiKey() {
|
||||
assertFailure { ChatGpt2.chat("1 gallon to liter", "", 0) }
|
||||
.isInstanceOf(ModuleException::class.java)
|
||||
.hasNoCause()
|
||||
}
|
||||
private val apiKey = getProperty(ChatGpt2.API_KEY_PROP)
|
||||
|
||||
@Test
|
||||
|
|
|
@ -32,9 +32,7 @@ package net.thauvin.erik.mobibot.modules
|
|||
|
||||
import assertk.all
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isGreaterThan
|
||||
import assertk.assertions.prop
|
||||
import assertk.assertions.*
|
||||
import net.thauvin.erik.crypto.CryptoPrice
|
||||
import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.currentPrice
|
||||
import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.getCurrencyName
|
||||
|
@ -42,15 +40,14 @@ import net.thauvin.erik.mobibot.modules.CryptoPrices.Companion.loadCurrencies
|
|||
import org.junit.jupiter.api.BeforeAll
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mockito
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import java.util.logging.ConsoleHandler
|
||||
import java.util.logging.Level
|
||||
import kotlin.test.Test
|
||||
|
||||
class CryptoPricesTest {
|
||||
init {
|
||||
loadCurrencies()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@BeforeAll
|
||||
|
@ -63,12 +60,71 @@ class CryptoPricesTest {
|
|||
}
|
||||
}
|
||||
|
||||
init {
|
||||
loadCurrencies()
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Command Response Tests")
|
||||
inner class CommandResponseTests {
|
||||
@Test
|
||||
fun `Current price for BTC`() {
|
||||
val cryptoPrices = CryptoPrices()
|
||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||
val captor = ArgumentCaptor.forClass(String::class.java)
|
||||
|
||||
cryptoPrices.commandResponse("channel", "crypto", "btc", event)
|
||||
|
||||
Mockito.verify(event, Mockito.times(1)).respond(captor.capture())
|
||||
assertThat(captor.value).startsWith("BTC current price is $")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Current price for BTC in EUR`() {
|
||||
val cryptoPrices = CryptoPrices()
|
||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||
val captor = ArgumentCaptor.forClass(String::class.java)
|
||||
|
||||
cryptoPrices.commandResponse("channel", "crypto", "eth eur", event)
|
||||
|
||||
Mockito.verify(event, Mockito.times(1)).respond(captor.capture())
|
||||
assertThat(captor.value).matches(Regex("ETH current price is €.* \\[Euro]"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Invalid crypto symbol`() {
|
||||
val cryptoPrices = CryptoPrices()
|
||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||
val captor = ArgumentCaptor.forClass(String::class.java)
|
||||
|
||||
cryptoPrices.commandResponse("channel", "crypto", "foo", event)
|
||||
|
||||
Mockito.verify(event, Mockito.times(1)).respond(captor.capture())
|
||||
assertThat(captor.value)
|
||||
.isEqualTo("${CryptoPrices.DEFAULT_ERROR_MESSAGE}: not found")
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Currency Name Tests")
|
||||
inner class CurrencyNameTests {
|
||||
@Test
|
||||
fun `Currency name for USD`() {
|
||||
assertThat(getCurrencyName("USD"), "USD").isEqualTo("United States Dollar")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Currency name for EUR`() {
|
||||
assertThat(getCurrencyName("EUR"), "EUR").isEqualTo("Euro")
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Current Price Tests")
|
||||
inner class CurrentPriceTests {
|
||||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
fun currentPriceBitcoin() {
|
||||
fun `Current price for Bitcoin`() {
|
||||
val price = currentPrice(listOf("BTC"))
|
||||
assertThat(price, "currentPrice(BTC)").all {
|
||||
prop(CryptoPrice::base).isEqualTo("BTC")
|
||||
|
@ -79,7 +135,7 @@ class CryptoPricesTest {
|
|||
|
||||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
fun currentPriceEthereum() {
|
||||
fun `Current price for Ethereum in Euro`() {
|
||||
val price = currentPrice(listOf("ETH", "EUR"))
|
||||
assertThat(price, "currentPrice(ETH, EUR)").all {
|
||||
prop(CryptoPrice::base).isEqualTo("ETH")
|
||||
|
@ -88,18 +144,4 @@ class CryptoPricesTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Currency Name Tests")
|
||||
inner class CurrencyNameTests {
|
||||
@Test
|
||||
fun getCurrencyNameUsd() {
|
||||
assertThat(getCurrencyName("USD"), "USD").isEqualTo("United States Dollar")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getCurrencyNameEur() {
|
||||
assertThat(getCurrencyName("EUR"), "EUR").isEqualTo("Euro")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,10 +32,7 @@ package net.thauvin.erik.mobibot.modules
|
|||
|
||||
import assertk.all
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.contains
|
||||
import assertk.assertions.isInstanceOf
|
||||
import assertk.assertions.matches
|
||||
import assertk.assertions.prop
|
||||
import assertk.assertions.*
|
||||
import net.thauvin.erik.mobibot.LocalProperties
|
||||
import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.convertCurrency
|
||||
import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.loadSymbols
|
||||
|
@ -44,6 +41,9 @@ import net.thauvin.erik.mobibot.msg.Message
|
|||
import net.thauvin.erik.mobibot.msg.PublicMessage
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mockito
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import kotlin.test.Test
|
||||
|
||||
class CurrencyConverterTest : LocalProperties() {
|
||||
|
@ -52,6 +52,37 @@ class CurrencyConverterTest : LocalProperties() {
|
|||
loadSymbols(apiKey)
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Command Response Tests")
|
||||
inner class CommandResponseTests {
|
||||
@Test
|
||||
fun `USD to CAD`() {
|
||||
val currencyConverter = CurrencyConverter()
|
||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||
val captor = ArgumentCaptor.forClass(String::class.java)
|
||||
|
||||
currencyConverter.properties.put(
|
||||
CurrencyConverter.API_KEY_PROP, getProperty(CurrencyConverter.API_KEY_PROP)
|
||||
)
|
||||
currencyConverter.commandResponse("channel", "currency", "1 USD to CAD", event)
|
||||
|
||||
Mockito.verify(event, Mockito.atLeastOnce()).respond(captor.capture())
|
||||
assertThat(captor.value).matches("1 United States Dollar = \\d+\\.\\d{2,3} Canadian Dollar".toRegex())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `API Key is not specified`() {
|
||||
val currencyConverter = CurrencyConverter()
|
||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||
val captor = ArgumentCaptor.forClass(String::class.java)
|
||||
|
||||
currencyConverter.commandResponse("channel", "currency", "1 USD to CAD", event)
|
||||
|
||||
Mockito.verify(event, Mockito.atLeastOnce()).respond(captor.capture())
|
||||
assertThat(captor.value).isEqualTo(CurrencyConverter.ERROR_MESSAGE_NO_API_KEY)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Currency Converter Tests")
|
||||
inner class CurrencyConverterTests {
|
||||
|
|
|
@ -35,42 +35,87 @@ package net.thauvin.erik.mobibot.modules
|
|||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.matches
|
||||
import assertk.assertions.startsWith
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.RepeatedTest
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mockito
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import kotlin.random.Random
|
||||
import kotlin.test.Test
|
||||
|
||||
class DiceTest {
|
||||
@Nested
|
||||
@DisplayName("Command Response Tests")
|
||||
inner class CommandResponseTests {
|
||||
@Test
|
||||
fun `Roll die`() {
|
||||
val dice = Dice()
|
||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||
val captor = ArgumentCaptor.forClass(String::class.java)
|
||||
|
||||
dice.commandResponse("channel", "dice", "", event)
|
||||
|
||||
Mockito.verify(event, Mockito.times(1)).respond(captor.capture())
|
||||
assertThat(captor.value).startsWith("you rolled")
|
||||
}
|
||||
|
||||
@RepeatedTest(3)
|
||||
fun `Roll die with 9 sides`() {
|
||||
val dice = Dice()
|
||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||
val captor = ArgumentCaptor.forClass(String::class.java)
|
||||
|
||||
dice.commandResponse("channel", "dice", "1d9", event)
|
||||
|
||||
Mockito.verify(event, Mockito.times(1)).respond(captor.capture())
|
||||
assertThat(captor.value).matches("you rolled \u0002[1-9]\u0002".toRegex())
|
||||
}
|
||||
|
||||
@RepeatedTest(3)
|
||||
fun `Roll dice`() {
|
||||
val dice = Dice()
|
||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||
val captor = ArgumentCaptor.forClass(String::class.java)
|
||||
|
||||
dice.commandResponse("channel", "dice", "2d6", event)
|
||||
|
||||
Mockito.verify(event, Mockito.times(1)).respond(captor.capture())
|
||||
assertThat(captor.value)
|
||||
.matches("you rolled \u0002[1-6]\u0002 \\+ \u0002[1-6]\u0002 = \u0002\\d{1,2}\\u0002".toRegex())
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Roll Tests")
|
||||
inner class RollTests {
|
||||
@Test
|
||||
fun `Roll 1 die with 1 side`() {
|
||||
assertThat(Dice.roll(1, 1)).isEqualTo("\u00021\u0002")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Roll 1 die with 6 sides`() {
|
||||
fun `Roll die`() {
|
||||
assertThat(Dice.roll(1, 6)).matches("\u0002[1-6]\u0002".toRegex())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Roll die with 1 side`() {
|
||||
assertThat(Dice.roll(1, 1)).isEqualTo("\u00021\u0002")
|
||||
}
|
||||
|
||||
@RepeatedTest(5)
|
||||
fun `Roll 1 die with random sides`() {
|
||||
fun `Roll die with random sides`() {
|
||||
assertThat(Dice.roll(1, Random.nextInt(1, 11))).matches("\u0002([1-9]|10)\u0002".toRegex())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Roll 2 dice`() {
|
||||
assertThat(Dice.roll(2, 6))
|
||||
.matches("\u0002[1-6]\u0002 \\+ \u0002[1-6]\u0002 = \u0002[1-9][0-2]?\u0002".toRegex())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Roll 2 dice with 1 side`() {
|
||||
assertThat(Dice.roll(2, 1)).isEqualTo("\u00021\u0002 + \u00021\u0002 = \u00022\u0002")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Roll 2 dice with 6 sides`() {
|
||||
assertThat(Dice.roll(2, 6))
|
||||
.matches("\u0002[1-6]\u0002 \\+ \u0002[1-6]\u0002 = \u0002[1-9][0-2]?\u0002".toRegex())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Roll 3 dice with 1 side`() {
|
||||
assertThat(Dice.roll(4, 1))
|
||||
|
|
|
@ -37,6 +37,9 @@ import net.thauvin.erik.mobibot.DisableOnCi
|
|||
import net.thauvin.erik.mobibot.LocalProperties
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mockito
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import kotlin.test.Test
|
||||
|
||||
class Gemini2Test : LocalProperties() {
|
||||
|
@ -67,6 +70,22 @@ class Gemini2Test : LocalProperties() {
|
|||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Command Response Tests")
|
||||
inner class CommandResponseTests {
|
||||
@Test
|
||||
fun moduleMisconfigured() {
|
||||
val gemini2 = Gemini2()
|
||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||
val captor = ArgumentCaptor.forClass(String::class.java)
|
||||
|
||||
gemini2.commandResponse("channel", "gemini", "1 liter to gallon", event)
|
||||
|
||||
Mockito.verify(event, Mockito.atLeastOnce()).respond(captor.capture())
|
||||
assertThat(captor.value).isEqualTo("The ${Gemini2.GEMINI_NAME} module is misconfigured.")
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("API Keys Test")
|
||||
inner class ApiKeysTest {
|
||||
|
|
|
@ -42,9 +42,16 @@ import net.thauvin.erik.mobibot.msg.ErrorMessage
|
|||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.kotlin.whenever
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import kotlin.test.Test
|
||||
|
||||
class GoogleSearchTest : LocalProperties() {
|
||||
private val apiKey = getProperty(GoogleSearch.API_KEY_PROP)
|
||||
private val cseKey = getProperty(GoogleSearch.CSE_KEY_PROP)
|
||||
|
||||
@Throws(ModuleException::class)
|
||||
fun sanitizedSearch(query: String, apiKey: String, cseKey: String): List<Message> {
|
||||
try {
|
||||
|
@ -59,6 +66,47 @@ class GoogleSearchTest : LocalProperties() {
|
|||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Command Response Tests")
|
||||
inner class CommandResponseTests {
|
||||
@Test
|
||||
fun `API Keys are missing`() {
|
||||
val googleSearch = GoogleSearch()
|
||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||
val captor = ArgumentCaptor.forClass(String::class.java)
|
||||
val user = Mockito.mock(org.pircbotx.User::class.java)
|
||||
|
||||
whenever(event.user).thenReturn(user)
|
||||
whenever(user.nick).thenReturn("mock")
|
||||
|
||||
googleSearch.commandResponse("channel", "google", "seattle seahawks", event)
|
||||
|
||||
Mockito.verify(event, Mockito.atLeastOnce()).respond(captor.capture())
|
||||
assertThat(captor.value)
|
||||
.isEqualTo("${GoogleSearch.SERVICE_NAME} is disabled. The API keys are missing.")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `No results found`() {
|
||||
val googleSearch = GoogleSearch()
|
||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||
val captor = ArgumentCaptor.forClass(String::class.java)
|
||||
val user = Mockito.mock(org.pircbotx.User::class.java)
|
||||
|
||||
whenever(event.user).thenReturn(user)
|
||||
whenever(user.nick).thenReturn("mock")
|
||||
|
||||
googleSearch.properties.put(GoogleSearch.API_KEY_PROP, apiKey)
|
||||
googleSearch.properties.put(GoogleSearch.CSE_KEY_PROP, cseKey)
|
||||
|
||||
googleSearch.commandResponse("channel", "google", "\"foobarbarfoofoobarblahblah\"", event)
|
||||
|
||||
Mockito.verify(event, Mockito.atLeastOnce()).respond(captor.capture())
|
||||
assertThat(captor.value)
|
||||
.isEqualTo("\u000304No results found.\u000F")
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("API Keys Test")
|
||||
inner class ApiKeysTest {
|
||||
|
@ -85,9 +133,6 @@ class GoogleSearchTest : LocalProperties() {
|
|||
@Nested
|
||||
@DisplayName("Search Tests")
|
||||
inner class SearchTests {
|
||||
private val apiKey = getProperty(GoogleSearch.API_KEY_PROP)
|
||||
private val cseKey = getProperty(GoogleSearch.CSE_KEY_PROP)
|
||||
|
||||
@Test
|
||||
fun `Query should not be empty`() {
|
||||
assertThat(sanitizedSearch("", apiKey, cseKey).first()).isInstanceOf(ErrorMessage::class.java)
|
||||
|
|
|
@ -33,13 +33,59 @@ package net.thauvin.erik.mobibot.modules
|
|||
import assertk.assertThat
|
||||
import assertk.assertions.any
|
||||
import assertk.assertions.contains
|
||||
import assertk.assertions.isEqualTo
|
||||
import net.thauvin.erik.mobibot.modules.Lookup.Companion.nslookup
|
||||
import net.thauvin.erik.mobibot.modules.Lookup.Companion.whois
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mockito
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import kotlin.test.Test
|
||||
|
||||
class LookupTest {
|
||||
@Nested
|
||||
@DisplayName("Command Response Tests")
|
||||
inner class CommandResponseTests {
|
||||
@Test
|
||||
fun lookupByHostname() {
|
||||
val lookup = Lookup()
|
||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||
val captor = ArgumentCaptor.forClass(String::class.java)
|
||||
|
||||
lookup.commandResponse("channel", "lookup", "ec2-54-234-237-183.compute-1.amazonaws.com", event)
|
||||
|
||||
Mockito.verify(event, Mockito.times(1)).respondWith(captor.capture())
|
||||
assertThat(captor.value).contains("54.234.237.183")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun lookupByAddress() {
|
||||
val lookup = Lookup()
|
||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||
val captor = ArgumentCaptor.forClass(String::class.java)
|
||||
|
||||
lookup.commandResponse("channel", "lookup", "54.234.237.183", event)
|
||||
|
||||
Mockito.verify(event, Mockito.times(1)).respondWith(captor.capture())
|
||||
assertThat(captor.value).contains("ec2-54-234-237-183.compute-1.amazonaws.com")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun lookupUnknownHostname() {
|
||||
val lookup = Lookup()
|
||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||
val captor = ArgumentCaptor.forClass(String::class.java)
|
||||
|
||||
lookup.commandResponse("channel", "lookup", "foobar", event)
|
||||
|
||||
Mockito.verify(event, Mockito.times(1)).respond(captor.capture())
|
||||
assertThat(captor.value).isEqualTo("Unknown host.")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Lookup Tests")
|
||||
inner class LookupTests {
|
||||
|
|
|
@ -30,25 +30,104 @@
|
|||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import assertk.assertFailure
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.contains
|
||||
import assertk.assertions.hasMessage
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isInstanceOf
|
||||
import net.thauvin.erik.mobibot.LocalProperties
|
||||
import net.thauvin.erik.mobibot.modules.Mastodon.Companion.toot
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.kotlin.whenever
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import kotlin.test.Test
|
||||
|
||||
class MastodonTest : LocalProperties() {
|
||||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
fun `Toot on Mastodon`() {
|
||||
val msg = "Testing Mastodon API from ${getHostName()}"
|
||||
assertThat(
|
||||
toot(
|
||||
getProperty(Mastodon.ACCESS_TOKEN_PROP),
|
||||
getProperty(Mastodon.INSTANCE_PROP),
|
||||
getProperty(Mastodon.HANDLE_PROP),
|
||||
msg,
|
||||
true
|
||||
)
|
||||
).contains(msg)
|
||||
@Nested
|
||||
@DisplayName("Command Response Tests")
|
||||
inner class CommandResponseTests {
|
||||
@Test
|
||||
fun `API Key is not specified`() {
|
||||
val mastodon = Mastodon()
|
||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||
val captor = ArgumentCaptor.forClass(String::class.java)
|
||||
val user = Mockito.mock(org.pircbotx.User::class.java)
|
||||
|
||||
whenever(event.user).thenReturn(user)
|
||||
whenever(user.nick).thenReturn("mock")
|
||||
|
||||
mastodon.commandResponse("channel", "toot", "This is a test.", event)
|
||||
|
||||
Mockito.verify(event, Mockito.atLeastOnce()).respond(captor.capture())
|
||||
assertThat(captor.value).isEqualTo("The access token is missing.")
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Toot Tests")
|
||||
inner class TootTests {
|
||||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
fun `Empty Access Token should throw exception`() {
|
||||
val msg = "Testing Mastodon API from ${getHostName()}"
|
||||
assertFailure {
|
||||
toot(
|
||||
"",
|
||||
getProperty(Mastodon.INSTANCE_PROP),
|
||||
getProperty(Mastodon.HANDLE_PROP),
|
||||
msg,
|
||||
true
|
||||
)
|
||||
}.isInstanceOf(ModuleException::class.java).hasMessage("The access token is missing.")
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
fun `Empty Handle should throw exception`() {
|
||||
val msg = "Testing Mastodon API from ${getHostName()}"
|
||||
assertFailure {
|
||||
toot(
|
||||
getProperty(Mastodon.ACCESS_TOKEN_PROP),
|
||||
getProperty(Mastodon.INSTANCE_PROP),
|
||||
"",
|
||||
msg,
|
||||
true
|
||||
)
|
||||
}.isInstanceOf(ModuleException::class.java).hasMessage("The Mastodon handle is missing.")
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
fun `Empty Instance should throw exception`() {
|
||||
val msg = "Testing Mastodon API from ${getHostName()}"
|
||||
assertFailure {
|
||||
toot(
|
||||
getProperty(Mastodon.ACCESS_TOKEN_PROP),
|
||||
"",
|
||||
getProperty(Mastodon.HANDLE_PROP),
|
||||
msg,
|
||||
true
|
||||
)
|
||||
}.isInstanceOf(ModuleException::class.java).hasMessage("The Mastodon instance is missing.")
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
fun `Toot on Mastodon`() {
|
||||
val msg = "Testing Mastodon API from ${getHostName()}"
|
||||
assertThat(
|
||||
toot(
|
||||
getProperty(Mastodon.ACCESS_TOKEN_PROP),
|
||||
getProperty(Mastodon.INSTANCE_PROP),
|
||||
getProperty(Mastodon.HANDLE_PROP),
|
||||
msg,
|
||||
true
|
||||
)
|
||||
).contains(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ class ModuleExceptionTest {
|
|||
@Test
|
||||
fun sanitizeMessage() {
|
||||
val apiKey = "1234567890"
|
||||
var e = ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL http://foo.com?apiKey=$apiKey&userID=me"))
|
||||
var e = ModuleException(DEBUG_MESSAGE, MESSAGE, IOException("URL https://foo.com?apiKey=$apiKey&userID=me"))
|
||||
assertThat(
|
||||
e.sanitize(apiKey, "", "me").message, "ModuleException(debugMessage, message, IOException(url))"
|
||||
).isNotNull().all {
|
||||
|
|
|
@ -38,7 +38,7 @@ import kotlin.test.Test
|
|||
|
||||
class PingTest {
|
||||
@Test
|
||||
fun `Get a radon ping`() {
|
||||
fun `Get a random ping`() {
|
||||
for (i in 0..9) {
|
||||
assertThat(Ping.PINGS, "Ping.PINGS[$i]").contains(randomPing())
|
||||
}
|
||||
|
|
|
@ -33,12 +33,36 @@ package net.thauvin.erik.mobibot.modules
|
|||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.matches
|
||||
import net.thauvin.erik.mobibot.modules.RockPaperScissors.Companion.winLoseOrDraw
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.RepeatedTest
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mockito
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import kotlin.test.Test
|
||||
|
||||
class RockPaperScissorsTest {
|
||||
@Nested
|
||||
@DisplayName("Command Response Tests")
|
||||
inner class CommandResponseTests {
|
||||
@RepeatedTest(3)
|
||||
fun `Play Rock Paper Scissors`() {
|
||||
val rockPaperScissors = RockPaperScissors()
|
||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||
val captor = ArgumentCaptor.forClass(String::class.java)
|
||||
|
||||
rockPaperScissors.commandResponse("channel", "rock", "", event)
|
||||
|
||||
Mockito.verify(event, Mockito.atLeastOnce()).respond(captor.capture())
|
||||
assertThat(captor.value)
|
||||
.matches(
|
||||
".* (vs\\.|crushes|covers|cuts) (ROCK|PAPER|SCISSORS) » You \u0002(tie|win|lose)\u0002.".toRegex()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Win, Lose or Draw Tests")
|
||||
inner class WinLoseOrDrawTests {
|
||||
|
|
|
@ -39,6 +39,11 @@ import net.thauvin.erik.mobibot.LocalProperties
|
|||
import net.thauvin.erik.mobibot.modules.StockQuote.Companion.getQuote
|
||||
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
||||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mockito
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import kotlin.test.Test
|
||||
|
||||
class StockQuoteTest : LocalProperties() {
|
||||
|
@ -61,55 +66,75 @@ class StockQuoteTest : LocalProperties() {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
fun `API key should not be empty`() {
|
||||
assertFailure { getSanitizedQuote("test", "") }.isInstanceOf(ModuleException::class.java)
|
||||
.messageContains("disabled")
|
||||
}
|
||||
@Nested
|
||||
@DisplayName("Command Response Tests")
|
||||
inner class CommandResponseTests {
|
||||
@Test
|
||||
fun `API Key is missing`() {
|
||||
val stockQuote = StockQuote()
|
||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||
val captor = ArgumentCaptor.forClass(String::class.java)
|
||||
|
||||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
fun `Symbol should not be empty`() {
|
||||
assertThat(getSanitizedQuote("", "apikey").first(), "getQuote(empty)").all {
|
||||
isInstanceOf(ErrorMessage::class.java)
|
||||
prop(Message::msg).isEqualTo(StockQuote.INVALID_SYMBOL)
|
||||
stockQuote.commandResponse("channel", "stock", "goog", event)
|
||||
|
||||
Mockito.verify(event, Mockito.atLeastOnce()).respond(captor.capture())
|
||||
assertThat(captor.value).isEqualTo("${StockQuote.SERVICE_NAME} is disabled. The API key is missing.")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
fun `Get stock quote for Apple`() {
|
||||
val symbol = "apple inc"
|
||||
val messages = getSanitizedQuote(symbol, apiKey)
|
||||
assertThat(messages, "response not empty").isNotEmpty()
|
||||
assertThat(messages, "getQuote($symbol)").index(0).prop(Message::msg)
|
||||
.matches("Symbol: AAPL .*".toRegex())
|
||||
assertThat(messages, "getQuote($symbol)").index(1).prop(Message::msg)
|
||||
.matches(buildMatch("Price").toRegex())
|
||||
assertThat(messages, "getQuote($symbol)").index(2).prop(Message::msg)
|
||||
.matches(buildMatch("Previous").toRegex())
|
||||
assertThat(messages, "getQuote($symbol)").index(3).prop(Message::msg)
|
||||
.matches(buildMatch("Open").toRegex())
|
||||
}
|
||||
@Nested
|
||||
@DisplayName("Get Quote Tests")
|
||||
inner class GetQuoteTests {
|
||||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
fun `API key should not be empty`() {
|
||||
assertFailure { getSanitizedQuote("test", "") }.isInstanceOf(ModuleException::class.java)
|
||||
.messageContains("disabled")
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
fun `Get stock quote for Google`() {
|
||||
val symbol = "goog"
|
||||
val messages = getSanitizedQuote(symbol, apiKey)
|
||||
assertThat(messages, "response not empty").isNotEmpty()
|
||||
assertThat(messages, "getQuote($symbol)").index(0).prop(Message::msg)
|
||||
.matches("Symbol: GOOG .*".toRegex())
|
||||
}
|
||||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
fun `Symbol should not be empty`() {
|
||||
assertThat(getSanitizedQuote("", "apikey").first(), "getQuote(empty)").all {
|
||||
isInstanceOf(ErrorMessage::class.java)
|
||||
prop(Message::msg).isEqualTo(StockQuote.INVALID_SYMBOL)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
fun `Invalid symbol should throw exception`() {
|
||||
val symbol = "foobar"
|
||||
assertThat(getSanitizedQuote(symbol, apiKey).first(), "getQuote($symbol)").all {
|
||||
isInstanceOf(ErrorMessage::class.java)
|
||||
prop(Message::msg).isEqualTo(StockQuote.INVALID_SYMBOL)
|
||||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
fun `Get stock quote for Apple`() {
|
||||
val symbol = "apple inc"
|
||||
val messages = getSanitizedQuote(symbol, apiKey)
|
||||
assertThat(messages, "response not empty").isNotEmpty()
|
||||
assertThat(messages, "getQuote($symbol)").index(0).prop(Message::msg)
|
||||
.matches("Symbol: AAPL .*".toRegex())
|
||||
assertThat(messages, "getQuote($symbol)").index(1).prop(Message::msg)
|
||||
.matches(buildMatch("Price").toRegex())
|
||||
assertThat(messages, "getQuote($symbol)").index(2).prop(Message::msg)
|
||||
.matches(buildMatch("Previous").toRegex())
|
||||
assertThat(messages, "getQuote($symbol)").index(3).prop(Message::msg)
|
||||
.matches(buildMatch("Open").toRegex())
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
fun `Get stock quote for Google`() {
|
||||
val symbol = "goog"
|
||||
val messages = getSanitizedQuote(symbol, apiKey)
|
||||
assertThat(messages, "response not empty").isNotEmpty()
|
||||
assertThat(messages, "getQuote($symbol)").index(0).prop(Message::msg)
|
||||
.matches("Symbol: GOOG .*".toRegex())
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(ModuleException::class)
|
||||
fun `Invalid symbol should throw exception`() {
|
||||
val symbol = "foobar"
|
||||
assertThat(getSanitizedQuote(symbol, apiKey).first(), "getQuote($symbol)").all {
|
||||
isInstanceOf(ErrorMessage::class.java)
|
||||
prop(Message::msg).isEqualTo(StockQuote.INVALID_SYMBOL)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
54
src/test/kotlin/net/thauvin/erik/mobibot/modules/WarTest.kt
Normal file
54
src/test/kotlin/net/thauvin/erik/mobibot/modules/WarTest.kt
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* WarTest.kt
|
||||
*
|
||||
* Copyright 2004-2025 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* 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.mobibot.modules
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.matches
|
||||
import org.junit.jupiter.api.RepeatedTest
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mockito
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
|
||||
class WarTest {
|
||||
@RepeatedTest(3)
|
||||
fun `Play war`() {
|
||||
val war = War()
|
||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||
val captor = ArgumentCaptor.forClass(String::class.java)
|
||||
|
||||
war.commandResponse("channel", "war", "", event)
|
||||
|
||||
Mockito.verify(event, Mockito.atLeastOnce()).respond(captor.capture())
|
||||
assertThat(captor.value)
|
||||
.matches("[\uD83C\uDCA0-\uD83C\uDCDF] {2}[\uD83C\uDCA0-\uD83C\uDCDF] {2}» .+(win|lose|tie).+!".toRegex())
|
||||
}
|
||||
}
|
|
@ -45,9 +45,28 @@ import net.thauvin.erik.mobibot.modules.Weather2.Companion.mphToKmh
|
|||
import net.thauvin.erik.mobibot.msg.Message
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mockito
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import kotlin.test.Test
|
||||
|
||||
class Weather2Test : LocalProperties() {
|
||||
@Nested
|
||||
@DisplayName("Command Response Tests")
|
||||
inner class CommandResponseTests {
|
||||
@Test
|
||||
fun `API Key is missing`() {
|
||||
val weather2 = Weather2()
|
||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||
val captor = ArgumentCaptor.forClass(String::class.java)
|
||||
|
||||
weather2.commandResponse("channel", "weather", "seattle, us", event)
|
||||
|
||||
Mockito.verify(event, Mockito.atLeastOnce()).respond(captor.capture())
|
||||
assertThat(captor.value).isEqualTo("${Weather2.WEATHER_NAME} is disabled. The API key is missing.")
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("API Key Tests")
|
||||
inner class ApiKeyTests {
|
||||
|
|
|
@ -35,6 +35,7 @@ import assertk.assertFailure
|
|||
import assertk.assertThat
|
||||
import assertk.assertions.contains
|
||||
import assertk.assertions.hasMessage
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isInstanceOf
|
||||
import net.thauvin.erik.mobibot.DisableOnCi
|
||||
import net.thauvin.erik.mobibot.ExceptionSanitizer.sanitize
|
||||
|
@ -45,10 +46,12 @@ import org.junit.jupiter.api.Nested
|
|||
import org.junit.jupiter.params.ParameterizedTest
|
||||
import org.junit.jupiter.params.provider.Arguments
|
||||
import org.junit.jupiter.params.provider.MethodSource
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mockito
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import java.util.stream.Stream
|
||||
import kotlin.test.Test
|
||||
|
||||
|
||||
class WolframAlphaTest : LocalProperties() {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
|
@ -61,6 +64,22 @@ class WolframAlphaTest : LocalProperties() {
|
|||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Command Response Tests")
|
||||
inner class CommandResponseTests {
|
||||
@Test
|
||||
fun appIdNotSpecified() {
|
||||
val wolframAlpha = WolframAlpha()
|
||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||
val captor = ArgumentCaptor.forClass(String::class.java)
|
||||
|
||||
wolframAlpha.commandResponse("channel", "wolfram", "1 liter to gallon", event)
|
||||
|
||||
Mockito.verify(event, Mockito.atLeastOnce()).respond(captor.capture())
|
||||
assertThat(captor.value).isEqualTo("No ${WolframAlpha.SERVICE_NAME} AppID specified.")
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("App ID Tests")
|
||||
inner class AppIdTests {
|
||||
|
|
|
@ -40,11 +40,31 @@ import net.thauvin.erik.mobibot.modules.WorldTime.Companion.COUNTRIES_MAP
|
|||
import net.thauvin.erik.mobibot.modules.WorldTime.Companion.time
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mockito
|
||||
import org.pircbotx.Colors
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import java.time.ZoneId
|
||||
import kotlin.test.Test
|
||||
|
||||
class WordTimeTest {
|
||||
@Nested
|
||||
@DisplayName("Command Response Tests")
|
||||
inner class CommandResponseTests {
|
||||
@Test
|
||||
fun `Time in Tokyo`() {
|
||||
val worldTime = WorldTime()
|
||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||
val captor = ArgumentCaptor.forClass(String::class.java)
|
||||
|
||||
worldTime.commandResponse("channel", "time", "jp", event)
|
||||
|
||||
Mockito.verify(event, Mockito.atLeastOnce()).respond(captor.capture())
|
||||
assertThat(captor.value)
|
||||
.matches("The time is \u0002([01]\\d|2[0-3]):([0-5]\\d)\u0002 on .+ in \u0002Tokyo\u0002".toRegex())
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Time Tests")
|
||||
inner class TimeTests {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue