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 @@ -53,4 +53,29 @@ class SelectAlternativeControllerTest {
}
confirmVerified(useCase)
}

@Test
fun `client reports that no alternative was found`() {

// given: a decision body with no alternative and no bike details
val pathVar = "123e4567-e89b-12d3-a456-426614174000"
val body = mapOf("alternativeFound" to false)
every { useCase.selectAlternative(any()) } just Runs
val operation =
post("/api/bike-leasing/{applicationId}/clarify-alternative", pathVar)
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(body))

// when: the request is performed
val response = mockMvc.perform(operation).andReturn()

// then: the use case is invoked with a command carrying no alternative bike and the response is 202
assertThat(response.response.status).isEqualTo(202)
verify {
useCase.selectAlternative(
SelectAlternativeUseCase.Command(ApplicationId.of(pathVar), alternativeFound = false, bikeId = null, bikeModel = null),
)
}
confirmVerified(useCase)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package io.miragon.blueprint.adapter.outbound.db

import io.miragon.blueprint.domain.bike.OrderId
import io.miragon.blueprint.domain.leasing.ContractId
import io.miragon.blueprint.domain.leasing.LeasingStatus
import io.miragon.blueprint.domain.leasing.testLeasingApplication
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test

class LeasingApplicationEntityMapperTest {

@Test
fun `toEntity copies every field including the nullable order and contract ids`() {
// given: a fully populated application carrying an order and a contract
val application = testLeasingApplication(
status = LeasingStatus.ACTIVE,
orderId = OrderId("ORDER-1"),
contractId = ContractId("CONTRACT-1"),
)

// when: it is mapped to its persistence entity
val entity = LeasingApplicationEntityMapper.toEntity(application)

// then: each field is carried over unchanged
assertThat(entity.applicationId).isEqualTo(application.id.value)
assertThat(entity.customerName).isEqualTo(application.customerName.value)
assertThat(entity.email).isEqualTo(application.email.value)
assertThat(entity.age).isEqualTo(application.age)
assertThat(entity.monthlyNetIncome).isEqualTo(application.monthlyNetIncome)
assertThat(entity.bikeId).isEqualTo(application.bikeId.value)
assertThat(entity.status).isEqualTo(application.status)
assertThat(entity.createdAt).isEqualTo(application.createdAt)
assertThat(entity.orderId).isEqualTo("ORDER-1")
assertThat(entity.contractId).isEqualTo("CONTRACT-1")
}

@Test
fun `toDomain reconstructs every field including the nullable order and contract ids`() {
// given: an entity carrying an order and a contract
val entity = LeasingApplicationEntityMapper.toEntity(
testLeasingApplication(
status = LeasingStatus.ACTIVE,
orderId = OrderId("ORDER-1"),
contractId = ContractId("CONTRACT-1"),
),
)

// when: it is mapped back to the domain
val application = LeasingApplicationEntityMapper.toDomain(entity)

// then: each field is reconstructed unchanged
assertThat(application.id.value).isEqualTo(entity.applicationId)
assertThat(application.customerName.value).isEqualTo(entity.customerName)
assertThat(application.email.value).isEqualTo(entity.email)
assertThat(application.age).isEqualTo(entity.age)
assertThat(application.monthlyNetIncome).isEqualTo(entity.monthlyNetIncome)
assertThat(application.bikeId.value).isEqualTo(entity.bikeId)
assertThat(application.status).isEqualTo(entity.status)
assertThat(application.createdAt).isEqualTo(entity.createdAt)
assertThat(application.orderId).isEqualTo(OrderId("ORDER-1"))
assertThat(application.contractId).isEqualTo(ContractId("CONTRACT-1"))
}

@Test
fun `toDomain keeps the order and contract ids null when the entity has none`() {
// given: an entity without an order or a contract
val entity = LeasingApplicationEntityMapper.toEntity(testLeasingApplication())

// when: it is mapped back to the domain
val application = LeasingApplicationEntityMapper.toDomain(entity)

// then: both optional references stay null
assertThat(application.orderId).isNull()
assertThat(application.contractId).isNull()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.miragon.blueprint.application.service

import io.miragon.blueprint.application.port.outbound.ContractPort
import io.miragon.blueprint.application.port.outbound.LeasingApplicationRepository
import io.miragon.blueprint.domain.leasing.ApplicationId
import io.miragon.blueprint.domain.leasing.ContractId
import io.miragon.blueprint.domain.leasing.testLeasingApplication
import io.mockk.Runs
Expand All @@ -10,6 +11,7 @@ import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.verify
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.jupiter.api.Test

class CancelContractServiceTest {
Expand All @@ -34,4 +36,32 @@ class CancelContractServiceTest {
verify { contract.revokeContract(ContractId("CONTRACT-1")) }
confirmVerified(repository, contract)
}

@Test
fun `cancelContract fails when the application is unknown`() {

// given: an id the repository cannot resolve
val id = ApplicationId.of("123e4567-e89b-12d3-a456-426614174000")
every { repository.findById(id) } returns null

// when/then: cancellation fails and no contract is touched
assertThatThrownBy { underTest.cancelContract(id) }
.isInstanceOf(IllegalStateException::class.java)
verify { repository.findById(id) }
confirmVerified(repository, contract)
}

@Test
fun `cancelContract fails when no contract was issued for the application`() {

// given: an application that never received a contract
val application = testLeasingApplication(contractId = null)
every { repository.findById(application.id) } returns application

// when/then: cancellation fails and no contract is touched
assertThatThrownBy { underTest.cancelContract(application.id) }
.isInstanceOf(IllegalStateException::class.java)
verify { repository.findById(application.id) }
confirmVerified(repository, contract)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.miragon.blueprint.domain.bike

import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.jupiter.api.Test

class BikeIdTest {

@Test
fun `exposes the wrapped bike id`() {
// given/when: a bike id is created from a non-blank value
val bikeId = BikeId("BIKE-900")
// then: the raw value is exposed unchanged
assertThat(bikeId.value).isEqualTo("BIKE-900")
}

@Test
fun `rejects a blank bike id`() {
// when/then: a blank value is refused
assertThatThrownBy { BikeId(" ") }
.isInstanceOf(IllegalArgumentException::class.java)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.miragon.blueprint.domain.bike

import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.jupiter.api.Test

class OrderIdTest {

@Test
fun `exposes the wrapped order id`() {
// given/when: an order id is created from a non-blank value
val orderId = OrderId("ORDER-1")
// then: the raw value is exposed unchanged
assertThat(orderId.value).isEqualTo("ORDER-1")
}

@Test
fun `rejects a blank order id`() {
// when/then: a blank value is refused
assertThatThrownBy { OrderId(" ") }
.isInstanceOf(IllegalArgumentException::class.java)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.miragon.blueprint.domain.leasing

import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import java.util.UUID

class ApplicationInvalidExceptionTest {

@Test
fun `carries the reason and a message naming the application`() {
// given: an application id and a rejection reason
val id = ApplicationId(UUID.fromString("123e4567-e89b-12d3-a456-426614174000"))
// when: the exception is raised
val exception = ApplicationInvalidException(id, "no income")
// then: reason and composed message are exposed
assertThat(exception.reason).isEqualTo("no income")
assertThat(exception.applicationId).isEqualTo(id)
assertThat(exception.message)
.isEqualTo("Application 123e4567-e89b-12d3-a456-426614174000 is invalid: no income")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.miragon.blueprint.domain.leasing

import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.jupiter.api.Test

class ContractIdTest {

@Test
fun `exposes the wrapped contract id`() {
// given/when: a contract id is created from a non-blank value
val contractId = ContractId("CONTRACT-1")
// then: the raw value is exposed unchanged
assertThat(contractId.value).isEqualTo("CONTRACT-1")
}

@Test
fun `rejects a blank contract id`() {
// when/then: a blank value is refused
assertThatThrownBy { ContractId(" ") }
.isInstanceOf(IllegalArgumentException::class.java)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.miragon.blueprint.domain.leasing

import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.jupiter.api.Test

class CustomerNameTest {

@Test
fun `exposes the wrapped name`() {
// given/when: a customer name is created from a non-blank value
val name = CustomerName("John Doe")
// then: the raw value is exposed unchanged
assertThat(name.value).isEqualTo("John Doe")
}

@Test
fun `rejects an empty name`() {
// when/then: an empty value is refused
assertThatThrownBy { CustomerName("") }
.isInstanceOf(IllegalArgumentException::class.java)
}

@Test
fun `rejects a blank name`() {
// when/then: a whitespace-only value is refused
assertThatThrownBy { CustomerName(" ") }
.isInstanceOf(IllegalArgumentException::class.java)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.miragon.blueprint.domain.leasing

import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.jupiter.api.Test

class EmailTest {

@Test
fun `exposes a valid email address`() {
// given/when: an email is created from a well-formed address
val email = Email("john.doe@test.com")
// then: the raw value is exposed unchanged
assertThat(email.value).isEqualTo("john.doe@test.com")
}

@Test
fun `rejects a malformed address`() {
// when/then: a value that is not an email is refused
assertThatThrownBy { Email("not-an-email") }
.isInstanceOf(IllegalArgumentException::class.java)
}

@Test
fun `rejects an empty address`() {
// when/then: an empty value is refused
assertThatThrownBy { Email("") }
.isInstanceOf(IllegalArgumentException::class.java)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ import org.junit.jupiter.api.Test

class LeasingApplicationTest {

@Test
fun `exposes the applicant's age and monthly net income`() {
// given: an application built with a specific age and income
val application = testLeasingApplication(age = 40, monthlyNetIncome = 4200.0)
// then: both fields are exposed unchanged
assertThat(application.age).isEqualTo(40)
assertThat(application.monthlyNetIncome).isEqualTo(4200.0)
}

@Test
fun `documentOrder attaches the order id and moves to ORDERED`() {
// given: a received application
Expand Down
Loading