package com.bookshop.structural.facade;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.bookshop.domain.Book;
import com.bookshop.domain.Customer;
import java.math.BigDecimal;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@DisplayName("Facade -- one checkout() call orchestrates inventory, payment and shipping")
class CheckoutFacadeTest {
private final Book book = new Book("978-0132350884", "Clean Code", "Robert C. Martin",
new BigDecimal("32.50"));
private final Customer alice = new Customer("c-1", "Alice", "alice@example.com", 1);
/** Records every charge so tests can assert what the facade did -- or didn't -- ask for. */
private static class RecordingPaymentService extends PaymentService {
private int chargesAttempted;
@Override
public String charge(Customer customer, BigDecimal amount) {
chargesAttempted++;
return super.charge(customer, amount);
}
}
private InventoryService inventory;
private RecordingPaymentService payments;
private CheckoutFacade checkout;
@BeforeEach
void setUp() {
inventory = new InventoryService();
payments = new RecordingPaymentService();
checkout = new CheckoutFacade(inventory, payments, new ShippingService());
}
@Test
@DisplayName("a single call performs the whole flow and returns everything the caller needs")
void oneCallDoesEverything() {
inventory.stock(book, 3);
CheckoutSummary summary = checkout.checkout(alice, List.of(book), "1 High Street");
assertNotNull(summary.transactionId());
assertNotNull(summary.shipmentId());
assertEquals(new BigDecimal("32.50"), summary.totalCharged());
assertEquals(2, inventory.stockLevel(book));
}
@Test
@DisplayName("the facade enforces the right order: no stock means no charge, ever")
void outOfStockStopsTheFlowBeforePayment() {
assertThrows(IllegalStateException.class,
() -> checkout.checkout(alice, List.of(book), "1 High Street"));
assertEquals(0, payments.chargesAttempted, "payment must never be attempted without stock");
assertEquals(0, inventory.stockLevel(book));
}
}