Implement currency converter module using data from Frankfurter.dev
This commit is contained in:
parent
38cf75f8c9
commit
4f55685eff
7 changed files with 98 additions and 109 deletions
|
@ -12,9 +12,9 @@
|
||||||
<ID>LongParameterList:EntryLink.kt$EntryLink$( // Link's comments val comments: MutableList<EntryComment> = mutableListOf(), // Tags/categories val tags: MutableList<SyndCategory> = mutableListOf(), // Channel var channel: String, // Creation date var date: Date = Calendar.getInstance().time, // Link's URL var link: String, // Author's login var login: String = "", // Author's nickname var nick: String, // Link's title var title: String )</ID>
|
<ID>LongParameterList:EntryLink.kt$EntryLink$( // Link's comments val comments: MutableList<EntryComment> = mutableListOf(), // Tags/categories val tags: MutableList<SyndCategory> = mutableListOf(), // Channel var channel: String, // Creation date var date: Date = Calendar.getInstance().time, // Link's URL var link: String, // Author's login var login: String = "", // Author's nickname var nick: String, // Link's title var title: String )</ID>
|
||||||
<ID>MagicNumber:Comment.kt$Comment$3</ID>
|
<ID>MagicNumber:Comment.kt$Comment$3</ID>
|
||||||
<ID>MagicNumber:CryptoPrices.kt$CryptoPrices$10</ID>
|
<ID>MagicNumber:CryptoPrices.kt$CryptoPrices$10</ID>
|
||||||
<ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$11</ID>
|
<ID>MagicNumber:CurrencyConverter2.kt$CurrencyConverter2$11</ID>
|
||||||
<ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$3</ID>
|
<ID>MagicNumber:CurrencyConverter2.kt$CurrencyConverter2.Companion$3</ID>
|
||||||
<ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$4</ID>
|
<ID>MagicNumber:CurrencyConverter2.kt$CurrencyConverter2.Companion$4</ID>
|
||||||
<ID>MagicNumber:Cycle.kt$Cycle$10</ID>
|
<ID>MagicNumber:Cycle.kt$Cycle$10</ID>
|
||||||
<ID>MagicNumber:Cycle.kt$Cycle$1000L</ID>
|
<ID>MagicNumber:Cycle.kt$Cycle$1000L</ID>
|
||||||
<ID>MagicNumber:Ignore.kt$Ignore$8</ID>
|
<ID>MagicNumber:Ignore.kt$Ignore$8</ID>
|
||||||
|
@ -47,8 +47,7 @@
|
||||||
<ID>NestedBlockDepth:Addons.kt$Addons$fun add(command: AbstractCommand): Boolean</ID>
|
<ID>NestedBlockDepth:Addons.kt$Addons$fun add(command: AbstractCommand): Boolean</ID>
|
||||||
<ID>NestedBlockDepth:Addons.kt$Addons$fun add(module: AbstractModule): Boolean</ID>
|
<ID>NestedBlockDepth:Addons.kt$Addons$fun add(module: AbstractModule): Boolean</ID>
|
||||||
<ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID>
|
<ID>NestedBlockDepth:Comment.kt$Comment$override fun commandResponse(channel: String, args: String, event: GenericMessageEvent)</ID>
|
||||||
<ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$@JvmStatic @Throws(ModuleException::class) fun loadSymbols(apiKey: String?)</ID>
|
<ID>NestedBlockDepth:CurrencyConverter2.kt$CurrencyConverter2.Companion$@JvmStatic fun convertCurrency(query: String): Message</ID>
|
||||||
<ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$@JvmStatic fun convertCurrency(apiKey: String?, query: String): Message</ID>
|
|
||||||
<ID>NestedBlockDepth:EntryLink.kt$EntryLink$private fun setTags(tags: List<String?>)</ID>
|
<ID>NestedBlockDepth:EntryLink.kt$EntryLink$private fun setTags(tags: List<String?>)</ID>
|
||||||
<ID>NestedBlockDepth:FeedsManager.kt$FeedsManager.Companion$@JvmStatic @Throws(IOException::class, FeedException::class) fun loadFeed(entries: Entries, currentFile: String = CURRENT_XML): String</ID>
|
<ID>NestedBlockDepth:FeedsManager.kt$FeedsManager.Companion$@JvmStatic @Throws(IOException::class, FeedException::class) fun loadFeed(entries: Entries, currentFile: String = CURRENT_XML): String</ID>
|
||||||
<ID>NestedBlockDepth:FeedsManager.kt$FeedsManager.Companion$@JvmStatic fun saveFeed(entries: Entries, currentFile: String = CURRENT_XML)</ID>
|
<ID>NestedBlockDepth:FeedsManager.kt$FeedsManager.Companion$@JvmStatic fun saveFeed(entries: Entries, currentFile: String = CURRENT_XML)</ID>
|
||||||
|
@ -87,7 +86,6 @@
|
||||||
<ID>UtilityClassWithPublicConstructor:LocalProperties.kt$LocalProperties</ID>
|
<ID>UtilityClassWithPublicConstructor:LocalProperties.kt$LocalProperties</ID>
|
||||||
<ID>WildcardImport:AddonsTest.kt$import net.thauvin.erik.mobibot.modules.*</ID>
|
<ID>WildcardImport:AddonsTest.kt$import net.thauvin.erik.mobibot.modules.*</ID>
|
||||||
<ID>WildcardImport:CryptoPricesTest.kt$import assertk.assertions.*</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:EntryLinkTest.kt$import assertk.assertions.*</ID>
|
||||||
<ID>WildcardImport:FeedMgrTest.kt$import assertk.assertions.*</ID>
|
<ID>WildcardImport:FeedMgrTest.kt$import assertk.assertions.*</ID>
|
||||||
<ID>WildcardImport:FeedReaderTest.kt$import assertk.assertions.*</ID>
|
<ID>WildcardImport:FeedReaderTest.kt$import assertk.assertions.*</ID>
|
||||||
|
|
|
@ -47,11 +47,6 @@ disabled-modules=mastodon
|
||||||
# Automatically post links to Mastodon
|
# Automatically post links to Mastodon
|
||||||
#mastodon-auto-post=true
|
#mastodon-auto-post=true
|
||||||
|
|
||||||
#
|
|
||||||
# Get Exchange Rate API key from: https://www.exchangerate-api.com/
|
|
||||||
#
|
|
||||||
#exchangerate-api-key=
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Create custom search engine at: https://programmablesearchengine.google.com/
|
# Create custom search engine at: https://programmablesearchengine.google.com/
|
||||||
# and get API key from: https://console.cloud.google.com/apis
|
# and get API key from: https://console.cloud.google.com/apis
|
||||||
|
|
|
@ -256,7 +256,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro
|
||||||
addons.add(Calc())
|
addons.add(Calc())
|
||||||
addons.add(ChatGpt2())
|
addons.add(ChatGpt2())
|
||||||
addons.add(CryptoPrices())
|
addons.add(CryptoPrices())
|
||||||
addons.add(CurrencyConverter())
|
addons.add(CurrencyConverter2())
|
||||||
addons.add(Dice())
|
addons.add(Dice())
|
||||||
addons.add(Gemini2())
|
addons.add(Gemini2())
|
||||||
addons.add(GoogleSearch())
|
addons.add(GoogleSearch())
|
||||||
|
|
|
@ -14,12 +14,12 @@ import java.time.ZoneId
|
||||||
*/
|
*/
|
||||||
object ReleaseInfo {
|
object ReleaseInfo {
|
||||||
const val PROJECT = "mobibot"
|
const val PROJECT = "mobibot"
|
||||||
const val VERSION = "0.8.0-rc+20250509223055"
|
const val VERSION = "0.8.0-rc+20250516012000"
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
@Suppress("MagicNumber")
|
@Suppress("MagicNumber")
|
||||||
val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant(
|
val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant(
|
||||||
Instant.ofEpochMilli(1746855055425L), ZoneId.systemDefault()
|
Instant.ofEpochMilli(1747383600399L), ZoneId.systemDefault()
|
||||||
)
|
)
|
||||||
|
|
||||||
const val WEBSITE = "https://mobitopia.org/mobibot/"
|
const val WEBSITE = "https://mobitopia.org/mobibot/"
|
||||||
|
|
|
@ -51,70 +51,62 @@ import java.util.*
|
||||||
/**
|
/**
|
||||||
* Converts between currencies.
|
* Converts between currencies.
|
||||||
*/
|
*/
|
||||||
class CurrencyConverter : AbstractModule() {
|
class CurrencyConverter2 : AbstractModule() {
|
||||||
override val name = "CurrencyConverter"
|
override val name = "CurrencyConverter"
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
|
||||||
* The API Key property.
|
|
||||||
*/
|
|
||||||
const val API_KEY_PROP = "exchangerate-api-key"
|
|
||||||
|
|
||||||
// Currency command
|
// Currency command
|
||||||
private const val CURRENCY_CMD = "currency"
|
private const val CURRENCY_CMD = "currency"
|
||||||
|
|
||||||
|
// Currency codes
|
||||||
|
private val CURRENCY_CODES: TreeMap<String, String> = TreeMap()
|
||||||
|
|
||||||
// Currency codes keyword
|
// Currency codes keyword
|
||||||
private const val CODES_KEYWORD = "codes"
|
private const val CODES_KEYWORD = "codes"
|
||||||
|
|
||||||
// Decimal format
|
// Decimal format
|
||||||
private val DECIMAL_FORMAT = DecimalFormat("0.00#")
|
private val DECIMAL_FORMAT = DecimalFormat("0.00#")
|
||||||
|
|
||||||
// Empty symbols table.
|
// Empty codes table.
|
||||||
private const val EMPTY_SYMBOLS_TABLE = "Sorry, but the currency table is empty."
|
private const val EMPTY_CODES_TABLE = "Sorry, but the currencies codes table is empty."
|
||||||
|
|
||||||
// Logger
|
// Logger
|
||||||
private val LOGGER: Logger = LoggerFactory.getLogger(CurrencyConverter::class.java)
|
private val LOGGER: Logger = LoggerFactory.getLogger(CurrencyConverter2::class.java)
|
||||||
|
|
||||||
// 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.
|
* Converts from a currency to another.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun convertCurrency(apiKey: String?, query: String): Message {
|
fun convertCurrency(query: String): Message {
|
||||||
if (apiKey.isNullOrEmpty()) {
|
|
||||||
throw ModuleException("${CURRENCY_CMD}($query)", ERROR_MESSAGE_NO_API_KEY)
|
|
||||||
}
|
|
||||||
|
|
||||||
val cmds = query.split(" ")
|
val cmds = query.split(" ")
|
||||||
return if (cmds.size == 4) {
|
return if (cmds.size == 4) {
|
||||||
if (cmds[3] == cmds[1] || "0" == cmds[0]) {
|
if (cmds[3] == cmds[1] || "0" == cmds[0]) {
|
||||||
PublicMessage("You're kidding, right?")
|
PublicMessage("You're kidding, right?")
|
||||||
} else {
|
} else {
|
||||||
val to = cmds[1].uppercase()
|
val from = cmds[1].uppercase()
|
||||||
val from = cmds[3].uppercase()
|
val to = cmds[3].uppercase()
|
||||||
if (SYMBOLS.contains(to) && SYMBOLS.contains(from)) {
|
if (CURRENCY_CODES.contains(to) && CURRENCY_CODES.contains(from)) {
|
||||||
try {
|
try {
|
||||||
val amt = cmds[0].replace(",", "")
|
val amt = cmds[0].replace(",", "")
|
||||||
val url = URL("https://v6.exchangerate-api.com/v6/$apiKey/pair/$to/$from/$amt")
|
val url = URL("https://api.frankfurter.dev/v1/latest?base=$from&symbols=$to")
|
||||||
val body = url.reader().body
|
val body = url.reader().body
|
||||||
val json = JSONObject(body)
|
if (LOGGER.isTraceEnabled) {
|
||||||
|
LOGGER.trace(body)
|
||||||
if (json.getString("result") == "success") {
|
|
||||||
val result = DECIMAL_FORMAT.format(json.getDouble("conversion_result"))
|
|
||||||
PublicMessage(
|
|
||||||
"${cmds[0]} ${SYMBOLS[to]} = $result ${SYMBOLS[from]}"
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
ErrorMessage("Sorry, an error occurred while converting the currencies.")
|
|
||||||
}
|
}
|
||||||
|
val json = JSONObject(body)
|
||||||
|
val rates = json.getJSONObject("rates")
|
||||||
|
val rate = rates.getDouble(to)
|
||||||
|
val result = DECIMAL_FORMAT.format(amt.toDouble() * rate)
|
||||||
|
|
||||||
|
|
||||||
|
PublicMessage(
|
||||||
|
"${cmds[0]} ${CURRENCY_CODES[from]} = $result ${CURRENCY_CODES[to]}"
|
||||||
|
)
|
||||||
|
} catch (nfe: NumberFormatException) {
|
||||||
|
if (LOGGER.isWarnEnabled) {
|
||||||
|
LOGGER.warn("IO error while converting currencies: ${nfe.message}", nfe)
|
||||||
|
}
|
||||||
|
ErrorMessage("Sorry, an error occurred while converting the currencies.")
|
||||||
} catch (ioe: IOException) {
|
} catch (ioe: IOException) {
|
||||||
if (LOGGER.isWarnEnabled) {
|
if (LOGGER.isWarnEnabled) {
|
||||||
LOGGER.warn("IO error while converting currencies: ${ioe.message}", ioe)
|
LOGGER.warn("IO error while converting currencies: ${ioe.message}", ioe)
|
||||||
|
@ -122,7 +114,9 @@ class CurrencyConverter : AbstractModule() {
|
||||||
ErrorMessage("Sorry, an IO error occurred while converting the currencies.")
|
ErrorMessage("Sorry, an IO error occurred while converting the currencies.")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ErrorMessage("Sounds like monopoly money to me!")
|
ErrorMessage(
|
||||||
|
"Sounds like monopoly money to me! Try looking up the supported currency codes."
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -131,44 +125,40 @@ class CurrencyConverter : AbstractModule() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the currency ISO symbols.
|
* Loads the currency ISO codes.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Throws(ModuleException::class)
|
@Throws(ModuleException::class)
|
||||||
fun loadSymbols(apiKey: String?) {
|
fun loadCurrencyCodes() {
|
||||||
if (!apiKey.isNullOrEmpty()) {
|
|
||||||
try {
|
try {
|
||||||
val url = URL("https://v6.exchangerate-api.com/v6/$apiKey/codes")
|
val url = URL("https://api.frankfurter.dev/v1/currencies")
|
||||||
val json = JSONObject(url.reader().body)
|
val body = url.reader().body
|
||||||
if (json.getString("result") == "success") {
|
val json = JSONObject(body)
|
||||||
val codes = json.getJSONArray("supported_codes")
|
if (LOGGER.isTraceEnabled) {
|
||||||
for (i in 0 until codes.length()) {
|
LOGGER.trace(body)
|
||||||
val code = codes.getJSONArray(i)
|
|
||||||
SYMBOLS[code.getString(0)] = code.getString(1)
|
|
||||||
}
|
}
|
||||||
|
json.keySet().forEach { key ->
|
||||||
|
CURRENCY_CODES[key] = json.getString(key)
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
throw ModuleException(
|
throw ModuleException(
|
||||||
"loadCodes(): IOE",
|
"loadCurrencyCodes(): IOE",
|
||||||
"An IO error has occurred while retrieving the currencies.",
|
"An IO error has occurred while retrieving the currency codes.",
|
||||||
e
|
e
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
commands.add(CURRENCY_CMD)
|
commands.add(CURRENCY_CMD)
|
||||||
initProperties(API_KEY_PROP)
|
|
||||||
loadSymbols(properties[ChatGpt2.API_KEY_PROP])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reload currency codes
|
// Reload currency codes
|
||||||
private fun reload(apiKey: String?) {
|
private fun reload() {
|
||||||
if (!apiKey.isNullOrEmpty() && SYMBOLS.isEmpty()) {
|
if (CURRENCY_CODES.isEmpty()) {
|
||||||
try {
|
try {
|
||||||
loadSymbols(apiKey)
|
loadCurrencyCodes()
|
||||||
} catch (e: ModuleException) {
|
} catch (e: ModuleException) {
|
||||||
if (LOGGER.isWarnEnabled) LOGGER.warn(e.debugMessage, e)
|
if (LOGGER.isWarnEnabled) LOGGER.warn(e.debugMessage, e)
|
||||||
}
|
}
|
||||||
|
@ -179,15 +169,15 @@ class CurrencyConverter : AbstractModule() {
|
||||||
* Converts the specified currencies.
|
* Converts the specified currencies.
|
||||||
*/
|
*/
|
||||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||||
reload(properties[API_KEY_PROP])
|
reload()
|
||||||
when {
|
when {
|
||||||
SYMBOLS.isEmpty() -> {
|
CURRENCY_CODES.isEmpty() -> {
|
||||||
event.respond(EMPTY_SYMBOLS_TABLE)
|
event.respond(EMPTY_CODES_TABLE)
|
||||||
}
|
}
|
||||||
|
|
||||||
args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ (to|in) [a-zA-Z]{3}+".toRegex()) -> {
|
args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ (to|in) [a-zA-Z]{3}+".toRegex()) -> {
|
||||||
try {
|
try {
|
||||||
val msg = convertCurrency(properties[API_KEY_PROP], args)
|
val msg = convertCurrency(args)
|
||||||
if (msg.isError) {
|
if (msg.isError) {
|
||||||
helpResponse(event)
|
helpResponse(event)
|
||||||
} else {
|
} else {
|
||||||
|
@ -201,7 +191,7 @@ class CurrencyConverter : AbstractModule() {
|
||||||
|
|
||||||
args.contains(CODES_KEYWORD) -> {
|
args.contains(CODES_KEYWORD) -> {
|
||||||
event.sendMessage("The supported currency codes are:")
|
event.sendMessage("The supported currency codes are:")
|
||||||
event.sendList(SYMBOLS.keys.toList(), 11, isIndent = true)
|
event.sendList(CURRENCY_CODES.keys.toList(), 11, isIndent = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
|
@ -211,10 +201,10 @@ class CurrencyConverter : AbstractModule() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun helpResponse(event: GenericMessageEvent): Boolean {
|
override fun helpResponse(event: GenericMessageEvent): Boolean {
|
||||||
reload(properties[API_KEY_PROP])
|
reload()
|
||||||
|
|
||||||
if (SYMBOLS.isEmpty()) {
|
if (CURRENCY_CODES.isEmpty()) {
|
||||||
event.sendMessage(EMPTY_SYMBOLS_TABLE)
|
event.sendMessage(EMPTY_CODES_TABLE)
|
||||||
} else {
|
} else {
|
||||||
val nick = event.bot().nick
|
val nick = event.bot().nick
|
||||||
event.sendMessage("To convert from one currency to another:")
|
event.sendMessage("To convert from one currency to another:")
|
|
@ -32,10 +32,12 @@ package net.thauvin.erik.mobibot.modules
|
||||||
|
|
||||||
import assertk.all
|
import assertk.all
|
||||||
import assertk.assertThat
|
import assertk.assertThat
|
||||||
import assertk.assertions.*
|
import assertk.assertions.contains
|
||||||
import net.thauvin.erik.mobibot.LocalProperties
|
import assertk.assertions.isInstanceOf
|
||||||
import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.convertCurrency
|
import assertk.assertions.matches
|
||||||
import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.loadSymbols
|
import assertk.assertions.prop
|
||||||
|
import net.thauvin.erik.mobibot.modules.CurrencyConverter2.Companion.convertCurrency
|
||||||
|
import net.thauvin.erik.mobibot.modules.CurrencyConverter2.Companion.loadCurrencyCodes
|
||||||
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
import net.thauvin.erik.mobibot.msg.ErrorMessage
|
||||||
import net.thauvin.erik.mobibot.msg.Message
|
import net.thauvin.erik.mobibot.msg.Message
|
||||||
import net.thauvin.erik.mobibot.msg.PublicMessage
|
import net.thauvin.erik.mobibot.msg.PublicMessage
|
||||||
|
@ -46,10 +48,9 @@ import org.mockito.Mockito
|
||||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
|
||||||
class CurrencyConverterTest : LocalProperties() {
|
class CurrencyConverter2Test {
|
||||||
init {
|
init {
|
||||||
val apiKey = getProperty(CurrencyConverter.API_KEY_PROP)
|
loadCurrencyCodes()
|
||||||
loadSymbols(apiKey)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
|
@ -57,41 +58,37 @@ class CurrencyConverterTest : LocalProperties() {
|
||||||
inner class CommandResponseTests {
|
inner class CommandResponseTests {
|
||||||
@Test
|
@Test
|
||||||
fun `USD to CAD`() {
|
fun `USD to CAD`() {
|
||||||
val currencyConverter = CurrencyConverter()
|
val currencyConverter = CurrencyConverter2()
|
||||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||||
val captor = ArgumentCaptor.forClass(String::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)
|
currencyConverter.commandResponse("channel", "currency", "1 USD to CAD", event)
|
||||||
|
|
||||||
Mockito.verify(event, Mockito.atLeastOnce()).respond(captor.capture())
|
Mockito.verify(event, Mockito.atLeastOnce()).respond(captor.capture())
|
||||||
assertThat(captor.value).matches("1 United States Dollar = \\d+\\.\\d{2,3} Canadian Dollar".toRegex())
|
assertThat(captor.value)
|
||||||
|
.matches("1 United States Dollar = \\d+\\.\\d{2,3} Canadian Dollar".toRegex())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `API Key is not specified`() {
|
fun `USD to GBP`() {
|
||||||
val currencyConverter = CurrencyConverter()
|
val currencyConverter = CurrencyConverter2()
|
||||||
val event = Mockito.mock(GenericMessageEvent::class.java)
|
val event = Mockito.mock(GenericMessageEvent::class.java)
|
||||||
val captor = ArgumentCaptor.forClass(String::class.java)
|
val captor = ArgumentCaptor.forClass(String::class.java)
|
||||||
|
|
||||||
currencyConverter.commandResponse("channel", "currency", "1 USD to CAD", event)
|
currencyConverter.commandResponse("channel", "currency", "1 usd to gbp", event)
|
||||||
|
|
||||||
Mockito.verify(event, Mockito.atLeastOnce()).respond(captor.capture())
|
Mockito.verify(event, Mockito.atLeastOnce()).respond(captor.capture())
|
||||||
assertThat(captor.value).isEqualTo(CurrencyConverter.ERROR_MESSAGE_NO_API_KEY)
|
assertThat(captor.value).matches("1 United States Dollar = \\d+\\.\\d{2,3} British Pound".toRegex())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
@DisplayName("Currency Converter Tests")
|
@DisplayName("Currency Converter Tests")
|
||||||
inner class CurrencyConverterTests {
|
inner class CurrencyConverterTests {
|
||||||
private val apiKey = getProperty(CurrencyConverter.API_KEY_PROP)
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Convert CAD to USD`() {
|
fun `Convert CAD to USD`() {
|
||||||
assertThat(
|
assertThat(
|
||||||
convertCurrency(apiKey, "100,000.00 CAD to USD").msg,
|
convertCurrency("100,000.00 CAD to USD").msg,
|
||||||
"convertCurrency(100,000.00 GBP to USD)"
|
"convertCurrency(100,000.00 GBP to USD)"
|
||||||
).matches("100,000.00 Canadian Dollar = \\d+\\.\\d{2,3} United States Dollar".toRegex())
|
).matches("100,000.00 Canadian Dollar = \\d+\\.\\d{2,3} United States Dollar".toRegex())
|
||||||
}
|
}
|
||||||
|
@ -99,7 +96,7 @@ class CurrencyConverterTest : LocalProperties() {
|
||||||
@Test
|
@Test
|
||||||
fun `Convert USD to EUR`() {
|
fun `Convert USD to EUR`() {
|
||||||
assertThat(
|
assertThat(
|
||||||
convertCurrency(apiKey, "100 USD to EUR").msg,
|
convertCurrency("100 USD to EUR").msg,
|
||||||
"convertCurrency(100 USD to EUR)"
|
"convertCurrency(100 USD to EUR)"
|
||||||
).matches("100 United States Dollar = \\d{2,3}\\.\\d{2,3} Euro".toRegex())
|
).matches("100 United States Dollar = \\d{2,3}\\.\\d{2,3} Euro".toRegex())
|
||||||
}
|
}
|
||||||
|
@ -107,14 +104,23 @@ class CurrencyConverterTest : LocalProperties() {
|
||||||
@Test
|
@Test
|
||||||
fun `Convert USD to GBP`() {
|
fun `Convert USD to GBP`() {
|
||||||
assertThat(
|
assertThat(
|
||||||
convertCurrency(apiKey, "1 USD to GBP").msg,
|
convertCurrency("1 USD to GBP").msg,
|
||||||
"convertCurrency(1 USD to BGP)"
|
"convertCurrency(1 USD to BGP)"
|
||||||
).matches("1 United States Dollar = 0\\.\\d{2,3} Pound Sterling".toRegex())
|
).matches("1 United States Dollar = 0\\.\\d{2,3} British Pound".toRegex())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Convert USD to Invalid Currency`() {
|
||||||
|
assertThat(convertCurrency("100 USD to FOO"), "convertCurrency(100 USD to FOO)").all {
|
||||||
|
prop(Message::msg)
|
||||||
|
.contains("Sounds like monopoly money to me! Try looking up the supported currency codes.")
|
||||||
|
isInstanceOf(ErrorMessage::class.java)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Convert USD to USD`() {
|
fun `Convert USD to USD`() {
|
||||||
assertThat(convertCurrency(apiKey, "100 USD to USD"), "convertCurrency(100 USD to USD)").all {
|
assertThat(convertCurrency("100 USD to USD"), "convertCurrency(100 USD to USD)").all {
|
||||||
prop(Message::msg).contains("You're kidding, right?")
|
prop(Message::msg).contains("You're kidding, right?")
|
||||||
isInstanceOf(PublicMessage::class.java)
|
isInstanceOf(PublicMessage::class.java)
|
||||||
}
|
}
|
||||||
|
@ -122,7 +128,7 @@ class CurrencyConverterTest : LocalProperties() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Invalid Query should throw exception`() {
|
fun `Invalid Query should throw exception`() {
|
||||||
assertThat(convertCurrency(apiKey, "100 USD"), "convertCurrency(100 USD)").all {
|
assertThat(convertCurrency("100 USD"), "convertCurrency(100 USD)").all {
|
||||||
prop(Message::msg).contains("Invalid query.")
|
prop(Message::msg).contains("Invalid query.")
|
||||||
isInstanceOf(ErrorMessage::class.java)
|
isInstanceOf(ErrorMessage::class.java)
|
||||||
}
|
}
|
|
@ -75,7 +75,7 @@
|
||||||
<div><code>mobibot: cryto btc</code></div>
|
<div><code>mobibot: cryto btc</code></div>
|
||||||
<div><code>mobibot: cryto eth eur</code></div>
|
<div><code>mobibot: cryto eth eur</code></div>
|
||||||
</li>
|
</li>
|
||||||
<li>Converting between currencies
|
<li>Converting between currencies using <a href="https://frankfurter.dev/">Frankfurter</a>
|
||||||
<div><code>mobibot: currency 17.54 USD to EUR</code></div>
|
<div><code>mobibot: currency 17.54 USD to EUR</code></div>
|
||||||
</li>
|
</li>
|
||||||
<li>Performing Google searches
|
<li>Performing Google searches
|
||||||
|
@ -86,7 +86,7 @@
|
||||||
<div><code>mobibot: chatgpt explain quantum computing in simple terms</code></div>
|
<div><code>mobibot: chatgpt explain quantum computing in simple terms</code></div>
|
||||||
<div><code>mobibot: gemini what are all the colors in a rainbow?</code></div>
|
<div><code>mobibot: gemini what are all the colors in a rainbow?</code></div>
|
||||||
</li>
|
</li>
|
||||||
<li>Displaying weather information
|
<li>Displaying weather information from <a href="https://openweathermap.org/">OpenWeatherMap</a>
|
||||||
<div><code>mobibot: weather san francisco</code></div>
|
<div><code>mobibot: weather san francisco</code></div>
|
||||||
<div><code>mobibot: weather 94123 </code></div>
|
<div><code>mobibot: weather 94123 </code></div>
|
||||||
<div><code>mobibot: weather tokyo, jp</code></div>
|
<div><code>mobibot: weather tokyo, jp</code></div>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue