StatisticsController.java

1
package edu.ucsb.cs156.dining.controllers;
2
3
import edu.ucsb.cs156.dining.entities.MenuItem;
4
import edu.ucsb.cs156.dining.entities.Review;
5
import edu.ucsb.cs156.dining.models.CommonsAverage;
6
import edu.ucsb.cs156.dining.models.CommonsAverageOverTime;
7
import edu.ucsb.cs156.dining.models.ItemStatistic;
8
import edu.ucsb.cs156.dining.models.MealAverage;
9
import edu.ucsb.cs156.dining.repositories.ReviewRepository;
10
import edu.ucsb.cs156.dining.statuses.ModerationStatus;
11
import io.swagger.v3.oas.annotations.Operation;
12
import io.swagger.v3.oas.annotations.Parameter;
13
import io.swagger.v3.oas.annotations.tags.Tag;
14
import java.time.LocalDateTime;
15
import java.time.format.DateTimeFormatter;
16
import java.util.ArrayList;
17
import java.util.Comparator;
18
import java.util.HashMap;
19
import java.util.List;
20
import java.util.Map;
21
import java.util.stream.Collectors;
22
import lombok.extern.slf4j.Slf4j;
23
import org.springframework.beans.factory.annotation.Autowired;
24
import org.springframework.http.ResponseEntity;
25
import org.springframework.security.access.prepost.PreAuthorize;
26
import org.springframework.web.bind.annotation.ExceptionHandler;
27
import org.springframework.web.bind.annotation.GetMapping;
28
import org.springframework.web.bind.annotation.PathVariable;
29
import org.springframework.web.bind.annotation.RequestMapping;
30
import org.springframework.web.bind.annotation.RequestParam;
31
import org.springframework.web.bind.annotation.RestController;
32
33
/**
34
 * REST controller that exposes aggregated review statistics. These endpoints power the "Review
35
 * Statistics" pages of the frontend (see issue #18).
36
 *
37
 * <p>Only reviews with moderation status {@link ModerationStatus#APPROVED} (or reviews that have no
38
 * comments and were therefore auto-approved) are considered when computing the statistics, so that
39
 * unmoderated user content does not influence what we publish.
40
 */
41
@Tag(name = "Statistics")
42
@RequestMapping("/api/statistics")
43
@RestController
44
@Slf4j
45
public class StatisticsController extends ApiController {
46
47
  @Autowired ReviewRepository reviewRepository;
48
49
  @ExceptionHandler(IllegalArgumentException.class)
50
  public ResponseEntity<Map<String, String>> handleValidationExceptions(
51
      IllegalArgumentException ex) {
52
    Map<String, String> errors = new HashMap<>();
53
    errors.put("error", ex.getMessage());
54 1 1. handleValidationExceptions : replaced return value with null for edu/ucsb/cs156/dining/controllers/StatisticsController::handleValidationExceptions → KILLED
    return ResponseEntity.badRequest().body(errors);
55
  }
56
57
  /** Supported time-period filters for the best/worst items endpoints. */
58
  public static final String PERIOD_ALL = "ALL";
59
60
  public static final String PERIOD_6M = "6M";
61
  public static final String PERIOD_1M = "1M";
62
  public static final String PERIOD_1W = "1W";
63
64
  private static final DateTimeFormatter MONTH_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM");
65
66
  /**
67
   * Returns the cutoff {@link LocalDateTime} for the supplied period. {@code ALL} (or any
68
   * unrecognised value) maps to {@code null}, meaning no lower bound.
69
   */
70
  static LocalDateTime cutoffForPeriod(String period, LocalDateTime now) {
71 1 1. cutoffForPeriod : negated conditional → KILLED
    if (period == null) {
72
      return null;
73
    }
74
    switch (period) {
75
      case PERIOD_6M:
76 1 1. cutoffForPeriod : replaced return value with null for edu/ucsb/cs156/dining/controllers/StatisticsController::cutoffForPeriod → KILLED
        return now.minusMonths(6);
77
      case PERIOD_1M:
78 1 1. cutoffForPeriod : replaced return value with null for edu/ucsb/cs156/dining/controllers/StatisticsController::cutoffForPeriod → KILLED
        return now.minusMonths(1);
79
      case PERIOD_1W:
80 1 1. cutoffForPeriod : replaced return value with null for edu/ucsb/cs156/dining/controllers/StatisticsController::cutoffForPeriod → KILLED
        return now.minusWeeks(1);
81
      case PERIOD_ALL:
82
      default:
83
        return null;
84
    }
85
  }
86
87
  /** Loads only approved reviews from the repository (the only ones we expose in statistics). */
88
  private List<Review> approvedReviews() {
89
    List<Review> approved = new ArrayList<>();
90
    for (Review r : reviewRepository.findByStatus(ModerationStatus.APPROVED)) {
91
      approved.add(r);
92
    }
93 1 1. approvedReviews : replaced return value with Collections.emptyList for edu/ucsb/cs156/dining/controllers/StatisticsController::approvedReviews → KILLED
    return approved;
94
  }
95
96
  /** Applies a "served on or after the cutoff" filter (if a cutoff was given). */
97
  private List<Review> filterByCutoff(List<Review> reviews, LocalDateTime cutoff) {
98 1 1. filterByCutoff : negated conditional → KILLED
    if (cutoff == null) {
99 1 1. filterByCutoff : replaced return value with Collections.emptyList for edu/ucsb/cs156/dining/controllers/StatisticsController::filterByCutoff → KILLED
      return reviews;
100
    }
101
    List<Review> filtered = new ArrayList<>();
102
    for (Review r : reviews) {
103 2 1. filterByCutoff : negated conditional → KILLED
2. filterByCutoff : negated conditional → KILLED
      if (r.getDateItemServed() != null && !r.getDateItemServed().isBefore(cutoff)) {
104
        filtered.add(r);
105
      }
106
    }
107 1 1. filterByCutoff : replaced return value with Collections.emptyList for edu/ucsb/cs156/dining/controllers/StatisticsController::filterByCutoff → KILLED
    return filtered;
108
  }
109
110
  /** Groups reviews by their associated menu item id and computes aggregate statistics. */
111
  private List<ItemStatistic> aggregateByItem(List<Review> reviews) {
112
    Map<Long, List<Review>> byItem = new HashMap<>();
113
    for (Review r : reviews) {
114
      MenuItem item = r.getItem();
115 1 1. aggregateByItem : negated conditional → KILLED
      if (item == null) {
116
        continue;
117
      }
118 1 1. lambda$aggregateByItem$0 : replaced return value with Collections.emptyList for edu/ucsb/cs156/dining/controllers/StatisticsController::lambda$aggregateByItem$0 → KILLED
      byItem.computeIfAbsent(item.getId(), k -> new ArrayList<>()).add(r);
119
    }
120
    List<ItemStatistic> stats = new ArrayList<>();
121
    for (Map.Entry<Long, List<Review>> entry : byItem.entrySet()) {
122
      List<Review> itemReviews = entry.getValue();
123
      MenuItem item = itemReviews.get(0).getItem();
124
      double total = 0.0;
125
      long count = 0L;
126
      for (Review r : itemReviews) {
127 1 1. aggregateByItem : negated conditional → KILLED
        if (r.getItemsStars() != null) {
128 1 1. aggregateByItem : Replaced double addition with subtraction → KILLED
          total += r.getItemsStars();
129 1 1. aggregateByItem : Replaced long addition with subtraction → KILLED
          count++;
130
        }
131
      }
132 1 1. aggregateByItem : negated conditional → KILLED
      if (count == 0L) {
133
        continue;
134
      }
135
      stats.add(
136
          ItemStatistic.builder()
137
              .itemId(entry.getKey())
138
              .itemName(item.getName())
139
              .diningCommonsCode(item.getDiningCommonsCode())
140
              .mealCode(item.getMealCode())
141 1 1. aggregateByItem : Replaced double division with multiplication → KILLED
              .station(item.getStation())
142
              .averageStars(total / count)
143
              .reviewCount(count)
144
              .build());
145
    }
146 1 1. aggregateByItem : replaced return value with Collections.emptyList for edu/ucsb/cs156/dining/controllers/StatisticsController::aggregateByItem → KILLED
    return stats;
147
  }
148
149
  /** Rejects negative limits before they reach {@code Stream.limit}, which throws. */
150
  private void validateLimit(int limit) {
151 2 1. validateLimit : negated conditional → KILLED
2. validateLimit : changed conditional boundary → KILLED
    if (limit < 0) {
152
      throw new IllegalArgumentException("limit must be non-negative");
153
    }
154
  }
155
156
  /** Best items endpoint, supports a time period filter and a maximum result count. */
157
  @Operation(summary = "Best rated items, optionally restricted to a recent time period")
158
  @PreAuthorize("hasRole('ROLE_USER')")
159
  @GetMapping("/items/best")
160
  public List<ItemStatistic> bestItems(
161
      @Parameter(name = "period", description = "ALL, 6M, 1M, or 1W")
162
          @RequestParam(name = "period", defaultValue = PERIOD_ALL)
163
          String period,
164
      @Parameter(name = "limit", description = "Maximum number of items to return")
165
          @RequestParam(name = "limit", defaultValue = "5")
166
          int limit) {
167
    log.info("statistics.bestItems period={} limit={}", period, limit);
168 1 1. bestItems : removed call to edu/ucsb/cs156/dining/controllers/StatisticsController::validateLimit → KILLED
    validateLimit(limit);
169
    LocalDateTime cutoff = cutoffForPeriod(period, LocalDateTime.now());
170
    List<ItemStatistic> stats = aggregateByItem(filterByCutoff(approvedReviews(), cutoff));
171 1 1. bestItems : removed call to java/util/List::sort → KILLED
    stats.sort(
172
        Comparator.comparingDouble(ItemStatistic::getAverageStars)
173
            .reversed()
174
            .thenComparing(Comparator.comparingLong(ItemStatistic::getReviewCount).reversed())
175
            .thenComparing(Comparator.comparing(ItemStatistic::getItemId)));
176 1 1. bestItems : replaced return value with Collections.emptyList for edu/ucsb/cs156/dining/controllers/StatisticsController::bestItems → KILLED
    return stats.stream().limit(limit).collect(Collectors.toCollection(ArrayList::new));
177
  }
178
179
  /** Worst items endpoint, supports a time period filter and a maximum result count. */
180
  @Operation(summary = "Worst rated items, optionally restricted to a recent time period")
181
  @PreAuthorize("hasRole('ROLE_USER')")
182
  @GetMapping("/items/worst")
183
  public List<ItemStatistic> worstItems(
184
      @Parameter(name = "period", description = "ALL, 6M, 1M, or 1W")
185
          @RequestParam(name = "period", defaultValue = PERIOD_ALL)
186
          String period,
187
      @Parameter(name = "limit", description = "Maximum number of items to return")
188
          @RequestParam(name = "limit", defaultValue = "5")
189
          int limit) {
190
    log.info("statistics.worstItems period={} limit={}", period, limit);
191 1 1. worstItems : removed call to edu/ucsb/cs156/dining/controllers/StatisticsController::validateLimit → KILLED
    validateLimit(limit);
192
    LocalDateTime cutoff = cutoffForPeriod(period, LocalDateTime.now());
193
    List<ItemStatistic> stats = aggregateByItem(filterByCutoff(approvedReviews(), cutoff));
194 1 1. worstItems : removed call to java/util/List::sort → KILLED
    stats.sort(
195
        Comparator.comparingDouble(ItemStatistic::getAverageStars)
196
            .thenComparing(Comparator.comparingLong(ItemStatistic::getReviewCount).reversed())
197
            .thenComparing(Comparator.comparing(ItemStatistic::getItemId)));
198 1 1. worstItems : replaced return value with Collections.emptyList for edu/ucsb/cs156/dining/controllers/StatisticsController::worstItems → KILLED
    return stats.stream().limit(limit).collect(Collectors.toCollection(ArrayList::new));
199
  }
200
201
  /** Average review score for each dining commons. */
202
  @Operation(summary = "Average review score for each dining commons")
203
  @PreAuthorize("hasRole('ROLE_USER')")
204
  @GetMapping("/commons/averages")
205
  public List<CommonsAverage> commonsAverages() {
206
    log.info("statistics.commonsAverages");
207
    Map<String, double[]> byCommons = new HashMap<>();
208
    for (Review r : approvedReviews()) {
209
      MenuItem item = r.getItem();
210 3 1. commonsAverages : negated conditional → KILLED
2. commonsAverages : negated conditional → KILLED
3. commonsAverages : negated conditional → KILLED
      if (item == null || item.getDiningCommonsCode() == null || r.getItemsStars() == null) {
211
        continue;
212
      }
213 1 1. lambda$commonsAverages$1 : replaced return value with null for edu/ucsb/cs156/dining/controllers/StatisticsController::lambda$commonsAverages$1 → KILLED
      double[] acc = byCommons.computeIfAbsent(item.getDiningCommonsCode(), k -> new double[2]);
214 1 1. commonsAverages : Replaced double addition with subtraction → KILLED
      acc[0] += r.getItemsStars();
215 1 1. commonsAverages : Replaced double addition with subtraction → KILLED
      acc[1] += 1.0;
216
    }
217
    List<CommonsAverage> result = new ArrayList<>();
218
    for (Map.Entry<String, double[]> entry : byCommons.entrySet()) {
219
      double[] acc = entry.getValue();
220
      long count = (long) acc[1];
221
      result.add(
222
          CommonsAverage.builder()
223 1 1. commonsAverages : Replaced double division with multiplication → KILLED
              .diningCommonsCode(entry.getKey())
224
              .averageStars(acc[0] / acc[1])
225
              .reviewCount(count)
226
              .build());
227
    }
228 1 1. commonsAverages : removed call to java/util/List::sort → KILLED
    result.sort(Comparator.comparing(CommonsAverage::getDiningCommonsCode));
229 1 1. commonsAverages : replaced return value with Collections.emptyList for edu/ucsb/cs156/dining/controllers/StatisticsController::commonsAverages → KILLED
    return result;
230
  }
231
232
  /** Average review score for each dining commons grouped by month (used to draw a graph). */
233
  @Operation(summary = "Average review score for each dining commons grouped by month")
234
  @PreAuthorize("hasRole('ROLE_USER')")
235
  @GetMapping("/commons/averages/overtime")
236
  public List<CommonsAverageOverTime> commonsAveragesOverTime() {
237
    log.info("statistics.commonsAveragesOverTime");
238
    Map<String, double[]> buckets = new HashMap<>();
239
    Map<String, String[]> labels = new HashMap<>();
240
    for (Review r : approvedReviews()) {
241
      MenuItem item = r.getItem();
242 1 1. commonsAveragesOverTime : negated conditional → KILLED
      if (item == null
243 1 1. commonsAveragesOverTime : negated conditional → KILLED
          || item.getDiningCommonsCode() == null
244 1 1. commonsAveragesOverTime : negated conditional → KILLED
          || r.getItemsStars() == null
245 1 1. commonsAveragesOverTime : negated conditional → KILLED
          || r.getDateItemServed() == null) {
246
        continue;
247
      }
248
      String code = item.getDiningCommonsCode();
249
      String period = r.getDateItemServed().format(MONTH_FORMATTER);
250
      String key = code + "|" + period;
251 1 1. lambda$commonsAveragesOverTime$2 : replaced return value with null for edu/ucsb/cs156/dining/controllers/StatisticsController::lambda$commonsAveragesOverTime$2 → KILLED
      double[] acc = buckets.computeIfAbsent(key, k -> new double[2]);
252 1 1. commonsAveragesOverTime : Replaced double addition with subtraction → KILLED
      acc[0] += r.getItemsStars();
253 1 1. commonsAveragesOverTime : Replaced double addition with subtraction → KILLED
      acc[1] += 1.0;
254 1 1. lambda$commonsAveragesOverTime$3 : replaced return value with null for edu/ucsb/cs156/dining/controllers/StatisticsController::lambda$commonsAveragesOverTime$3 → KILLED
      labels.computeIfAbsent(key, k -> new String[] {code, period});
255
    }
256
    List<CommonsAverageOverTime> result = new ArrayList<>();
257
    for (Map.Entry<String, double[]> entry : buckets.entrySet()) {
258
      String[] label = labels.get(entry.getKey());
259
      double[] acc = entry.getValue();
260
      long count = (long) acc[1];
261
      result.add(
262
          CommonsAverageOverTime.builder()
263
              .diningCommonsCode(label[0])
264 1 1. commonsAveragesOverTime : Replaced double division with multiplication → KILLED
              .period(label[1])
265
              .averageStars(acc[0] / acc[1])
266
              .reviewCount(count)
267
              .build());
268
    }
269 1 1. commonsAveragesOverTime : removed call to java/util/List::sort → KILLED
    result.sort(
270
        Comparator.comparing(CommonsAverageOverTime::getDiningCommonsCode)
271
            .thenComparing(CommonsAverageOverTime::getPeriod));
272 1 1. commonsAveragesOverTime : replaced return value with Collections.emptyList for edu/ucsb/cs156/dining/controllers/StatisticsController::commonsAveragesOverTime → KILLED
    return result;
273
  }
274
275
  /** Average review score for each meal slot at the supplied dining commons. */
276
  @Operation(summary = "Average review score grouped by meal for a single dining commons")
277
  @PreAuthorize("hasRole('ROLE_USER')")
278
  @GetMapping("/commons/{code}/meals/averages")
279
  public List<MealAverage> commonsMealAverages(
280
      @Parameter(name = "code", description = "dining commons code, e.g. 'carrillo'")
281
          @PathVariable("code")
282
          String code) {
283
    log.info("statistics.commonsMealAverages code={}", code);
284
    Map<String, double[]> byMeal = new HashMap<>();
285
    for (Review r : approvedReviews()) {
286
      MenuItem item = r.getItem();
287 1 1. commonsMealAverages : negated conditional → KILLED
      if (item == null
288 1 1. commonsMealAverages : negated conditional → KILLED
          || item.getMealCode() == null
289 1 1. commonsMealAverages : negated conditional → KILLED
          || r.getItemsStars() == null
290 1 1. commonsMealAverages : negated conditional → KILLED
          || !code.equals(item.getDiningCommonsCode())) {
291
        continue;
292
      }
293 1 1. lambda$commonsMealAverages$4 : replaced return value with null for edu/ucsb/cs156/dining/controllers/StatisticsController::lambda$commonsMealAverages$4 → KILLED
      double[] acc = byMeal.computeIfAbsent(item.getMealCode(), k -> new double[2]);
294 1 1. commonsMealAverages : Replaced double addition with subtraction → KILLED
      acc[0] += r.getItemsStars();
295 1 1. commonsMealAverages : Replaced double addition with subtraction → KILLED
      acc[1] += 1.0;
296
    }
297
    List<MealAverage> result = new ArrayList<>();
298
    for (Map.Entry<String, double[]> entry : byMeal.entrySet()) {
299
      double[] acc = entry.getValue();
300
      long count = (long) acc[1];
301
      result.add(
302
          MealAverage.builder()
303
              .diningCommonsCode(code)
304 1 1. commonsMealAverages : Replaced double division with multiplication → KILLED
              .mealCode(entry.getKey())
305
              .averageStars(acc[0] / acc[1])
306
              .reviewCount(count)
307
              .build());
308
    }
309 1 1. commonsMealAverages : removed call to java/util/List::sort → KILLED
    result.sort(Comparator.comparing(MealAverage::getMealCode));
310 1 1. commonsMealAverages : replaced return value with Collections.emptyList for edu/ucsb/cs156/dining/controllers/StatisticsController::commonsMealAverages → KILLED
    return result;
311
  }
312
}

Mutations

54

1.1
Location : handleValidationExceptions
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:worst_items_returns_bad_request_for_negative_limit()]
replaced return value with null for edu/ucsb/cs156/dining/controllers/StatisticsController::handleValidationExceptions → KILLED

71

1.1
Location : cutoffForPeriod
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:cutoffForPeriod_null_returns_null()]
negated conditional → KILLED

76

1.1
Location : cutoffForPeriod
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:cutoffForPeriod_6M()]
replaced return value with null for edu/ucsb/cs156/dining/controllers/StatisticsController::cutoffForPeriod → KILLED

78

1.1
Location : cutoffForPeriod
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:cutoffForPeriod_1M()]
replaced return value with null for edu/ucsb/cs156/dining/controllers/StatisticsController::cutoffForPeriod → KILLED

80

1.1
Location : cutoffForPeriod
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:cutoffForPeriod_1W()]
replaced return value with null for edu/ucsb/cs156/dining/controllers/StatisticsController::cutoffForPeriod → KILLED

93

1.1
Location : approvedReviews
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_averages_ignores_reviews_missing_required_fields()]
replaced return value with Collections.emptyList for edu/ucsb/cs156/dining/controllers/StatisticsController::approvedReviews → KILLED

98

1.1
Location : filterByCutoff
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:best_items_drops_items_whose_reviews_are_all_starless()]
negated conditional → KILLED

99

1.1
Location : filterByCutoff
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:best_items_default_limit_returns_at_most_five()]
replaced return value with Collections.emptyList for edu/ucsb/cs156/dining/controllers/StatisticsController::filterByCutoff → KILLED

103

1.1
Location : filterByCutoff
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:best_items_drops_reviews_with_null_served_date_when_period_set()]
negated conditional → KILLED

2.2
Location : filterByCutoff
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:worst_items_filters_by_period()]
negated conditional → KILLED

107

1.1
Location : filterByCutoff
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:worst_items_filters_by_period()]
replaced return value with Collections.emptyList for edu/ucsb/cs156/dining/controllers/StatisticsController::filterByCutoff → KILLED

115

1.1
Location : aggregateByItem
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:best_items_default_limit_returns_at_most_five()]
negated conditional → KILLED

118

1.1
Location : lambda$aggregateByItem$0
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:best_items_drops_items_whose_reviews_are_all_starless()]
replaced return value with Collections.emptyList for edu/ucsb/cs156/dining/controllers/StatisticsController::lambda$aggregateByItem$0 → KILLED

127

1.1
Location : aggregateByItem
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:best_items_drops_items_whose_reviews_are_all_starless()]
negated conditional → KILLED

128

1.1
Location : aggregateByItem
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:best_items_ignores_reviews_with_null_stars()]
Replaced double addition with subtraction → KILLED

129

1.1
Location : aggregateByItem
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:best_items_ignores_reviews_with_null_stars()]
Replaced long addition with subtraction → KILLED

132

1.1
Location : aggregateByItem
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:best_items_drops_items_whose_reviews_are_all_starless()]
negated conditional → KILLED

141

1.1
Location : aggregateByItem
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:worst_items_breaks_ties_using_review_count_then_id()]
Replaced double division with multiplication → KILLED

146

1.1
Location : aggregateByItem
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:best_items_default_limit_returns_at_most_five()]
replaced return value with Collections.emptyList for edu/ucsb/cs156/dining/controllers/StatisticsController::aggregateByItem → KILLED

151

1.1
Location : validateLimit
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:worst_items_returns_bad_request_for_negative_limit()]
negated conditional → KILLED

2.2
Location : validateLimit
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:best_items_accepts_zero_limit()]
changed conditional boundary → KILLED

168

1.1
Location : bestItems
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:best_items_returns_bad_request_for_negative_limit()]
removed call to edu/ucsb/cs156/dining/controllers/StatisticsController::validateLimit → KILLED

171

1.1
Location : bestItems
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:best_items_breaks_ties_using_review_count_then_id()]
removed call to java/util/List::sort → KILLED

176

1.1
Location : bestItems
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:best_items_default_limit_returns_at_most_five()]
replaced return value with Collections.emptyList for edu/ucsb/cs156/dining/controllers/StatisticsController::bestItems → KILLED

191

1.1
Location : worstItems
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:worst_items_returns_bad_request_for_negative_limit()]
removed call to edu/ucsb/cs156/dining/controllers/StatisticsController::validateLimit → KILLED

194

1.1
Location : worstItems
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:worst_items_breaks_ties_using_review_count_then_id()]
removed call to java/util/List::sort → KILLED

198

1.1
Location : worstItems
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:worst_items_filters_by_period()]
replaced return value with Collections.emptyList for edu/ucsb/cs156/dining/controllers/StatisticsController::worstItems → KILLED

210

1.1
Location : commonsAverages
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_averages_ignores_reviews_missing_required_fields()]
negated conditional → KILLED

2.2
Location : commonsAverages
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_averages_ignores_reviews_missing_required_fields()]
negated conditional → KILLED

3.3
Location : commonsAverages
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_averages_ignores_reviews_missing_required_fields()]
negated conditional → KILLED

213

1.1
Location : lambda$commonsAverages$1
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_averages_ignores_reviews_missing_required_fields()]
replaced return value with null for edu/ucsb/cs156/dining/controllers/StatisticsController::lambda$commonsAverages$1 → KILLED

214

1.1
Location : commonsAverages
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_averages_ignores_reviews_missing_required_fields()]
Replaced double addition with subtraction → KILLED

215

1.1
Location : commonsAverages
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_averages_ignores_reviews_missing_required_fields()]
Replaced double addition with subtraction → KILLED

223

1.1
Location : commonsAverages
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_averages_groups_by_commons_and_sorts_alphabetically()]
Replaced double division with multiplication → KILLED

228

1.1
Location : commonsAverages
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_averages_sorts_alphabetically_when_data_arrives_unsorted()]
removed call to java/util/List::sort → KILLED

229

1.1
Location : commonsAverages
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_averages_ignores_reviews_missing_required_fields()]
replaced return value with Collections.emptyList for edu/ucsb/cs156/dining/controllers/StatisticsController::commonsAverages → KILLED

242

1.1
Location : commonsAveragesOverTime
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_averages_over_time_ignores_reviews_missing_required_fields()]
negated conditional → KILLED

243

1.1
Location : commonsAveragesOverTime
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_averages_over_time_ignores_reviews_missing_required_fields()]
negated conditional → KILLED

244

1.1
Location : commonsAveragesOverTime
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_averages_over_time_ignores_reviews_missing_required_fields()]
negated conditional → KILLED

245

1.1
Location : commonsAveragesOverTime
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_averages_over_time_ignores_reviews_missing_required_fields()]
negated conditional → KILLED

251

1.1
Location : lambda$commonsAveragesOverTime$2
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_averages_over_time_ignores_reviews_missing_required_fields()]
replaced return value with null for edu/ucsb/cs156/dining/controllers/StatisticsController::lambda$commonsAveragesOverTime$2 → KILLED

252

1.1
Location : commonsAveragesOverTime
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_averages_over_time_ignores_reviews_missing_required_fields()]
Replaced double addition with subtraction → KILLED

253

1.1
Location : commonsAveragesOverTime
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_averages_over_time_ignores_reviews_missing_required_fields()]
Replaced double addition with subtraction → KILLED

254

1.1
Location : lambda$commonsAveragesOverTime$3
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_averages_over_time_ignores_reviews_missing_required_fields()]
replaced return value with null for edu/ucsb/cs156/dining/controllers/StatisticsController::lambda$commonsAveragesOverTime$3 → KILLED

264

1.1
Location : commonsAveragesOverTime
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_averages_over_time_groups_by_month_and_sorts()]
Replaced double division with multiplication → KILLED

269

1.1
Location : commonsAveragesOverTime
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_averages_over_time_groups_by_month_and_sorts()]
removed call to java/util/List::sort → KILLED

272

1.1
Location : commonsAveragesOverTime
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_averages_over_time_ignores_reviews_missing_required_fields()]
replaced return value with Collections.emptyList for edu/ucsb/cs156/dining/controllers/StatisticsController::commonsAveragesOverTime → KILLED

287

1.1
Location : commonsMealAverages
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_meal_averages_ignores_reviews_missing_required_fields()]
negated conditional → KILLED

288

1.1
Location : commonsMealAverages
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_meal_averages_ignores_reviews_missing_required_fields()]
negated conditional → KILLED

289

1.1
Location : commonsMealAverages
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_meal_averages_ignores_reviews_missing_required_fields()]
negated conditional → KILLED

290

1.1
Location : commonsMealAverages
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_meal_averages_returns_empty_for_unknown_commons()]
negated conditional → KILLED

293

1.1
Location : lambda$commonsMealAverages$4
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_meal_averages_ignores_reviews_missing_required_fields()]
replaced return value with null for edu/ucsb/cs156/dining/controllers/StatisticsController::lambda$commonsMealAverages$4 → KILLED

294

1.1
Location : commonsMealAverages
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_meal_averages_ignores_reviews_missing_required_fields()]
Replaced double addition with subtraction → KILLED

295

1.1
Location : commonsMealAverages
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_meal_averages_ignores_reviews_missing_required_fields()]
Replaced double addition with subtraction → KILLED

304

1.1
Location : commonsMealAverages
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_meal_averages_returns_only_matching_commons_and_groups_by_meal()]
Replaced double division with multiplication → KILLED

309

1.1
Location : commonsMealAverages
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_meal_averages_returns_only_matching_commons_and_groups_by_meal()]
removed call to java/util/List::sort → KILLED

310

1.1
Location : commonsMealAverages
Killed by : edu.ucsb.cs156.dining.controllers.StatisticsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.dining.controllers.StatisticsControllerTests]/[method:commons_meal_averages_ignores_reviews_missing_required_fields()]
replaced return value with Collections.emptyList for edu/ucsb/cs156/dining/controllers/StatisticsController::commonsMealAverages → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0