ReviewService.java

1
package com.rbctcsworld.ecommerce.review;
2
3
import com.rbctcsworld.ecommerce.auth.AppUser;
4
import com.rbctcsworld.ecommerce.auth.UserRepository;
5
import com.rbctcsworld.ecommerce.common.exception.BusinessRuleException;
6
import com.rbctcsworld.ecommerce.common.exception.ConflictException;
7
import com.rbctcsworld.ecommerce.common.exception.ForbiddenException;
8
import com.rbctcsworld.ecommerce.common.exception.NotFoundException;
9
import com.rbctcsworld.ecommerce.order.OrderRepository;
10
import com.rbctcsworld.ecommerce.order.OrderStatus;
11
import com.rbctcsworld.ecommerce.product.Product;
12
import com.rbctcsworld.ecommerce.product.ProductRepository;
13
import com.rbctcsworld.ecommerce.review.ReviewDtos.ProductReviews;
14
import com.rbctcsworld.ecommerce.review.ReviewDtos.RatingSummary;
15
import com.rbctcsworld.ecommerce.review.ReviewDtos.ReviewResponse;
16
import org.springframework.stereotype.Service;
17
import org.springframework.transaction.annotation.Transactional;
18
19
import java.math.BigDecimal;
20
import java.math.RoundingMode;
21
import java.time.Clock;
22
import java.time.LocalDateTime;
23
import java.util.Comparator;
24
import java.util.LinkedHashMap;
25
import java.util.List;
26
import java.util.Map;
27
import java.util.Set;
28
import java.util.stream.Collectors;
29
30
/**
31
 * Business rules:
32
 *  1. Rating 1..5, title <= 100, body <= 2000 characters (400). Blank title/body are stored as null.
33
 *  2. Verified purchase only: the customer must have RECEIVED the product (an order with it that is
34
 *     DELIVERED, RETURN_REQUESTED or RETURNED). Otherwise 403.
35
 *  3. One review per customer per product (409). The customer may edit or delete it.
36
 *  4. Another customer's review id behaves as "not found" (404, IDOR protection).
37
 *  5. Only PUBLISHED reviews are listed and counted. An admin can hide / re-publish (moderation);
38
 *     editing a hidden review does not publish it again.
39
 *  6. The product's averageRating (1 decimal, HALF_UP) and reviewCount are recalculated in the same
40
 *     transaction as every change, while the product row is locked (SELECT ... FOR UPDATE), so two
41
 *     reviews saved at the same moment can never leave a wrong average.
42
 *  7. Public responses show a masked reviewer name, never the e-mail address.
43
 */
44
@Service
45
@Transactional
46
public class ReviewService {
47
48
    static final Set<String> RECEIVED = Set.of(OrderStatus.DELIVERED, OrderStatus.RETURN_REQUESTED, OrderStatus.RETURNED);
49
    public static final List<String> SORTS = List.of("newest", "highest", "lowest");
50
51
    private final ReviewRepository reviews;
52
    private final ProductRepository products;
53
    private final OrderRepository orders;
54
    private final UserRepository users;
55
    private final Clock clock;
56
57
    public ReviewService(ReviewRepository reviews, ProductRepository products, OrderRepository orders,
58
                         UserRepository users, Clock clock) {
59
        this.reviews = reviews;
60
        this.products = products;
61
        this.orders = orders;
62
        this.users = users;
63
        this.clock = clock;
64
    }
65
66
    // ---------- customer ----------
67
68
    public ReviewResponse create(String email, Long productId, int rating, String title, String body) {
69
        AppUser user = user(email);
70
        Product product = products.lockById(productId)
71
                .filter(Product::isActive)
72 1 1. lambda$create$0 : replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::lambda$create$0 → KILLED
                .orElseThrow(() -> new NotFoundException("Product not found: " + productId));
73 1 1. create : removed conditional - replaced equality check with false → KILLED
        if (!orders.hasReceived(user.getId(), productId, RECEIVED)) {
74
            throw new ForbiddenException("Only customers who received this product can review it");
75
        }
76 1 1. create : removed conditional - replaced equality check with false → KILLED
        if (reviews.existsByUserIdAndProductId(user.getId(), productId)) {
77
            throw new ConflictException("You have already reviewed this product - edit your review instead");
78
        }
79
        Review r = reviews.saveAndFlush(new Review(productId, user.getId(), rating, clean(title), clean(body), now()));
80 1 1. create : removed call to com/rbctcsworld/ecommerce/review/ReviewService::recalculate → KILLED
        recalculate(product);
81 1 1. create : replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::create → KILLED
        return toResponse(r, email);
82
    }
83
84
    public ReviewResponse update(String email, Long reviewId, int rating, String title, String body) {
85
        AppUser user = user(email);
86
        Review r = owned(reviewId, user.getId());
87
        Product product = lockedProduct(r.getProductId());
88 1 1. update : removed call to com/rbctcsworld/ecommerce/review/Review::edit → KILLED
        r.edit(rating, clean(title), clean(body), now());
89
        reviews.saveAndFlush(r);
90 1 1. update : removed call to com/rbctcsworld/ecommerce/review/ReviewService::recalculate → KILLED
        recalculate(product);
91 1 1. update : replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::update → KILLED
        return toResponse(r, email);
92
    }
93
94
    public void delete(String email, Long reviewId) {
95
        AppUser user = user(email);
96
        Review r = owned(reviewId, user.getId());
97
        Product product = lockedProduct(r.getProductId());
98 1 1. delete : removed call to com/rbctcsworld/ecommerce/review/ReviewRepository::delete → KILLED
        reviews.delete(r);
99 1 1. delete : removed call to com/rbctcsworld/ecommerce/review/ReviewRepository::flush → KILLED
        reviews.flush();
100 1 1. delete : removed call to com/rbctcsworld/ecommerce/review/ReviewService::recalculate → KILLED
        recalculate(product);
101
    }
102
103
    @Transactional(readOnly = true)
104
    public List<ReviewResponse> mine(String email) {
105 2 1. mine : replaced return value with Collections.emptyList for com/rbctcsworld/ecommerce/review/ReviewService::mine → NO_COVERAGE
2. lambda$mine$1 : replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::lambda$mine$1 → NO_COVERAGE
        return reviews.findByUserIdOrderByIdDesc(user(email).getId()).stream().map(r -> toResponse(r, email)).toList();
106
    }
107
108
    // ---------- public ----------
109
110
    @Transactional(readOnly = true)
111
    public ProductReviews forProduct(Long productId, String sort) {
112 2 1. forProduct : removed conditional - replaced equality check with false → KILLED
2. forProduct : removed conditional - replaced equality check with false → KILLED
        String s = sort == null || sort.isBlank() ? "newest" : sort.trim().toLowerCase();
113 1 1. forProduct : removed conditional - replaced equality check with false → KILLED
        if (!SORTS.contains(s)) {
114
            throw new BusinessRuleException("sort must be one of " + SORTS);
115
        }
116
        Product product = products.findByIdAndActiveTrue(productId)
117 1 1. lambda$forProduct$2 : replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::lambda$forProduct$2 → NO_COVERAGE
                .orElseThrow(() -> new NotFoundException("Product not found: " + productId));
118
        List<Review> published = reviews.findByProductIdAndStatus(productId, Review.PUBLISHED);
119
        Map<Long, String> names = reviewerNames(published);
120
        List<ReviewResponse> list = published.stream()
121
                .sorted(comparator(s))
122 1 1. lambda$forProduct$3 : replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::lambda$forProduct$3 → KILLED
                .map(r -> toResponse(r, names.get(r.getUserId())))
123
                .toList();
124
        RatingSummary summary = summarize(reviews.ratingHistogram(productId));
125 1 1. forProduct : replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::forProduct → KILLED
        return new ProductReviews(product.getId(), summary.average(), summary.count(), summary.distribution(), list);
126
    }
127
128
    // ---------- admin moderation ----------
129
130
    @Transactional(readOnly = true)
131
    public List<ReviewResponse> adminList(String status) {
132
        List<Review> list;
133 2 1. adminList : removed conditional - replaced equality check with false → KILLED
2. adminList : removed conditional - replaced equality check with false → KILLED
        if (status == null || status.isBlank()) {
134
            list = reviews.findAllByOrderByIdDesc();
135
        } else {
136
            String st = status.trim().toUpperCase();
137 2 1. adminList : removed conditional - replaced equality check with false → KILLED
2. adminList : removed conditional - replaced equality check with false → KILLED
            if (!st.equals(Review.PUBLISHED) && !st.equals(Review.HIDDEN)) {
138
                throw new BusinessRuleException("status must be PUBLISHED or HIDDEN");
139
            }
140
            list = reviews.findByStatusOrderByIdDesc(st);
141
        }
142
        Map<Long, String> names = reviewerNames(list);
143 2 1. adminList : replaced return value with Collections.emptyList for com/rbctcsworld/ecommerce/review/ReviewService::adminList → KILLED
2. lambda$adminList$4 : replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::lambda$adminList$4 → KILLED
        return list.stream().map(r -> toResponse(r, names.get(r.getUserId()))).toList();
144
    }
145
146
    public ReviewResponse moderate(Long reviewId, String newStatus) {
147 1 1. lambda$moderate$5 : replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::lambda$moderate$5 → NO_COVERAGE
        Review r = reviews.findById(reviewId).orElseThrow(() -> new NotFoundException("Review not found: " + reviewId));
148
        Product product = lockedProduct(r.getProductId());
149 1 1. moderate : removed call to com/rbctcsworld/ecommerce/review/Review::setStatus → NO_COVERAGE
        r.setStatus(newStatus);
150
        reviews.saveAndFlush(r);
151 1 1. moderate : removed call to com/rbctcsworld/ecommerce/review/ReviewService::recalculate → NO_COVERAGE
        recalculate(product);
152 1 1. moderate : replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::moderate → NO_COVERAGE
        return toResponse(r, users.findById(r.getUserId()).map(AppUser::getEmail).orElse(null));
153
    }
154
155
    // ---------- rating maths (pure, unit tested) ----------
156
157
    /** Turns [rating, count] rows into average (1 decimal, HALF_UP), total count and a 5..1 distribution. */
158
    static RatingSummary summarize(List<Object[]> histogram) {
159
        Map<Integer, Long> distribution = new LinkedHashMap<>();
160 3 1. summarize : Changed increment from -1 to 1 → TIMED_OUT
2. summarize : removed conditional - replaced comparison check with false → KILLED
3. summarize : changed conditional boundary → KILLED
        for (int star = 5; star >= 1; star--) distribution.put(star, 0L);
161
        long count = 0;
162
        long sum = 0;
163
        for (Object[] row : histogram) {
164
            int star = ((Number) row[0]).intValue();
165
            long n = ((Number) row[1]).longValue();
166
            distribution.put(star, n);
167 1 1. summarize : Replaced long addition with subtraction → KILLED
            count += n;
168 2 1. summarize : Replaced long addition with subtraction → KILLED
2. summarize : Replaced long multiplication with division → KILLED
            sum += star * n;
169
        }
170 1 1. summarize : removed conditional - replaced equality check with false → KILLED
        BigDecimal average = count == 0
171
                ? BigDecimal.ZERO.setScale(1)
172
                : BigDecimal.valueOf(sum).divide(BigDecimal.valueOf(count), 1, RoundingMode.HALF_UP);
173 1 1. summarize : replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::summarize → KILLED
        return new RatingSummary(average, (int) count, distribution);
174
    }
175
176
    /** "jane.doe@x.com" -> "ja***". Short local parts keep only the first character. */
177
    static String mask(String email) {
178 3 1. mask : replaced return value with "" for com/rbctcsworld/ecommerce/review/ReviewService::mask → KILLED
2. mask : removed conditional - replaced equality check with false → KILLED
3. mask : removed conditional - replaced equality check with false → KILLED
        if (email == null || email.isBlank()) return "Customer";
179
        String local = email.substring(0, Math.max(email.indexOf('@'), 0));
180 1 1. mask : removed conditional - replaced equality check with false → KILLED
        if (local.isEmpty()) local = email;
181 3 1. mask : removed conditional - replaced comparison check with false → KILLED
2. mask : replaced return value with "" for com/rbctcsworld/ecommerce/review/ReviewService::mask → KILLED
3. mask : changed conditional boundary → KILLED
        return (local.length() <= 2 ? local.substring(0, 1) : local.substring(0, 2)) + "***";
182
    }
183
184
    // ---------- helpers ----------
185
186
    private void recalculate(Product product) {
187
        RatingSummary s = summarize(reviews.ratingHistogram(product.getId()));
188 1 1. recalculate : removed call to com/rbctcsworld/ecommerce/product/Product::applyRating → KILLED
        product.applyRating(s.average(), s.count());
189
        products.save(product);
190
    }
191
192
    private static Comparator<Review> comparator(String sort) {
193
        Comparator<Review> newest = Comparator.comparing(Review::getCreatedAt).thenComparing(Review::getId).reversed();
194 1 1. comparator : replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::comparator → KILLED
        return switch (sort) {
195
            case "highest" -> Comparator.comparingInt(Review::getRating).reversed().thenComparing(newest);
196
            case "lowest" -> Comparator.comparingInt(Review::getRating).thenComparing(newest);
197
            default -> newest;
198
        };
199
    }
200
201
    private Map<Long, String> reviewerNames(List<Review> list) {
202
        Set<Long> ids = list.stream().map(Review::getUserId).collect(Collectors.toSet());
203 2 1. lambda$reviewerNames$6 : replaced return value with "" for com/rbctcsworld/ecommerce/review/ReviewService::lambda$reviewerNames$6 → NO_COVERAGE
2. reviewerNames : replaced return value with Collections.emptyMap for com/rbctcsworld/ecommerce/review/ReviewService::reviewerNames → KILLED
        return users.findAllById(ids).stream().collect(Collectors.toMap(AppUser::getId, AppUser::getEmail, (a, b) -> a));
204
    }
205
206
    private Product lockedProduct(Long productId) {
207 2 1. lambda$lockedProduct$7 : replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::lambda$lockedProduct$7 → NO_COVERAGE
2. lockedProduct : replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::lockedProduct → KILLED
        return products.lockById(productId).orElseThrow(() -> new NotFoundException("Product not found: " + productId));
208
    }
209
210
    private Review owned(Long reviewId, Long userId) {
211 1 1. owned : replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::owned → KILLED
        return reviews.findByIdAndUserId(reviewId, userId)
212 1 1. lambda$owned$8 : replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::lambda$owned$8 → KILLED
                .orElseThrow(() -> new NotFoundException("Review not found: " + reviewId));
213
    }
214
215
    private ReviewResponse toResponse(Review r, String reviewerEmail) {
216 1 1. toResponse : replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::toResponse → KILLED
        return new ReviewResponse(r.getId(), r.getProductId(), r.getRating(), r.getTitle(), r.getBody(),
217
                mask(reviewerEmail), true, r.getStatus(), r.getCreatedAt(), r.getUpdatedAt());
218
    }
219
220
    private static String clean(String s) {
221 3 1. clean : removed conditional - replaced equality check with false → KILLED
2. clean : replaced return value with "" for com/rbctcsworld/ecommerce/review/ReviewService::clean → KILLED
3. clean : removed conditional - replaced equality check with false → KILLED
        return s == null || s.isBlank() ? null : s.trim();
222
    }
223
224
    private LocalDateTime now() {
225 1 1. now : replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::now → KILLED
        return LocalDateTime.now(clock);
226
    }
227
228
    private AppUser user(String email) {
229 2 1. lambda$user$9 : replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::lambda$user$9 → NO_COVERAGE
2. user : replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::user → KILLED
        return users.findByEmail(email).orElseThrow(() -> new NotFoundException("User not found"));
230
    }
231
232
}

Mutations

72

1.1
Location : lambda$create$0
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:inactiveProductCannotBeReviewed()]
replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::lambda$create$0 → KILLED

73

1.1
Location : create
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:customerWhoNeverReceivedTheProductIsForbidden()]
removed conditional - replaced equality check with false → KILLED

76

1.1
Location : create
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:secondReviewOfTheSameProductIsAConflict()]
removed conditional - replaced equality check with false → KILLED

80

1.1
Location : create
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:verifiedBuyerCanReviewAndProductRatingIsRecalculated()]
removed call to com/rbctcsworld/ecommerce/review/ReviewService::recalculate → KILLED

81

1.1
Location : create
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:verifiedBuyerCanReviewAndProductRatingIsRecalculated()]
replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::create → KILLED

88

1.1
Location : update
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:editRecalculatesAndKeepsHiddenStatus()]
removed call to com/rbctcsworld/ecommerce/review/Review::edit → KILLED

90

1.1
Location : update
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:editRecalculatesAndKeepsHiddenStatus()]
removed call to com/rbctcsworld/ecommerce/review/ReviewService::recalculate → KILLED

91

1.1
Location : update
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:editRecalculatesAndKeepsHiddenStatus()]
replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::update → KILLED

98

1.1
Location : delete
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:deletingTheLastReviewResetsTheRating()]
removed call to com/rbctcsworld/ecommerce/review/ReviewRepository::delete → KILLED

99

1.1
Location : delete
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:deletingTheLastReviewResetsTheRating()]
removed call to com/rbctcsworld/ecommerce/review/ReviewRepository::flush → KILLED

100

1.1
Location : delete
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:deletingTheLastReviewResetsTheRating()]
removed call to com/rbctcsworld/ecommerce/review/ReviewService::recalculate → KILLED

105

1.1
Location : mine
Killed by : none
replaced return value with Collections.emptyList for com/rbctcsworld/ecommerce/review/ReviewService::mine → NO_COVERAGE

2.2
Location : lambda$mine$1
Killed by : none
replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::lambda$mine$1 → NO_COVERAGE

112

1.1
Location : forProduct
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:unknownSortIsRejected()]
removed conditional - replaced equality check with false → KILLED

2.2
Location : forProduct
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:productPageShowsNewestFirstByDefaultWithMaskedNames()]
removed conditional - replaced equality check with false → KILLED

113

1.1
Location : forProduct
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:unknownSortIsRejected()]
removed conditional - replaced equality check with false → KILLED

117

1.1
Location : lambda$forProduct$2
Killed by : none
replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::lambda$forProduct$2 → NO_COVERAGE

122

1.1
Location : lambda$forProduct$3
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:productPageShowsNewestFirstByDefaultWithMaskedNames()]
replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::lambda$forProduct$3 → KILLED

125

1.1
Location : forProduct
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:productPageShowsNewestFirstByDefaultWithMaskedNames()]
replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::forProduct → KILLED

133

1.1
Location : adminList
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:emptyModerationFilterListsEveryReview()]
removed conditional - replaced equality check with false → KILLED

2.2
Location : adminList
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:unknownModerationStatusIsRejected()]
removed conditional - replaced equality check with false → KILLED

137

1.1
Location : adminList
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:unknownModerationStatusIsRejected()]
removed conditional - replaced equality check with false → KILLED

2.2
Location : adminList
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:unknownModerationStatusIsRejected()]
removed conditional - replaced equality check with false → KILLED

143

1.1
Location : adminList
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:emptyModerationFilterListsEveryReview()]
replaced return value with Collections.emptyList for com/rbctcsworld/ecommerce/review/ReviewService::adminList → KILLED

2.2
Location : lambda$adminList$4
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:emptyModerationFilterListsEveryReview()]
replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::lambda$adminList$4 → KILLED

147

1.1
Location : lambda$moderate$5
Killed by : none
replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::lambda$moderate$5 → NO_COVERAGE

149

1.1
Location : moderate
Killed by : none
removed call to com/rbctcsworld/ecommerce/review/Review::setStatus → NO_COVERAGE

151

1.1
Location : moderate
Killed by : none
removed call to com/rbctcsworld/ecommerce/review/ReviewService::recalculate → NO_COVERAGE

152

1.1
Location : moderate
Killed by : none
replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::moderate → NO_COVERAGE

160

1.1
Location : summarize
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:noReviewsMeansZeroAndAllFiveDistributionKeys()]
removed conditional - replaced comparison check with false → KILLED

2.2
Location : summarize
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:noReviewsMeansZeroAndAllFiveDistributionKeys()]
changed conditional boundary → KILLED

3.3
Location : summarize
Killed by : none
Changed increment from -1 to 1 → TIMED_OUT

167

1.1
Location : summarize
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[test-template:averageIsRoundedHalfUpToOneDecimal(java.lang.String, java.lang.String)]/[test-template-invocation:#3]
Replaced long addition with subtraction → KILLED

168

1.1
Location : summarize
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[test-template:averageIsRoundedHalfUpToOneDecimal(java.lang.String, java.lang.String)]/[test-template-invocation:#3]
Replaced long addition with subtraction → KILLED

2.2
Location : summarize
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[test-template:averageIsRoundedHalfUpToOneDecimal(java.lang.String, java.lang.String)]/[test-template-invocation:#3]
Replaced long multiplication with division → KILLED

170

1.1
Location : summarize
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:noReviewsMeansZeroAndAllFiveDistributionKeys()]
removed conditional - replaced equality check with false → KILLED

173

1.1
Location : summarize
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:noReviewsMeansZeroAndAllFiveDistributionKeys()]
replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::summarize → KILLED

178

1.1
Location : mask
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[test-template:reviewerNameFallbacks(java.lang.String, java.lang.String)]/[test-template-invocation:#1]
replaced return value with "" for com/rbctcsworld/ecommerce/review/ReviewService::mask → KILLED

2.2
Location : mask
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[test-template:reviewerNameFallbacks(java.lang.String, java.lang.String)]/[test-template-invocation:#3]
removed conditional - replaced equality check with false → KILLED

3.3
Location : mask
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[test-template:reviewerNameFallbacks(java.lang.String, java.lang.String)]/[test-template-invocation:#2]
removed conditional - replaced equality check with false → KILLED

180

1.1
Location : mask
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[test-template:reviewerNameFallbacks(java.lang.String, java.lang.String)]/[test-template-invocation:#3]
removed conditional - replaced equality check with false → KILLED

181

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

2.2
Location : mask
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[test-template:reviewerNameFallbacks(java.lang.String, java.lang.String)]/[test-template-invocation:#3]
replaced return value with "" for com/rbctcsworld/ecommerce/review/ReviewService::mask → KILLED

3.3
Location : mask
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[test-template:reviewerNameIsMasked(java.lang.String, java.lang.String)]/[test-template-invocation:#2]
changed conditional boundary → KILLED

188

1.1
Location : recalculate
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:deletingTheLastReviewResetsTheRating()]
removed call to com/rbctcsworld/ecommerce/product/Product::applyRating → KILLED

194

1.1
Location : comparator
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:productPageShowsNewestFirstByDefaultWithMaskedNames()]
replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::comparator → KILLED

203

1.1
Location : lambda$reviewerNames$6
Killed by : none
replaced return value with "" for com/rbctcsworld/ecommerce/review/ReviewService::lambda$reviewerNames$6 → NO_COVERAGE

2.2
Location : reviewerNames
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:productPageShowsNewestFirstByDefaultWithMaskedNames()]
replaced return value with Collections.emptyMap for com/rbctcsworld/ecommerce/review/ReviewService::reviewerNames → KILLED

207

1.1
Location : lockedProduct
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:deletingTheLastReviewResetsTheRating()]
replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::lockedProduct → KILLED

2.2
Location : lambda$lockedProduct$7
Killed by : none
replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::lambda$lockedProduct$7 → NO_COVERAGE

211

1.1
Location : owned
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:deletingTheLastReviewResetsTheRating()]
replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::owned → KILLED

212

1.1
Location : lambda$owned$8
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:editingSomeoneElsesReviewLooksLikeNotFound()]
replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::lambda$owned$8 → KILLED

216

1.1
Location : toResponse
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:emptyModerationFilterListsEveryReview()]
replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::toResponse → KILLED

221

1.1
Location : clean
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:verifiedBuyerCanReviewAndProductRatingIsRecalculated()]
removed conditional - replaced equality check with false → KILLED

2.2
Location : clean
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:verifiedBuyerCanReviewAndProductRatingIsRecalculated()]
replaced return value with "" for com/rbctcsworld/ecommerce/review/ReviewService::clean → KILLED

3.3
Location : clean
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:verifiedBuyerCanReviewAndProductRatingIsRecalculated()]
removed conditional - replaced equality check with false → KILLED

225

1.1
Location : now
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:editRecalculatesAndKeepsHiddenStatus()]
replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::now → KILLED

229

1.1
Location : user
Killed by : com.rbctcsworld.ecommerce.review.ReviewServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.review.ReviewServiceTest]/[method:customerWhoNeverReceivedTheProductIsForbidden()]
replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::user → KILLED

2.2
Location : lambda$user$9
Killed by : none
replaced return value with null for com/rbctcsworld/ecommerce/review/ReviewService::lambda$user$9 → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.30.0 support