Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,40 @@ void aPendingMovementHasOnlyTheDateItWasEntered() {
assertNull(pending.bookingDate());
assertNull(pending.valueDate());
assertEquals(new PaymentSymbols(null, null, null), pending.symbols());
// Czech text arrives intact only while the body is decoded as UTF-8.
assertEquals("Okamžitá platba", pending.message());
}

@Test
void aBookedMovementKeepsItsCzechText() {
answer(TRANSACTIONS, 200, fixture("live/booked.json"));

var booked = client.transactions(TOKEN, "acc-1", FROM, TO).getFirst();

assertTrue(booked.raw().contains("OKAMŽITÁ ÚHRADA"), booked.raw());
}

@Test
void anUnknownWayOfPayingIsOtherAndKeptInRaw() {
var page = (ObjectNode) JSON.readTree(movement("BOOK", "CRDT"));
((ObjectNode) page.get("transactions").get(0))
.withObject("/entryDetails/transactionDetails/references")
.put("clearingSystemReference", "PAYMENT_BY_CARRIER_PIGEON");
answer(TRANSACTIONS, 200, page.toString());

var movement = client.transactions(TOKEN, "acc-1", FROM, TO).getFirst();

assertEquals(Transaction.Method.OTHER, movement.method());
assertTrue(movement.raw().contains("PAYMENT_BY_CARRIER_PIGEON"), movement.raw());
}

@Test
void aCancellationIsReversed() {
var page = (ObjectNode) JSON.readTree(movement("BOOK", "DBIT"));
((ObjectNode) page.get("transactions").get(0)).put("reversalIndicator", true);
answer(TRANSACTIONS, 200, page.toString());

assertTrue(client.transactions(TOKEN, "acc-1", FROM, TO).getFirst().reversed());
}

@Test
Expand Down Expand Up @@ -551,6 +585,13 @@ void anySeparatorCopiedFromAStatementOrAPageMatches() {
assertEquals("acc-1", Objects.requireNonNull(found).id());
}

@Test
@SuppressWarnings("NullAway") // a Java caller can still pass null
void aNullIbanIsRefusedBeforeAnyRequest() {
assertThrows(NullPointerException.class, () -> client.accountByIban(TOKEN, null));
assertTrue(requests.isEmpty());
}

@ParameterizedTest
@ValueSource(strings = {"", " ", "\u00A0", "-"})
void aBlankIbanIsTheCallersMistakeNotARevokedAccount(String blank) {
Expand Down Expand Up @@ -605,6 +646,33 @@ void aHolderReadsTheAccountsAgainForEveryIban() {
.count());
}

@Test
void aHolderReadsTheAccountItNames() {
answer(ACCOUNTS, 200, TWO_ACCOUNTS_ONE_WITHOUT_IBAN);
answer(ACCOUNTS + "/acc-0/balance", 200, fixture("live/balance.json"));
answer(ACCOUNTS + "/acc-0/transactions", 200, NO_TRANSACTIONS);
var me = client.with(TOKEN);

assertEquals(
List.of("acc-0", "acc-1"),
me.accounts().stream().map(Account::id).toList());
me.balance("acc-0");
me.transactions("acc-0").between(FROM, TO).list();

assertEquals(
List.of(ACCOUNTS, ACCOUNTS + "/acc-0/balance", ACCOUNTS + "/acc-0/transactions"),
requests.stream().map(r -> r.getRequestURI().getPath()).toList());
}

@Test
void aHolderBalanceWithoutAnAccountRefusesATokenWithTwo() {
answer(ACCOUNTS, 200, TWO_ACCOUNTS_ONE_WITHOUT_IBAN);

var refused = assertThrows(
IllegalStateException.class, () -> client.with(TOKEN).balance());
assertTrue(String.valueOf(refused.getMessage()).contains("opens 2 accounts"), refused.getMessage());
}

@Test
void aQueryWithoutAnAccountRefusesATokenWithTwo() {
answer(ACCOUNTS, 200, """
Expand Down
13 changes: 13 additions & 0 deletions modules/moneta-api/src/test/java/cz/insuit/moneta/MoneyTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@ void neverRounds() {
assertEquals(new BigDecimal("100"), new Money(new BigDecimal("100.0"), Currency.getInstance("JPY")).amount());
}

@Test
void negatesWithTheSameCurrency() {
assertEquals(new Money(new BigDecimal("-12.50"), CZK), new Money(new BigDecimal("12.5"), CZK).negate());
}

@Test
void aCurrencyWithoutMinorDigitsKeepsTheAmountAsWritten() {
// XXX has getDefaultFractionDigits() == -1, which would otherwise be a negative scale.
var none = Currency.getInstance("XXX");
assertEquals(new BigDecimal("12.5"), new Money(new BigDecimal("12.50"), none).amount());
assertEquals(new BigDecimal("100"), new Money(new BigDecimal("100"), none).amount());
}

@Test
void printsAsAnAmountAndACode() {
assertEquals("1234.10 CZK", new Money(new BigDecimal("1234.1"), CZK).toString());
Expand Down
12 changes: 12 additions & 0 deletions testing/kotlin/src/test/kotlin/example/kotlin/KotlinCallerTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@ class KotlinCallerTest {
assertTrue(errors.isNotEmpty(), "iban read as non-null compiled, so Kotlin sees it as a platform type")
}

@Test
fun aLookupThatMayFindNothingIsHandledWithAnElvis() {
val source = "fun f(me: cz.insuit.moneta.AccountHolder): String = me.accountByIban(\"x\")?.id ?: \"none\""
assertEquals(listOf<String>(), compile(source))
}

@Test
fun aLookupThatMayFindNothingCannotBeReadAsNonNull() {
val errors = compile("fun f(me: cz.insuit.moneta.AccountHolder): String = me.accountByIban(\"x\").id")
assertTrue(errors.isNotEmpty(), "accountByIban read as non-null compiled, so its @Nullable is lost")
}

/** The errors compiling [source] against the library gives, empty when it compiles. */
private fun compile(source: String): List<String> {
val dir = createTempDirectory()
Expand Down
Loading