/*
* This file is part of GNU Taler
* (C) 2020 Taler Systems S.A.
*
* GNU Taler is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation; either version 3, or (at your option) any later version.
*
* GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* GNU Taler; see the file COPYING. If not, see
*/
package net.taler.common
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.fasterxml.jackson.module.kotlin.readValue
import org.json.JSONObject
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Assert.fail
import org.junit.Test
class AmountTest {
@Test
fun `test fromJSONString() works`() {
var str = "TESTKUDOS:23.42"
var amount = Amount.fromJSONString(str)
assertEquals(str, amount.toJSONString())
assertEquals("TESTKUDOS", amount.currency)
assertEquals(23, amount.value)
assertEquals((0.42 * 1e8).toInt(), amount.fraction)
assertEquals("23.42 TESTKUDOS", amount.toString())
str = "EUR:500000000.00000001"
amount = Amount.fromJSONString(str)
assertEquals(str, amount.toJSONString())
assertEquals("EUR", amount.currency)
assertEquals(500000000, amount.value)
assertEquals(1, amount.fraction)
assertEquals("500000000.00000001 EUR", amount.toString())
str = "EUR:1500000000.00000003"
amount = Amount.fromJSONString(str)
assertEquals(str, amount.toJSONString())
assertEquals("EUR", amount.currency)
assertEquals(1500000000, amount.value)
assertEquals(3, amount.fraction)
assertEquals("1500000000.00000003 EUR", amount.toString())
}
@Test
fun `test fromJSONString() accepts max values, rejects above`() {
val maxValue = 4503599627370496
val str = "TESTKUDOS123:$maxValue.99999999"
val amount = Amount.fromJSONString(str)
assertEquals(str, amount.toJSONString())
assertEquals("TESTKUDOS123", amount.currency)
assertEquals(maxValue, amount.value)
assertEquals("$maxValue.99999999 TESTKUDOS123", amount.toString())
// longer currency not accepted
assertThrows("longer currency was accepted") {
Amount.fromJSONString("TESTKUDOS1234:$maxValue.99999999")
}
// max value + 1 not accepted
assertThrows("max value + 1 was accepted") {
Amount.fromJSONString("TESTKUDOS123:${maxValue + 1}.99999999")
}
// max fraction + 1 not accepted
assertThrows("max fraction + 1 was accepted") {
Amount.fromJSONString("TESTKUDOS123:$maxValue.999999990")
}
}
@Test
fun `test JSON deserialization()`() {
val mapper = ObjectMapper().registerModule(KotlinModule())
var str = "TESTKUDOS:23.42"
var amount: Amount = mapper.readValue("\"$str\"")
assertEquals(str, amount.toJSONString())
assertEquals("TESTKUDOS", amount.currency)
assertEquals(23, amount.value)
assertEquals((0.42 * 1e8).toInt(), amount.fraction)
assertEquals("23.42 TESTKUDOS", amount.toString())
str = "EUR:500000000.00000001"
amount = mapper.readValue("\"$str\"")
assertEquals(str, amount.toJSONString())
assertEquals("EUR", amount.currency)
assertEquals(500000000, amount.value)
assertEquals(1, amount.fraction)
assertEquals("500000000.00000001 EUR", amount.toString())
str = "EUR:1500000000.00000003"
amount = mapper.readValue("\"$str\"")
assertEquals(str, amount.toJSONString())
assertEquals("EUR", amount.currency)
assertEquals(1500000000, amount.value)
assertEquals(3, amount.fraction)
assertEquals("1500000000.00000003 EUR", amount.toString())
}
@Test
fun `test fromJSONString() rejections`() {
assertThrows {
Amount.fromJSONString("TESTKUDOS:0,5")
}
assertThrows {
Amount.fromJSONString("+TESTKUDOS:0.5")
}
assertThrows {
Amount.fromJSONString("0.5")
}
assertThrows {
Amount.fromJSONString(":0.5")
}
assertThrows {
Amount.fromJSONString("EUR::0.5")
}
assertThrows {
Amount.fromJSONString("EUR:.5")
}
}
@Test
fun `test fromJsonObject() works`() {
val map = mapOf(
"currency" to "TESTKUDOS",
"value" to "23",
"fraction" to "42000000"
)
val amount = Amount.fromJsonObject(JSONObject(map))
assertEquals("TESTKUDOS:23.42", amount.toJSONString())
assertEquals("TESTKUDOS", amount.currency)
assertEquals(23, amount.value)
assertEquals(42000000, amount.fraction)
assertEquals("23.42 TESTKUDOS", amount.toString())
}
@Test
fun `test fromJsonObject() accepts max values, rejects above`() {
val maxValue = 4503599627370496
val maxFraction = 99999999
var map = mapOf(
"currency" to "TESTKUDOS123",
"value" to "$maxValue",
"fraction" to "$maxFraction"
)
val amount = Amount.fromJsonObject(JSONObject(map))
assertEquals("TESTKUDOS123:$maxValue.$maxFraction", amount.toJSONString())
assertEquals("TESTKUDOS123", amount.currency)
assertEquals(maxValue, amount.value)
assertEquals(maxFraction, amount.fraction)
assertEquals("$maxValue.$maxFraction TESTKUDOS123", amount.toString())
// longer currency not accepted
assertThrows("longer currency was accepted") {
map = mapOf(
"currency" to "TESTKUDOS1234",
"value" to "$maxValue",
"fraction" to "$maxFraction"
)
Amount.fromJsonObject(JSONObject(map))
}
// max value + 1 not accepted
assertThrows("max value + 1 was accepted") {
map = mapOf(
"currency" to "TESTKUDOS123",
"value" to "${maxValue + 1}",
"fraction" to "$maxFraction"
)
Amount.fromJsonObject(JSONObject(map))
}
// max fraction + 1 not accepted
assertThrows("max fraction + 1 was accepted") {
map = mapOf(
"currency" to "TESTKUDOS123",
"value" to "$maxValue",
"fraction" to "${maxFraction + 1}"
)
Amount.fromJsonObject(JSONObject(map))
}
}
@Test
fun `test addition`() {
assertEquals(
Amount.fromJSONString("EUR:2"),
Amount.fromJSONString("EUR:1") + Amount.fromJSONString("EUR:1")
)
assertEquals(
Amount.fromJSONString("EUR:3"),
Amount.fromJSONString("EUR:1.5") + Amount.fromJSONString("EUR:1.5")
)
assertEquals(
Amount.fromJSONString("EUR:500000000.00000002"),
Amount.fromJSONString("EUR:500000000.00000001") + Amount.fromJSONString("EUR:0.00000001")
)
assertThrows("addition didn't overflow") {
Amount.fromJSONString("EUR:4503599627370496.99999999") + Amount.fromJSONString("EUR:0.00000001")
}
assertThrows("addition didn't overflow") {
Amount.fromJSONString("EUR:4000000000000000") + Amount.fromJSONString("EUR:4000000000000000")
}
}
@Test
fun `test times`() {
assertEquals(
Amount.fromJSONString("EUR:2"),
Amount.fromJSONString("EUR:2") * 1
)
assertEquals(
Amount.fromJSONString("EUR:2"),
Amount.fromJSONString("EUR:1") * 2
)
assertEquals(
Amount.fromJSONString("EUR:4.5"),
Amount.fromJSONString("EUR:1.5") * 3
)
assertEquals(
Amount.fromJSONString("EUR:1500000000.00000003"),
Amount.fromJSONString("EUR:500000000.00000001") * 3
)
assertThrows("times didn't overflow") {
Amount.fromJSONString("EUR:4000000000000000") * 2
}
}
@Test
fun `test subtraction`() {
assertEquals(
Amount.fromJSONString("EUR:0"),
Amount.fromJSONString("EUR:1") - Amount.fromJSONString("EUR:1")
)
assertEquals(
Amount.fromJSONString("EUR:1.5"),
Amount.fromJSONString("EUR:3") - Amount.fromJSONString("EUR:1.5")
)
assertEquals(
Amount.fromJSONString("EUR:500000000.00000001"),
Amount.fromJSONString("EUR:500000000.00000002") - Amount.fromJSONString("EUR:0.00000001")
)
assertThrows("subtraction didn't underflow") {
Amount.fromJSONString("EUR:23.42") - Amount.fromJSONString("EUR:42.23")
}
assertThrows("subtraction didn't underflow") {
Amount.fromJSONString("EUR:0.5") - Amount.fromJSONString("EUR:0.50000001")
}
}
@Test
fun `test isZero()`() {
assertTrue(Amount.zero("EUR").isZero())
assertTrue(Amount.fromJSONString("EUR:0").isZero())
assertTrue(Amount.fromJSONString("EUR:0.0").isZero())
assertTrue(Amount.fromJSONString("EUR:0.00000").isZero())
assertTrue((Amount.fromJSONString("EUR:1.001") - Amount.fromJSONString("EUR:1.001")).isZero())
assertFalse(Amount.fromJSONString("EUR:0.00000001").isZero())
assertFalse(Amount.fromJSONString("EUR:1.0").isZero())
assertFalse(Amount.fromJSONString("EUR:0001.0").isZero())
}
@Test
fun `test comparision`() {
assertTrue(Amount.fromJSONString("EUR:0") <= Amount.fromJSONString("EUR:0"))
assertTrue(Amount.fromJSONString("EUR:0") <= Amount.fromJSONString("EUR:0.00000001"))
assertTrue(Amount.fromJSONString("EUR:0") < Amount.fromJSONString("EUR:0.00000001"))
assertTrue(Amount.fromJSONString("EUR:0") < Amount.fromJSONString("EUR:1"))
assertTrue(Amount.fromJSONString("EUR:0") == Amount.fromJSONString("EUR:0"))
assertTrue(Amount.fromJSONString("EUR:42") == Amount.fromJSONString("EUR:42"))
assertTrue(Amount.fromJSONString("EUR:42.00000001") == Amount.fromJSONString("EUR:42.00000001"))
assertTrue(Amount.fromJSONString("EUR:42.00000001") >= Amount.fromJSONString("EUR:42.00000001"))
assertTrue(Amount.fromJSONString("EUR:42.00000002") >= Amount.fromJSONString("EUR:42.00000001"))
assertTrue(Amount.fromJSONString("EUR:42.00000002") > Amount.fromJSONString("EUR:42.00000001"))
assertTrue(Amount.fromJSONString("EUR:0.00000002") > Amount.fromJSONString("EUR:0.00000001"))
assertTrue(Amount.fromJSONString("EUR:0.00000001") > Amount.fromJSONString("EUR:0"))
assertTrue(Amount.fromJSONString("EUR:2") > Amount.fromJSONString("EUR:1"))
assertThrows("could compare amounts with different currencies") {
Amount.fromJSONString("EUR:0.5") < Amount.fromJSONString("USD:0.50000001")
}
}
private inline fun assertThrows(
msg: String? = null,
function: () -> Any
) {
try {
function.invoke()
fail(msg)
} catch (e: Exception) {
assertTrue(e is T)
}
}
}