Moved to exchangerate.host API for currency conversion

This commit is contained in:
Erik C. Thauvin 2022-07-10 13:53:32 -07:00
parent a33b8354b8
commit 070d29c7dd
7 changed files with 77 additions and 121 deletions

2
.idea/misc.xml generated
View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="17" project-jdk-type="JavaSDK" /> <component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="18" project-jdk-type="JavaSDK" />
</project> </project>

View file

@ -1,9 +1,11 @@
# mobibot
[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot) [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_mobibot&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_mobibot)
[![Known Vulnerabilities](https://snyk.io/test/github/ethauvin/mobibot/badge.svg?targetFile=build.gradle)](https://snyk.io/test/github/ethauvin/mobibot?targetFile=build.gradle) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master) [![Known Vulnerabilities](https://snyk.io/test/github/ethauvin/mobibot/badge.svg?targetFile=build.gradle)](https://snyk.io/test/github/ethauvin/mobibot?targetFile=build.gradle) [![GitHub CI](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml/badge.svg)](https://github.com/ethauvin/mobibot/actions/workflows/gradle.yml) [![CircleCI](https://circleci.com/gh/ethauvin/mobibot/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/mobibot/tree/master)
Some very basic instructions: Some very basic instructions:
``` ```sh
{ clone with git or download the ZIP } { clone with git or download the ZIP }
git clone https://github.com/ethauvin/mobibot.git git clone https://github.com/ethauvin/mobibot.git

View file

@ -5,10 +5,10 @@ plugins {
id 'io.gitlab.arturbosch.detekt' version '1.20.0' id 'io.gitlab.arturbosch.detekt' version '1.20.0'
id 'java' id 'java'
id 'net.thauvin.erik.gradle.semver' version '1.0.4' id 'net.thauvin.erik.gradle.semver' version '1.0.4'
id 'org.jetbrains.kotlin.jvm' version '1.6.21' id 'org.jetbrains.kotlin.jvm' version '1.7.10'
id 'org.jetbrains.kotlin.kapt' version '1.6.21' id 'org.jetbrains.kotlin.kapt' version '1.7.10'
id 'org.jetbrains.kotlinx.kover' version '0.5.0' id 'org.jetbrains.kotlinx.kover' version '0.5.1'
id 'org.sonarqube' version '3.3' id 'org.sonarqube' version '3.4.0.2513'
id 'pmd' id 'pmd'
} }
@ -27,8 +27,8 @@ def isNonStable = { String version ->
mainClassName = packageName + '.Mobibot' mainClassName = packageName + '.Mobibot'
ext.versions = [ ext.versions = [
log4j: '2.17.2', log4j: '2.18.0',
pmd : '6.44.0', pmd : '6.47.0',
] ]
repositories { repositories {
@ -59,7 +59,7 @@ dependencies {
// Kotlin // Kotlin
implementation platform('org.jetbrains.kotlin:kotlin-bom') implementation platform('org.jetbrains.kotlin:kotlin-bom')
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.3'
// Logging // Logging
implementation 'org.slf4j:slf4j-api:1.7.36' implementation 'org.slf4j:slf4j-api:1.7.36'
@ -68,11 +68,11 @@ dependencies {
implementation "org.apache.logging.log4j:log4j-slf4j-impl:$versions.log4j" implementation "org.apache.logging.log4j:log4j-slf4j-impl:$versions.log4j"
implementation 'com.rometools:rome:1.18.0' implementation 'com.rometools:rome:1.18.0'
implementation 'com.squareup.okhttp3:okhttp:4.9.3' implementation 'com.squareup.okhttp3:okhttp:4.10.0'
implementation 'net.aksingh:owm-japis:2.5.3.0' implementation 'net.aksingh:owm-japis:2.5.3.0'
implementation 'net.objecthunter:exp4j:0.4.8' implementation 'net.objecthunter:exp4j:0.4.8'
implementation 'org.json:json:20220320' implementation 'org.json:json:20220320'
implementation 'org.jsoup:jsoup:1.14.3' implementation 'org.jsoup:jsoup:1.15.2'
implementation 'org.twitter4j:twitter4j-core:4.0.7' implementation 'org.twitter4j:twitter4j-core:4.0.7'
implementation 'net.thauvin.erik:cryptoprice:0.9.0' implementation 'net.thauvin.erik:cryptoprice:0.9.0'
@ -81,7 +81,7 @@ dependencies {
testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.25' testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.25'
// testImplementation 'org.mockito.kotlin:mockito-kotlin:4.0.0' // testImplementation 'org.mockito.kotlin:mockito-kotlin:4.0.0'
// testImplementation "org.mockito:mockito-core:4.0.0" // testImplementation "org.mockito:mockito-core:4.0.0"
testImplementation 'org.testng:testng:7.5' testImplementation 'org.testng:testng:7.6.1'
} }
test { test {

View file

@ -13,13 +13,10 @@
<ID>LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean )</ID> <ID>LongParameterList:Twitter.kt$Twitter.Companion$( consumerKey: String?, consumerSecret: String?, token: String?, tokenSecret: String?, handle: String?, message: String, isDm: Boolean )</ID>
<ID>MagicNumber:Comment.kt$Comment$3</ID> <ID>MagicNumber:Comment.kt$Comment$3</ID>
<ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$11</ID> <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$11</ID>
<ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter$3</ID>
<ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$3</ID> <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$3</ID>
<ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$4</ID> <ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$4</ID>
<ID>MagicNumber:CurrencyConverter.kt$CurrencyConverter.Companion$8</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:Dice.kt$Dice$6</ID>
<ID>MagicNumber:Ignore.kt$Ignore$8</ID> <ID>MagicNumber:Ignore.kt$Ignore$8</ID>
<ID>MagicNumber:Info.kt$Info.Companion$30</ID> <ID>MagicNumber:Info.kt$Info.Companion$30</ID>
<ID>MagicNumber:Info.kt$Info.Companion$365</ID> <ID>MagicNumber:Info.kt$Info.Companion$365</ID>
@ -47,6 +44,7 @@
<ID>NestedBlockDepth:Addons.kt$Addons$ fun add(module: AbstractModule)</ID> <ID>NestedBlockDepth:Addons.kt$Addons$ fun add(module: AbstractModule)</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 fun convertCurrency(query: String): Message</ID> <ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$ @JvmStatic fun convertCurrency(query: String): Message</ID>
<ID>NestedBlockDepth:CurrencyConverter.kt$CurrencyConverter.Companion$@JvmStatic @Throws(ModuleException::class) fun loadSymbols()</ID>
<ID>NestedBlockDepth:EntryLink.kt$EntryLink$ private fun setTags(tags: List&lt;String?&gt;)</ID> <ID>NestedBlockDepth:EntryLink.kt$EntryLink$ private fun setTags(tags: List&lt;String?&gt;)</ID>
<ID>NestedBlockDepth:FeedsMgr.kt$FeedsMgr.Companion$ @JvmStatic @Throws(IOException::class, FeedException::class) fun loadFeed(entries: Entries, currentFile: String = currentXml): String</ID> <ID>NestedBlockDepth:FeedsMgr.kt$FeedsMgr.Companion$ @JvmStatic @Throws(IOException::class, FeedException::class) fun loadFeed(entries: Entries, currentFile: String = currentXml): String</ID>
<ID>NestedBlockDepth:FeedsMgr.kt$FeedsMgr.Companion$ @JvmStatic fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID> <ID>NestedBlockDepth:FeedsMgr.kt$FeedsMgr.Companion$ @JvmStatic fun saveFeed(entries: Entries, currentFile: String = currentXml)</ID>
@ -73,6 +71,5 @@
<ID>TooGenericExceptionCaught:StockQuote.kt$StockQuote.Companion$e: NullPointerException</ID> <ID>TooGenericExceptionCaught:StockQuote.kt$StockQuote.Companion$e: NullPointerException</ID>
<ID>TooGenericExceptionCaught:Weather2.kt$Weather2.Companion$e: NullPointerException</ID> <ID>TooGenericExceptionCaught:Weather2.kt$Weather2.Companion$e: NullPointerException</ID>
<ID>TooManyFunctions:Tell.kt$Tell : AbstractCommand</ID> <ID>TooManyFunctions:Tell.kt$Tell : AbstractCommand</ID>
<ID>UnusedPrivateMember:Dice.kt$Dice$private val sides = 6</ID>
</CurrentIssues> </CurrentIssues>
</SmellBaseline> </SmellBaseline>

View file

@ -31,27 +31,22 @@
*/ */
package net.thauvin.erik.mobibot.modules package net.thauvin.erik.mobibot.modules
import net.thauvin.erik.mobibot.Utils.bold
import net.thauvin.erik.mobibot.Utils.bot import net.thauvin.erik.mobibot.Utils.bot
import net.thauvin.erik.mobibot.Utils.helpCmdSyntax import net.thauvin.erik.mobibot.Utils.helpCmdSyntax
import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.Utils.helpFormat
import net.thauvin.erik.mobibot.Utils.reader
import net.thauvin.erik.mobibot.Utils.sendList import net.thauvin.erik.mobibot.Utils.sendList
import net.thauvin.erik.mobibot.Utils.sendMessage import net.thauvin.erik.mobibot.Utils.sendMessage
import net.thauvin.erik.mobibot.Utils.today import net.thauvin.erik.mobibot.Utils.today
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
import org.jdom2.JDOMException import org.json.JSONObject
import org.jdom2.input.SAXBuilder
import org.pircbotx.hooks.types.GenericMessageEvent import org.pircbotx.hooks.types.GenericMessageEvent
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.IOException import java.io.IOException
import java.net.URL import java.net.URL
import java.text.NumberFormat
import java.util.Currency
import java.util.Locale
import javax.xml.XMLConstants
/** /**
* The CurrencyConverter module. * The CurrencyConverter module.
@ -64,7 +59,7 @@ class CurrencyConverter : ThreadedModule() {
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) { override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
synchronized(this) { synchronized(this) {
if (pubDate != today()) { if (pubDate != today()) {
EXCHANGE_RATES.clear() SYMBOLS.clear()
} }
} }
super.commandResponse(channel, cmd, args, event) super.commandResponse(channel, cmd, args, event)
@ -74,52 +69,55 @@ class CurrencyConverter : ThreadedModule() {
* Converts the specified currencies. * Converts the specified currencies.
*/ */
override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) { override fun run(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
if (EXCHANGE_RATES.isEmpty()) { if (SYMBOLS.isEmpty()) {
try { try {
loadRates() loadSymbols()
} catch (e: ModuleException) { } catch (e: ModuleException) {
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
} }
} }
if (EXCHANGE_RATES.isEmpty()) { if (SYMBOLS.isEmpty()) {
event.respond(EMPTY_RATE_TABLE) event.respond(EMPTY_SYMBOLS_TABLE)
} else if (args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+".toRegex())) { } else if (args.matches("\\d+([,\\d]+)?(\\.\\d+)? [a-zA-Z]{3}+ to [a-zA-Z]{3}+".toRegex())) {
val msg = convertCurrency(args) val msg = convertCurrency(args)
event.respond(msg.msg) event.respond(msg.msg)
if (msg.isError) { if (msg.isError) {
helpResponse(event) helpResponse(event)
} }
} else if (args.contains(CURRENCY_RATES_KEYWORD)) { } else if (args.contains(CURRENCY_SYMBOLS_KEYWORD)) {
event.sendMessage("The reference rates for ${pubDate.bold()} are:") event.sendMessage("The supported currency symbols are: ")
event.sendList(currencyRates(), 3, " ", isIndent = true) event.sendList(ArrayList(SYMBOLS.keys.sorted()), 11, isIndent = true)
} else { } else {
helpResponse(event) helpResponse(event)
} }
} }
override fun helpResponse(event: GenericMessageEvent): Boolean { override fun helpResponse(event: GenericMessageEvent): Boolean {
if (EXCHANGE_RATES.isEmpty()) { if (SYMBOLS.isEmpty()) {
try { try {
loadRates() loadSymbols()
} catch (e: ModuleException) { } catch (e: ModuleException) {
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e) if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
} }
} }
if (EXCHANGE_RATES.isEmpty()) { if (SYMBOLS.isEmpty()) {
event.sendMessage(EMPTY_RATE_TABLE) event.sendMessage(EMPTY_SYMBOLS_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:")
event.sendMessage(helpFormat(helpCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled))) event.sendMessage(helpFormat(helpCmdSyntax("%c $CURRENCY_CMD 100 USD to EUR", nick, isPrivateMsgEnabled)))
event.sendMessage("For a listing of current reference rates:")
event.sendMessage( event.sendMessage(
helpFormat( helpFormat(
helpCmdSyntax("%c $CURRENCY_CMD $CURRENCY_RATES_KEYWORD", nick, isPrivateMsgEnabled) helpCmdSyntax("%c $CURRENCY_CMD 50,000 GBP to BTC", nick, isPrivateMsgEnabled)
)
)
event.sendMessage("To list the supported currency symbols: ")
event.sendMessage(
helpFormat(
helpCmdSyntax("%c $CURRENCY_CMD $CURRENCY_SYMBOLS_KEYWORD", nick, isPrivateMsgEnabled)
) )
) )
event.sendMessage("The supported currencies are: ")
event.sendList(ArrayList(EXCHANGE_RATES.keys), 11, isIndent = true)
} }
return true return true
} }
@ -128,27 +126,18 @@ class CurrencyConverter : ThreadedModule() {
// Currency command // Currency command
private const val CURRENCY_CMD = "currency" private const val CURRENCY_CMD = "currency"
// Rates keyword // Currency synbols keywords
private const val CURRENCY_RATES_KEYWORD = "rates" private const val CURRENCY_SYMBOLS_KEYWORD = "symbols"
// Empty rate table. // Empty symbols table.
private const val EMPTY_RATE_TABLE = "Sorry, but the exchange rate table is empty." private const val EMPTY_SYMBOLS_TABLE = "Sorry, but the currency symbols table is empty."
// Exchange rates // Currency Symbols
private val EXCHANGE_RATES: MutableMap<String, String> = mutableMapOf() private val SYMBOLS: MutableMap<String, String> = mutableMapOf()
// Exchange rates table URL
private const val EXCHANGE_TABLE_URL = "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml"
// Last exchange rates table publication date // Last exchange rates table publication date
private var pubDate = "" private var pubDate = ""
private fun Double.formatCurrency(currency: String): String =
NumberFormat.getCurrencyInstance(Locale.getDefault(Locale.Category.FORMAT)).let {
it.currency = Currency.getInstance(currency)
it.format(this)
}
/** /**
* Converts from a currency to another. * Converts from a currency to another.
*/ */
@ -161,17 +150,21 @@ class CurrencyConverter : ThreadedModule() {
} else { } else {
val to = cmds[1].uppercase() val to = cmds[1].uppercase()
val from = cmds[3].uppercase() val from = cmds[3].uppercase()
val toRate = EXCHANGE_RATES[to] if (SYMBOLS.contains(to) && SYMBOLS.contains(from)) {
val fromRate = EXCHANGE_RATES[from]
if (!toRate.isNullOrBlank() && !fromRate.isNullOrBlank()) {
try { try {
val amt = cmds[0].replace(",", "").toDouble() val amt = cmds[0].replace(",", "")
val url = URL("https://api.exchangerate.host/convert?from=$to&to=$from&amount=$amt")
val json = JSONObject(url.reader())
if (json.getBoolean("success")) {
PublicMessage( PublicMessage(
amt.formatCurrency(to) + " = " "${cmds[0]} ${SYMBOLS[to]} = ${json.get("result")} ${SYMBOLS[from]}"
+ (amt * toRate.toDouble() / fromRate.toDouble()).formatCurrency(from)
) )
} catch (e: NumberFormatException) { } else {
ErrorMessage("Let's try with some real numbers next time, okay?") ErrorMessage("Sorry, an error occurred while converting the currencies.")
}
} catch (ignore: IOException) {
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!")
@ -180,48 +173,23 @@ class CurrencyConverter : ThreadedModule() {
} else ErrorMessage("Invalid query. Let's try again.") } else ErrorMessage("Invalid query. Let's try again.")
} }
@JvmStatic
fun currencyRates(): List<String> {
val rates = buildList {
for ((key, value) in EXCHANGE_RATES.toSortedMap()) {
add("$key: ${value.padStart(8)}")
}
}
return rates
}
@JvmStatic @JvmStatic
@Throws(ModuleException::class) @Throws(ModuleException::class)
fun loadRates() { fun loadSymbols() {
if (EXCHANGE_RATES.isEmpty()) { if (SYMBOLS.isEmpty()) {
try { try {
val builder = SAXBuilder() val url = URL("https://api.exchangerate.host/symbols")
// See https://rules.sonarsourcecom/java/tag/owasp/RSPEC-2755 val json = JSONObject(url.reader())
builder.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "") if (json.getBoolean("success")) {
builder.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "") val symbols = json.getJSONObject("symbols")
builder.ignoringElementContentWhitespace = true for (key in symbols.keys()) {
val doc = builder.build(URL(EXCHANGE_TABLE_URL)) SYMBOLS[key] = symbols.getJSONObject(key).getString("description")
val root = doc.rootElement }
val ns = root.getNamespace("")
val cubeRoot = root.getChild("Cube", ns)
val cubeTime = cubeRoot.getChild("Cube", ns)
pubDate = cubeTime.getAttribute("time").value
val cubes = cubeTime.children
for (cube in cubes) {
EXCHANGE_RATES[cube.getAttribute("currency").value] = cube.getAttribute("rate").value
} }
EXCHANGE_RATES["EUR"] = "1"
} catch (e: JDOMException) {
throw ModuleException(
"loadRates(): JDOM",
"An JDOM parsing error has occurred while parsing the exchange rates table.",
e
)
} catch (e: IOException) { } catch (e: IOException) {
throw ModuleException( throw ModuleException(
"loadRates(): IOE", "loadSymbols(): IOE",
"An IO error has occurred while parsing the exchange rates table.", "An IO error has occurred while retrieving the currency symbols table.",
e e
) )
} }

View file

@ -33,16 +33,12 @@ package net.thauvin.erik.mobibot.modules
import assertk.all import assertk.all
import assertk.assertThat import assertk.assertThat
import assertk.assertions.any
import assertk.assertions.contains import assertk.assertions.contains
import assertk.assertions.isGreaterThan
import assertk.assertions.isInstanceOf import assertk.assertions.isInstanceOf
import assertk.assertions.matches import assertk.assertions.matches
import assertk.assertions.prop import assertk.assertions.prop
import assertk.assertions.size
import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.convertCurrency import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.convertCurrency
import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.currencyRates import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.loadSymbols
import net.thauvin.erik.mobibot.modules.CurrencyConverter.Companion.loadRates
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
@ -56,7 +52,7 @@ class CurrencyConverterTest {
@BeforeClass @BeforeClass
@Throws(ModuleException::class) @Throws(ModuleException::class)
fun before() { fun before() {
loadRates() loadSymbols()
} }
@Test @Test
@ -64,7 +60,11 @@ class CurrencyConverterTest {
assertThat( assertThat(
convertCurrency("100 USD to EUR").msg, convertCurrency("100 USD to EUR").msg,
"100 USD to EUR" "100 USD to EUR"
).matches("\\$100\\.00 = €\\d{2,3}\\.\\d{2}".toRegex()) ).matches("100 United States Dollar = \\d{2,3}\\.\\d+ Euro".toRegex())
assertThat(
convertCurrency("100,000.00 GBP to BTC").msg,
"100 USD to EUR"
).matches("100,000.00 British Pound Sterling = \\d{1,2}\\.\\d+ Bitcoin".toRegex())
assertThat(convertCurrency("100 USD to USD"), "100 USD to USD").all { assertThat(convertCurrency("100 USD to USD"), "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)
@ -74,15 +74,4 @@ class CurrencyConverterTest {
isInstanceOf(ErrorMessage::class.java) isInstanceOf(ErrorMessage::class.java)
} }
} }
@Test
fun testCurrencyRates() {
val rates = currencyRates()
assertThat(rates).all {
size().isGreaterThan(30)
any { it.matches("[A-Z]{3}: +[\\d.]+".toRegex()) }
contains("EUR: 1")
}
}
} }

View file

@ -1,9 +1,9 @@
#Generated by the Semver Plugin for Gradle #Generated by the Semver Plugin for Gradle
#Tue Apr 19 17:22:30 PDT 2022 #Sun Jul 10 14:02:30 PDT 2022
version.buildmeta=221 version.buildmeta=257
version.major=0 version.major=0
version.minor=8 version.minor=8
version.patch=0 version.patch=0
version.prerelease=rc version.prerelease=rc
version.project=mobibot version.project=mobibot
version.semver=0.8.0-rc+221 version.semver=0.8.0-rc+257