ConceptsController.java

1
package edu.ucsb.cs.scaffold.controller;
2
3
import com.fasterxml.jackson.core.JsonProcessingException;
4
import com.fasterxml.jackson.core.type.TypeReference;
5
import com.fasterxml.jackson.databind.ObjectMapper;
6
import edu.ucsb.cs.scaffold.entity.Concept;
7
import edu.ucsb.cs.scaffold.entity.ConceptEdge;
8
import edu.ucsb.cs.scaffold.entity.Course;
9
import edu.ucsb.cs.scaffold.entity.PracticeProblem;
10
import edu.ucsb.cs.scaffold.errors.EntityNotFoundException;
11
import edu.ucsb.cs.scaffold.model.CreateConceptDTO;
12
import edu.ucsb.cs.scaffold.model.CreateSubconceptDTO;
13
import edu.ucsb.cs.scaffold.model.UpdateConceptDTO;
14
import edu.ucsb.cs.scaffold.model.UpdateSubconceptDTO;
15
import edu.ucsb.cs.scaffold.model.UserState;
16
import edu.ucsb.cs.scaffold.repository.ConceptEdgeRepository;
17
import edu.ucsb.cs.scaffold.repository.ConceptRepository;
18
import edu.ucsb.cs.scaffold.repository.CourseRepository;
19
import edu.ucsb.cs.scaffold.repository.PracticeProblemRepository;
20
import edu.ucsb.cs.scaffold.repository.UserStateRepository;
21
import edu.ucsb.cs.scaffold.services.ConceptGraphService;
22
import edu.ucsb.cs.scaffold.services.MarkdownService;
23
import io.swagger.v3.oas.annotations.Operation;
24
import io.swagger.v3.oas.annotations.Parameter;
25
import io.swagger.v3.oas.annotations.tags.Tag;
26
import java.util.ArrayList;
27
import java.util.Comparator;
28
import java.util.HashMap;
29
import java.util.HashSet;
30
import java.util.LinkedHashMap;
31
import java.util.List;
32
import java.util.Map;
33
import java.util.Objects;
34
import java.util.stream.Collectors;
35
import lombok.RequiredArgsConstructor;
36
import org.springframework.security.access.prepost.PreAuthorize;
37
import org.springframework.web.bind.annotation.DeleteMapping;
38
import org.springframework.web.bind.annotation.GetMapping;
39
import org.springframework.web.bind.annotation.PostMapping;
40
import org.springframework.web.bind.annotation.PutMapping;
41
import org.springframework.web.bind.annotation.RequestBody;
42
import org.springframework.web.bind.annotation.RequestParam;
43
import org.springframework.web.bind.annotation.RestController;
44
45
@Tag(name = "Concepts")
46
@RestController
47
@RequiredArgsConstructor
48
public class ConceptsController extends ApiController {
49
50
  public static final int MAX_RENDERED_CONCEPT_LABEL_LENGTH = Concept.MAX_RENDERED_LABEL_LENGTH;
51
  public static final int MAX_RENDERED_SUBCONCEPT_LABEL_LENGTH =
52
      Concept.MAX_RENDERED_SUBCONCEPT_LABEL_LENGTH;
53
54
  // Applied to every new top-level concept; users cannot assign a color at creation time for
55
  // now. Top-level concepts are assumed throughout the frontend (node styling, drag-out
56
  // detail edges) to always have a real color; a null/blank color silently breaks that
57
  // styling (e.g. an SVG edge with no stroke color renders with stroke: none and is
58
  // invisible). Matches the "Level 1" swatch in the frontend's concept-graph legend.
59
  public static final String DEFAULT_TOP_LEVEL_COLOR = "#c99ffe";
60
61
  // The display order of subconcepts within a parent. sortOrder is author-controlled
62
  // (see reorderSubconcepts); the id tiebreaker makes the order deterministic even for
63
  // rows with equal or missing sortOrder (pre-backfill data, concurrent-create ties).
64
  private static final Comparator<Concept> SUBCONCEPT_DISPLAY_ORDER =
65
      Comparator.comparing(Concept::getSortOrder, Comparator.nullsLast(Comparator.naturalOrder()))
66
          .thenComparing(Concept::getId);
67
68
  private static final Comparator<Concept> COURSE_CONCEPT_TABLE_ORDER =
69
      Comparator.comparing(Concept::getLevel, Comparator.nullsLast(Comparator.naturalOrder()))
70
          .thenComparing(Concept::getX, Comparator.nullsLast(Comparator.naturalOrder()))
71
          .thenComparing(Concept::getId);
72
73
  private final ConceptRepository conceptRepository;
74
  private final PracticeProblemRepository practiceProblemRepository;
75
  private final ConceptEdgeRepository conceptEdgeRepository;
76
  private final CourseRepository courseRepository;
77
  private final UserStateRepository userStateRepository;
78
  private final MarkdownService markdownService;
79
  private final ConceptGraphService conceptGraphService;
80
  private final ObjectMapper objectMapper;
81
82
  @Operation(summary = "Get description/example/practiceUrl content for every concept in a course")
83
  @PreAuthorize("hasRole('ROLE_USER')")
84
  @GetMapping("/api/concepts/content")
85
  public Map<Long, ConceptContentDTO> getContent(
86
      @Parameter(description = "id of the course") @RequestParam Long courseId) {
87
    Map<Long, String> urlByConceptId = firstUrlByConceptId(courseId);
88
89
    Map<Long, ConceptContentDTO> result = new LinkedHashMap<>();
90
    for (Concept concept : conceptRepository.findByCourseId(courseId)) {
91 1 1. getContent : negated conditional → KILLED
      Long parentId = concept.isSubconcept() ? concept.getParent().getId() : null;
92
      result.put(
93
          concept.getId(),
94
          new ConceptContentDTO(
95
              concept.getId(),
96
              parentId,
97
              markdownService.toHtml(concept.getDescription()),
98
              markdownService.toHtml(concept.getExample()),
99
              urlByConceptId.get(concept.getId())));
100
    }
101 1 1. getContent : replaced return value with Collections.emptyMap for edu/ucsb/cs/scaffold/controller/ConceptsController::getContent → KILLED
    return result;
102
  }
103
104
  @Operation(summary = "Get the top-level concepts for a course in table order")
105
  @PreAuthorize("hasRole('ROLE_USER')")
106
  @GetMapping("/api/concepts/course")
107
  public List<CourseConceptDTO> getCourseConcepts(
108
      @Parameter(description = "id of the course") @RequestParam Long courseId) {
109 1 1. getCourseConcepts : replaced return value with Collections.emptyList for edu/ucsb/cs/scaffold/controller/ConceptsController::getCourseConcepts → KILLED
    return conceptRepository.findByCourseId(courseId).stream()
110 2 1. lambda$getCourseConcepts$0 : replaced boolean return with true for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$getCourseConcepts$0 → KILLED
2. lambda$getCourseConcepts$0 : negated conditional → KILLED
        .filter(concept -> !concept.isSubconcept())
111
        .sorted(COURSE_CONCEPT_TABLE_ORDER)
112
        .map(
113
            concept ->
114 1 1. lambda$getCourseConcepts$1 : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$getCourseConcepts$1 → KILLED
                new CourseConceptDTO(
115
                    concept.getId(),
116
                    concept.getLabel(),
117
                    concept.getDescription(),
118
                    concept.getExample(),
119
                    concept.getLevel(),
120
                    concept.getX(),
121
                    concept.getY()))
122
        .toList();
123
  }
124
125
  @Operation(summary = "Get the major concepts and their subconcepts for a course")
126
  @PreAuthorize("hasRole('ROLE_USER')")
127
  @GetMapping("/api/concepts/graph")
128
  public List<MajorConceptDTO> getGraph(
129
      @Parameter(description = "id of the course") @RequestParam Long courseId) {
130
    List<Concept> concepts = conceptRepository.findByCourseId(courseId);
131
132
    Map<Long, List<Concept>> subconceptsByParentId =
133
        concepts.stream()
134
            .filter(Concept::isSubconcept)
135
            .sorted(SUBCONCEPT_DISPLAY_ORDER)
136
            .collect(
137
                Collectors.groupingBy(
138 1 1. lambda$getGraph$2 : replaced Long return value with 0L for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$getGraph$2 → KILLED
                    concept -> concept.getParent().getId(),
139
                    LinkedHashMap::new,
140
                    Collectors.toList()));
141
142
    List<MajorConceptDTO> result = new ArrayList<>();
143
    concepts.stream()
144 2 1. lambda$getGraph$3 : negated conditional → KILLED
2. lambda$getGraph$3 : replaced boolean return with true for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$getGraph$3 → KILLED
        .filter(concept -> !concept.isSubconcept())
145
        .sorted(Comparator.comparing(Concept::getId))
146 1 1. getGraph : removed call to java/util/stream/Stream::forEach → KILLED
        .forEach(
147
            major -> {
148
              List<SubconceptDTO> subconceptDtos =
149
                  subconceptsByParentId.getOrDefault(major.getId(), List.of()).stream()
150
                      .map(
151
                          sub ->
152 1 1. lambda$getGraph$4 : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$getGraph$4 → KILLED
                              new SubconceptDTO(
153
                                  sub.getId(),
154
                                  sub.getParent().getId(),
155
                                  markdownService.toInlineHtml(sub.getLabel())))
156
                      .toList();
157
              result.add(
158
                  new MajorConceptDTO(
159
                      major.getId(),
160
                      markdownService.toInlineHtml(major.getLabel()),
161
                      major.getColor(),
162
                      subconceptDtos));
163
            });
164 1 1. getGraph : replaced return value with Collections.emptyList for edu/ucsb/cs/scaffold/controller/ConceptsController::getGraph → KILLED
    return result;
165
  }
166
167
  @Operation(summary = "Get the graph positions of the top-level concepts in a course")
168
  @PreAuthorize("hasRole('ROLE_USER')")
169
  @GetMapping("/api/concepts/positions")
170
  public Map<Long, PositionDTO> getPositions(
171
      @Parameter(description = "id of the course") @RequestParam Long courseId) {
172
    Map<Long, PositionDTO> result = new LinkedHashMap<>();
173
    for (Concept concept : conceptRepository.findByCourseId(courseId)) {
174 1 1. getPositions : negated conditional → KILLED
      if (!concept.isSubconcept()) {
175
        result.put(concept.getId(), new PositionDTO(concept.getX(), concept.getY()));
176
      }
177
    }
178 1 1. getPositions : replaced return value with Collections.emptyMap for edu/ucsb/cs/scaffold/controller/ConceptsController::getPositions → KILLED
    return result;
179
  }
180
181
  @Operation(summary = "Get all top-level concepts for a course")
182
  @PreAuthorize("hasRole('ROLE_USER')")
183
  @GetMapping("/api/concepts/top-level")
184
  public List<TopLevelConceptDTO> getTopLevelConcepts(
185
      @Parameter(description = "id of the course") @RequestParam Long courseId) {
186 1 1. getTopLevelConcepts : replaced return value with Collections.emptyList for edu/ucsb/cs/scaffold/controller/ConceptsController::getTopLevelConcepts → KILLED
    return conceptRepository.findByCourseId(courseId).stream()
187 2 1. lambda$getTopLevelConcepts$6 : negated conditional → KILLED
2. lambda$getTopLevelConcepts$6 : replaced boolean return with true for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$getTopLevelConcepts$6 → KILLED
        .filter(concept -> !concept.isSubconcept())
188
        .sorted(Comparator.comparing(Concept::getId))
189
        .map(
190
            concept ->
191 1 1. lambda$getTopLevelConcepts$7 : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$getTopLevelConcepts$7 → KILLED
                new TopLevelConceptDTO(
192
                    concept.getId(),
193
                    concept.getLabel(),
194
                    concept.getLevel(),
195
                    concept.getX(),
196
                    concept.getY()))
197
        .toList();
198
  }
199
200
  @Operation(summary = "Get all subconcepts for a course in table-row format")
201
  @PreAuthorize("hasRole('ROLE_USER')")
202
  @GetMapping("/api/concepts/subconcepts")
203
  public List<SubConceptTableRowDTO> getSubconcepts(
204
      @Parameter(description = "id of the course") @RequestParam Long courseId) {
205 1 1. getSubconcepts : replaced return value with Collections.emptyList for edu/ucsb/cs/scaffold/controller/ConceptsController::getSubconcepts → KILLED
    return conceptRepository.findByCourseId(courseId).stream()
206
        .filter(Concept::isSubconcept)
207
        .sorted(
208
            Comparator.comparing(
209 1 1. lambda$getSubconcepts$8 : replaced Integer return value with 0 for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$getSubconcepts$8 → KILLED
                    (Concept c) -> c.getParent().getLevel(),
210
                    Comparator.nullsLast(Comparator.naturalOrder()))
211
                .thenComparing(
212 1 1. lambda$getSubconcepts$9 : replaced Integer return value with 0 for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$getSubconcepts$9 → KILLED
                    c -> c.getParent().getX(), Comparator.nullsLast(Comparator.naturalOrder()))
213
                .thenComparing(
214
                    Concept::getSortOrder, Comparator.nullsLast(Comparator.naturalOrder()))
215
                .thenComparing(Concept::getId))
216
        .map(
217
            concept ->
218 1 1. lambda$getSubconcepts$10 : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$getSubconcepts$10 → KILLED
                new SubConceptTableRowDTO(
219
                    concept.getId(),
220
                    concept.getLabel(),
221
                    concept.getDescription(),
222
                    concept.getExample(),
223
                    concept.getParent().getId(),
224
                    concept.getParent().getLabel(),
225
                    concept.getParent().getLevel(),
226
                    concept.getParent().getX(),
227
                    concept.getSortOrder()))
228
        .toList();
229
  }
230
231
  @Operation(summary = "Get the prerequisite edges between concepts in a course")
232
  @PreAuthorize("hasRole('ROLE_USER')")
233
  @GetMapping("/api/concepts/edges")
234
  public List<EdgeDTO> getEdges(
235
      @Parameter(description = "id of the course") @RequestParam Long courseId) {
236
    List<EdgeDTO> result = new ArrayList<>();
237
    for (ConceptEdge edge : conceptEdgeRepository.findByCourseId(courseId)) {
238
      result.add(
239
          new EdgeDTO(
240
              edge.getId(), edge.getSource().getId(), edge.getTarget().getId(), edge.getColor()));
241
    }
242 1 1. getEdges : replaced return value with Collections.emptyList for edu/ucsb/cs/scaffold/controller/ConceptsController::getEdges → KILLED
    return result;
243
  }
244
245
  @Operation(
246
      summary = "Create a new top-level concept",
247
      description =
248
          """
249
          Accepts a YAML (or JSON) request body. label, x, and y are required; color and
250
          level are not user-settable (every new top-level concept starts at level 1 with
251
          the default color, until a scaffold reset recomputes them). label, description,
252
          and example are Markdown; they are sanitized and canonicalized before being
253
          stored. YAML block scalars (|) make multi-line Markdown easy to enter through
254
          Swagger.
255
          """)
256
  @io.swagger.v3.oas.annotations.parameters.RequestBody(
257
      content =
258
          @io.swagger.v3.oas.annotations.media.Content(
259
              mediaType = "application/yaml",
260
              schema =
261
                  @io.swagger.v3.oas.annotations.media.Schema(
262
                      implementation = CreateConceptDTO.class),
263
              examples =
264
                  @io.swagger.v3.oas.annotations.media.ExampleObject(
265
                      value =
266
                          """
267
                          courseId: 1
268
                          label: Arrays
269
                          description: |
270
                            An *array* is a fixed-size collection of elements.
271
                          example: |
272
                            ```java
273
                            int[] arr = new int[5];
274
                            ```
275
                          x: 0
276
                          y: 0
277
                          """)))
278
  @PreAuthorize("@CourseSecurity.hasManagePermissions(#root, #dto.courseId)")
279
  @PostMapping(
280
      value = "/api/concept",
281
      consumes = {"application/yaml", "application/x-yaml", "application/json"})
282
  public Concept postConcept(@RequestBody CreateConceptDTO dto) throws EntityNotFoundException {
283 1 1. postConcept : negated conditional → KILLED
    if (dto.getCourseId() == null) {
284
      throw new IllegalArgumentException("courseId is required");
285
    }
286
    Course course =
287
        courseRepository
288
            .findById(dto.getCourseId())
289 1 1. lambda$postConcept$11 : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$postConcept$11 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Course.class, dto.getCourseId()));
290
291
    String cleanLabel = cleanAndValidateLabel(dto.getLabel());
292 2 1. postConcept : negated conditional → KILLED
2. postConcept : negated conditional → KILLED
    if (dto.getX() == null || dto.getY() == null) {
293
      throw new IllegalArgumentException("x and y are required for a top-level concept");
294
    }
295
296
    Concept concept =
297
        Concept.builder()
298
            .course(course)
299
            .label(cleanLabel)
300
            .description(markdownService.clean(dto.getDescription()))
301
            .example(markdownService.clean(dto.getExample()))
302
            .color(DEFAULT_TOP_LEVEL_COLOR)
303
            .level(1)
304
            .x(dto.getX())
305
            .y(dto.getY())
306
            .build();
307 1 1. postConcept : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::postConcept → KILLED
    return conceptRepository.save(concept);
308
  }
309
310
  @Operation(
311
      summary = "Create a new subconcept of a top-level concept",
312
      description =
313
          """
314
          Accepts a YAML (or JSON) request body. The parent must be an existing top-level
315
          concept in the same course, and the label must be unique among the parent's
316
          subconcepts. Subconcepts have no name, position, or color of their own. label,
317
          description, and example are Markdown; they are sanitized and canonicalized before
318
          being stored.
319
          """)
320
  @io.swagger.v3.oas.annotations.parameters.RequestBody(
321
      content =
322
          @io.swagger.v3.oas.annotations.media.Content(
323
              mediaType = "application/yaml",
324
              schema =
325
                  @io.swagger.v3.oas.annotations.media.Schema(
326
                      implementation = CreateSubconceptDTO.class),
327
              examples =
328
                  @io.swagger.v3.oas.annotations.media.ExampleObject(
329
                      value =
330
                          """
331
                          courseId: 1
332
                          parentConceptId: 42
333
                          label: Accessing a value
334
                          description: |
335
                            Use square brackets with an index.
336
                          example: |
337
                            ```java
338
                            int first = arr[0];
339
                            ```
340
                          """)))
341
  @PreAuthorize("@CourseSecurity.hasManagePermissions(#root, #dto.courseId)")
342
  @PostMapping(
343
      value = "/api/concept/subconcept",
344
      consumes = {"application/yaml", "application/x-yaml", "application/json"})
345
  public Concept postSubconcept(@RequestBody CreateSubconceptDTO dto)
346
      throws EntityNotFoundException {
347 1 1. postSubconcept : negated conditional → KILLED
    if (dto.getCourseId() == null) {
348
      throw new IllegalArgumentException("courseId is required");
349
    }
350 1 1. postSubconcept : negated conditional → KILLED
    if (dto.getParentConceptId() == null) {
351
      throw new IllegalArgumentException("parentConceptId is required");
352
    }
353
    Course course =
354
        courseRepository
355
            .findById(dto.getCourseId())
356 1 1. lambda$postSubconcept$12 : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$postSubconcept$12 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Course.class, dto.getCourseId()));
357
358
    String cleanLabel = cleanAndValidateSubconceptLabel(dto.getLabel());
359
360
    Concept parent =
361
        conceptRepository
362
            .findById(dto.getParentConceptId())
363
            .orElseThrow(
364 1 1. lambda$postSubconcept$13 : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$postSubconcept$13 → KILLED
                () -> new EntityNotFoundException(Concept.class, dto.getParentConceptId()));
365 1 1. postSubconcept : negated conditional → KILLED
    if (!parent.getCourse().getId().equals(dto.getCourseId())) {
366
      throw new IllegalArgumentException(
367
          "parentConceptId %d belongs to a different course".formatted(dto.getParentConceptId()));
368
    }
369 1 1. postSubconcept : negated conditional → KILLED
    if (parent.isSubconcept()) {
370
      throw new IllegalArgumentException(
371
          "parentConceptId %d is a subconcept; concepts can only be nested one level deep"
372
              .formatted(dto.getParentConceptId()));
373
    }
374 1 1. postSubconcept : removed call to edu/ucsb/cs/scaffold/controller/ConceptsController::rejectDuplicateLabelUnderParent → KILLED
    rejectDuplicateLabelUnderParent(parent, cleanLabel);
375
376
    Concept concept =
377
        Concept.builder()
378
            .course(course)
379
            .label(cleanLabel)
380
            .description(markdownService.clean(dto.getDescription()))
381
            .example(markdownService.clean(dto.getExample()))
382
            .parent(parent)
383
            .sortOrder(nextSortOrder(parent.getId()))
384
            .build();
385 1 1. postSubconcept : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::postSubconcept → KILLED
    return conceptRepository.save(concept);
386
  }
387
388
  @Operation(summary = "Update the label, description, and example of a top-level concept")
389
  @PreAuthorize("@CourseSecurity.hasConceptManagementPermissions(#root, #conceptId)")
390
  @PutMapping("/api/concept/put")
391
  public Concept putConcept(
392
      @Parameter(name = "conceptId") @RequestParam Long conceptId,
393
      @RequestBody UpdateConceptDTO dto)
394
      throws EntityNotFoundException {
395
396
    Concept concept =
397
        conceptRepository
398
            .findById(conceptId)
399 1 1. lambda$putConcept$14 : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$putConcept$14 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Concept.class, conceptId));
400
401 1 1. putConcept : negated conditional → KILLED
    if (concept.isSubconcept()) {
402
      throw new IllegalArgumentException(
403
          "concept %d is a subconcept; use PUT /api/concept/subconcept/put to update it"
404
              .formatted(conceptId));
405
    }
406
407 1 1. putConcept : removed call to edu/ucsb/cs/scaffold/entity/Concept::setLabel → KILLED
    concept.setLabel(cleanAndValidateLabel(dto.getLabel()));
408 1 1. putConcept : removed call to edu/ucsb/cs/scaffold/entity/Concept::setDescription → KILLED
    concept.setDescription(markdownService.clean(dto.getDescription()));
409 1 1. putConcept : removed call to edu/ucsb/cs/scaffold/entity/Concept::setExample → KILLED
    concept.setExample(markdownService.clean(dto.getExample()));
410 1 1. putConcept : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::putConcept → KILLED
    return conceptRepository.save(concept);
411
  }
412
413
  @Operation(summary = "Update the label, description, and example of a subconcept")
414
  @PreAuthorize("@CourseSecurity.hasConceptManagementPermissions(#root, #conceptId)")
415
  @PutMapping("/api/concept/subconcept/put")
416
  public Concept putSubconcept(
417
      @Parameter(name = "conceptId") @RequestParam Long conceptId,
418
      @RequestBody UpdateSubconceptDTO dto)
419
      throws EntityNotFoundException {
420
421
    Concept concept =
422
        conceptRepository
423
            .findById(conceptId)
424 1 1. lambda$putSubconcept$15 : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$putSubconcept$15 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Concept.class, conceptId));
425
426 1 1. putSubconcept : negated conditional → KILLED
    if (!concept.isSubconcept()) {
427
      throw new IllegalArgumentException(
428
          "concept %d is not a subconcept; use PUT /api/concept/put to update it"
429
              .formatted(conceptId));
430
    }
431
432
    String cleanLabel = cleanAndValidateSubconceptLabel(dto.getLabel());
433 1 1. putSubconcept : removed call to edu/ucsb/cs/scaffold/controller/ConceptsController::rejectDuplicateLabelUnderParent → KILLED
    rejectDuplicateLabelUnderParent(concept.getParent(), cleanLabel, concept.getId());
434
435 1 1. putSubconcept : removed call to edu/ucsb/cs/scaffold/entity/Concept::setLabel → KILLED
    concept.setLabel(cleanLabel);
436 1 1. putSubconcept : removed call to edu/ucsb/cs/scaffold/entity/Concept::setDescription → KILLED
    concept.setDescription(markdownService.clean(dto.getDescription()));
437 1 1. putSubconcept : removed call to edu/ucsb/cs/scaffold/entity/Concept::setExample → KILLED
    concept.setExample(markdownService.clean(dto.getExample()));
438 1 1. putSubconcept : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::putSubconcept → KILLED
    return conceptRepository.save(concept);
439
  }
440
441
  @Operation(summary = "Add a practice problem URL to a concept")
442
  @PreAuthorize("@CourseSecurity.hasConceptManagementPermissions(#root, #conceptId)")
443
  @PostMapping("/api/concepts/practiceproblems/post")
444
  public PracticeProblem postPracticeProblem(
445
      @Parameter(name = "conceptId") @RequestParam Long conceptId,
446
      @Parameter(name = "url") @RequestParam String url)
447
      throws EntityNotFoundException {
448
449
    Concept concept =
450
        conceptRepository
451
            .findById(conceptId)
452 1 1. lambda$postPracticeProblem$16 : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$postPracticeProblem$16 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Concept.class, conceptId));
453
454
    String cleanUrl = url.strip();
455 1 1. postPracticeProblem : negated conditional → KILLED
    if (cleanUrl.isEmpty()) {
456
      throw new IllegalArgumentException("url may not be empty");
457
    }
458
    Long courseId = concept.getCourse().getId();
459
    if (practiceProblemRepository
460
        .findByCourseIdAndConceptIdAndUrl(courseId, conceptId, cleanUrl)
461 1 1. postPracticeProblem : negated conditional → KILLED
        .isPresent()) {
462
      throw new IllegalArgumentException(
463
          "concept %d already has practice problem url %s".formatted(conceptId, cleanUrl));
464
    }
465
466
    PracticeProblem practiceProblem =
467
        PracticeProblem.builder()
468
            .course(concept.getCourse())
469
            .concept(concept)
470
            .url(cleanUrl)
471
            .build();
472 1 1. postPracticeProblem : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::postPracticeProblem → KILLED
    return practiceProblemRepository.save(practiceProblem);
473
  }
474
475
  @Operation(
476
      summary = "Designate an existing concept as a subconcept of a top-level concept",
477
      description =
478
          """
479
          Sets the parent of concept conceptId to parentConceptId. The concept must have no
480
          subconcepts of its own and no prerequisite edges (delete those first). The parent
481
          must be a top-level concept in the same course. The concept keeps its x,y position
482
          (if any) and takes on the parent's color.
483
          """)
484
  @PreAuthorize("@CourseSecurity.hasConceptManagementPermissions(#root, #conceptId)")
485
  @PutMapping("/api/concepts/designate")
486
  public Concept designateSubconcept(
487
      @Parameter(name = "conceptId") @RequestParam Long conceptId,
488
      @Parameter(name = "parentConceptId") @RequestParam Long parentConceptId)
489
      throws EntityNotFoundException {
490
491
    Concept concept =
492
        conceptRepository
493
            .findById(conceptId)
494 1 1. lambda$designateSubconcept$17 : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$designateSubconcept$17 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Concept.class, conceptId));
495
    Concept parent =
496
        conceptRepository
497
            .findById(parentConceptId)
498 1 1. lambda$designateSubconcept$18 : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$designateSubconcept$18 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Concept.class, parentConceptId));
499
500 1 1. designateSubconcept : negated conditional → KILLED
    if (concept.getId().equals(parent.getId())) {
501
      throw new IllegalArgumentException("a concept cannot be its own parent");
502
    }
503 1 1. designateSubconcept : negated conditional → KILLED
    if (!parent.getCourse().getId().equals(concept.getCourse().getId())) {
504
      throw new IllegalArgumentException(
505
          "parentConceptId %d belongs to a different course".formatted(parentConceptId));
506
    }
507 1 1. designateSubconcept : negated conditional → KILLED
    if (parent.isSubconcept()) {
508
      throw new IllegalArgumentException(
509
          "parentConceptId %d is a subconcept; concepts can only be nested one level deep"
510
              .formatted(parentConceptId));
511
    }
512 1 1. designateSubconcept : negated conditional → KILLED
    if (!conceptRepository.findByParentId(conceptId).isEmpty()) {
513
      throw new IllegalArgumentException(
514
          "concept %d has subconcepts of its own, so it cannot become a subconcept"
515
              .formatted(conceptId));
516
    }
517 1 1. designateSubconcept : negated conditional → KILLED
    if (!conceptEdgeRepository.findBySourceIdOrTargetId(conceptId, conceptId).isEmpty()) {
518
      throw new IllegalArgumentException(
519
          "concept %d has prerequisite edges; delete them before designating it as a subconcept"
520
              .formatted(conceptId));
521
    }
522 1 1. designateSubconcept : removed call to edu/ucsb/cs/scaffold/controller/ConceptsController::rejectDuplicateLabelUnderParent → KILLED
    rejectDuplicateLabelUnderParent(parent, concept.getLabel());
523
524 1 1. designateSubconcept : removed call to edu/ucsb/cs/scaffold/entity/Concept::setParent → KILLED
    concept.setParent(parent);
525 1 1. designateSubconcept : removed call to edu/ucsb/cs/scaffold/entity/Concept::setColor → KILLED
    concept.setColor(parent.getColor());
526 1 1. designateSubconcept : removed call to edu/ucsb/cs/scaffold/entity/Concept::setSortOrder → KILLED
    concept.setSortOrder(nextSortOrder(parent.getId()));
527 1 1. designateSubconcept : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::designateSubconcept → KILLED
    return conceptRepository.save(concept);
528
  }
529
530
  @Operation(
531
      summary = "Split a subconcept off into its own top-level concept",
532
      description =
533
          """
534
          Severs the concept's relationship with its parent, making it a top-level concept
535
          at the given x,y position. If the concept has no color, it inherits its former
536
          parent's color.
537
          """)
538
  @PreAuthorize("@CourseSecurity.hasConceptManagementPermissions(#root, #conceptId)")
539
  @PutMapping("/api/concepts/splitoff")
540
  public Concept splitOffSubconcept(
541
      @Parameter(name = "conceptId") @RequestParam Long conceptId,
542
      @Parameter(name = "x") @RequestParam Integer x,
543
      @Parameter(name = "y") @RequestParam Integer y)
544
      throws EntityNotFoundException {
545
546
    Concept concept =
547
        conceptRepository
548
            .findById(conceptId)
549 1 1. lambda$splitOffSubconcept$19 : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$splitOffSubconcept$19 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Concept.class, conceptId));
550
551 1 1. splitOffSubconcept : negated conditional → KILLED
    if (!concept.isSubconcept()) {
552
      throw new IllegalArgumentException(
553
          "concept %d is already a top-level concept".formatted(conceptId));
554
    }
555
556 1 1. splitOffSubconcept : negated conditional → KILLED
    if (concept.getColor() == null) {
557 1 1. splitOffSubconcept : removed call to edu/ucsb/cs/scaffold/entity/Concept::setColor → KILLED
      concept.setColor(concept.getParent().getColor());
558
    }
559 1 1. splitOffSubconcept : removed call to edu/ucsb/cs/scaffold/entity/Concept::setParent → KILLED
    concept.setParent(null);
560 1 1. splitOffSubconcept : removed call to edu/ucsb/cs/scaffold/entity/Concept::setSortOrder → KILLED
    concept.setSortOrder(null);
561 1 1. splitOffSubconcept : removed call to edu/ucsb/cs/scaffold/entity/Concept::setX → KILLED
    concept.setX(x);
562 1 1. splitOffSubconcept : removed call to edu/ucsb/cs/scaffold/entity/Concept::setY → KILLED
    concept.setY(y);
563 1 1. splitOffSubconcept : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::splitOffSubconcept → KILLED
    return conceptRepository.save(concept);
564
  }
565
566
  @Operation(
567
      summary = "Reorder the subconcepts of a top-level concept",
568
      description =
569
          """
570
          Takes the complete ordered list of the parent's subconcept ids and rewrites every
571
          subconcept's sort position to its index in the list. Sending the whole permutation
572
          (rather than individual move operations) makes the update atomic and idempotent:
573
          concurrent reorders resolve to one complete, coherent ordering (last writer wins),
574
          and any sort-position ties left by concurrent subconcept creation are cleaned up as
575
          a side effect. The list must contain each current subconcept of the parent exactly
576
          once. Returns the subconcepts in their new order.
577
          """)
578
  @PreAuthorize("@CourseSecurity.hasConceptManagementPermissions(#root, #parentConceptId)")
579
  @PutMapping("/api/concepts/subconcepts/reorder")
580
  public List<SubconceptDTO> reorderSubconcepts(
581
      @Parameter(name = "parentConceptId") @RequestParam Long parentConceptId,
582
      @RequestBody List<Long> orderedSubconceptIds)
583
      throws EntityNotFoundException {
584
585
    Concept parent =
586
        conceptRepository
587
            .findById(parentConceptId)
588 1 1. lambda$reorderSubconcepts$20 : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$reorderSubconcepts$20 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Concept.class, parentConceptId));
589 1 1. reorderSubconcepts : negated conditional → KILLED
    if (parent.isSubconcept()) {
590
      throw new IllegalArgumentException(
591
          "concept %d is a subconcept; only top-level concepts have subconcepts to reorder"
592
              .formatted(parentConceptId));
593
    }
594
595
    List<Concept> subconcepts = conceptRepository.findByParentId(parentConceptId);
596
    Map<Long, Concept> subconceptById = new HashMap<>();
597
    for (Concept subconcept : subconcepts) {
598
      subconceptById.put(subconcept.getId(), subconcept);
599
    }
600 1 1. reorderSubconcepts : negated conditional → KILLED
    if (new HashSet<>(orderedSubconceptIds).size() != orderedSubconceptIds.size()
601 1 1. reorderSubconcepts : negated conditional → KILLED
        || orderedSubconceptIds.size() != subconcepts.size()
602 1 1. reorderSubconcepts : negated conditional → KILLED
        || !subconceptById.keySet().containsAll(orderedSubconceptIds)) {
603
      throw new IllegalArgumentException(
604
          "orderedSubconceptIds must contain the id of each subconcept of concept %d exactly once"
605
              .formatted(parentConceptId));
606
    }
607
608 2 1. reorderSubconcepts : negated conditional → KILLED
2. reorderSubconcepts : changed conditional boundary → KILLED
    for (int i = 0; i < orderedSubconceptIds.size(); i++) {
609 1 1. reorderSubconcepts : removed call to edu/ucsb/cs/scaffold/entity/Concept::setSortOrder → KILLED
      subconceptById.get(orderedSubconceptIds.get(i)).setSortOrder(i);
610
    }
611
    conceptRepository.saveAll(subconcepts);
612
613 1 1. reorderSubconcepts : replaced return value with Collections.emptyList for edu/ucsb/cs/scaffold/controller/ConceptsController::reorderSubconcepts → KILLED
    return orderedSubconceptIds.stream()
614
        .map(subconceptById::get)
615
        .map(
616
            sub ->
617 1 1. lambda$reorderSubconcepts$21 : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$reorderSubconcepts$21 → KILLED
                new SubconceptDTO(
618
                    sub.getId(),
619
                    sub.getParent().getId(),
620
                    markdownService.toInlineHtml(sub.getLabel())))
621
        .toList();
622
  }
623
624
  @Operation(summary = "Create a prerequisite edge between two top-level concepts")
625
  @PreAuthorize("@CourseSecurity.hasConceptManagementPermissions(#root, #sourceConceptId)")
626
  @PostMapping("/api/concepts/edges/post")
627
  public ConceptEdge postConceptEdge(
628
      @Parameter(name = "sourceConceptId") @RequestParam Long sourceConceptId,
629
      @Parameter(name = "targetConceptId") @RequestParam Long targetConceptId)
630
      throws EntityNotFoundException {
631
632
    Concept source =
633
        conceptRepository
634
            .findById(sourceConceptId)
635 1 1. lambda$postConceptEdge$22 : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$postConceptEdge$22 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Concept.class, sourceConceptId));
636
    Concept target =
637
        conceptRepository
638
            .findById(targetConceptId)
639 1 1. lambda$postConceptEdge$23 : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$postConceptEdge$23 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Concept.class, targetConceptId));
640
641 1 1. postConceptEdge : negated conditional → KILLED
    if (source.getId().equals(target.getId())) {
642
      throw new IllegalArgumentException("an edge cannot connect a concept to itself");
643
    }
644 1 1. postConceptEdge : negated conditional → KILLED
    if (!source.getCourse().getId().equals(target.getCourse().getId())) {
645
      throw new IllegalArgumentException("both concepts must belong to the same course");
646
    }
647 2 1. postConceptEdge : negated conditional → KILLED
2. postConceptEdge : negated conditional → KILLED
    if (source.isSubconcept() || target.isSubconcept()) {
648
      throw new IllegalArgumentException("edges may only connect top-level concepts");
649
    }
650
    if (conceptEdgeRepository
651
        .findBySourceIdAndTargetId(sourceConceptId, targetConceptId)
652 1 1. postConceptEdge : negated conditional → KILLED
        .isPresent()) {
653
      throw new IllegalArgumentException(
654
          "edge from concept %d to concept %d already exists"
655
              .formatted(sourceConceptId, targetConceptId));
656
    }
657 1 1. postConceptEdge : negated conditional → KILLED
    if (conceptGraphService.wouldCreateCycle(
658
        conceptEdgeRepository.findByCourseId(source.getCourse().getId()),
659
        sourceConceptId,
660
        targetConceptId)) {
661
      throw new IllegalArgumentException(
662
          "edge from concept %d to concept %d would create a cycle"
663
              .formatted(sourceConceptId, targetConceptId));
664
    }
665
666
    ConceptEdge edge =
667
        ConceptEdge.builder().course(source.getCourse()).source(source).target(target).build();
668 1 1. postConceptEdge : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::postConceptEdge → KILLED
    return conceptEdgeRepository.save(edge);
669
  }
670
671
  @Operation(summary = "Delete a prerequisite edge")
672
  @PreAuthorize("@CourseSecurity.hasConceptEdgeManagementPermissions(#root, #id)")
673
  @DeleteMapping("/api/concepts/edges/delete")
674
  public Object deleteConceptEdge(@Parameter(name = "id") @RequestParam Long id)
675
      throws EntityNotFoundException {
676
    ConceptEdge edge =
677
        conceptEdgeRepository
678
            .findById(id)
679 1 1. lambda$deleteConceptEdge$24 : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$deleteConceptEdge$24 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(ConceptEdge.class, id));
680 1 1. deleteConceptEdge : removed call to edu/ucsb/cs/scaffold/repository/ConceptEdgeRepository::delete → KILLED
    conceptEdgeRepository.delete(edge);
681 1 1. deleteConceptEdge : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::deleteConceptEdge → KILLED
    return genericMessage("ConceptEdge with id %s deleted".formatted(id));
682
  }
683
684
  @Operation(
685
      summary = "Delete a concept; deleting a top-level concept also deletes its subconcepts")
686
  @PreAuthorize("@CourseSecurity.hasConceptManagementPermissions(#root, #conceptId)")
687
  @DeleteMapping("/api/concept/delete")
688
  public Object deleteConcept(@Parameter(name = "conceptId") @RequestParam Long conceptId)
689
      throws EntityNotFoundException {
690
    Concept concept =
691
        conceptRepository
692
            .findById(conceptId)
693 1 1. lambda$deleteConcept$25 : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$deleteConcept$25 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Concept.class, conceptId));
694
695
    Course course = concept.getCourse();
696 1 1. deleteConcept : negated conditional → KILLED
    if (concept.isSubconcept()) {
697 1 1. deleteConcept : removed call to edu/ucsb/cs/scaffold/controller/ConceptsController::deleteConceptArtifacts → KILLED
      deleteConceptArtifacts(course.getId(), List.of(conceptId));
698 1 1. deleteConcept : removed call to edu/ucsb/cs/scaffold/repository/ConceptRepository::delete → KILLED
      conceptRepository.delete(concept);
699 1 1. deleteConcept : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::deleteConcept → KILLED
      return genericMessage("Concept with id %s deleted".formatted(conceptId));
700
    }
701
702
    List<Concept> subconcepts = conceptRepository.findByParentId(conceptId);
703
    List<Long> conceptIdsToDelete = new ArrayList<>();
704
    conceptIdsToDelete.add(conceptId);
705
    conceptIdsToDelete.addAll(subconcepts.stream().map(Concept::getId).toList());
706 1 1. deleteConcept : removed call to edu/ucsb/cs/scaffold/controller/ConceptsController::deleteConceptArtifacts → KILLED
    deleteConceptArtifacts(course.getId(), conceptIdsToDelete);
707 1 1. deleteConcept : removed call to edu/ucsb/cs/scaffold/repository/ConceptRepository::deleteAll → KILLED
    conceptRepository.deleteAll(subconcepts);
708 1 1. deleteConcept : removed call to edu/ucsb/cs/scaffold/repository/ConceptRepository::delete → KILLED
    conceptRepository.delete(concept);
709 1 1. deleteConcept : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::deleteConcept → KILLED
    return genericMessage("Concept with id %s deleted".formatted(conceptId));
710
  }
711
712
  @Operation(
713
      summary = "Recompute a course's concept-graph structure",
714
      description =
715
          """
716
          Runs cycle detection, transitive reduction, longest-path leveling, and layout over
717
          a course's top-level concepts and prerequisite edges, persisting the results:
718
          edges found to be part of a cycle are colored red and excluded from the rest of
719
          the analysis (their endpoints keep whatever level/position they already had);
720
          edges made redundant by the graph's transitive structure are deleted; every other
721
          top-level concept is assigned a longest-path level (roots are level 1), a color
722
          from that level, and an x,y position (each level arranged left to right, sorted by
723
          prior x then id, centered at x=0, stacked above the previous level).
724
          """)
725
  @PreAuthorize("@CourseSecurity.hasManagePermissions(#root, #courseId)")
726
  @PostMapping("/api/course/scaffold/reset")
727
  public ScaffoldResetResponseDTO resetCourseScaffold(
728
      @Parameter(name = "courseId") @RequestParam Long courseId) throws EntityNotFoundException {
729
    Course course =
730
        courseRepository
731
            .findById(courseId)
732 1 1. lambda$resetCourseScaffold$26 : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$resetCourseScaffold$26 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Course.class, courseId));
733
734
    List<Concept> topLevelConcepts =
735
        conceptRepository.findByCourseId(courseId).stream()
736 2 1. lambda$resetCourseScaffold$27 : negated conditional → KILLED
2. lambda$resetCourseScaffold$27 : replaced boolean return with true for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$resetCourseScaffold$27 → KILLED
            .filter(concept -> !concept.isSubconcept())
737
            .toList();
738
    List<ConceptEdge> edges = conceptEdgeRepository.findByCourseId(courseId);
739
740
    Map<Long, Integer> priorXByConceptId =
741
        priorXByConceptId(topLevelConcepts, callerPrivatePositions(courseId));
742
    ConceptGraphService.ResetResult result =
743
        conceptGraphService.reset(
744
            topLevelConcepts, edges, priorXByConceptId, course.getXSpacing(), course.getYSpacing());
745
746
    for (ConceptEdge edge : edges) {
747 1 1. resetCourseScaffold : removed call to edu/ucsb/cs/scaffold/entity/ConceptEdge::setColor → KILLED
      edge.setColor(
748 1 1. resetCourseScaffold : negated conditional → KILLED
          result.cycleEdgeIds().contains(edge.getId())
749
              ? ConceptGraphService.CYCLE_EDGE_COLOR
750
              : null);
751
    }
752
    conceptEdgeRepository.saveAll(edges);
753
754
    List<ConceptEdge> removedEdges =
755 2 1. lambda$resetCourseScaffold$28 : replaced boolean return with true for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$resetCourseScaffold$28 → KILLED
2. lambda$resetCourseScaffold$28 : replaced boolean return with false for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$resetCourseScaffold$28 → KILLED
        edges.stream().filter(edge -> result.removedEdgeIds().contains(edge.getId())).toList();
756 1 1. resetCourseScaffold : removed call to edu/ucsb/cs/scaffold/repository/ConceptEdgeRepository::deleteAll → KILLED
    conceptEdgeRepository.deleteAll(removedEdges);
757
758
    for (Concept concept : topLevelConcepts) {
759
      int level = result.levelByConceptId().getOrDefault(concept.getId(), 1);
760
      ConceptGraphService.Position position = result.positionByConceptId().get(concept.getId());
761 1 1. resetCourseScaffold : removed call to edu/ucsb/cs/scaffold/entity/Concept::setLevel → KILLED
      concept.setLevel(level);
762 1 1. resetCourseScaffold : removed call to edu/ucsb/cs/scaffold/entity/Concept::setColor → KILLED
      concept.setColor(conceptGraphService.colorForLevel(level));
763 1 1. resetCourseScaffold : removed call to edu/ucsb/cs/scaffold/entity/Concept::setX → KILLED
      concept.setX(position.x());
764 1 1. resetCourseScaffold : removed call to edu/ucsb/cs/scaffold/entity/Concept::setY → KILLED
      concept.setY(position.y());
765
    }
766
    conceptRepository.saveAll(topLevelConcepts);
767 1 1. resetCourseScaffold : removed call to edu/ucsb/cs/scaffold/controller/ConceptsController::clearPrivateTopLevelPositions → KILLED
    clearPrivateTopLevelPositions(courseId);
768
769
    List<CycleEdgeDTO> cycleEdgeDtos =
770
        edges.stream()
771 2 1. lambda$resetCourseScaffold$29 : replaced boolean return with false for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$resetCourseScaffold$29 → KILLED
2. lambda$resetCourseScaffold$29 : replaced boolean return with true for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$resetCourseScaffold$29 → KILLED
            .filter(edge -> result.cycleEdgeIds().contains(edge.getId()))
772
            .map(
773
                edge ->
774 1 1. lambda$resetCourseScaffold$30 : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$resetCourseScaffold$30 → KILLED
                    new CycleEdgeDTO(
775
                        edge.getId(), edge.getSource().getId(), edge.getTarget().getId()))
776
            .toList();
777
    List<RemovedEdgeDTO> removedEdgeDtos =
778
        removedEdges.stream()
779
            .map(
780
                edge ->
781 1 1. lambda$resetCourseScaffold$31 : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$resetCourseScaffold$31 → KILLED
                    new RemovedEdgeDTO(
782
                        edge.getId(), edge.getSource().getId(), edge.getTarget().getId()))
783
            .toList();
784
    List<LevelAssignmentDTO> levelDtos =
785
        topLevelConcepts.stream()
786
            .sorted(Comparator.comparing(Concept::getId))
787
            .map(
788
                concept ->
789 1 1. lambda$resetCourseScaffold$32 : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$resetCourseScaffold$32 → KILLED
                    new LevelAssignmentDTO(
790
                        concept.getId(),
791
                        concept.getLabel(),
792
                        concept.getLevel(),
793
                        concept.getColor(),
794
                        concept.getX(),
795
                        concept.getY()))
796
            .toList();
797
798
    ScaffoldResetReportDTO report =
799
        new ScaffoldResetReportDTO(cycleEdgeDtos, removedEdgeDtos, levelDtos);
800 1 1. resetCourseScaffold : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::resetCourseScaffold → KILLED
    return new ScaffoldResetResponseDTO(report, getGraph(courseId), getEdges(courseId));
801
  }
802
803
  @Operation(
804
      summary = "Update the x/y spacing used by POST /api/course/scaffold/reset for a course",
805
      description =
806
          """
807
          Sets the horizontal (xSpacing) and vertical (ySpacing) pixel spacing, between
808
          concepts on the same level and between levels respectively, that the next
809
          POST /api/course/scaffold/reset for this course will use to lay out top-level
810
          concepts. Does not itself change any concept's position.
811
          """)
812
  @PreAuthorize("@CourseSecurity.hasManagePermissions(#root, #courseId)")
813
  @PutMapping("/api/course/scaffold/spacing")
814
  public ScaffoldSpacingDTO updateScaffoldSpacing(
815
      @Parameter(name = "courseId") @RequestParam Long courseId,
816
      @Parameter(name = "xSpacing") @RequestParam int xSpacing,
817
      @Parameter(name = "ySpacing") @RequestParam int ySpacing)
818
      throws EntityNotFoundException {
819
    Course course =
820
        courseRepository
821
            .findById(courseId)
822 1 1. lambda$updateScaffoldSpacing$33 : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$updateScaffoldSpacing$33 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Course.class, courseId));
823
824 4 1. updateScaffoldSpacing : changed conditional boundary → KILLED
2. updateScaffoldSpacing : changed conditional boundary → KILLED
3. updateScaffoldSpacing : negated conditional → KILLED
4. updateScaffoldSpacing : negated conditional → KILLED
    if (xSpacing <= 0 || ySpacing <= 0) {
825
      throw new IllegalArgumentException("xSpacing and ySpacing must be positive");
826
    }
827
828 1 1. updateScaffoldSpacing : removed call to edu/ucsb/cs/scaffold/entity/Course::setXSpacing → KILLED
    course.setXSpacing(xSpacing);
829 1 1. updateScaffoldSpacing : removed call to edu/ucsb/cs/scaffold/entity/Course::setYSpacing → KILLED
    course.setYSpacing(ySpacing);
830
    Course savedCourse = courseRepository.save(course);
831
832 1 1. updateScaffoldSpacing : replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::updateScaffoldSpacing → KILLED
    return new ScaffoldSpacingDTO(savedCourse.getXSpacing(), savedCourse.getYSpacing());
833
  }
834
835
  private String cleanAndValidateLabel(String label) {
836 1 1. cleanAndValidateLabel : replaced return value with "" for edu/ucsb/cs/scaffold/controller/ConceptsController::cleanAndValidateLabel → KILLED
    return cleanAndValidateLabel(label, MAX_RENDERED_CONCEPT_LABEL_LENGTH);
837
  }
838
839
  private String cleanAndValidateSubconceptLabel(String label) {
840 1 1. cleanAndValidateSubconceptLabel : replaced return value with "" for edu/ucsb/cs/scaffold/controller/ConceptsController::cleanAndValidateSubconceptLabel → KILLED
    return cleanAndValidateLabel(label, MAX_RENDERED_SUBCONCEPT_LABEL_LENGTH);
841
  }
842
843
  private String cleanAndValidateLabel(String label, int maxRenderedLength) {
844
    String cleanLabel = markdownService.cleanLabel(label);
845 2 1. cleanAndValidateLabel : negated conditional → KILLED
2. cleanAndValidateLabel : negated conditional → KILLED
    if (cleanLabel == null || cleanLabel.isEmpty()) {
846
      throw new IllegalArgumentException("label may not be empty");
847
    }
848
    int renderedLength = markdownService.renderedLength(cleanLabel);
849 2 1. cleanAndValidateLabel : negated conditional → KILLED
2. cleanAndValidateLabel : changed conditional boundary → KILLED
    if (renderedLength > maxRenderedLength) {
850
      throw new IllegalArgumentException(
851
          "label renders to %d characters; the maximum is %d"
852
              .formatted(renderedLength, maxRenderedLength));
853
    }
854 1 1. cleanAndValidateLabel : replaced return value with "" for edu/ucsb/cs/scaffold/controller/ConceptsController::cleanAndValidateLabel → KILLED
    return cleanLabel;
855
  }
856
857
  /**
858
   * The sort position for a subconcept newly added to the given parent: one past the highest
859
   * existing position, so new subconcepts always append at the end of the author's ordering.
860
   * Subconcepts predating the sort_order column may have null positions; they are ignored here
861
   * (they display last, after positioned rows, until the author reorders).
862
   */
863
  private int nextSortOrder(Long parentId) {
864 1 1. nextSortOrder : replaced int return with 0 for edu/ucsb/cs/scaffold/controller/ConceptsController::nextSortOrder → KILLED
    return conceptRepository.findByParentId(parentId).stream()
865
            .map(Concept::getSortOrder)
866
            .filter(Objects::nonNull)
867
            .mapToInt(Integer::intValue)
868
            .max()
869 1 1. nextSortOrder : Replaced integer addition with subtraction → KILLED
            .orElse(-1)
870
        + 1;
871
  }
872
873
  private void rejectDuplicateLabelUnderParent(Concept parent, String label) {
874 1 1. rejectDuplicateLabelUnderParent : removed call to edu/ucsb/cs/scaffold/controller/ConceptsController::rejectDuplicateLabelUnderParent → KILLED
    rejectDuplicateLabelUnderParent(parent, label, null);
875
  }
876
877
  private void rejectDuplicateLabelUnderParent(
878
      Concept parent, String label, Long excludeConceptId) {
879
    Concept duplicate =
880
        conceptRepository.findByParentIdAndLabel(parent.getId(), label).orElse(null);
881 2 1. rejectDuplicateLabelUnderParent : negated conditional → KILLED
2. rejectDuplicateLabelUnderParent : negated conditional → KILLED
    if (duplicate != null && !Objects.equals(duplicate.getId(), excludeConceptId)) {
882
      throw new IllegalArgumentException(
883
          "concept %d already has a subconcept with label %s".formatted(parent.getId(), label));
884
    }
885
  }
886
887
  private void deleteConceptArtifacts(Long courseId, List<Long> conceptIds) {
888
    Map<Long, ConceptEdge> distinctEdges = new LinkedHashMap<>();
889
    for (Long id : conceptIds) {
890 1 1. deleteConceptArtifacts : removed call to edu/ucsb/cs/scaffold/repository/PracticeProblemRepository::deleteAll → KILLED
      practiceProblemRepository.deleteAll(
891
          practiceProblemRepository.findByCourseIdAndConceptId(courseId, id));
892
      for (ConceptEdge edge : conceptEdgeRepository.findBySourceIdOrTargetId(id, id)) {
893
        distinctEdges.put(edge.getId(), edge);
894
      }
895
    }
896 1 1. deleteConceptArtifacts : removed call to edu/ucsb/cs/scaffold/repository/ConceptEdgeRepository::deleteAll → KILLED
    conceptEdgeRepository.deleteAll(distinctEdges.values());
897
  }
898
899
  /**
900
   * The requesting user's own private, unsaved top-level position overrides for the course (see
901
   * {@link UserState#getTopLevelPositions()}), keyed by the concept's numeric id as a string. Empty
902
   * if the user has never dragged a top-level node or has no saved state for the course.
903
   */
904
  private Map<String, StoredPosition> callerPrivatePositions(Long courseId) {
905
    Long userId = getCurrentUser().getUser().getId();
906 1 1. callerPrivatePositions : replaced return value with Collections.emptyMap for edu/ucsb/cs/scaffold/controller/ConceptsController::callerPrivatePositions → KILLED
    return userStateRepository
907
        .findByUseridAndCourseId(userId, courseId)
908 1 1. lambda$callerPrivatePositions$34 : replaced return value with Collections.emptyMap for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$callerPrivatePositions$34 → KILLED
        .map(state -> parseTopLevelPositions(state.getTopLevelPositions()))
909
        .orElseGet(Map::of);
910
  }
911
912
  private Map<String, StoredPosition> parseTopLevelPositions(String json) {
913
    try {
914 1 1. parseTopLevelPositions : replaced return value with Collections.emptyMap for edu/ucsb/cs/scaffold/controller/ConceptsController::parseTopLevelPositions → KILLED
      return objectMapper.readValue(json, new TypeReference<Map<String, StoredPosition>>() {});
915
    } catch (JsonProcessingException e) {
916
      throw new IllegalStateException("Unable to parse stored top-level position overrides", e);
917
    }
918
  }
919
920
  /**
921
   * The x value {@link ConceptGraphService#reset} should sort each concept by: the caller's private
922
   * override for that concept if they have dragged it, otherwise the concept's own persisted x.
923
   */
924
  private Map<Long, Integer> priorXByConceptId(
925
      List<Concept> topLevelConcepts, Map<String, StoredPosition> privatePositions) {
926
    Map<Long, Integer> result = new HashMap<>();
927
    for (Concept concept : topLevelConcepts) {
928
      StoredPosition override = privatePositions.get(String.valueOf(concept.getId()));
929 2 1. priorXByConceptId : negated conditional → KILLED
2. priorXByConceptId : negated conditional → KILLED
      Integer x = override != null && override.x() != null ? override.x() : concept.getX();
930
      result.put(concept.getId(), x);
931
    }
932 1 1. priorXByConceptId : replaced return value with Collections.emptyMap for edu/ucsb/cs/scaffold/controller/ConceptsController::priorXByConceptId → KILLED
    return result;
933
  }
934
935
  /**
936
   * Clears every user's private top-level position overrides for the course. Called after a
937
   * successful reset, since a structural change (a concept moving to a different level) can make a
938
   * stale private override render in the wrong row.
939
   */
940
  private void clearPrivateTopLevelPositions(Long courseId) {
941
    List<UserState> states = userStateRepository.findByCourseId(courseId);
942
    for (UserState state : states) {
943 1 1. clearPrivateTopLevelPositions : removed call to edu/ucsb/cs/scaffold/model/UserState::setTopLevelPositions → KILLED
      state.setTopLevelPositions("{}");
944
    }
945
    userStateRepository.saveAll(states);
946
  }
947
948
  private record StoredPosition(Integer x, Integer y) {}
949
950
  private Map<Long, String> firstUrlByConceptId(Long courseId) {
951
    Map<Long, String> result = new LinkedHashMap<>();
952
    List<PracticeProblem> problems =
953
        practiceProblemRepository.findByCourseId(courseId).stream()
954
            .sorted(Comparator.comparing(PracticeProblem::getId))
955
            .toList();
956
    for (PracticeProblem problem : problems) {
957
      result.putIfAbsent(problem.getConcept().getId(), problem.getUrl());
958
    }
959 1 1. firstUrlByConceptId : replaced return value with Collections.emptyMap for edu/ucsb/cs/scaffold/controller/ConceptsController::firstUrlByConceptId → KILLED
    return result;
960
  }
961
962
  public record ConceptContentDTO(
963
      Long id, Long parentId, String descriptionHtml, String exampleHtml, String practiceUrl) {}
964
965
  public record CourseConceptDTO(
966
      Long id,
967
      String label,
968
      String description,
969
      String example,
970
      Integer level,
971
      Integer x,
972
      Integer y) {}
973
974
  public record MajorConceptDTO(
975
      Long id, String labelHtml, String color, List<SubconceptDTO> subconcepts) {}
976
977
  public record SubconceptDTO(Long id, Long parentId, String labelHtml) {}
978
979
  public record TopLevelConceptDTO(Long id, String label, Integer level, Integer x, Integer y) {}
980
981
  public record SubConceptTableRowDTO(
982
      Long id,
983
      String label,
984
      String description,
985
      String example,
986
      Long parentId,
987
      String parentLabel,
988
      Integer parentLevel,
989
      Integer parentX,
990
      Integer sortOrder) {}
991
992
  public record PositionDTO(Integer x, Integer y) {}
993
994
  public record EdgeDTO(Long id, Long sourceId, Long targetId, String color) {}
995
996
  public record CycleEdgeDTO(Long edgeId, Long sourceId, Long targetId) {}
997
998
  public record RemovedEdgeDTO(Long edgeId, Long sourceId, Long targetId) {}
999
1000
  public record LevelAssignmentDTO(
1001
      Long id, String label, Integer level, String color, Integer x, Integer y) {}
1002
1003
  public record ScaffoldResetReportDTO(
1004
      List<CycleEdgeDTO> cycleEdges,
1005
      List<RemovedEdgeDTO> removedEdges,
1006
      List<LevelAssignmentDTO> levels) {}
1007
1008
  public record ScaffoldResetResponseDTO(
1009
      ScaffoldResetReportDTO report, List<MajorConceptDTO> graph, List<EdgeDTO> edges) {}
1010
1011
  public record ScaffoldSpacingDTO(int xSpacing, int ySpacing) {}
1012
}

Mutations

91

1.1
Location : getContent
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:logged_in_user_can_get_concept_content()]
negated conditional → KILLED

101

1.1
Location : getContent
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:logged_in_user_can_get_concept_content()]
replaced return value with Collections.emptyMap for edu/ucsb/cs/scaffold/controller/ConceptsController::getContent → KILLED

109

1.1
Location : getCourseConcepts
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:logged_in_user_can_get_course_concepts_sorted_by_level_then_x()]
replaced return value with Collections.emptyList for edu/ucsb/cs/scaffold/controller/ConceptsController::getCourseConcepts → KILLED

110

1.1
Location : lambda$getCourseConcepts$0
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:logged_in_user_can_get_course_concepts_sorted_by_level_then_x()]
replaced boolean return with true for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$getCourseConcepts$0 → KILLED

2.2
Location : lambda$getCourseConcepts$0
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:logged_in_user_can_get_course_concepts_sorted_by_level_then_x()]
negated conditional → KILLED

114

1.1
Location : lambda$getCourseConcepts$1
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:logged_in_user_can_get_course_concepts_sorted_by_level_then_x()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$getCourseConcepts$1 → KILLED

138

1.1
Location : lambda$getGraph$2
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:logged_in_user_can_get_concept_graph()]
replaced Long return value with 0L for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$getGraph$2 → KILLED

144

1.1
Location : lambda$getGraph$3
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:logged_in_user_can_get_concept_graph()]
negated conditional → KILLED

2.2
Location : lambda$getGraph$3
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:logged_in_user_can_get_concept_graph()]
replaced boolean return with true for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$getGraph$3 → KILLED

146

1.1
Location : getGraph
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:logged_in_user_can_get_concept_graph()]
removed call to java/util/stream/Stream::forEach → KILLED

152

1.1
Location : lambda$getGraph$4
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:logged_in_user_can_get_concept_graph()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$getGraph$4 → KILLED

164

1.1
Location : getGraph
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:logged_in_user_can_get_concept_graph()]
replaced return value with Collections.emptyList for edu/ucsb/cs/scaffold/controller/ConceptsController::getGraph → KILLED

174

1.1
Location : getPositions
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:logged_in_user_can_get_concept_positions()]
negated conditional → KILLED

178

1.1
Location : getPositions
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:logged_in_user_can_get_concept_positions()]
replaced return value with Collections.emptyMap for edu/ucsb/cs/scaffold/controller/ConceptsController::getPositions → KILLED

186

1.1
Location : getTopLevelConcepts
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:top_level_concepts_excludes_subconcepts()]
replaced return value with Collections.emptyList for edu/ucsb/cs/scaffold/controller/ConceptsController::getTopLevelConcepts → KILLED

187

1.1
Location : lambda$getTopLevelConcepts$6
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:top_level_concepts_sorted_by_id()]
negated conditional → KILLED

2.2
Location : lambda$getTopLevelConcepts$6
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:top_level_concepts_excludes_subconcepts()]
replaced boolean return with true for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$getTopLevelConcepts$6 → KILLED

191

1.1
Location : lambda$getTopLevelConcepts$7
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:top_level_concepts_sorted_by_id()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$getTopLevelConcepts$7 → KILLED

205

1.1
Location : getSubconcepts
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:subconcepts_level_takes_priority_over_x_in_sort()]
replaced return value with Collections.emptyList for edu/ucsb/cs/scaffold/controller/ConceptsController::getSubconcepts → KILLED

209

1.1
Location : lambda$getSubconcepts$8
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:subconcepts_level_takes_priority_over_x_in_sort()]
replaced Integer return value with 0 for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$getSubconcepts$8 → KILLED

212

1.1
Location : lambda$getSubconcepts$9
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:subconcepts_sorted_by_parent_level_then_x_then_sort_order()]
replaced Integer return value with 0 for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$getSubconcepts$9 → KILLED

218

1.1
Location : lambda$getSubconcepts$10
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:subconcepts_level_takes_priority_over_x_in_sort()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$getSubconcepts$10 → KILLED

242

1.1
Location : getEdges
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:logged_in_user_sees_an_edges_cycle_color_when_set()]
replaced return value with Collections.emptyList for edu/ucsb/cs/scaffold/controller/ConceptsController::getEdges → KILLED

283

1.1
Location : postConcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_concept_returns_404_when_course_does_not_exist()]
negated conditional → KILLED

289

1.1
Location : lambda$postConcept$11
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_concept_returns_404_when_course_does_not_exist()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$postConcept$11 → KILLED

292

1.1
Location : postConcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_concept_requires_y()]
negated conditional → KILLED

2.2
Location : postConcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_concept_requires_x()]
negated conditional → KILLED

307

1.1
Location : postConcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_post_a_top_level_concept_with_a_json_body()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::postConcept → KILLED

347

1.1
Location : postSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_subconcept_requires_a_courseId()]
negated conditional → KILLED

350

1.1
Location : postSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_subconcept_requires_a_parentConceptId()]
negated conditional → KILLED

356

1.1
Location : lambda$postSubconcept$12
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_subconcept_returns_404_when_course_does_not_exist()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$postSubconcept$12 → KILLED

364

1.1
Location : lambda$postSubconcept$13
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_subconcept_returns_404_when_parent_does_not_exist()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$postSubconcept$13 → KILLED

365

1.1
Location : postSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_subconcept_rejects_a_parent_that_is_itself_a_subconcept()]
negated conditional → KILLED

369

1.1
Location : postSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_subconcept_rejects_a_parent_that_is_itself_a_subconcept()]
negated conditional → KILLED

374

1.1
Location : postSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_subconcept_rejects_a_duplicate_label_under_the_same_parent()]
removed call to edu/ucsb/cs/scaffold/controller/ConceptsController::rejectDuplicateLabelUnderParent → KILLED

385

1.1
Location : postSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_subconcept_assigns_sortOrder_0_when_the_parent_has_no_subconcepts()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::postSubconcept → KILLED

399

1.1
Location : lambda$putConcept$14
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:put_concept_returns_404_when_concept_does_not_exist()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$putConcept$14 → KILLED

401

1.1
Location : putConcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:put_concept_rejects_a_subconcept()]
negated conditional → KILLED

407

1.1
Location : putConcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_update_a_top_level_concept()]
removed call to edu/ucsb/cs/scaffold/entity/Concept::setLabel → KILLED

408

1.1
Location : putConcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_update_a_top_level_concept()]
removed call to edu/ucsb/cs/scaffold/entity/Concept::setDescription → KILLED

409

1.1
Location : putConcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_update_a_top_level_concept()]
removed call to edu/ucsb/cs/scaffold/entity/Concept::setExample → KILLED

410

1.1
Location : putConcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_update_a_top_level_concept()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::putConcept → KILLED

424

1.1
Location : lambda$putSubconcept$15
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:put_subconcept_returns_404_when_concept_does_not_exist()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$putSubconcept$15 → KILLED

426

1.1
Location : putSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:put_subconcept_rejects_a_top_level_concept()]
negated conditional → KILLED

433

1.1
Location : putSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:put_subconcept_rejects_a_duplicate_label_under_the_same_parent()]
removed call to edu/ucsb/cs/scaffold/controller/ConceptsController::rejectDuplicateLabelUnderParent → KILLED

435

1.1
Location : putSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_update_a_subconcept()]
removed call to edu/ucsb/cs/scaffold/entity/Concept::setLabel → KILLED

436

1.1
Location : putSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:put_subconcept_allows_reusing_its_own_existing_label()]
removed call to edu/ucsb/cs/scaffold/entity/Concept::setDescription → KILLED

437

1.1
Location : putSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:put_subconcept_allows_reusing_its_own_existing_label()]
removed call to edu/ucsb/cs/scaffold/entity/Concept::setExample → KILLED

438

1.1
Location : putSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:put_subconcept_allows_reusing_its_own_existing_label()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::putSubconcept → KILLED

452

1.1
Location : lambda$postPracticeProblem$16
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_practice_problem_returns_404_when_concept_does_not_exist()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$postPracticeProblem$16 → KILLED

455

1.1
Location : postPracticeProblem
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_practice_problem_rejects_a_blank_url()]
negated conditional → KILLED

461

1.1
Location : postPracticeProblem
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_practice_problem_rejects_a_duplicate_url()]
negated conditional → KILLED

472

1.1
Location : postPracticeProblem
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_add_a_practice_problem_url()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::postPracticeProblem → KILLED

494

1.1
Location : lambda$designateSubconcept$17
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:designate_returns_404_when_concept_does_not_exist()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$designateSubconcept$17 → KILLED

498

1.1
Location : lambda$designateSubconcept$18
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:designate_returns_404_when_parent_does_not_exist()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$designateSubconcept$18 → KILLED

500

1.1
Location : designateSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:designate_rejects_making_a_concept_its_own_parent()]
negated conditional → KILLED

503

1.1
Location : designateSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:designate_rejects_a_parent_that_is_itself_a_subconcept()]
negated conditional → KILLED

507

1.1
Location : designateSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:designate_rejects_a_parent_that_is_itself_a_subconcept()]
negated conditional → KILLED

512

1.1
Location : designateSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:designate_rejects_a_concept_that_has_subconcepts()]
negated conditional → KILLED

517

1.1
Location : designateSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:designate_rejects_a_concept_that_has_prerequisite_edges()]
negated conditional → KILLED

522

1.1
Location : designateSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:designate_rejects_a_duplicate_label_under_the_new_parent()]
removed call to edu/ucsb/cs/scaffold/controller/ConceptsController::rejectDuplicateLabelUnderParent → KILLED

524

1.1
Location : designateSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_designate_a_concept_as_a_subconcept()]
removed call to edu/ucsb/cs/scaffold/entity/Concept::setParent → KILLED

525

1.1
Location : designateSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_designate_a_concept_as_a_subconcept()]
removed call to edu/ucsb/cs/scaffold/entity/Concept::setColor → KILLED

526

1.1
Location : designateSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:designate_appends_the_concept_after_the_new_parents_subconcepts()]
removed call to edu/ucsb/cs/scaffold/entity/Concept::setSortOrder → KILLED

527

1.1
Location : designateSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:designate_appends_the_concept_after_the_new_parents_subconcepts()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::designateSubconcept → KILLED

549

1.1
Location : lambda$splitOffSubconcept$19
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:split_off_returns_404_when_concept_does_not_exist()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$splitOffSubconcept$19 → KILLED

551

1.1
Location : splitOffSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:split_off_rejects_a_concept_that_is_already_top_level()]
negated conditional → KILLED

556

1.1
Location : splitOffSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:split_off_keeps_the_subconcepts_own_color_when_it_has_one()]
negated conditional → KILLED

557

1.1
Location : splitOffSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_split_off_a_subconcept()]
removed call to edu/ucsb/cs/scaffold/entity/Concept::setColor → KILLED

559

1.1
Location : splitOffSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_split_off_a_subconcept()]
removed call to edu/ucsb/cs/scaffold/entity/Concept::setParent → KILLED

560

1.1
Location : splitOffSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:split_off_clears_the_subconcepts_sortOrder()]
removed call to edu/ucsb/cs/scaffold/entity/Concept::setSortOrder → KILLED

561

1.1
Location : splitOffSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_split_off_a_subconcept()]
removed call to edu/ucsb/cs/scaffold/entity/Concept::setX → KILLED

562

1.1
Location : splitOffSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_split_off_a_subconcept()]
removed call to edu/ucsb/cs/scaffold/entity/Concept::setY → KILLED

563

1.1
Location : splitOffSubconcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_split_off_a_subconcept()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::splitOffSubconcept → KILLED

588

1.1
Location : lambda$reorderSubconcepts$20
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reorder_returns_404_when_the_parent_does_not_exist()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$reorderSubconcepts$20 → KILLED

589

1.1
Location : reorderSubconcepts
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reorder_accepts_an_empty_list_for_a_parent_with_no_subconcepts()]
negated conditional → KILLED

600

1.1
Location : reorderSubconcepts
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reorder_accepts_an_empty_list_for_a_parent_with_no_subconcepts()]
negated conditional → KILLED

601

1.1
Location : reorderSubconcepts
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reorder_accepts_an_empty_list_for_a_parent_with_no_subconcepts()]
negated conditional → KILLED

602

1.1
Location : reorderSubconcepts
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reorder_accepts_an_empty_list_for_a_parent_with_no_subconcepts()]
negated conditional → KILLED

608

1.1
Location : reorderSubconcepts
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reorder_accepts_an_empty_list_for_a_parent_with_no_subconcepts()]
negated conditional → KILLED

2.2
Location : reorderSubconcepts
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reorder_accepts_an_empty_list_for_a_parent_with_no_subconcepts()]
changed conditional boundary → KILLED

609

1.1
Location : reorderSubconcepts
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_reorder_subconcepts()]
removed call to edu/ucsb/cs/scaffold/entity/Concept::setSortOrder → KILLED

613

1.1
Location : reorderSubconcepts
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_reorder_subconcepts()]
replaced return value with Collections.emptyList for edu/ucsb/cs/scaffold/controller/ConceptsController::reorderSubconcepts → KILLED

617

1.1
Location : lambda$reorderSubconcepts$21
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_reorder_subconcepts()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$reorderSubconcepts$21 → KILLED

635

1.1
Location : lambda$postConceptEdge$22
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_edge_returns_404_when_source_does_not_exist()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$postConceptEdge$22 → KILLED

639

1.1
Location : lambda$postConceptEdge$23
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_edge_returns_404_when_target_does_not_exist()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$postConceptEdge$23 → KILLED

641

1.1
Location : postConceptEdge
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_edge_rejects_concepts_from_different_courses()]
negated conditional → KILLED

644

1.1
Location : postConceptEdge
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_edge_rejects_concepts_from_different_courses()]
negated conditional → KILLED

647

1.1
Location : postConceptEdge
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_edge_rejects_a_subconcept_target()]
negated conditional → KILLED

2.2
Location : postConceptEdge
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_edge_rejects_a_subconcept_source()]
negated conditional → KILLED

652

1.1
Location : postConceptEdge
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_edge_rejects_a_duplicate_edge()]
negated conditional → KILLED

657

1.1
Location : postConceptEdge
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_edge_rejects_an_edge_that_would_create_a_cycle()]
negated conditional → KILLED

668

1.1
Location : postConceptEdge
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_post_a_concept_edge()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::postConceptEdge → KILLED

679

1.1
Location : lambda$deleteConceptEdge$24
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:delete_edge_returns_404_when_edge_does_not_exist()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$deleteConceptEdge$24 → KILLED

680

1.1
Location : deleteConceptEdge
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_delete_a_concept_edge()]
removed call to edu/ucsb/cs/scaffold/repository/ConceptEdgeRepository::delete → KILLED

681

1.1
Location : deleteConceptEdge
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_delete_a_concept_edge()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::deleteConceptEdge → KILLED

693

1.1
Location : lambda$deleteConcept$25
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:delete_concept_returns_404_when_concept_does_not_exist()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$deleteConcept$25 → KILLED

696

1.1
Location : deleteConcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_delete_a_top_level_concept_and_its_subconcepts()]
negated conditional → KILLED

697

1.1
Location : deleteConcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_delete_a_subconcept_directly()]
removed call to edu/ucsb/cs/scaffold/controller/ConceptsController::deleteConceptArtifacts → KILLED

698

1.1
Location : deleteConcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_delete_a_subconcept_directly()]
removed call to edu/ucsb/cs/scaffold/repository/ConceptRepository::delete → KILLED

699

1.1
Location : deleteConcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_delete_a_subconcept_directly()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::deleteConcept → KILLED

706

1.1
Location : deleteConcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_delete_a_top_level_concept_and_its_subconcepts()]
removed call to edu/ucsb/cs/scaffold/controller/ConceptsController::deleteConceptArtifacts → KILLED

707

1.1
Location : deleteConcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_delete_a_top_level_concept_and_its_subconcepts()]
removed call to edu/ucsb/cs/scaffold/repository/ConceptRepository::deleteAll → KILLED

708

1.1
Location : deleteConcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_delete_a_top_level_concept_and_its_subconcepts()]
removed call to edu/ucsb/cs/scaffold/repository/ConceptRepository::delete → KILLED

709

1.1
Location : deleteConcept
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_delete_a_top_level_concept_and_its_subconcepts()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::deleteConcept → KILLED

732

1.1
Location : lambda$resetCourseScaffold$26
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_course_scaffold_returns_404_when_course_does_not_exist()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$resetCourseScaffold$26 → KILLED

736

1.1
Location : lambda$resetCourseScaffold$27
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_excludes_subconcepts_from_the_top_level_analysis()]
negated conditional → KILLED

2.2
Location : lambda$resetCourseScaffold$27
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_excludes_subconcepts_from_the_top_level_analysis()]
replaced boolean return with true for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$resetCourseScaffold$27 → KILLED

747

1.1
Location : resetCourseScaffold
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_colors_a_two_node_cycle_red_and_falls_both_concepts_back_to_level_one()]
removed call to edu/ucsb/cs/scaffold/entity/ConceptEdge::setColor → KILLED

748

1.1
Location : resetCourseScaffold
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_ranks_and_lays_out_a_simple_chain()]
negated conditional → KILLED

755

1.1
Location : lambda$resetCourseScaffold$28
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_ranks_and_lays_out_a_simple_chain()]
replaced boolean return with true for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$resetCourseScaffold$28 → KILLED

2.2
Location : lambda$resetCourseScaffold$28
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_deletes_an_edge_made_redundant_by_a_longer_path()]
replaced boolean return with false for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$resetCourseScaffold$28 → KILLED

756

1.1
Location : resetCourseScaffold
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_deletes_an_edge_made_redundant_by_a_longer_path()]
removed call to edu/ucsb/cs/scaffold/repository/ConceptEdgeRepository::deleteAll → KILLED

761

1.1
Location : resetCourseScaffold
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_ranks_and_lays_out_a_simple_chain()]
removed call to edu/ucsb/cs/scaffold/entity/Concept::setLevel → KILLED

762

1.1
Location : resetCourseScaffold
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_ranks_and_lays_out_a_simple_chain()]
removed call to edu/ucsb/cs/scaffold/entity/Concept::setColor → KILLED

763

1.1
Location : resetCourseScaffold
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_sorts_by_the_callers_private_position_override_instead_of_the_saved_x()]
removed call to edu/ucsb/cs/scaffold/entity/Concept::setX → KILLED

764

1.1
Location : resetCourseScaffold
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_uses_the_courses_xSpacing_and_ySpacing_instead_of_the_defaults()]
removed call to edu/ucsb/cs/scaffold/entity/Concept::setY → KILLED

767

1.1
Location : resetCourseScaffold
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_clears_every_users_private_top_level_position_overrides()]
removed call to edu/ucsb/cs/scaffold/controller/ConceptsController::clearPrivateTopLevelPositions → KILLED

771

1.1
Location : lambda$resetCourseScaffold$29
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_colors_a_two_node_cycle_red_and_falls_both_concepts_back_to_level_one()]
replaced boolean return with false for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$resetCourseScaffold$29 → KILLED

2.2
Location : lambda$resetCourseScaffold$29
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_ranks_and_lays_out_a_simple_chain()]
replaced boolean return with true for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$resetCourseScaffold$29 → KILLED

774

1.1
Location : lambda$resetCourseScaffold$30
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_colors_a_two_node_cycle_red_and_falls_both_concepts_back_to_level_one()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$resetCourseScaffold$30 → KILLED

781

1.1
Location : lambda$resetCourseScaffold$31
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_deletes_an_edge_made_redundant_by_a_longer_path()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$resetCourseScaffold$31 → KILLED

789

1.1
Location : lambda$resetCourseScaffold$32
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_excludes_subconcepts_from_the_top_level_analysis()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$resetCourseScaffold$32 → KILLED

800

1.1
Location : resetCourseScaffold
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_excludes_subconcepts_from_the_top_level_analysis()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::resetCourseScaffold → KILLED

822

1.1
Location : lambda$updateScaffoldSpacing$33
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:update_scaffold_spacing_returns_404_when_course_does_not_exist()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$updateScaffoldSpacing$33 → KILLED

824

1.1
Location : updateScaffoldSpacing
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:update_scaffold_spacing_rejects_a_zero_ySpacing()]
changed conditional boundary → KILLED

2.2
Location : updateScaffoldSpacing
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:update_scaffold_spacing_rejects_a_non_positive_xSpacing()]
changed conditional boundary → KILLED

3.3
Location : updateScaffoldSpacing
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:update_scaffold_spacing_rejects_a_non_positive_xSpacing()]
negated conditional → KILLED

4.4
Location : updateScaffoldSpacing
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:update_scaffold_spacing_rejects_a_non_positive_ySpacing()]
negated conditional → KILLED

828

1.1
Location : updateScaffoldSpacing
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:update_scaffold_spacing_saves_the_new_values_and_returns_them()]
removed call to edu/ucsb/cs/scaffold/entity/Course::setXSpacing → KILLED

829

1.1
Location : updateScaffoldSpacing
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:update_scaffold_spacing_saves_the_new_values_and_returns_them()]
removed call to edu/ucsb/cs/scaffold/entity/Course::setYSpacing → KILLED

832

1.1
Location : updateScaffoldSpacing
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:update_scaffold_spacing_saves_the_new_values_and_returns_them()]
replaced return value with null for edu/ucsb/cs/scaffold/controller/ConceptsController::updateScaffoldSpacing → KILLED

836

1.1
Location : cleanAndValidateLabel
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_post_a_top_level_concept_with_a_json_body()]
replaced return value with "" for edu/ucsb/cs/scaffold/controller/ConceptsController::cleanAndValidateLabel → KILLED

840

1.1
Location : cleanAndValidateSubconceptLabel
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:put_subconcept_rejects_a_duplicate_label_under_the_same_parent()]
replaced return value with "" for edu/ucsb/cs/scaffold/controller/ConceptsController::cleanAndValidateSubconceptLabel → KILLED

845

1.1
Location : cleanAndValidateLabel
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_concept_requires_a_label()]
negated conditional → KILLED

2.2
Location : cleanAndValidateLabel
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:put_concept_rejects_an_empty_label()]
negated conditional → KILLED

849

1.1
Location : cleanAndValidateLabel
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_subconcept_returns_404_when_parent_does_not_exist()]
negated conditional → KILLED

2.2
Location : cleanAndValidateLabel
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:post_concept_accepts_a_label_whose_rendered_length_is_exactly_the_maximum()]
changed conditional boundary → KILLED

854

1.1
Location : cleanAndValidateLabel
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_post_a_top_level_concept_with_a_json_body()]
replaced return value with "" for edu/ucsb/cs/scaffold/controller/ConceptsController::cleanAndValidateLabel → KILLED

864

1.1
Location : nextSortOrder
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:designate_appends_the_concept_after_the_new_parents_subconcepts()]
replaced int return with 0 for edu/ucsb/cs/scaffold/controller/ConceptsController::nextSortOrder → KILLED

869

1.1
Location : nextSortOrder
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:designate_appends_the_concept_after_the_new_parents_subconcepts()]
Replaced integer addition with subtraction → KILLED

874

1.1
Location : rejectDuplicateLabelUnderParent
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:designate_rejects_a_duplicate_label_under_the_new_parent()]
removed call to edu/ucsb/cs/scaffold/controller/ConceptsController::rejectDuplicateLabelUnderParent → KILLED

881

1.1
Location : rejectDuplicateLabelUnderParent
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:designate_rejects_a_duplicate_label_under_the_new_parent()]
negated conditional → KILLED

2.2
Location : rejectDuplicateLabelUnderParent
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:designate_rejects_a_duplicate_label_under_the_new_parent()]
negated conditional → KILLED

890

1.1
Location : deleteConceptArtifacts
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_delete_a_subconcept_directly()]
removed call to edu/ucsb/cs/scaffold/repository/PracticeProblemRepository::deleteAll → KILLED

896

1.1
Location : deleteConceptArtifacts
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:instructor_can_delete_a_subconcept_directly()]
removed call to edu/ucsb/cs/scaffold/repository/ConceptEdgeRepository::deleteAll → KILLED

906

1.1
Location : callerPrivatePositions
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_sorts_by_the_callers_private_position_override_instead_of_the_saved_x()]
replaced return value with Collections.emptyMap for edu/ucsb/cs/scaffold/controller/ConceptsController::callerPrivatePositions → KILLED

908

1.1
Location : lambda$callerPrivatePositions$34
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_sorts_by_the_callers_private_position_override_instead_of_the_saved_x()]
replaced return value with Collections.emptyMap for edu/ucsb/cs/scaffold/controller/ConceptsController::lambda$callerPrivatePositions$34 → KILLED

914

1.1
Location : parseTopLevelPositions
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_sorts_by_the_callers_private_position_override_instead_of_the_saved_x()]
replaced return value with Collections.emptyMap for edu/ucsb/cs/scaffold/controller/ConceptsController::parseTopLevelPositions → KILLED

929

1.1
Location : priorXByConceptId
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_excludes_subconcepts_from_the_top_level_analysis()]
negated conditional → KILLED

2.2
Location : priorXByConceptId
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_sorts_by_the_callers_private_position_override_instead_of_the_saved_x()]
negated conditional → KILLED

932

1.1
Location : priorXByConceptId
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_sorts_by_the_callers_private_position_override_instead_of_the_saved_x()]
replaced return value with Collections.emptyMap for edu/ucsb/cs/scaffold/controller/ConceptsController::priorXByConceptId → KILLED

943

1.1
Location : clearPrivateTopLevelPositions
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_clears_every_users_private_top_level_position_overrides()]
removed call to edu/ucsb/cs/scaffold/model/UserState::setTopLevelPositions → KILLED

959

1.1
Location : firstUrlByConceptId
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:logged_in_user_can_get_concept_content()]
replaced return value with Collections.emptyMap for edu/ucsb/cs/scaffold/controller/ConceptsController::firstUrlByConceptId → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0