package com.bookshop.creational.builder;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.bookshop.domain.Book;
import com.bookshop.domain.Customer;
import java.math.BigDecimal;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@DisplayName("Builder -- assembling an Order readably, with validation in one place")
class OrderBuilderTest {
private final Customer alice = new Customer("c-1", "Alice", "alice@example.com", 3);
private final Book refactoring = new Book("978-0134757599", "Refactoring", "Martin Fowler",
new BigDecimal("47.99"));
private final Book effectiveJava = new Book("978-0134685991", "Effective Java", "Joshua Bloch",
new BigDecimal("39.99"));
@Test
@DisplayName("call sites read like a sentence instead of a null-riddled constructor")
void buildsFullOrderFluently() {
Order order = Order.forCustomer(alice)
.add(refactoring)
.add(effectiveJava)
.giftMessage("Happy birthday!")
.express()
.build();
assertEquals(alice, order.customer());
assertEquals(2, order.books().size());
assertEquals("Happy birthday!", order.giftMessage().orElseThrow());
assertTrue(order.expressDelivery());
assertTrue(order.promoCode().isEmpty());
assertEquals(new BigDecimal("87.98"), order.subtotal());
}
@Test
@DisplayName("optional fields can simply be omitted -- no telescoping overloads needed")
void omittedOptionalsGetSafeDefaults() {
Order order = Order.forCustomer(alice).add(refactoring).build();
assertTrue(order.giftMessage().isEmpty());
assertTrue(order.deliveryInstructions().isEmpty());
assertEquals(false, order.expressDelivery());
}
@Test
@DisplayName("build() is the single validation point -- invalid orders never exist")
void rejectsEmptyOrder() {
Order.Builder builder = Order.forCustomer(alice);
assertThrows(IllegalStateException.class, builder::build);
}
@Test
@DisplayName("the built order is immutable -- its book list cannot be modified")
void builtOrderIsImmutable() {
Order order = Order.forCustomer(alice).add(refactoring).build();
assertThrows(UnsupportedOperationException.class, () -> order.books().add(effectiveJava));
}
}