mirror of
https://github.com/s-frick/effigenix.git
synced 2026-03-28 06:29:35 +01:00
Einheitliches Paginierungs-Pattern mit page, size und Multi-Field sort
für alle 14 List-Endpoints. Response-Format ändert sich von [...] zu
{ content: [...], page: { number, size, totalElements, totalPages } }.
Backend:
- Shared Kernel: Page<T>, PageRequest, SortField, SortDirection
- PaginationHelper (SQL ORDER BY mit Whitelist), PageResponse DTO
- Paginated Methoden in allen 14 Domain-Repos + JDBC-Implementierungen
- Safety-Limit (500) für findAllBelowMinimumLevel/ExpiryRelevantBatches
- Alle List-Use-Cases akzeptieren PageRequest, liefern Page<T>
- Alle Controller mit page/size/sort Query-Params + PageResponse
Frontend:
- PagedResponse<T> Type auf nested page-Format aktualisiert
- Alle 14 API-Client-Resourcen liefern PagedResponse mit PaginationParams
- Alle Hooks mit Pagination-State (currentPage, totalPages, pageSize)
- Alle List-Screens mit Seiten-Navigation (Pfeiltasten) und Footer
Loadtest:
- Podman-Support im justfile (DOCKER_HOST auto-detect)
- Verschärfte Performance-Schwellwerte basierend auf Ist-Werten
1119 lines
47 KiB
Java
1119 lines
47 KiB
Java
package de.effigenix.application.masterdata;
|
|
|
|
import de.effigenix.application.masterdata.customer.*;
|
|
import de.effigenix.application.masterdata.customer.command.*;
|
|
import de.effigenix.domain.masterdata.*;
|
|
import de.effigenix.domain.masterdata.article.ArticleId;
|
|
import de.effigenix.domain.masterdata.customer.*;
|
|
import de.effigenix.shared.common.Address;
|
|
import de.effigenix.shared.common.ContactInfo;
|
|
import de.effigenix.shared.common.Money;
|
|
import de.effigenix.shared.common.Page;
|
|
import de.effigenix.shared.common.PageRequest;
|
|
import de.effigenix.shared.common.PaymentTerms;
|
|
import de.effigenix.shared.common.RepositoryError;
|
|
import de.effigenix.shared.common.Result;
|
|
import de.effigenix.shared.persistence.UnitOfWork;
|
|
import de.effigenix.shared.security.ActorId;
|
|
import org.junit.jupiter.api.BeforeEach;
|
|
import org.junit.jupiter.api.DisplayName;
|
|
import org.junit.jupiter.api.Nested;
|
|
import org.junit.jupiter.api.Test;
|
|
import org.junit.jupiter.api.extension.ExtendWith;
|
|
import org.mockito.Mock;
|
|
import org.mockito.junit.jupiter.MockitoExtension;
|
|
|
|
import java.math.BigDecimal;
|
|
import java.time.LocalDate;
|
|
import java.time.OffsetDateTime;
|
|
import java.time.ZoneOffset;
|
|
import java.util.List;
|
|
import java.util.Optional;
|
|
import java.util.Set;
|
|
import java.util.function.Supplier;
|
|
|
|
import static org.assertj.core.api.Assertions.assertThat;
|
|
import static org.mockito.ArgumentMatchers.any;
|
|
import static org.mockito.Mockito.*;
|
|
|
|
@ExtendWith(MockitoExtension.class)
|
|
@DisplayName("Customer Use Cases")
|
|
class CustomerUseCaseTest {
|
|
|
|
@Mock private CustomerRepository customerRepository;
|
|
@Mock private UnitOfWork unitOfWork;
|
|
|
|
private ActorId performedBy;
|
|
|
|
@BeforeEach
|
|
void setUp() {
|
|
lenient().when(unitOfWork.executeAtomically(any()))
|
|
.thenAnswer(inv -> ((Supplier<?>)inv.getArgument(0)).get());
|
|
performedBy = ActorId.of("admin-user");
|
|
}
|
|
|
|
// ==================== Helpers ====================
|
|
|
|
private static Customer existingB2BCustomer(String id) {
|
|
return Customer.reconstitute(
|
|
CustomerId.of(id),
|
|
new CustomerName("Metzgerei Müller"),
|
|
CustomerType.B2B,
|
|
new Address("Hauptstr.", "10", "80331", "München", "DE"),
|
|
new ContactInfo("+49 89 12345", "info@mueller.de", "Hans Müller"),
|
|
new PaymentTerms(30, "30 Tage netto"),
|
|
List.of(),
|
|
null,
|
|
Set.of(),
|
|
CustomerStatus.ACTIVE,
|
|
OffsetDateTime.now(ZoneOffset.UTC).minusDays(10),
|
|
OffsetDateTime.now(ZoneOffset.UTC).minusDays(1)
|
|
);
|
|
}
|
|
|
|
private static Customer existingB2CCustomer(String id) {
|
|
return Customer.reconstitute(
|
|
CustomerId.of(id),
|
|
new CustomerName("Max Mustermann"),
|
|
CustomerType.B2C,
|
|
new Address("Berliner Str.", "5", "10115", "Berlin", "DE"),
|
|
new ContactInfo("+49 30 98765", null, null),
|
|
null,
|
|
List.of(),
|
|
null,
|
|
Set.of(),
|
|
CustomerStatus.ACTIVE,
|
|
OffsetDateTime.now(ZoneOffset.UTC).minusDays(5),
|
|
OffsetDateTime.now(ZoneOffset.UTC).minusDays(1)
|
|
);
|
|
}
|
|
|
|
private static Customer inactiveCustomer(String id) {
|
|
return Customer.reconstitute(
|
|
CustomerId.of(id),
|
|
new CustomerName("Alte Bäckerei"),
|
|
CustomerType.B2B,
|
|
new Address("Bahnhofstr.", "1", "60329", "Frankfurt", "DE"),
|
|
new ContactInfo("+49 69 11111", null, null),
|
|
null,
|
|
List.of(),
|
|
null,
|
|
Set.of(),
|
|
CustomerStatus.INACTIVE,
|
|
OffsetDateTime.now(ZoneOffset.UTC).minusDays(30),
|
|
OffsetDateTime.now(ZoneOffset.UTC).minusDays(2)
|
|
);
|
|
}
|
|
|
|
private static CreateCustomerCommand validCreateCommand() {
|
|
return new CreateCustomerCommand(
|
|
"Neuer Kunde GmbH", CustomerType.B2B,
|
|
"Industriestr.", "42", "70173", "Stuttgart", "DE",
|
|
"+49 711 55555", "kontakt@neuer-kunde.de", "Anna Schmidt",
|
|
14, "14 Tage netto"
|
|
);
|
|
}
|
|
|
|
// ==================== CreateCustomer ====================
|
|
|
|
@Nested
|
|
@DisplayName("CreateCustomer")
|
|
class CreateCustomerTests {
|
|
|
|
private CreateCustomer createCustomer;
|
|
|
|
@BeforeEach
|
|
void setUp() {
|
|
createCustomer = new CreateCustomer(customerRepository, unitOfWork);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should create customer successfully")
|
|
void shouldCreateCustomerSuccessfully() {
|
|
var cmd = validCreateCommand();
|
|
when(customerRepository.existsByName(any())).thenReturn(Result.success(false));
|
|
when(customerRepository.save(any())).thenReturn(Result.success(null));
|
|
|
|
var result = createCustomer.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isSuccess()).isTrue();
|
|
var customer = result.unsafeGetValue();
|
|
assertThat(customer.name().value()).isEqualTo("Neuer Kunde GmbH");
|
|
assertThat(customer.type()).isEqualTo(CustomerType.B2B);
|
|
assertThat(customer.status()).isEqualTo(CustomerStatus.ACTIVE);
|
|
assertThat(customer.billingAddress().street()).isEqualTo("Industriestr.");
|
|
assertThat(customer.contactInfo().phone()).isEqualTo("+49 711 55555");
|
|
verify(customerRepository).save(any(Customer.class));
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail when customer name already exists")
|
|
void shouldFailWhenNameAlreadyExists() {
|
|
var cmd = validCreateCommand();
|
|
when(customerRepository.existsByName(any())).thenReturn(Result.success(true));
|
|
|
|
var result = createCustomer.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.CustomerNameAlreadyExists.class);
|
|
verify(customerRepository, never()).save(any());
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with ValidationFailure when name is blank")
|
|
void shouldFailWhenNameIsBlank() {
|
|
var cmd = new CreateCustomerCommand(
|
|
"", CustomerType.B2B,
|
|
"Str.", null, "12345", "City", "DE",
|
|
"+49 1", null, null, null, null
|
|
);
|
|
|
|
var result = createCustomer.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.ValidationFailure.class);
|
|
verify(customerRepository, never()).save(any());
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with RepositoryFailure when existsByName fails")
|
|
void shouldFailWhenExistsByNameFails() {
|
|
var cmd = validCreateCommand();
|
|
when(customerRepository.existsByName(any()))
|
|
.thenReturn(Result.failure(new RepositoryError.DatabaseError("connection lost")));
|
|
|
|
var result = createCustomer.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.RepositoryFailure.class);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with RepositoryFailure when save fails")
|
|
void shouldFailWhenSaveFails() {
|
|
var cmd = validCreateCommand();
|
|
when(customerRepository.existsByName(any())).thenReturn(Result.success(false));
|
|
when(customerRepository.save(any()))
|
|
.thenReturn(Result.failure(new RepositoryError.DatabaseError("disk full")));
|
|
|
|
var result = createCustomer.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.RepositoryFailure.class);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should create customer without optional payment terms")
|
|
void shouldCreateWithoutPaymentTerms() {
|
|
var cmd = new CreateCustomerCommand(
|
|
"Einfach GmbH", CustomerType.B2C,
|
|
"Str.", null, "12345", "City", "DE",
|
|
"+49 1", null, null, null, null
|
|
);
|
|
when(customerRepository.existsByName(any())).thenReturn(Result.success(false));
|
|
when(customerRepository.save(any())).thenReturn(Result.success(null));
|
|
|
|
var result = createCustomer.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isSuccess()).isTrue();
|
|
assertThat(result.unsafeGetValue().paymentTerms()).isNull();
|
|
}
|
|
}
|
|
|
|
// ==================== UpdateCustomer ====================
|
|
|
|
@Nested
|
|
@DisplayName("UpdateCustomer")
|
|
class UpdateCustomerTests {
|
|
|
|
private UpdateCustomer updateCustomer;
|
|
|
|
@BeforeEach
|
|
void setUp() {
|
|
updateCustomer = new UpdateCustomer(customerRepository, unitOfWork);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should update customer successfully")
|
|
void shouldUpdateCustomerSuccessfully() {
|
|
var customerId = "cust-1";
|
|
var cmd = new UpdateCustomerCommand(
|
|
customerId, "Neuer Name", "Neue Str.", "99", "99999", "Neustadt", "DE",
|
|
"+49 999 0000", "neu@test.de", "Neue Person", 60, "60 Tage netto"
|
|
);
|
|
when(customerRepository.findById(CustomerId.of(customerId)))
|
|
.thenReturn(Result.success(Optional.of(existingB2BCustomer(customerId))));
|
|
when(customerRepository.save(any())).thenReturn(Result.success(null));
|
|
|
|
var result = updateCustomer.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isSuccess()).isTrue();
|
|
assertThat(result.unsafeGetValue().name().value()).isEqualTo("Neuer Name");
|
|
verify(customerRepository).save(any(Customer.class));
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with CustomerNotFound when customer does not exist")
|
|
void shouldFailWhenCustomerNotFound() {
|
|
var cmd = new UpdateCustomerCommand(
|
|
"nonexistent", null, null, null, null, null, null,
|
|
null, null, null, null, null
|
|
);
|
|
when(customerRepository.findById(CustomerId.of("nonexistent")))
|
|
.thenReturn(Result.success(Optional.empty()));
|
|
|
|
var result = updateCustomer.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.CustomerNotFound.class);
|
|
verify(customerRepository, never()).save(any());
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with RepositoryFailure when findById fails")
|
|
void shouldFailWhenFindByIdFails() {
|
|
var cmd = new UpdateCustomerCommand(
|
|
"cust-1", null, null, null, null, null, null,
|
|
null, null, null, null, null
|
|
);
|
|
when(customerRepository.findById(CustomerId.of("cust-1")))
|
|
.thenReturn(Result.failure(new RepositoryError.DatabaseError("timeout")));
|
|
|
|
var result = updateCustomer.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.RepositoryFailure.class);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with RepositoryFailure when save fails")
|
|
void shouldFailWhenSaveFails() {
|
|
var customerId = "cust-1";
|
|
var cmd = new UpdateCustomerCommand(
|
|
customerId, "Updated", null, null, null, null, null,
|
|
null, null, null, null, null
|
|
);
|
|
when(customerRepository.findById(CustomerId.of(customerId)))
|
|
.thenReturn(Result.success(Optional.of(existingB2BCustomer(customerId))));
|
|
when(customerRepository.save(any()))
|
|
.thenReturn(Result.failure(new RepositoryError.DatabaseError("disk full")));
|
|
|
|
var result = updateCustomer.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.RepositoryFailure.class);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with ValidationFailure when updated name is blank")
|
|
void shouldFailWhenUpdatedNameIsBlank() {
|
|
var customerId = "cust-1";
|
|
var cmd = new UpdateCustomerCommand(
|
|
customerId, "", null, null, null, null, null,
|
|
null, null, null, null, null
|
|
);
|
|
when(customerRepository.findById(CustomerId.of(customerId)))
|
|
.thenReturn(Result.success(Optional.of(existingB2BCustomer(customerId))));
|
|
|
|
var result = updateCustomer.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.ValidationFailure.class);
|
|
verify(customerRepository, never()).save(any());
|
|
}
|
|
}
|
|
|
|
// ==================== GetCustomer ====================
|
|
|
|
@Nested
|
|
@DisplayName("GetCustomer")
|
|
class GetCustomerTests {
|
|
|
|
private GetCustomer getCustomer;
|
|
|
|
@BeforeEach
|
|
void setUp() {
|
|
getCustomer = new GetCustomer(customerRepository);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should return customer when found")
|
|
void shouldReturnCustomerWhenFound() {
|
|
var customerId = CustomerId.of("cust-1");
|
|
when(customerRepository.findById(customerId))
|
|
.thenReturn(Result.success(Optional.of(existingB2BCustomer("cust-1"))));
|
|
|
|
var result = getCustomer.execute(customerId);
|
|
|
|
assertThat(result.isSuccess()).isTrue();
|
|
assertThat(result.unsafeGetValue().id()).isEqualTo(customerId);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with CustomerNotFound when not found")
|
|
void shouldFailWhenNotFound() {
|
|
var customerId = CustomerId.of("nonexistent");
|
|
when(customerRepository.findById(customerId))
|
|
.thenReturn(Result.success(Optional.empty()));
|
|
|
|
var result = getCustomer.execute(customerId);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.CustomerNotFound.class);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with RepositoryFailure when repository fails")
|
|
void shouldFailWhenRepositoryFails() {
|
|
var customerId = CustomerId.of("cust-1");
|
|
when(customerRepository.findById(customerId))
|
|
.thenReturn(Result.failure(new RepositoryError.DatabaseError("connection lost")));
|
|
|
|
var result = getCustomer.execute(customerId);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.RepositoryFailure.class);
|
|
}
|
|
}
|
|
|
|
// ==================== ListCustomers ====================
|
|
|
|
@Nested
|
|
@DisplayName("ListCustomers")
|
|
class ListCustomersTests {
|
|
|
|
private ListCustomers listCustomers;
|
|
|
|
@BeforeEach
|
|
void setUp() {
|
|
listCustomers = new ListCustomers(customerRepository);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should return all customers")
|
|
void shouldReturnAllCustomers() {
|
|
var customers = List.of(existingB2BCustomer("c1"), existingB2CCustomer("c2"));
|
|
var pageRequest = PageRequest.of(0, 100);
|
|
when(customerRepository.findAll(pageRequest))
|
|
.thenReturn(Result.success(Page.of(customers, 0, 100, 2)));
|
|
|
|
var result = listCustomers.execute(pageRequest);
|
|
|
|
assertThat(result.isSuccess()).isTrue();
|
|
assertThat(result.unsafeGetValue().content()).hasSize(2);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should return empty list when no customers exist")
|
|
void shouldReturnEmptyList() {
|
|
var pageRequest = PageRequest.of(0, 100);
|
|
when(customerRepository.findAll(pageRequest))
|
|
.thenReturn(Result.success(Page.empty(100)));
|
|
|
|
var result = listCustomers.execute(pageRequest);
|
|
|
|
assertThat(result.isSuccess()).isTrue();
|
|
assertThat(result.unsafeGetValue().content()).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with RepositoryFailure when findAll fails")
|
|
void shouldFailWhenFindAllFails() {
|
|
var pageRequest = PageRequest.of(0, 100);
|
|
when(customerRepository.findAll(pageRequest))
|
|
.thenReturn(Result.failure(new RepositoryError.DatabaseError("timeout")));
|
|
|
|
var result = listCustomers.execute(pageRequest);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.RepositoryFailure.class);
|
|
}
|
|
}
|
|
|
|
// ==================== ActivateCustomer ====================
|
|
|
|
@Nested
|
|
@DisplayName("ActivateCustomer")
|
|
class ActivateCustomerTests {
|
|
|
|
private ActivateCustomer activateCustomer;
|
|
|
|
@BeforeEach
|
|
void setUp() {
|
|
activateCustomer = new ActivateCustomer(customerRepository, unitOfWork);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should activate an inactive customer")
|
|
void shouldActivateInactiveCustomer() {
|
|
var customerId = CustomerId.of("cust-1");
|
|
when(customerRepository.findById(customerId))
|
|
.thenReturn(Result.success(Optional.of(inactiveCustomer("cust-1"))));
|
|
when(customerRepository.save(any())).thenReturn(Result.success(null));
|
|
|
|
var result = activateCustomer.execute(customerId, performedBy);
|
|
|
|
assertThat(result.isSuccess()).isTrue();
|
|
assertThat(result.unsafeGetValue().status()).isEqualTo(CustomerStatus.ACTIVE);
|
|
verify(customerRepository).save(any(Customer.class));
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with CustomerNotFound when customer does not exist")
|
|
void shouldFailWhenCustomerNotFound() {
|
|
var customerId = CustomerId.of("nonexistent");
|
|
when(customerRepository.findById(customerId))
|
|
.thenReturn(Result.success(Optional.empty()));
|
|
|
|
var result = activateCustomer.execute(customerId, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.CustomerNotFound.class);
|
|
verify(customerRepository, never()).save(any());
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with RepositoryFailure when findById fails")
|
|
void shouldFailWhenFindByIdFails() {
|
|
var customerId = CustomerId.of("cust-1");
|
|
when(customerRepository.findById(customerId))
|
|
.thenReturn(Result.failure(new RepositoryError.DatabaseError("connection lost")));
|
|
|
|
var result = activateCustomer.execute(customerId, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.RepositoryFailure.class);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with RepositoryFailure when save fails")
|
|
void shouldFailWhenSaveFails() {
|
|
var customerId = CustomerId.of("cust-1");
|
|
when(customerRepository.findById(customerId))
|
|
.thenReturn(Result.success(Optional.of(inactiveCustomer("cust-1"))));
|
|
when(customerRepository.save(any()))
|
|
.thenReturn(Result.failure(new RepositoryError.DatabaseError("disk full")));
|
|
|
|
var result = activateCustomer.execute(customerId, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.RepositoryFailure.class);
|
|
}
|
|
}
|
|
|
|
// ==================== DeactivateCustomer ====================
|
|
|
|
@Nested
|
|
@DisplayName("DeactivateCustomer")
|
|
class DeactivateCustomerTests {
|
|
|
|
private DeactivateCustomer deactivateCustomer;
|
|
|
|
@BeforeEach
|
|
void setUp() {
|
|
deactivateCustomer = new DeactivateCustomer(customerRepository, unitOfWork);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should deactivate an active customer")
|
|
void shouldDeactivateActiveCustomer() {
|
|
var customerId = CustomerId.of("cust-1");
|
|
when(customerRepository.findById(customerId))
|
|
.thenReturn(Result.success(Optional.of(existingB2BCustomer("cust-1"))));
|
|
when(customerRepository.save(any())).thenReturn(Result.success(null));
|
|
|
|
var result = deactivateCustomer.execute(customerId, performedBy);
|
|
|
|
assertThat(result.isSuccess()).isTrue();
|
|
assertThat(result.unsafeGetValue().status()).isEqualTo(CustomerStatus.INACTIVE);
|
|
verify(customerRepository).save(any(Customer.class));
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with CustomerNotFound when customer does not exist")
|
|
void shouldFailWhenCustomerNotFound() {
|
|
var customerId = CustomerId.of("nonexistent");
|
|
when(customerRepository.findById(customerId))
|
|
.thenReturn(Result.success(Optional.empty()));
|
|
|
|
var result = deactivateCustomer.execute(customerId, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.CustomerNotFound.class);
|
|
verify(customerRepository, never()).save(any());
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with RepositoryFailure when findById fails")
|
|
void shouldFailWhenFindByIdFails() {
|
|
var customerId = CustomerId.of("cust-1");
|
|
when(customerRepository.findById(customerId))
|
|
.thenReturn(Result.failure(new RepositoryError.DatabaseError("connection lost")));
|
|
|
|
var result = deactivateCustomer.execute(customerId, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.RepositoryFailure.class);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with RepositoryFailure when save fails")
|
|
void shouldFailWhenSaveFails() {
|
|
var customerId = CustomerId.of("cust-1");
|
|
when(customerRepository.findById(customerId))
|
|
.thenReturn(Result.success(Optional.of(existingB2BCustomer("cust-1"))));
|
|
when(customerRepository.save(any()))
|
|
.thenReturn(Result.failure(new RepositoryError.DatabaseError("disk full")));
|
|
|
|
var result = deactivateCustomer.execute(customerId, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.RepositoryFailure.class);
|
|
}
|
|
}
|
|
|
|
// ==================== AddDeliveryAddress ====================
|
|
|
|
@Nested
|
|
@DisplayName("AddDeliveryAddress")
|
|
class AddDeliveryAddressTests {
|
|
|
|
private AddDeliveryAddress addDeliveryAddress;
|
|
|
|
@BeforeEach
|
|
void setUp() {
|
|
addDeliveryAddress = new AddDeliveryAddress(customerRepository, unitOfWork);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should add delivery address successfully")
|
|
void shouldAddDeliveryAddressSuccessfully() {
|
|
var customerId = "cust-1";
|
|
var cmd = new AddDeliveryAddressCommand(
|
|
customerId, "Lager Süd", "Lagerstr.", "1", "80000", "München", "DE",
|
|
"Max Lager", "Tor 3 benutzen"
|
|
);
|
|
when(customerRepository.findById(CustomerId.of(customerId)))
|
|
.thenReturn(Result.success(Optional.of(existingB2BCustomer(customerId))));
|
|
when(customerRepository.save(any())).thenReturn(Result.success(null));
|
|
|
|
var result = addDeliveryAddress.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isSuccess()).isTrue();
|
|
assertThat(result.unsafeGetValue().deliveryAddresses()).hasSize(1);
|
|
assertThat(result.unsafeGetValue().deliveryAddresses().getFirst().label()).isEqualTo("Lager Süd");
|
|
verify(customerRepository).save(any(Customer.class));
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with CustomerNotFound when customer does not exist")
|
|
void shouldFailWhenCustomerNotFound() {
|
|
var cmd = new AddDeliveryAddressCommand(
|
|
"nonexistent", "Label", "Str.", null, "12345", "City", "DE", null, null
|
|
);
|
|
when(customerRepository.findById(CustomerId.of("nonexistent")))
|
|
.thenReturn(Result.success(Optional.empty()));
|
|
|
|
var result = addDeliveryAddress.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.CustomerNotFound.class);
|
|
verify(customerRepository, never()).save(any());
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with ValidationFailure when address is invalid")
|
|
void shouldFailWhenAddressIsInvalid() {
|
|
var customerId = "cust-1";
|
|
var cmd = new AddDeliveryAddressCommand(
|
|
customerId, "Label", "", null, "", "", "XX",
|
|
null, null
|
|
);
|
|
when(customerRepository.findById(CustomerId.of(customerId)))
|
|
.thenReturn(Result.success(Optional.of(existingB2BCustomer(customerId))));
|
|
|
|
var result = addDeliveryAddress.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.ValidationFailure.class);
|
|
verify(customerRepository, never()).save(any());
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with RepositoryFailure when findById fails")
|
|
void shouldFailWhenFindByIdFails() {
|
|
var cmd = new AddDeliveryAddressCommand(
|
|
"cust-1", "Label", "Str.", null, "12345", "City", "DE", null, null
|
|
);
|
|
when(customerRepository.findById(CustomerId.of("cust-1")))
|
|
.thenReturn(Result.failure(new RepositoryError.DatabaseError("timeout")));
|
|
|
|
var result = addDeliveryAddress.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.RepositoryFailure.class);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with RepositoryFailure when save fails")
|
|
void shouldFailWhenSaveFails() {
|
|
var customerId = "cust-1";
|
|
var cmd = new AddDeliveryAddressCommand(
|
|
customerId, "Label", "Str.", null, "12345", "City", "DE", null, null
|
|
);
|
|
when(customerRepository.findById(CustomerId.of(customerId)))
|
|
.thenReturn(Result.success(Optional.of(existingB2BCustomer(customerId))));
|
|
when(customerRepository.save(any()))
|
|
.thenReturn(Result.failure(new RepositoryError.DatabaseError("disk full")));
|
|
|
|
var result = addDeliveryAddress.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.RepositoryFailure.class);
|
|
}
|
|
}
|
|
|
|
// ==================== RemoveDeliveryAddress ====================
|
|
|
|
@Nested
|
|
@DisplayName("RemoveDeliveryAddress")
|
|
class RemoveDeliveryAddressTests {
|
|
|
|
private RemoveDeliveryAddress removeDeliveryAddress;
|
|
|
|
@BeforeEach
|
|
void setUp() {
|
|
removeDeliveryAddress = new RemoveDeliveryAddress(customerRepository, unitOfWork);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should remove delivery address successfully")
|
|
void shouldRemoveDeliveryAddressSuccessfully() {
|
|
var customerId = "cust-1";
|
|
var customerWithAddr = Customer.reconstitute(
|
|
CustomerId.of(customerId),
|
|
new CustomerName("Metzgerei Müller"),
|
|
CustomerType.B2B,
|
|
new Address("Hauptstr.", "10", "80331", "München", "DE"),
|
|
new ContactInfo("+49 89 12345", null, null),
|
|
null,
|
|
List.of(new DeliveryAddress("Lager Süd",
|
|
new Address("Lagerstr.", "1", "80000", "München", "DE"), null, null)),
|
|
null, Set.of(), CustomerStatus.ACTIVE,
|
|
OffsetDateTime.now(ZoneOffset.UTC), OffsetDateTime.now(ZoneOffset.UTC)
|
|
);
|
|
var cmd = new RemoveDeliveryAddressCommand(customerId, "Lager Süd");
|
|
when(customerRepository.findById(CustomerId.of(customerId)))
|
|
.thenReturn(Result.success(Optional.of(customerWithAddr)));
|
|
when(customerRepository.save(any())).thenReturn(Result.success(null));
|
|
|
|
var result = removeDeliveryAddress.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isSuccess()).isTrue();
|
|
assertThat(result.unsafeGetValue().deliveryAddresses()).isEmpty();
|
|
verify(customerRepository).save(any(Customer.class));
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with CustomerNotFound when customer does not exist")
|
|
void shouldFailWhenCustomerNotFound() {
|
|
var cmd = new RemoveDeliveryAddressCommand("nonexistent", "Label");
|
|
when(customerRepository.findById(CustomerId.of("nonexistent")))
|
|
.thenReturn(Result.success(Optional.empty()));
|
|
|
|
var result = removeDeliveryAddress.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.CustomerNotFound.class);
|
|
verify(customerRepository, never()).save(any());
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with RepositoryFailure when findById fails")
|
|
void shouldFailWhenFindByIdFails() {
|
|
var cmd = new RemoveDeliveryAddressCommand("cust-1", "Label");
|
|
when(customerRepository.findById(CustomerId.of("cust-1")))
|
|
.thenReturn(Result.failure(new RepositoryError.DatabaseError("timeout")));
|
|
|
|
var result = removeDeliveryAddress.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.RepositoryFailure.class);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with RepositoryFailure when save fails")
|
|
void shouldFailWhenSaveFails() {
|
|
var customerId = "cust-1";
|
|
var cmd = new RemoveDeliveryAddressCommand(customerId, "Label");
|
|
when(customerRepository.findById(CustomerId.of(customerId)))
|
|
.thenReturn(Result.success(Optional.of(existingB2BCustomer(customerId))));
|
|
when(customerRepository.save(any()))
|
|
.thenReturn(Result.failure(new RepositoryError.DatabaseError("disk full")));
|
|
|
|
var result = removeDeliveryAddress.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.RepositoryFailure.class);
|
|
}
|
|
}
|
|
|
|
// ==================== SetPreferences ====================
|
|
|
|
@Nested
|
|
@DisplayName("SetPreferences")
|
|
class SetPreferencesTests {
|
|
|
|
private SetPreferences setPreferences;
|
|
|
|
@BeforeEach
|
|
void setUp() {
|
|
setPreferences = new SetPreferences(customerRepository, unitOfWork);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should set preferences successfully")
|
|
void shouldSetPreferencesSuccessfully() {
|
|
var customerId = "cust-1";
|
|
var prefs = Set.of(CustomerPreference.BIO, CustomerPreference.REGIONAL);
|
|
var cmd = new SetPreferencesCommand(customerId, prefs);
|
|
when(customerRepository.findById(CustomerId.of(customerId)))
|
|
.thenReturn(Result.success(Optional.of(existingB2BCustomer(customerId))));
|
|
when(customerRepository.save(any())).thenReturn(Result.success(null));
|
|
|
|
var result = setPreferences.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isSuccess()).isTrue();
|
|
assertThat(result.unsafeGetValue().preferences())
|
|
.containsExactlyInAnyOrder(CustomerPreference.BIO, CustomerPreference.REGIONAL);
|
|
verify(customerRepository).save(any(Customer.class));
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should clear preferences with empty set")
|
|
void shouldClearPreferencesWithEmptySet() {
|
|
var customerId = "cust-1";
|
|
var cmd = new SetPreferencesCommand(customerId, Set.of());
|
|
when(customerRepository.findById(CustomerId.of(customerId)))
|
|
.thenReturn(Result.success(Optional.of(existingB2BCustomer(customerId))));
|
|
when(customerRepository.save(any())).thenReturn(Result.success(null));
|
|
|
|
var result = setPreferences.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isSuccess()).isTrue();
|
|
assertThat(result.unsafeGetValue().preferences()).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with CustomerNotFound when customer does not exist")
|
|
void shouldFailWhenCustomerNotFound() {
|
|
var cmd = new SetPreferencesCommand("nonexistent", Set.of(CustomerPreference.BIO));
|
|
when(customerRepository.findById(CustomerId.of("nonexistent")))
|
|
.thenReturn(Result.success(Optional.empty()));
|
|
|
|
var result = setPreferences.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.CustomerNotFound.class);
|
|
verify(customerRepository, never()).save(any());
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with RepositoryFailure when findById fails")
|
|
void shouldFailWhenFindByIdFails() {
|
|
var cmd = new SetPreferencesCommand("cust-1", Set.of());
|
|
when(customerRepository.findById(CustomerId.of("cust-1")))
|
|
.thenReturn(Result.failure(new RepositoryError.DatabaseError("timeout")));
|
|
|
|
var result = setPreferences.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.RepositoryFailure.class);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with RepositoryFailure when save fails")
|
|
void shouldFailWhenSaveFails() {
|
|
var customerId = "cust-1";
|
|
var cmd = new SetPreferencesCommand(customerId, Set.of(CustomerPreference.HALAL));
|
|
when(customerRepository.findById(CustomerId.of(customerId)))
|
|
.thenReturn(Result.success(Optional.of(existingB2BCustomer(customerId))));
|
|
when(customerRepository.save(any()))
|
|
.thenReturn(Result.failure(new RepositoryError.DatabaseError("disk full")));
|
|
|
|
var result = setPreferences.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.RepositoryFailure.class);
|
|
}
|
|
}
|
|
|
|
// ==================== SetFrameContract ====================
|
|
|
|
@Nested
|
|
@DisplayName("SetFrameContract")
|
|
class SetFrameContractTests {
|
|
|
|
private SetFrameContract setFrameContract;
|
|
|
|
@BeforeEach
|
|
void setUp() {
|
|
setFrameContract = new SetFrameContract(customerRepository, unitOfWork);
|
|
}
|
|
|
|
private SetFrameContractCommand validFrameContractCommand(String customerId) {
|
|
return new SetFrameContractCommand(
|
|
customerId,
|
|
LocalDate.now(),
|
|
LocalDate.now().plusMonths(12),
|
|
DeliveryRhythm.WEEKLY,
|
|
List.of(new SetFrameContractCommand.LineItem(
|
|
"article-1", new BigDecimal("9.99"), new BigDecimal("100"), Unit.KG
|
|
))
|
|
);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should set frame contract on B2B customer successfully")
|
|
void shouldSetFrameContractOnB2BCustomer() {
|
|
var customerId = "cust-1";
|
|
var cmd = validFrameContractCommand(customerId);
|
|
when(customerRepository.findById(CustomerId.of(customerId)))
|
|
.thenReturn(Result.success(Optional.of(existingB2BCustomer(customerId))));
|
|
when(customerRepository.save(any())).thenReturn(Result.success(null));
|
|
|
|
var result = setFrameContract.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isSuccess()).isTrue();
|
|
assertThat(result.unsafeGetValue().frameContract()).isNotNull();
|
|
verify(customerRepository).save(any(Customer.class));
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with FrameContractNotAllowed when customer is B2C")
|
|
void shouldFailWhenCustomerIsB2C() {
|
|
var customerId = "cust-b2c";
|
|
var cmd = validFrameContractCommand(customerId);
|
|
when(customerRepository.findById(CustomerId.of(customerId)))
|
|
.thenReturn(Result.success(Optional.of(existingB2CCustomer(customerId))));
|
|
|
|
var result = setFrameContract.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.FrameContractNotAllowed.class);
|
|
verify(customerRepository, never()).save(any());
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with CustomerNotFound when customer does not exist")
|
|
void shouldFailWhenCustomerNotFound() {
|
|
var cmd = validFrameContractCommand("nonexistent");
|
|
when(customerRepository.findById(CustomerId.of("nonexistent")))
|
|
.thenReturn(Result.success(Optional.empty()));
|
|
|
|
var result = setFrameContract.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.CustomerNotFound.class);
|
|
verify(customerRepository, never()).save(any());
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with InvalidFrameContract when validUntil is before validFrom")
|
|
void shouldFailWhenDatesInvalid() {
|
|
var customerId = "cust-1";
|
|
var cmd = new SetFrameContractCommand(
|
|
customerId,
|
|
LocalDate.now().plusMonths(12),
|
|
LocalDate.now(), // before validFrom
|
|
DeliveryRhythm.WEEKLY,
|
|
List.of(new SetFrameContractCommand.LineItem(
|
|
"article-1", new BigDecimal("9.99"), new BigDecimal("100"), Unit.KG
|
|
))
|
|
);
|
|
when(customerRepository.findById(CustomerId.of(customerId)))
|
|
.thenReturn(Result.success(Optional.of(existingB2BCustomer(customerId))));
|
|
|
|
var result = setFrameContract.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.InvalidFrameContract.class);
|
|
verify(customerRepository, never()).save(any());
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with InvalidFrameContract when line items are empty")
|
|
void shouldFailWhenLineItemsEmpty() {
|
|
var customerId = "cust-1";
|
|
var cmd = new SetFrameContractCommand(
|
|
customerId, LocalDate.now(), LocalDate.now().plusMonths(6),
|
|
DeliveryRhythm.MONTHLY, List.of()
|
|
);
|
|
when(customerRepository.findById(CustomerId.of(customerId)))
|
|
.thenReturn(Result.success(Optional.of(existingB2BCustomer(customerId))));
|
|
|
|
var result = setFrameContract.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.InvalidFrameContract.class);
|
|
verify(customerRepository, never()).save(any());
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with RepositoryFailure when findById fails")
|
|
void shouldFailWhenFindByIdFails() {
|
|
var cmd = validFrameContractCommand("cust-1");
|
|
when(customerRepository.findById(CustomerId.of("cust-1")))
|
|
.thenReturn(Result.failure(new RepositoryError.DatabaseError("timeout")));
|
|
|
|
var result = setFrameContract.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.RepositoryFailure.class);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with RepositoryFailure when save fails")
|
|
void shouldFailWhenSaveFails() {
|
|
var customerId = "cust-1";
|
|
var cmd = validFrameContractCommand(customerId);
|
|
when(customerRepository.findById(CustomerId.of(customerId)))
|
|
.thenReturn(Result.success(Optional.of(existingB2BCustomer(customerId))));
|
|
when(customerRepository.save(any()))
|
|
.thenReturn(Result.failure(new RepositoryError.DatabaseError("disk full")));
|
|
|
|
var result = setFrameContract.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.RepositoryFailure.class);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with ValidationFailure when price is negative")
|
|
void shouldFailWhenPriceIsNegative() {
|
|
var customerId = "cust-1";
|
|
var cmd = new SetFrameContractCommand(
|
|
customerId, LocalDate.now(), LocalDate.now().plusMonths(6),
|
|
DeliveryRhythm.MONTHLY,
|
|
List.of(new SetFrameContractCommand.LineItem(
|
|
"article-1", new BigDecimal("-5.00"), new BigDecimal("100"), Unit.KG
|
|
))
|
|
);
|
|
when(customerRepository.findById(CustomerId.of(customerId)))
|
|
.thenReturn(Result.success(Optional.of(existingB2BCustomer(customerId))));
|
|
|
|
var result = setFrameContract.execute(cmd, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.ValidationFailure.class);
|
|
verify(customerRepository, never()).save(any());
|
|
}
|
|
}
|
|
|
|
// ==================== RemoveFrameContract ====================
|
|
|
|
@Nested
|
|
@DisplayName("RemoveFrameContract")
|
|
class RemoveFrameContractTests {
|
|
|
|
private RemoveFrameContract removeFrameContract;
|
|
|
|
@BeforeEach
|
|
void setUp() {
|
|
removeFrameContract = new RemoveFrameContract(customerRepository, unitOfWork);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should remove frame contract successfully")
|
|
void shouldRemoveFrameContractSuccessfully() {
|
|
var customerId = CustomerId.of("cust-1");
|
|
var customerWithContract = existingB2BCustomer("cust-1");
|
|
// Set a frame contract on the customer via reconstitute
|
|
var contractCustomer = Customer.reconstitute(
|
|
customerId,
|
|
new CustomerName("Metzgerei Müller"),
|
|
CustomerType.B2B,
|
|
new Address("Hauptstr.", "10", "80331", "München", "DE"),
|
|
new ContactInfo("+49 89 12345", null, null),
|
|
null,
|
|
List.of(),
|
|
FrameContract.reconstitute(
|
|
FrameContractId.generate(),
|
|
LocalDate.now().minusMonths(1),
|
|
LocalDate.now().plusMonths(11),
|
|
DeliveryRhythm.WEEKLY,
|
|
List.of(new ContractLineItem(
|
|
ArticleId.of("a1"), Money.euro(new BigDecimal("10.00")),
|
|
new BigDecimal("50"), Unit.KG))
|
|
),
|
|
Set.of(), CustomerStatus.ACTIVE,
|
|
OffsetDateTime.now(ZoneOffset.UTC), OffsetDateTime.now(ZoneOffset.UTC)
|
|
);
|
|
when(customerRepository.findById(customerId))
|
|
.thenReturn(Result.success(Optional.of(contractCustomer)));
|
|
when(customerRepository.save(any())).thenReturn(Result.success(null));
|
|
|
|
var result = removeFrameContract.execute(customerId, performedBy);
|
|
|
|
assertThat(result.isSuccess()).isTrue();
|
|
assertThat(result.unsafeGetValue().frameContract()).isNull();
|
|
verify(customerRepository).save(any(Customer.class));
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should succeed even when no frame contract exists")
|
|
void shouldSucceedWhenNoFrameContract() {
|
|
var customerId = CustomerId.of("cust-1");
|
|
when(customerRepository.findById(customerId))
|
|
.thenReturn(Result.success(Optional.of(existingB2BCustomer("cust-1"))));
|
|
when(customerRepository.save(any())).thenReturn(Result.success(null));
|
|
|
|
var result = removeFrameContract.execute(customerId, performedBy);
|
|
|
|
assertThat(result.isSuccess()).isTrue();
|
|
assertThat(result.unsafeGetValue().frameContract()).isNull();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with CustomerNotFound when customer does not exist")
|
|
void shouldFailWhenCustomerNotFound() {
|
|
var customerId = CustomerId.of("nonexistent");
|
|
when(customerRepository.findById(customerId))
|
|
.thenReturn(Result.success(Optional.empty()));
|
|
|
|
var result = removeFrameContract.execute(customerId, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.CustomerNotFound.class);
|
|
verify(customerRepository, never()).save(any());
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with RepositoryFailure when findById fails")
|
|
void shouldFailWhenFindByIdFails() {
|
|
var customerId = CustomerId.of("cust-1");
|
|
when(customerRepository.findById(customerId))
|
|
.thenReturn(Result.failure(new RepositoryError.DatabaseError("connection lost")));
|
|
|
|
var result = removeFrameContract.execute(customerId, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.RepositoryFailure.class);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("should fail with RepositoryFailure when save fails")
|
|
void shouldFailWhenSaveFails() {
|
|
var customerId = CustomerId.of("cust-1");
|
|
when(customerRepository.findById(customerId))
|
|
.thenReturn(Result.success(Optional.of(existingB2BCustomer("cust-1"))));
|
|
when(customerRepository.save(any()))
|
|
.thenReturn(Result.failure(new RepositoryError.DatabaseError("disk full")));
|
|
|
|
var result = removeFrameContract.execute(customerId, performedBy);
|
|
|
|
assertThat(result.isFailure()).isTrue();
|
|
assertThat(result.unsafeGetError()).isInstanceOf(CustomerError.RepositoryFailure.class);
|
|
}
|
|
}
|
|
}
|