OrderService.java

1
package com.rbctcsworld.ecommerce.order;
2
3
import com.rbctcsworld.ecommerce.audit.AuditEvent;
4
import com.rbctcsworld.ecommerce.auth.AppUser;
5
import com.rbctcsworld.ecommerce.auth.UserRepository;
6
import com.rbctcsworld.ecommerce.cart.CartItem;
7
import com.rbctcsworld.ecommerce.cart.CartItemRepository;
8
import com.rbctcsworld.ecommerce.common.exception.BusinessRuleException;
9
import com.rbctcsworld.ecommerce.common.exception.NotFoundException;
10
import com.rbctcsworld.ecommerce.common.exception.PaymentDeclinedException;
11
import com.rbctcsworld.ecommerce.coupon.Coupon;
12
import com.rbctcsworld.ecommerce.coupon.CouponService;
13
import com.rbctcsworld.ecommerce.inventory.InventoryService;
14
import com.rbctcsworld.ecommerce.order.OrderDtos.OrderLine;
15
import com.rbctcsworld.ecommerce.order.OrderDtos.OrderResponse;
16
import com.rbctcsworld.ecommerce.order.OrderDtos.QuoteResponse;
17
import com.rbctcsworld.ecommerce.order.OrderDtos.TrackingResponse;
18
import com.rbctcsworld.ecommerce.payment.PaymentDtos.PayRequest;
19
import com.rbctcsworld.ecommerce.payment.PaymentDtos.PaySavedRequest;
20
import com.rbctcsworld.ecommerce.payment.PaymentMethodService;
21
import com.rbctcsworld.ecommerce.payment.PaymentService;
22
import com.rbctcsworld.ecommerce.payment.PaymentTransaction;
23
import com.rbctcsworld.ecommerce.pricing.PricingService;
24
import com.rbctcsworld.ecommerce.pricing.PricingService.PriceBreakdown;
25
import com.rbctcsworld.ecommerce.product.Product;
26
import com.rbctcsworld.ecommerce.notification.OrderNotificationEvent;
27
import org.springframework.context.ApplicationEventPublisher;
28
import org.springframework.stereotype.Service;
29
import org.springframework.transaction.annotation.Transactional;
30
31
import java.math.BigDecimal;
32
import java.security.SecureRandom;
33
import java.time.LocalDate;
34
import java.time.format.DateTimeFormatter;
35
import java.util.Comparator;
36
import java.util.List;
37
38
/**
39
 * Place order = ONE database transaction:
40
 *   1. read the customer's cart (400 if empty) and check that there is a delivery address (400 if not)
41
 *   2. validate the optional coupon and calculate discount, shipping, tax, total
42
 *   3. for every line (sorted by product id to avoid deadlocks) reserve stock atomically (409 if not possible)
43
 *   4. save order + items + stock movements + coupon redemption
44
 *   5. empty the cart
45
 * If any step fails, everything is rolled back: no stock changes, no order, no coupon use, cart untouched.
46
 *
47
 * Pay  = PLACED -> PAID with a successful charge (a declined card is recorded but the order stays PLACED).
48
 * Cancel = PLACED/PAID -> CANCELLED, stock released, and a refund if it was PAID.
49
 */
50
@Service
51
@Transactional
52
public class OrderService {
53
54
    private static final String ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"; // no 0/O/1/I confusion
55
    private static final SecureRandom RANDOM = new SecureRandom();
56
57
    private final OrderRepository orders;
58
    private final CartItemRepository cartItems;
59
    private final UserRepository users;
60
    private final InventoryService inventory;
61
    private final PricingService pricing;
62
    private final CouponService coupons;
63
    private final PaymentService payments;
64
    private final PaymentMethodService paymentMethods;
65
    private final ApplicationEventPublisher events;
66
67
    public OrderService(OrderRepository orders, CartItemRepository cartItems, UserRepository users,
68
                        InventoryService inventory, PricingService pricing, CouponService coupons,
69
                        PaymentService payments, PaymentMethodService paymentMethods, ApplicationEventPublisher events) {
70
        this.orders = orders;
71
        this.cartItems = cartItems;
72
        this.users = users;
73
        this.inventory = inventory;
74
        this.pricing = pricing;
75
        this.coupons = coupons;
76
        this.payments = payments;
77
        this.paymentMethods = paymentMethods;
78
        this.events = events;
79
    }
80
81
    // ---------------------------------------------------------------- checkout
82
83
    /** Price preview for the current cart (+ optional coupon). Read-only: nothing is reserved or redeemed. */
84
    @Transactional(readOnly = true)
85
    public QuoteResponse quote(String email, String couponCode) {
86
        AppUser user = user(email);
87
        List<CartItem> lines = cartLines(user);
88
        List<OrderLine> orderLines = lines.stream().map(OrderService::toLine).toList();
89
        BigDecimal subtotal = orderLines.stream().map(OrderLine::lineTotal).reduce(BigDecimal.ZERO, BigDecimal::add);
90 1 1. quote : removed conditional - replaced equality check with false → NO_COVERAGE
        Coupon coupon = hasText(couponCode) ? coupons.validate(couponCode, user.getId(), subtotal) : null;
91 1 1. quote : removed conditional - replaced equality check with false → NO_COVERAGE
        PriceBreakdown p = pricing.price(subtotal, coupon == null ? BigDecimal.ZERO : coupon.discountFor(subtotal));
92
        int qty = orderLines.stream().mapToInt(OrderLine::quantity).sum();
93 2 1. quote : removed conditional - replaced equality check with false → NO_COVERAGE
2. quote : replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::quote → NO_COVERAGE
        return new QuoteResponse(orderLines, qty, p.subtotal(), coupon == null ? null : coupon.getCode(),
94
                p.discount(), p.shippingFee(), p.tax(), p.total());
95
    }
96
97
    public OrderResponse placeOrder(String email, String couponCode, ShippingAddress address) {
98
        AppUser user = user(email);
99
        List<CartItem> lines = cartLines(user);
100 1 1. placeOrder : removed conditional - replaced equality check with false → KILLED
        if (address == null) {
101
            throw new BusinessRuleException("Shipping address is required");
102
        }
103
104
        CustomerOrder order = new CustomerOrder(newOrderNumber(), user.getId());
105 1 1. placeOrder : removed call to com/rbctcsworld/ecommerce/order/CustomerOrder::shipTo → KILLED
        order.shipTo(address.normalized());
106
        for (CartItem line : lines) {
107
            Product p = line.getProduct();
108 1 1. placeOrder : removed call to com/rbctcsworld/ecommerce/order/CustomerOrder::addItem → KILLED
            order.addItem(new OrderItem(p.getId(), p.getSku(), p.displayName(), p.currentPrice(), line.getQuantity()));
109
        }
110
111 1 1. placeOrder : removed conditional - replaced equality check with false → KILLED
        Coupon coupon = hasText(couponCode) ? coupons.validate(couponCode, user.getId(), order.getSubtotal()) : null;
112
        PriceBreakdown price = pricing.price(order.getSubtotal(),
113 1 1. placeOrder : removed conditional - replaced equality check with false → KILLED
                coupon == null ? BigDecimal.ZERO : coupon.discountFor(order.getSubtotal()));
114 1 1. placeOrder : removed call to com/rbctcsworld/ecommerce/order/CustomerOrder::applyPricing → KILLED
        order.applyPricing(price.discount(), price.shippingFee(), price.tax(), price.total(),
115 1 1. placeOrder : removed conditional - replaced equality check with false → KILLED
                coupon == null ? null : coupon.getCode());
116
        orders.save(order); // gets the id used by stock movements / redemption
117
118
        for (CartItem line : lines) {
119 1 1. placeOrder : removed call to com/rbctcsworld/ecommerce/inventory/InventoryService::reserve → KILLED
            inventory.reserve(line.getProduct(), line.getQuantity(), order.getId(), address.state());
120
        }
121 1 1. placeOrder : removed conditional - replaced equality check with false → KILLED
        if (coupon != null) {
122 1 1. placeOrder : removed call to com/rbctcsworld/ecommerce/coupon/CouponService::redeem → KILLED
            coupons.redeem(coupon, user.getId(), order.getId());
123
        }
124
125 1 1. placeOrder : removed call to com/rbctcsworld/ecommerce/cart/CartItemRepository::deleteByUserId → KILLED
        cartItems.deleteByUserId(user.getId());
126 1 1. placeOrder : removed call to org/springframework/context/ApplicationEventPublisher::publishEvent → KILLED
        events.publishEvent(OrderNotificationEvent.of(order.getId(), OrderNotificationEvent.Type.ORDER_PLACED));
127 1 1. placeOrder : replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::placeOrder → KILLED
        return OrderResponse.from(order);
128
    }
129
130
    /**
131
     * noRollbackFor: a declined card must still leave its FAILED payment_transactions row in the database.
132
     */
133
    @Transactional(noRollbackFor = PaymentDeclinedException.class)
134
    public OrderResponse pay(String email, Long orderId, PayRequest card) {
135
        CustomerOrder order = owned(email, orderId);
136 1 1. pay : removed call to com/rbctcsworld/ecommerce/order/CustomerOrder::assertCanMoveTo → KILLED
        order.assertCanMoveTo(OrderStatus.PAID);                      // 409 before any money moves
137
        payments.charge(order.getId(), order.getTotal(), card);   // throws 400 / 402 on problems
138
        OrderResponse paid = markPaid(order);
139 1 1. pay : removed conditional - replaced equality check with false → SURVIVED
        if (Boolean.TRUE.equals(card.saveCard())) {
140
            // "save this card": only after the money moved, and it can never fail the payment
141 1 1. pay : removed call to com/rbctcsworld/ecommerce/payment/PaymentMethodService::saveAfterPayment → NO_COVERAGE
            paymentMethods.saveAfterPayment(email, card.cardNumber(), card.expiryMonth(), card.expiryYear());
142
        }
143 1 1. pay : replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::pay → KILLED
        return paid;
144
    }
145
146
    /** Pays with a card from the wallet; the CVV is asked again and never kept. 404 for somebody else's card. */
147
    @Transactional(noRollbackFor = PaymentDeclinedException.class)
148
    public OrderResponse paySaved(String email, Long orderId, PaySavedRequest request) {
149
        CustomerOrder order = owned(email, orderId);
150 1 1. paySaved : removed call to com/rbctcsworld/ecommerce/order/CustomerOrder::assertCanMoveTo → NO_COVERAGE
        order.assertCanMoveTo(OrderStatus.PAID);
151
        payments.chargeSaved(order.getId(), order.getTotal(), paymentMethods.ownedBy(email, request.paymentMethodId()));
152 1 1. paySaved : replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::paySaved → NO_COVERAGE
        return markPaid(order);
153
    }
154
155
    private OrderResponse markPaid(CustomerOrder order) {
156
        String from = order.getStatus();
157 1 1. markPaid : removed call to com/rbctcsworld/ecommerce/order/CustomerOrder::markPaid → KILLED
        order.markPaid();
158 1 1. markPaid : removed call to org/springframework/context/ApplicationEventPublisher::publishEvent → KILLED
        events.publishEvent(AuditEvent.orderStatus(order.getId(), from, order.getStatus()));
159 1 1. markPaid : removed call to org/springframework/context/ApplicationEventPublisher::publishEvent → KILLED
        events.publishEvent(OrderNotificationEvent.of(order.getId(), OrderNotificationEvent.Type.PAYMENT_RECEIVED));
160 1 1. markPaid : replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::markPaid → KILLED
        return OrderResponse.from(order);
161
    }
162
163
    // ---------------------------------------------------------------- after checkout
164
165
    @Transactional(readOnly = true)
166
    public List<OrderResponse> myOrders(String email) {
167 1 1. myOrders : replaced return value with Collections.emptyList for com/rbctcsworld/ecommerce/order/OrderService::myOrders → NO_COVERAGE
        return orders.findByUserIdOrderByIdDesc(user(email).getId()).stream().map(OrderResponse::from).toList();
168
    }
169
170
    @Transactional(readOnly = true)
171
    public OrderResponse myOrder(String email, Long orderId) {
172 1 1. myOrder : replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::myOrder → NO_COVERAGE
        return OrderResponse.from(owned(email, orderId));
173
    }
174
175
    @Transactional(readOnly = true)
176
    public TrackingResponse myTracking(String email, Long orderId) {
177 1 1. myTracking : replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::myTracking → NO_COVERAGE
        return TrackingResponse.from(owned(email, orderId));
178
    }
179
180
    @Transactional(readOnly = true)
181
    public List<PaymentTransaction> myPayments(String email, Long orderId) {
182 1 1. myPayments : replaced return value with Collections.emptyList for com/rbctcsworld/ecommerce/order/OrderService::myPayments → NO_COVERAGE
        return payments.history(owned(email, orderId).getId());
183
    }
184
185
    public OrderResponse cancel(String email, Long orderId) {
186
        CustomerOrder order = owned(email, orderId);
187 1 1. cancel : removed call to com/rbctcsworld/ecommerce/order/CustomerOrder::assertCanMoveTo → KILLED
        order.assertCanMoveTo(OrderStatus.CANCELLED);                 // SHIPPED and later: 409
188
        order.getItems().stream()
189
                .sorted(Comparator.comparing(OrderItem::getProductId))
190 2 1. cancel : removed call to java/util/stream/Stream::forEach → KILLED
2. lambda$cancel$0 : removed call to com/rbctcsworld/ecommerce/inventory/InventoryService::release → KILLED
                .forEach(i -> inventory.release(i.getProductId(), i.getQuantity(), order.getId()));
191
        BigDecimal refunded = null;
192 1 1. cancel : removed conditional - replaced equality check with false → KILLED
        if (order.isPaid()) {
193
            payments.refund(order.getId(), order.getTotal());
194
            refunded = order.getTotal();
195
        }
196
        String from = order.getStatus();
197 1 1. cancel : removed call to com/rbctcsworld/ecommerce/order/CustomerOrder::cancel → KILLED
        order.cancel();
198 1 1. cancel : removed call to org/springframework/context/ApplicationEventPublisher::publishEvent → KILLED
        events.publishEvent(AuditEvent.orderStatus(order.getId(), from, order.getStatus()));
199 1 1. cancel : removed call to org/springframework/context/ApplicationEventPublisher::publishEvent → KILLED
        events.publishEvent(new OrderNotificationEvent(order.getId(), OrderNotificationEvent.Type.ORDER_CANCELLED, refunded));
200 1 1. cancel : replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::cancel → KILLED
        return OrderResponse.from(order);
201
    }
202
203
    @Transactional(readOnly = true)
204
    public List<OrderResponse> allOrders() {
205 1 1. allOrders : replaced return value with Collections.emptyList for com/rbctcsworld/ecommerce/order/OrderService::allOrders → NO_COVERAGE
        return orders.findAllByOrderByIdDesc().stream().map(OrderResponse::from).toList();
206
    }
207
208
    // ---------------------------------------------------------------- helpers
209
210
    private List<CartItem> cartLines(AppUser user) {
211
        List<CartItem> lines = cartItems.findByUserIdOrderByIdAsc(user.getId()).stream()
212 1 1. lambda$cartLines$1 : replaced Long return value with 0L for com/rbctcsworld/ecommerce/order/OrderService::lambda$cartLines$1 → KILLED
                .sorted(Comparator.comparing(ci -> ci.getProduct().getId()))
213
                .toList();
214 1 1. cartLines : removed conditional - replaced equality check with false → KILLED
        if (lines.isEmpty()) {
215
            throw new BusinessRuleException("Cart is empty");
216
        }
217 1 1. cartLines : replaced return value with Collections.emptyList for com/rbctcsworld/ecommerce/order/OrderService::cartLines → KILLED
        return lines;
218
    }
219
220
    private static OrderLine toLine(CartItem ci) {
221
        Product p = ci.getProduct();
222 1 1. toLine : replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::toLine → NO_COVERAGE
        return new OrderLine(p.getId(), p.getSku(), p.displayName(), p.currentPrice(), ci.getQuantity(),
223
                p.currentPrice().multiply(BigDecimal.valueOf(ci.getQuantity())));
224
    }
225
226
    /** Also used by ReturnService: the order if it belongs to this customer, otherwise 404 (IDOR). */
227
    public CustomerOrder owned(String email, Long orderId) {
228 1 1. owned : replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::owned → KILLED
        return orders.findByIdAndUserId(orderId, user(email).getId())
229 1 1. lambda$owned$2 : replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::lambda$owned$2 → KILLED
                .orElseThrow(() -> new NotFoundException("Order not found: " + orderId));
230
    }
231
232
    private AppUser user(String email) {
233 2 1. lambda$user$3 : replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::lambda$user$3 → NO_COVERAGE
2. user : replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::user → KILLED
        return users.findByEmail(email).orElseThrow(() -> new NotFoundException("User not found"));
234
    }
235
236
    private static boolean hasText(String s) {
237 3 1. hasText : replaced boolean return with true for com/rbctcsworld/ecommerce/order/OrderService::hasText → KILLED
2. hasText : removed conditional - replaced equality check with false → KILLED
3. hasText : removed conditional - replaced equality check with false → KILLED
        return s != null && !s.isBlank();
238
    }
239
240
    /** e.g. ORD-20261002-7KQ2MZ */
241
    static String newOrderNumber() {
242
        StringBuilder sb = new StringBuilder("ORD-")
243
                .append(LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE)).append('-');
244 2 1. newOrderNumber : removed conditional - replaced comparison check with false → KILLED
2. newOrderNumber : changed conditional boundary → KILLED
        for (int i = 0; i < 6; i++) {
245
            sb.append(ALPHABET.charAt(RANDOM.nextInt(ALPHABET.length())));
246
        }
247 1 1. newOrderNumber : replaced return value with "" for com/rbctcsworld/ecommerce/order/OrderService::newOrderNumber → KILLED
        return sb.toString();
248
    }
249
}

Mutations

90

1.1
Location : quote
Killed by : none
removed conditional - replaced equality check with false → NO_COVERAGE

91

1.1
Location : quote
Killed by : none
removed conditional - replaced equality check with false → NO_COVERAGE

93

1.1
Location : quote
Killed by : none
removed conditional - replaced equality check with false → NO_COVERAGE

2.2
Location : quote
Killed by : none
replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::quote → NO_COVERAGE

100

1.1
Location : placeOrder
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:anOrderWithoutAnAddressIsRejectedBeforeAnythingIsSaved()]
removed conditional - replaced equality check with false → KILLED

105

1.1
Location : placeOrder
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:theAddressIsStoredTrimmedWithTheStateInCapitals()]
removed call to com/rbctcsworld/ecommerce/order/CustomerOrder::shipTo → KILLED

108

1.1
Location : placeOrder
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:couponIsValidatedAppliedBeforeShippingAndRedeemed()]
removed call to com/rbctcsworld/ecommerce/order/CustomerOrder::addItem → KILLED

111

1.1
Location : placeOrder
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:couponIsValidatedAppliedBeforeShippingAndRedeemed()]
removed conditional - replaced equality check with false → KILLED

113

1.1
Location : placeOrder
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:stockFailureLeavesCartUntouched()]
removed conditional - replaced equality check with false → KILLED

114

1.1
Location : placeOrder
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:couponIsValidatedAppliedBeforeShippingAndRedeemed()]
removed call to com/rbctcsworld/ecommerce/order/CustomerOrder::applyPricing → KILLED

115

1.1
Location : placeOrder
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:stockFailureLeavesCartUntouched()]
removed conditional - replaced equality check with false → KILLED

119

1.1
Location : placeOrder
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:stockFailureLeavesCartUntouched()]
removed call to com/rbctcsworld/ecommerce/inventory/InventoryService::reserve → KILLED

121

1.1
Location : placeOrder
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:couponIsValidatedAppliedBeforeShippingAndRedeemed()]
removed conditional - replaced equality check with false → KILLED

122

1.1
Location : placeOrder
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:couponIsValidatedAppliedBeforeShippingAndRedeemed()]
removed call to com/rbctcsworld/ecommerce/coupon/CouponService::redeem → KILLED

125

1.1
Location : placeOrder
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:placeOrderSnapshotsPricesReservesStockAndClearsCart()]
removed call to com/rbctcsworld/ecommerce/cart/CartItemRepository::deleteByUserId → KILLED

126

1.1
Location : placeOrder
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:placeOrderSnapshotsPricesReservesStockAndClearsCart()]
removed call to org/springframework/context/ApplicationEventPublisher::publishEvent → KILLED

127

1.1
Location : placeOrder
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:theAddressIsStoredTrimmedWithTheStateInCapitals()]
replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::placeOrder → KILLED

136

1.1
Location : pay
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:paidOrderCannotBePaidAgain()]
removed call to com/rbctcsworld/ecommerce/order/CustomerOrder::assertCanMoveTo → KILLED

139

1.1
Location : pay
Killed by : none
removed conditional - replaced equality check with false → SURVIVED
Covering tests

141

1.1
Location : pay
Killed by : none
removed call to com/rbctcsworld/ecommerce/payment/PaymentMethodService::saveAfterPayment → NO_COVERAGE

143

1.1
Location : pay
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:successfulPaymentChargesTheTotalAndMarksThePaidTime()]
replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::pay → KILLED

150

1.1
Location : paySaved
Killed by : none
removed call to com/rbctcsworld/ecommerce/order/CustomerOrder::assertCanMoveTo → NO_COVERAGE

152

1.1
Location : paySaved
Killed by : none
replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::paySaved → NO_COVERAGE

157

1.1
Location : markPaid
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:successfulPaymentChargesTheTotalAndMarksThePaidTime()]
removed call to com/rbctcsworld/ecommerce/order/CustomerOrder::markPaid → KILLED

158

1.1
Location : markPaid
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:successfulPaymentChargesTheTotalAndMarksThePaidTime()]
removed call to org/springframework/context/ApplicationEventPublisher::publishEvent → KILLED

159

1.1
Location : markPaid
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:successfulPaymentChargesTheTotalAndMarksThePaidTime()]
removed call to org/springframework/context/ApplicationEventPublisher::publishEvent → KILLED

160

1.1
Location : markPaid
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:successfulPaymentChargesTheTotalAndMarksThePaidTime()]
replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::markPaid → KILLED

167

1.1
Location : myOrders
Killed by : none
replaced return value with Collections.emptyList for com/rbctcsworld/ecommerce/order/OrderService::myOrders → NO_COVERAGE

172

1.1
Location : myOrder
Killed by : none
replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::myOrder → NO_COVERAGE

177

1.1
Location : myTracking
Killed by : none
replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::myTracking → NO_COVERAGE

182

1.1
Location : myPayments
Killed by : none
replaced return value with Collections.emptyList for com/rbctcsworld/ecommerce/order/OrderService::myPayments → NO_COVERAGE

187

1.1
Location : cancel
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:cancellingTwiceIsConflict()]
removed call to com/rbctcsworld/ecommerce/order/CustomerOrder::assertCanMoveTo → KILLED

190

1.1
Location : cancel
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:cancelReleasesEveryLineAndSetsStatus()]
removed call to java/util/stream/Stream::forEach → KILLED

2.2
Location : lambda$cancel$0
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:cancelReleasesEveryLineAndSetsStatus()]
removed call to com/rbctcsworld/ecommerce/inventory/InventoryService::release → KILLED

192

1.1
Location : cancel
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:cancellingAPaidOrderRefundsTheTotal()]
removed conditional - replaced equality check with false → KILLED

197

1.1
Location : cancel
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:cancellingAnUnpaidOrderDoesNotRefund()]
removed call to com/rbctcsworld/ecommerce/order/CustomerOrder::cancel → KILLED

198

1.1
Location : cancel
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:cancellingAnUnpaidOrderDoesNotRefund()]
removed call to org/springframework/context/ApplicationEventPublisher::publishEvent → KILLED

199

1.1
Location : cancel
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:cancellingAnUnpaidOrderDoesNotRefund()]
removed call to org/springframework/context/ApplicationEventPublisher::publishEvent → KILLED

200

1.1
Location : cancel
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:cancelReleasesEveryLineAndSetsStatus()]
replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::cancel → KILLED

205

1.1
Location : allOrders
Killed by : none
replaced return value with Collections.emptyList for com/rbctcsworld/ecommerce/order/OrderService::allOrders → NO_COVERAGE

212

1.1
Location : lambda$cartLines$1
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:placeOrderSnapshotsPricesReservesStockAndClearsCart()]
replaced Long return value with 0L for com/rbctcsworld/ecommerce/order/OrderService::lambda$cartLines$1 → KILLED

214

1.1
Location : cartLines
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:emptyCartCannotBeOrdered()]
removed conditional - replaced equality check with false → KILLED

217

1.1
Location : cartLines
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:stockFailureLeavesCartUntouched()]
replaced return value with Collections.emptyList for com/rbctcsworld/ecommerce/order/OrderService::cartLines → KILLED

222

1.1
Location : toLine
Killed by : none
replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::toLine → NO_COVERAGE

228

1.1
Location : owned
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:cancellingTwiceIsConflict()]
replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::owned → KILLED

229

1.1
Location : lambda$owned$2
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:anotherCustomersOrderIsNotFound()]
replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::lambda$owned$2 → KILLED

233

1.1
Location : user
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:emptyCartCannotBeOrdered()]
replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::user → KILLED

2.2
Location : lambda$user$3
Killed by : none
replaced return value with null for com/rbctcsworld/ecommerce/order/OrderService::lambda$user$3 → NO_COVERAGE

237

1.1
Location : hasText
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:blankCouponCodeMeansNoCoupon()]
replaced boolean return with true for com/rbctcsworld/ecommerce/order/OrderService::hasText → KILLED

2.2
Location : hasText
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:couponIsValidatedAppliedBeforeShippingAndRedeemed()]
removed conditional - replaced equality check with false → KILLED

3.3
Location : hasText
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:couponIsValidatedAppliedBeforeShippingAndRedeemed()]
removed conditional - replaced equality check with false → KILLED

244

1.1
Location : newOrderNumber
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:orderNumbersAreUniqueAndReadable()]
removed conditional - replaced comparison check with false → KILLED

2.2
Location : newOrderNumber
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:orderNumbersAreUniqueAndReadable()]
changed conditional boundary → KILLED

247

1.1
Location : newOrderNumber
Killed by : com.rbctcsworld.ecommerce.order.OrderServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.order.OrderServiceTest]/[method:orderNumbersAreUniqueAndReadable()]
replaced return value with "" for com/rbctcsworld/ecommerce/order/OrderService::newOrderNumber → KILLED

Active mutators

Tests examined


Report generated by PIT 1.30.0 support