Skip to content

Commit

Permalink
Added CurrentExchangeRateProvider, prepared build gradle for integrat…
Browse files Browse the repository at this point in the history
…ion tests
  • Loading branch information
kamil.jedrzejuk committed Oct 3, 2024
1 parent 339e246 commit 6291c32
Show file tree
Hide file tree
Showing 12 changed files with 151 additions and 44 deletions.
1 change: 0 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ insert_final_newline = true

[*.kt]
max_line_length = 100
disabled_rules = no-wildcard-imports

[*.{kt,kts}]
import_order = camelcase, spaces, semicolons
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,4 @@ bin/
### Mac OS ###
.DS_Store
/.idea/
/.kotlin/errors/*.log
64 changes: 61 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
plugins {
kotlin("jvm") version "2.0.20"
id("io.gitlab.arturbosch.detekt") version("1.23.7")
id("org.jlleitschuh.gradle.ktlint") version("12.1.1")
kotlin("jvm") version "2.0.10"
kotlin("plugin.spring") version "2.0.10"
id("org.springframework.boot") version "3.1.2"
id("io.spring.dependency-management") version "1.1.2"
id("io.gitlab.arturbosch.detekt") version "1.23.7"
id("org.jlleitschuh.gradle.ktlint") version "12.1.1"
}

group = "camilyed.github.io"
Expand All @@ -12,16 +15,71 @@ repositories {
}

dependencies {
// Spring Boot dependencies
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.springframework.boot:spring-boot-starter-web")

// Spring Boot Test
testImplementation("org.springframework.boot:spring-boot-starter-test")

// Test dependencies
testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.0")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.7.0")
testImplementation("io.strikt:strikt-core:0.34.0")

// Integration Test dependencies
testImplementation("org.springframework.boot:spring-boot-starter-test")

testImplementation("org.testcontainers:junit-jupiter:1.20.2")
testImplementation("org.testcontainers:postgresql:1.20.2")
testImplementation("org.flywaydb:flyway-core")
testRuntimeOnly("org.postgresql:postgresql")
}

tasks.test {
useJUnitPlatform()
}

kotlin {
jvmToolchain(21)
}

tasks.withType<Test> {
useJUnitPlatform()

testLogging {
events("passed", "skipped", "failed")
}
}

val integrationTest: SourceSet =
sourceSets.create("integrationTest") {
java {
compileClasspath += sourceSets.main.get().output + sourceSets.test.get().output
runtimeClasspath += sourceSets.main.get().output + sourceSets.test.get().output
srcDir("src/integration-test/java")
}
resources.srcDir("src/integration-test/resources")
}

configurations[integrationTest.implementationConfigurationName].extendsFrom(configurations.testImplementation.get())
configurations[integrationTest.runtimeOnlyConfigurationName].extendsFrom(configurations.testRuntimeOnly.get())

val integrationTestTask =
tasks.register<Test>("integrationTest") {
group = "verification"

useJUnitPlatform()

testClassesDirs = integrationTest.output.classesDirs
classpath = sourceSets["integrationTest"].runtimeClasspath

shouldRunAfter("test")
}

tasks.check {
dependsOn(integrationTestTask)
}

apply(plugin = "org.jlleitschuh.gradle.ktlint")
apply(plugin = "io.gitlab.arturbosch.detekt")
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import camilyed.github.io.currencyexchangeapi.domain.Account
import camilyed.github.io.currencyexchangeapi.domain.AccountNotFoundException
import camilyed.github.io.currencyexchangeapi.domain.AccountRepository
import camilyed.github.io.currencyexchangeapi.domain.AccountSnapshot
import camilyed.github.io.currencyexchangeapi.domain.ExchangeRate
import camilyed.github.io.currencyexchangeapi.domain.CurrentExchangeRateProvider
import java.math.BigDecimal
import java.util.UUID

class AccountService(
private val repository: AccountRepository,
private val currentExchangeRateProvider: CurrentExchangeRateProvider,
) {
fun create(command: CreateAccountCommand): AccountSnapshot {
val id = repository.nextAccountId()
Expand All @@ -27,13 +28,15 @@ class AccountService(

fun exchangePlnToUsd(command: ExchangePlnToUsdCommand): AccountSnapshot {
val account = findAccount(command.accountId)
account.exchangePlnToUsd(Money.pln(command.amount), ExchangeRate(command.exchangeRate))
val currentExchange = currentExchangeRateProvider.currentExchange()
account.exchangePlnToUsd(Money.pln(command.amount), currentExchange)
return account.toSnapshot()
}

fun exchangeUsdToPln(command: ExchangeUsdToPlnCommand): AccountSnapshot {
val account = findAccount(command.accountId)
account.exchangeUsdToPln(Money.usd(command.amount), ExchangeRate(command.exchangeRate))
val currentExchange = currentExchangeRateProvider.currentExchange()
account.exchangeUsdToPln(Money.usd(command.amount), currentExchange)
return account.toSnapshot()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@ import java.util.UUID
data class ExchangePlnToUsdCommand(
val accountId: UUID,
val amount: BigDecimal,
val exchangeRate: BigDecimal,
)
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@ import java.util.UUID
data class ExchangeUsdToPlnCommand(
val accountId: UUID,
val amount: BigDecimal,
val exchangeRate: BigDecimal,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package camilyed.github.io.currencyexchangeapi.domain

interface CurrentExchangeRateProvider {
fun currentExchange(): ExchangeRate
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import camilyed.github.io.currencyexchangeapi.domain.InsufficientFundsException
import camilyed.github.io.currencyexchangeapi.domain.InvalidAmountException
import camilyed.github.io.currencyexchangeapi.domain.InvalidExchangeRateException
import camilyed.github.io.currencyexchangeapi.testing.ability.CreateAccountAbility
import camilyed.github.io.currencyexchangeapi.testing.ability.GetCurrentExchangeRateAbility
import camilyed.github.io.currencyexchangeapi.testing.ability.SetNextAccountIdAbility
import camilyed.github.io.currencyexchangeapi.testing.assertions.hasBalanceInPln
import camilyed.github.io.currencyexchangeapi.testing.assertions.hasBalanceInUsd
Expand All @@ -19,6 +20,8 @@ import camilyed.github.io.currencyexchangeapi.testing.builders.ExchangePlnToUsdC
import camilyed.github.io.currencyexchangeapi.testing.builders.ExchangeUsdToPlnCommandBuilder
import camilyed.github.io.currencyexchangeapi.testing.builders.ExchangeUsdToPlnCommandBuilder.Companion.anExchangeToPln
import camilyed.github.io.currencyexchangeapi.testing.fakes.TestingAccountRepository
import camilyed.github.io.currencyexchangeapi.testing.fakes.TestingCurrentExchangeRateProvider
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import strikt.api.expectCatching
import strikt.api.expectThat
Expand All @@ -28,9 +31,19 @@ import strikt.assertions.isFailure
import strikt.assertions.message
import java.util.UUID

class AccountServiceTest : SetNextAccountIdAbility, CreateAccountAbility {
class AccountServiceTest :
SetNextAccountIdAbility,
CreateAccountAbility,
GetCurrentExchangeRateAbility {
override val accountRepository = TestingAccountRepository()
private val accountService = AccountService(accountRepository)
override val exchangeRateProvider = TestingCurrentExchangeRateProvider()

private val accountService = AccountService(accountRepository, exchangeRateProvider)

@BeforeEach
fun setup() {
currentExchangeRateIs("4.00")
}

@Test
fun `should create a new account with valid details`() {
Expand Down Expand Up @@ -115,13 +128,15 @@ class AccountServiceTest : SetNextAccountIdAbility, CreateAccountAbility {
// given
var account = thereIsAnAccount(anAccount().withBalancePln("1000.00"))

// and
currentExchangeRateIs("4.0")

// when
account =
exchange(
anExchangeToUsd()
.withAccountId(account.id)
.withAmount("400.00")
.withExchangeRate("4.0"),
.withAmount("400.00"),
)

// then
Expand Down Expand Up @@ -174,13 +189,15 @@ class AccountServiceTest : SetNextAccountIdAbility, CreateAccountAbility {
.withBalanceUsd("100.00"),
)

// and
currentExchangeRateIs("4.0")

// when
account =
exchange(
anExchangeToPln()
.withAccountId(account.id)
.withAmount("100.00")
.withExchangeRate("4.0"),
.withAmount("100.00"),
)

// then
Expand Down Expand Up @@ -262,13 +279,15 @@ class AccountServiceTest : SetNextAccountIdAbility, CreateAccountAbility {
// given
val account = thereIsAnAccount(anAccount().withBalancePln("1000.00").withBalanceUsd("0.00"))

// and
currentExchangeRateIs("4.0")

// when
val updatedAccount =
exchange(
anExchangeToUsd()
.withAccountId(account.id)
.withAmount("1000.00")
.withExchangeRate("4.0"),
.withAmount("1000.00"),
)

// then
Expand All @@ -282,13 +301,15 @@ class AccountServiceTest : SetNextAccountIdAbility, CreateAccountAbility {
// given
val account = thereIsAnAccount(anAccount().withBalanceUsd("100.00").withBalancePln("0.00"))

// and
currentExchangeRateIs("4.0")

// when
val updatedAccount =
exchange(
anExchangeToPln()
.withAccountId(account.id)
.withAmount("100.00")
.withExchangeRate("4.0"),
.withAmount("100.00"),
)

// then
Expand All @@ -300,15 +321,11 @@ class AccountServiceTest : SetNextAccountIdAbility, CreateAccountAbility {
@Test
fun `should throw exception for zero exchange rate`() {
// given
val account = thereIsAnAccount(anAccount())
thereIsAnAccount(anAccount())

// then
// and
expectCatching {
exchange(
anExchangeToUsd()
.withAccountId(account.id)
.withExchangeRate("0.00"),
)
currentExchangeRateIs("0.0")
}.isFailure()
.isA<InvalidExchangeRateException>()
.message.isEqualTo("Exchange rate must be greater than 0")
Expand All @@ -319,13 +336,15 @@ class AccountServiceTest : SetNextAccountIdAbility, CreateAccountAbility {
// given
val account = thereIsAnAccount(anAccount().withBalancePln("1000.00"))

// and
currentExchangeRateIs("4.0")

// when
val updatedAccount =
exchange(
anExchangeToUsd()
.withAccountId(account.id)
.withAmount("123.456")
.withExchangeRate("4.0"),
.withAmount("123.456"),
)

// then
Expand All @@ -339,13 +358,15 @@ class AccountServiceTest : SetNextAccountIdAbility, CreateAccountAbility {
// given
val account = thereIsAnAccount(anAccount().withBalanceUsd("100.00").withBalancePln("0.00"))

// and
currentExchangeRateIs("4.0")

// when
val updatedAccount =
exchange(
anExchangeToPln()
.withAccountId(account.id)
.withAmount("33.335")
.withExchangeRate("4.0"),
.withAmount("33.335"),
)

// then
Expand All @@ -359,13 +380,15 @@ class AccountServiceTest : SetNextAccountIdAbility, CreateAccountAbility {
// given
val account = thereIsAnAccount(anAccount().withBalancePln("1000.00"))

// and
currentExchangeRateIs("3.33")

// when
val updatedAccount =
exchange(
anExchangeToUsd()
.withAccountId(account.id)
.withAmount("1000.00")
.withExchangeRate("3.33"),
.withAmount("1000.00"),
)

// then
Expand All @@ -379,13 +402,15 @@ class AccountServiceTest : SetNextAccountIdAbility, CreateAccountAbility {
// given
val account = thereIsAnAccount(anAccount().withBalanceUsd("100.00").withBalancePln("0.00"))

// and
currentExchangeRateIs("4.5")

// when
val updatedAccount =
exchange(
anExchangeToPln()
.withAccountId(account.id)
.withAmount("100.00")
.withExchangeRate("4.5"),
.withAmount("100.00"),
)

// then
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package camilyed.github.io.currencyexchangeapi.testing.ability

import camilyed.github.io.currencyexchangeapi.domain.ExchangeRate
import camilyed.github.io.currencyexchangeapi.testing.fakes.TestingCurrentExchangeRateProvider
import java.math.BigDecimal

interface GetCurrentExchangeRateAbility {
val exchangeRateProvider: TestingCurrentExchangeRateProvider

fun currentExchangeRateIs(rate: String) {
exchangeRateProvider.setCurrentExchange(ExchangeRate(BigDecimal(rate)))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,15 @@ import java.util.UUID
class ExchangePlnToUsdCommandBuilder private constructor() {
private var accountId: UUID = UUID.randomUUID()
private var amount: BigDecimal = BigDecimal("100.00")
private var exchangeRate: BigDecimal = BigDecimal("4.0")

fun withAccountId(accountId: UUID) = apply { this.accountId = accountId }

fun withAmount(amount: String) = apply { this.amount = BigDecimal(amount) }

fun withExchangeRate(exchangeRate: String) =
apply { this.exchangeRate = BigDecimal(exchangeRate) }

fun build(): ExchangePlnToUsdCommand {
return ExchangePlnToUsdCommand(
accountId = accountId,
amount = amount,
exchangeRate = exchangeRate,
)
}

Expand Down
Loading

0 comments on commit 6291c32

Please sign in to comment.