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 }