cashless2ecash

cashless2ecash: pay with cards for digital cash (experimental)
Log | Files | Refs | README

amount_test.go (9130B)


      1 // This file is part of taler-go, the Taler Go implementation.
      2 // Copyright (C) 2022 Martin Schanzenbach
      3 // Copyright (C) 2024 Joel Häberli
      4 //
      5 // Taler Go is free software: you can redistribute it and/or modify it
      6 // under the terms of the GNU Affero General Public License as published
      7 // by the Free Software Foundation, either version 3 of the License,
      8 // or (at your option) any later version.
      9 //
     10 // Taler Go is distributed in the hope that it will be useful, but
     11 // WITHOUT ANY WARRANTY; without even the implied warranty of
     12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13 // Affero General Public License for more details.
     14 //
     15 // You should have received a copy of the GNU Affero General Public License
     16 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
     17 //
     18 // SPDX-License-Identifier: AGPL3.0-or-later
     19 
     20 package internal_utils_test
     21 
     22 import (
     23 	internal_utils "c2ec/internal/utils"
     24 	"fmt"
     25 	"strconv"
     26 	"testing"
     27 )
     28 
     29 var a = internal_utils.Amount{
     30 	Currency: "EUR",
     31 	Value:    1,
     32 	Fraction: 50000000,
     33 }
     34 var b = internal_utils.Amount{
     35 	Currency: "EUR",
     36 	Value:    23,
     37 	Fraction: 70007000,
     38 }
     39 var c = internal_utils.Amount{
     40 	Currency: "EUR",
     41 	Value:    25,
     42 	Fraction: 20007000,
     43 }
     44 
     45 func TestAmountAdd(t *testing.T) {
     46 	d, err := a.Add(b, 8)
     47 	if err != nil {
     48 		t.Errorf("Failed adding amount")
     49 	}
     50 	if c.String(8) != d.String(8) {
     51 		t.Errorf("Failed to add to correct amount")
     52 	}
     53 }
     54 
     55 func TestAmountSub(t *testing.T) {
     56 	d, err := c.Sub(b, 8)
     57 	if err != nil {
     58 		t.Errorf("Failed substracting amount")
     59 	}
     60 	if a.String(8) != d.String(8) {
     61 		t.Errorf("Failed to substract to correct amount")
     62 	}
     63 }
     64 
     65 func TestAmountLarge(t *testing.T) {
     66 	x, err := internal_utils.ParseAmount("EUR:50", 2)
     67 	if err != nil {
     68 		fmt.Println(err)
     69 		t.Errorf("Failed")
     70 	}
     71 	_, err = x.Add(a, 2)
     72 	if err != nil {
     73 		fmt.Println(err)
     74 		t.Errorf("Failed")
     75 	}
     76 }
     77 
     78 func TestAmountSub2(t *testing.T) {
     79 
     80 	amnts := []string{
     81 		"CHF:30",
     82 		"EUR:20.34",
     83 		"CHF:23.99",
     84 		"CHF:50.35",
     85 		"USD:109992332",
     86 		"CHF:0.0",
     87 		"EUR:00.0",
     88 		"USD:0.00",
     89 		"CHF:00.00",
     90 	}
     91 
     92 	for _, a := range amnts {
     93 		am, err := internal_utils.ParseAmount(a, 2)
     94 		if err != nil {
     95 			fmt.Println("parsing failed!", a, err)
     96 			t.FailNow()
     97 		}
     98 		fmt.Println("subtracting", am.String(2))
     99 		a2, err := am.Sub(*am, 2)
    100 		if err != nil {
    101 			fmt.Println("subtracting failed!", a, err)
    102 			t.FailNow()
    103 		}
    104 		fmt.Println("subtraction result", a2.String(2))
    105 		if !a2.IsZero() {
    106 			fmt.Println("subtracting failure... expected zero amount but was", a2.String(2))
    107 		}
    108 	}
    109 }
    110 
    111 func TestAmountSub3(t *testing.T) {
    112 
    113 	amnts := []string{
    114 		"CHF:30.0004",
    115 		"CHF:30.004",
    116 		"CHF:30.04",
    117 		"CHF:30.4",
    118 		"CHF:30",
    119 	}
    120 
    121 	for _, a := range amnts {
    122 		am, err := internal_utils.ParseAmount(a, 4)
    123 		if err != nil {
    124 			fmt.Println("parsing failed!", a, err)
    125 			t.FailNow()
    126 		}
    127 		fmt.Println("subtracting", am.String(4))
    128 		a2, err := am.Sub(*am, 4)
    129 		if err != nil {
    130 			fmt.Println("subtracting failed!", a, err)
    131 			t.FailNow()
    132 		}
    133 		fmt.Println("subtraction result", a2.String(4))
    134 		if !a2.IsZero() && a2.String(4) != am.Currency+":0" {
    135 			fmt.Println("subtracting failure... expected zero amount but was", a2.String(4))
    136 		}
    137 	}
    138 }
    139 
    140 func TestParseValid(t *testing.T) {
    141 
    142 	amnts := []string{
    143 		"CHF:30",
    144 		"EUR:20.34",
    145 		"CHF:23.99",
    146 		"CHF:50.35",
    147 		"USD:109992332",
    148 		"CHF:0.0",
    149 		"EUR:00.0",
    150 		"USD:0.00",
    151 		"CHF:00.00",
    152 	}
    153 
    154 	for _, a := range amnts {
    155 		_, err := internal_utils.ParseAmount(a, 2)
    156 		if err != nil {
    157 			fmt.Println("failed!", a)
    158 			t.FailNow()
    159 		}
    160 	}
    161 }
    162 
    163 func TestParseInvalid(t *testing.T) {
    164 
    165 	amnts := []string{
    166 		"CHF",
    167 		"EUR:.34",
    168 		"CHF:23.",
    169 		"EUR:452:001",
    170 		"USD:1099928583593859583332",
    171 		"CHF:4564:005",
    172 		"CHF:.40",
    173 	}
    174 
    175 	for _, a := range amnts {
    176 		_, err := internal_utils.ParseAmount(a, 2)
    177 		if err == nil {
    178 			fmt.Println("failed! (expected error)", a)
    179 			t.FailNow()
    180 		}
    181 	}
    182 }
    183 
    184 func TestFormatAmountValid(t *testing.T) {
    185 
    186 	amnts := []string{
    187 		"CHF:30",
    188 		"EUR:20.34",
    189 		"CHF:23.99",
    190 		"USD:109992332",
    191 		"CHF:20.05",
    192 		"USD:109992332.01",
    193 		"CHF:10.00",
    194 		"",
    195 	}
    196 	amntsParsed := make([]internal_utils.Amount, 0)
    197 	for _, a := range amnts {
    198 		a, err := internal_utils.ParseAmount(a, 2)
    199 		if err != nil {
    200 			fmt.Println("failed!", err)
    201 			t.FailNow()
    202 		}
    203 		amntsParsed = append(amntsParsed, *a)
    204 	}
    205 
    206 	amntsFormatted := make([]string, 0)
    207 	for _, a := range amntsParsed {
    208 		amntsFormatted = append(amntsFormatted, internal_utils.FormatAmount(&a, 2))
    209 	}
    210 
    211 	for i, frmtd := range amntsFormatted {
    212 		fmt.Println(frmtd)
    213 		expectation, err1 := internal_utils.ParseAmount(amnts[i], 2)
    214 		reality, err2 := internal_utils.ParseAmount(frmtd, 2)
    215 		if err1 != nil || err2 != nil {
    216 			fmt.Println("failed!", err1, err2)
    217 			t.FailNow()
    218 		}
    219 
    220 		if expectation.Currency != reality.Currency ||
    221 			expectation.Value != reality.Value ||
    222 			expectation.Fraction != reality.Fraction {
    223 
    224 			fmt.Println("failed!", amnts[i], frmtd)
    225 			t.FailNow()
    226 		}
    227 
    228 		fmt.Println("success!", amnts[i], frmtd)
    229 	}
    230 }
    231 
    232 func TestFormatAmountInvalid(t *testing.T) {
    233 
    234 	amnts := []string{
    235 		"CHF:30",
    236 		"EUR:20.34",
    237 		"CHF:23.99",
    238 		"USD:109992332",
    239 		"USD:30.30",
    240 	}
    241 	amntsParsed := make([]internal_utils.Amount, 0)
    242 	for _, a := range amnts {
    243 		a, err := internal_utils.ParseAmount(a, 2)
    244 		if err != nil {
    245 			fmt.Println("failed!", err)
    246 			t.FailNow()
    247 		}
    248 		amntsParsed = append(amntsParsed, *a)
    249 	}
    250 
    251 	amntsFormatted := make([]string, 0)
    252 	for _, a := range amntsParsed {
    253 		amntsFormatted = append(amntsFormatted, internal_utils.FormatAmount(&a, 2))
    254 	}
    255 
    256 	for i, frmtd := range amntsFormatted {
    257 		fmt.Println(frmtd)
    258 		expectation, err1 := internal_utils.ParseAmount(amnts[i], 2)
    259 		reality, err2 := internal_utils.ParseAmount(frmtd, 2)
    260 		if err1 != nil || err2 != nil {
    261 			fmt.Println("failed!", err1, err2)
    262 			t.FailNow()
    263 		}
    264 
    265 		if expectation.Currency != reality.Currency ||
    266 			expectation.Value != reality.Value ||
    267 			expectation.Fraction != reality.Fraction {
    268 
    269 			fmt.Println("failed!", amnts[i], frmtd)
    270 			t.FailNow()
    271 		}
    272 	}
    273 }
    274 
    275 func TestFeesSub(t *testing.T) {
    276 
    277 	amountWithFeesStr := fmt.Sprintf("%s:%s", "CHF", "5.00")
    278 	amountWithFees, err := internal_utils.ParseAmount(amountWithFeesStr, 3)
    279 	if err != nil {
    280 		fmt.Println("failed!", err)
    281 		t.FailNow()
    282 	}
    283 
    284 	fees, err := internal_utils.ParseAmount("CHF:0.005", 3)
    285 	if err != nil {
    286 		fmt.Println("failed!", err)
    287 		t.FailNow()
    288 	}
    289 
    290 	refundAmount, err := amountWithFees.Sub(*fees, 3)
    291 	if err != nil {
    292 		fmt.Println("failed!", err)
    293 		t.FailNow()
    294 	}
    295 
    296 	if amnt := refundAmount.String(3); amnt != "CHF:4.995" {
    297 		fmt.Println("expected the refund amount to be CHF:4.995, but it was", amnt)
    298 	} else {
    299 		fmt.Println("refundable amount:", amnt)
    300 	}
    301 }
    302 
    303 func TestFloat64ToAmount(t *testing.T) {
    304 
    305 	type tuple struct {
    306 		a string
    307 		b int
    308 	}
    309 
    310 	floats := map[float64]tuple{
    311 		2.345:    {"2.345", 3},
    312 		4.204:    {"4.204", 3},
    313 		1293.2:   {"1293.2", 1},
    314 		1294.2:   {"1294.20", 2},
    315 		1295.02:  {"1295.02", 2},
    316 		2424.003: {"2424.003", 3},
    317 	}
    318 
    319 	for k, v := range floats {
    320 
    321 		str := strconv.FormatFloat(k, 'f', v.b, 64)
    322 		if str != v.a {
    323 			fmt.Println("failed! expected", v.a, "got", str)
    324 			t.FailNow()
    325 		}
    326 	}
    327 }
    328 
    329 func TestIsSmallerThan(t *testing.T) {
    330 	amnts := []string{
    331 		"CHF:0",
    332 		"CHF:0.01",
    333 		"CHF:0.1",
    334 		"CHF:10",
    335 		"CHF:20",
    336 		"CHF:20.01",
    337 		"CHF:20.02",
    338 		"CHF:20.023",
    339 	}
    340 	amntsParsed := make([]internal_utils.Amount, 0)
    341 	for _, a := range amnts {
    342 		a, err := internal_utils.ParseAmount(a, 3)
    343 		if err != nil {
    344 			fmt.Println("failed!", err)
    345 			t.FailNow()
    346 		}
    347 		amntsParsed = append(amntsParsed, *a)
    348 	}
    349 
    350 	for i, current := range amntsParsed {
    351 		if i == 0 {
    352 			continue
    353 		}
    354 
    355 		last := amntsParsed[i-1]
    356 		fmt.Printf("checking: %s < %s\n", last.String(3), current.String(3))
    357 		if smaller, err := last.IsSmallerThan(current); !smaller || err != nil {
    358 			fmt.Println("failed!", err)
    359 			t.FailNow()
    360 		}
    361 	}
    362 }
    363 
    364 func TestIsSmallerThanNegative(t *testing.T) {
    365 	amnts := []string{
    366 		"EUR:20.05",
    367 		"EUR:0.05",
    368 		"EUR:0.05",
    369 	}
    370 	amntsParsed := make([]internal_utils.Amount, 0)
    371 	for _, a := range amnts {
    372 		a, err := internal_utils.ParseAmount(a, 2)
    373 		if err != nil {
    374 			fmt.Println("failed!", err)
    375 			t.FailNow()
    376 		}
    377 		amntsParsed = append(amntsParsed, *a)
    378 	}
    379 
    380 	for i, current := range amntsParsed {
    381 		if i == 0 {
    382 			continue
    383 		}
    384 
    385 		last := amntsParsed[i-1]
    386 		fmt.Printf("checking (negative): %s < %s\n", last.String(2), current.String(2))
    387 		if smaller, err := last.IsSmallerThan(current); smaller || err != nil {
    388 			fmt.Println("failed!", err)
    389 			t.FailNow()
    390 		}
    391 	}
    392 }
    393 
    394 func TestIsEqualTo(t *testing.T) {
    395 	amnts := []string{
    396 		"CHF:10",
    397 		"CHF:10.00",
    398 		"CHF:10.1",
    399 		"CHF:10.10",
    400 		"CHF:10.01",
    401 		"CHF:10.01",
    402 	}
    403 	amntsParsed := make([]internal_utils.Amount, 0)
    404 	for _, a := range amnts {
    405 		a, err := internal_utils.ParseAmount(a, 2)
    406 		if err != nil {
    407 			fmt.Println("failed!", err)
    408 			t.FailNow()
    409 		}
    410 		amntsParsed = append(amntsParsed, *a)
    411 	}
    412 
    413 	doubleJump := 1
    414 	for doubleJump <= len(amntsParsed) {
    415 		current := amntsParsed[doubleJump]
    416 		last := amntsParsed[doubleJump-1]
    417 		fmt.Printf("checking: %s = %s\n", last.String(2), current.String(2))
    418 		if equal, err := last.IsEqualTo(current); !equal || err != nil {
    419 			fmt.Println("failed!", err)
    420 			t.FailNow()
    421 		}
    422 		doubleJump += 2
    423 	}
    424 }