FulfillmentService.java

1
package com.rbctcsworld.ecommerce.fulfillment;
2
3
import com.rbctcsworld.ecommerce.audit.AuditEvent;
4
import com.rbctcsworld.ecommerce.common.exception.NotFoundException;
5
import com.rbctcsworld.ecommerce.order.CustomerOrder;
6
import com.rbctcsworld.ecommerce.order.OrderDtos.OrderResponse;
7
import com.rbctcsworld.ecommerce.order.OrderDtos.TrackingResponse;
8
import com.rbctcsworld.ecommerce.order.OrderRepository;
9
import com.rbctcsworld.ecommerce.order.OrderStatus;
10
import com.rbctcsworld.ecommerce.notification.OrderNotificationEvent;
11
import org.springframework.context.ApplicationEventPublisher;
12
import org.springframework.stereotype.Service;
13
import org.springframework.transaction.annotation.Transactional;
14
15
import java.security.SecureRandom;
16
17
/**
18
 * Warehouse side (ADMIN): ship a PAID order, mark a SHIPPED order delivered.
19
 * Plus the PUBLIC tracking lookup by tracking number.
20
 */
21
@Service
22
@Transactional
23
public class FulfillmentService {
24
25
    private static final SecureRandom RANDOM = new SecureRandom();
26
27
    private final OrderRepository orders;
28
    private final ApplicationEventPublisher events;
29
30
    public FulfillmentService(OrderRepository orders, ApplicationEventPublisher events) {
31
        this.orders = orders;
32
        this.events = events;
33
    }
34
35
    /** 200 SHIPPED | 404 | 409 unless PAID (an unpaid or cancelled order must never leave the warehouse). */
36
    public OrderResponse ship(Long orderId, String carrier) {
37
        CustomerOrder order = order(orderId);
38 1 1. ship : removed call to com/rbctcsworld/ecommerce/order/CustomerOrder::assertCanMoveTo → KILLED
        order.assertCanMoveTo(OrderStatus.SHIPPED);
39
        String from = order.getStatus();
40 1 1. ship : removed call to com/rbctcsworld/ecommerce/order/CustomerOrder::ship → KILLED
        order.ship(carrier, newTrackingNumber(carrier));
41 1 1. ship : removed call to org/springframework/context/ApplicationEventPublisher::publishEvent → KILLED
        events.publishEvent(AuditEvent.orderStatus(order.getId(), from, order.getStatus()));
42 1 1. ship : removed call to org/springframework/context/ApplicationEventPublisher::publishEvent → KILLED
        events.publishEvent(OrderNotificationEvent.of(order.getId(), OrderNotificationEvent.Type.ORDER_SHIPPED));
43 1 1. ship : replaced return value with null for com/rbctcsworld/ecommerce/fulfillment/FulfillmentService::ship → KILLED
        return OrderResponse.from(order);
44
    }
45
46
    /** 200 DELIVERED | 404 | 409 unless SHIPPED. */
47
    public OrderResponse deliver(Long orderId) {
48
        CustomerOrder order = order(orderId);
49
        String from = order.getStatus();
50 1 1. deliver : removed call to com/rbctcsworld/ecommerce/order/CustomerOrder::deliver → KILLED
        order.deliver();
51 1 1. deliver : removed call to org/springframework/context/ApplicationEventPublisher::publishEvent → KILLED
        events.publishEvent(AuditEvent.orderStatus(order.getId(), from, order.getStatus()));
52 1 1. deliver : removed call to org/springframework/context/ApplicationEventPublisher::publishEvent → KILLED
        events.publishEvent(OrderNotificationEvent.of(order.getId(), OrderNotificationEvent.Type.ORDER_DELIVERED));
53 1 1. deliver : replaced return value with null for com/rbctcsworld/ecommerce/fulfillment/FulfillmentService::deliver → KILLED
        return OrderResponse.from(order);
54
    }
55
56
    /** Public: anyone with the tracking number sees status + timeline, but no personal or price data. */
57
    @Transactional(readOnly = true)
58
    public TrackingResponse track(String trackingNumber) {
59 1 1. track : replaced return value with null for com/rbctcsworld/ecommerce/fulfillment/FulfillmentService::track → NO_COVERAGE
        return orders.findByTrackingNumber(trackingNumber.trim().toUpperCase())
60
                .map(TrackingResponse::from)
61 1 1. lambda$track$0 : replaced return value with null for com/rbctcsworld/ecommerce/fulfillment/FulfillmentService::lambda$track$0 → KILLED
                .orElseThrow(() -> new NotFoundException("Tracking number not found"));
62
    }
63
64
    private CustomerOrder order(Long id) {
65 2 1. lambda$order$1 : replaced return value with null for com/rbctcsworld/ecommerce/fulfillment/FulfillmentService::lambda$order$1 → NO_COVERAGE
2. order : replaced return value with null for com/rbctcsworld/ecommerce/fulfillment/FulfillmentService::order → KILLED
        return orders.findById(id).orElseThrow(() -> new NotFoundException("Order not found: " + id));
66
    }
67
68
    /** e.g. UPS-482910384756 (carrier + 12 random digits, unique). */
69
    String newTrackingNumber(String carrier) {
70
        String tn;
71
        do {
72
            StringBuilder sb = new StringBuilder(carrier).append('-');
73 3 1. newTrackingNumber : Changed increment from 1 to -1 → TIMED_OUT
2. newTrackingNumber : removed conditional - replaced comparison check with false → KILLED
3. newTrackingNumber : changed conditional boundary → KILLED
            for (int i = 0; i < 12; i++) sb.append(RANDOM.nextInt(10));
74
            tn = sb.toString();
75 1 1. newTrackingNumber : removed conditional - replaced equality check with false → TIMED_OUT
        } while (orders.existsByTrackingNumber(tn));
76 1 1. newTrackingNumber : replaced return value with "" for com/rbctcsworld/ecommerce/fulfillment/FulfillmentService::newTrackingNumber → KILLED
        return tn;
77
    }
78
}

Mutations

38

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

40

1.1
Location : ship
Killed by : com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest]/[test-template:shipPaidOrderGeneratesTrackingNumber(java.lang.String)]/[test-template-invocation:#2]
removed call to com/rbctcsworld/ecommerce/order/CustomerOrder::ship → KILLED

41

1.1
Location : ship
Killed by : com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest]/[test-template:shipPaidOrderGeneratesTrackingNumber(java.lang.String)]/[test-template-invocation:#2]
removed call to org/springframework/context/ApplicationEventPublisher::publishEvent → KILLED

42

1.1
Location : ship
Killed by : com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest]/[test-template:shipPaidOrderGeneratesTrackingNumber(java.lang.String)]/[test-template-invocation:#2]
removed call to org/springframework/context/ApplicationEventPublisher::publishEvent → KILLED

43

1.1
Location : ship
Killed by : com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest]/[test-template:shipPaidOrderGeneratesTrackingNumber(java.lang.String)]/[test-template-invocation:#2]
replaced return value with null for com/rbctcsworld/ecommerce/fulfillment/FulfillmentService::ship → KILLED

50

1.1
Location : deliver
Killed by : com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest]/[method:deliverRequiresShipped()]
removed call to com/rbctcsworld/ecommerce/order/CustomerOrder::deliver → KILLED

51

1.1
Location : deliver
Killed by : com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest]/[method:deliveringTellsTheCustomer()]
removed call to org/springframework/context/ApplicationEventPublisher::publishEvent → KILLED

52

1.1
Location : deliver
Killed by : com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest]/[method:deliveringTellsTheCustomer()]
removed call to org/springframework/context/ApplicationEventPublisher::publishEvent → KILLED

53

1.1
Location : deliver
Killed by : com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest]/[method:deliveringTellsTheCustomer()]
replaced return value with null for com/rbctcsworld/ecommerce/fulfillment/FulfillmentService::deliver → KILLED

59

1.1
Location : track
Killed by : none
replaced return value with null for com/rbctcsworld/ecommerce/fulfillment/FulfillmentService::track → NO_COVERAGE

61

1.1
Location : lambda$track$0
Killed by : com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest]/[method:unknownTrackingNumberIs404()]
replaced return value with null for com/rbctcsworld/ecommerce/fulfillment/FulfillmentService::lambda$track$0 → KILLED

65

1.1
Location : lambda$order$1
Killed by : none
replaced return value with null for com/rbctcsworld/ecommerce/fulfillment/FulfillmentService::lambda$order$1 → NO_COVERAGE

2.2
Location : order
Killed by : com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest]/[test-template:onlyPaidOrdersShip(java.lang.String)]/[test-template-invocation:#2]
replaced return value with null for com/rbctcsworld/ecommerce/fulfillment/FulfillmentService::order → KILLED

73

1.1
Location : newTrackingNumber
Killed by : com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest]/[test-template:shipPaidOrderGeneratesTrackingNumber(java.lang.String)]/[test-template-invocation:#2]
removed conditional - replaced comparison check with false → KILLED

2.2
Location : newTrackingNumber
Killed by : none
Changed increment from 1 to -1 → TIMED_OUT

3.3
Location : newTrackingNumber
Killed by : com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest]/[test-template:shipPaidOrderGeneratesTrackingNumber(java.lang.String)]/[test-template-invocation:#2]
changed conditional boundary → KILLED

75

1.1
Location : newTrackingNumber
Killed by : none
removed conditional - replaced equality check with false → TIMED_OUT

76

1.1
Location : newTrackingNumber
Killed by : com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.fulfillment.FulfillmentServiceTest]/[test-template:shipPaidOrderGeneratesTrackingNumber(java.lang.String)]/[test-template-invocation:#2]
replaced return value with "" for com/rbctcsworld/ecommerce/fulfillment/FulfillmentService::newTrackingNumber → KILLED

Active mutators

Tests examined


Report generated by PIT 1.30.0 support