CoursesController.java

1
package edu.ucsb.cs156.frontiers.controllers;
2
3
import com.fasterxml.jackson.core.JsonProcessingException;
4
import edu.ucsb.cs156.frontiers.entities.Course;
5
import edu.ucsb.cs156.frontiers.entities.CourseStaff;
6
import edu.ucsb.cs156.frontiers.entities.RosterStudent;
7
import edu.ucsb.cs156.frontiers.entities.User;
8
import edu.ucsb.cs156.frontiers.enums.OrgStatus;
9
import edu.ucsb.cs156.frontiers.enums.School;
10
import edu.ucsb.cs156.frontiers.errors.EntityNotFoundException;
11
import edu.ucsb.cs156.frontiers.errors.InvalidInstallationTypeException;
12
import edu.ucsb.cs156.frontiers.models.CourseWarning;
13
import edu.ucsb.cs156.frontiers.models.CurrentUser;
14
import edu.ucsb.cs156.frontiers.repositories.AdminRepository;
15
import edu.ucsb.cs156.frontiers.repositories.CourseRepository;
16
import edu.ucsb.cs156.frontiers.repositories.CourseStaffRepository;
17
import edu.ucsb.cs156.frontiers.repositories.InstructorRepository;
18
import edu.ucsb.cs156.frontiers.repositories.RosterStudentRepository;
19
import edu.ucsb.cs156.frontiers.repositories.UserRepository;
20
import edu.ucsb.cs156.frontiers.services.OrganizationLinkerService;
21
import io.swagger.v3.oas.annotations.Operation;
22
import io.swagger.v3.oas.annotations.Parameter;
23
import io.swagger.v3.oas.annotations.tags.Tag;
24
import java.security.NoSuchAlgorithmException;
25
import java.security.spec.InvalidKeySpecException;
26
import java.util.ArrayList;
27
import java.util.List;
28
import java.util.Map;
29
import java.util.Optional;
30
import java.util.stream.Collectors;
31
import lombok.extern.slf4j.Slf4j;
32
import org.springframework.beans.factory.annotation.Autowired;
33
import org.springframework.http.HttpHeaders;
34
import org.springframework.http.HttpStatus;
35
import org.springframework.http.ResponseEntity;
36
import org.springframework.security.access.prepost.PreAuthorize;
37
import org.springframework.web.bind.annotation.*;
38
39
@Tag(name = "Course")
40
@RequestMapping("/api/courses")
41
@RestController
42
@Slf4j
43
public class CoursesController extends ApiController {
44
45
  @Autowired private CourseRepository courseRepository;
46
47
  @Autowired private UserRepository userRepository;
48
49
  @Autowired private RosterStudentRepository rosterStudentRepository;
50
51
  @Autowired private CourseStaffRepository courseStaffRepository;
52
53
  @Autowired private InstructorRepository instructorRepository;
54
55
  @Autowired private AdminRepository adminRepository;
56
57
  @Autowired private OrganizationLinkerService linkerService;
58
59
  /**
60
   * This method creates a new Course.
61
   *
62
   * @param courseName the name of the course
63
   * @param term the term of the course
64
   * @param school the school of the course
65
   * @param canvasApiToken the Canvas API token (optional)
66
   * @param canvasCourseId the Canvas course ID (optional)
67
   */
68
  @Operation(summary = "Create a new course")
69
  @PreAuthorize("hasRole('ROLE_ADMIN') || hasRole('ROLE_INSTRUCTOR')")
70
  @PostMapping("/post")
71
  public InstructorCourseView postCourse(
72
      @Parameter(name = "courseName") @RequestParam String courseName,
73
      @Parameter(name = "term") @RequestParam String term,
74
      @Parameter(name = "school") @RequestParam School school,
75
      @Parameter(name = "canvasApiToken") @RequestParam(required = false) String canvasApiToken,
76
      @Parameter(name = "canvasCourseId") @RequestParam(required = false) String canvasCourseId) {
77
    // get current date right now and set status to pending
78
    CurrentUser currentUser = getCurrentUser();
79
    Course course =
80
        Course.builder()
81
            .courseName(courseName)
82
            .term(term)
83
            .school(school)
84
            .instructorEmail(currentUser.getUser().getEmail().strip())
85
            .canvasApiToken(canvasApiToken)
86
            .canvasCourseId(canvasCourseId)
87
            .build();
88
    Course savedCourse = courseRepository.save(course);
89
90 1 1. postCourse : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::postCourse → KILLED
    return new InstructorCourseView(savedCourse);
91
  }
92
93
  /** Projection of Course entity with fields that are relevant for instructors and admins */
94
  public static record InstructorCourseView(
95
      Long id,
96
      String installationId,
97
      String orgName,
98
      String courseName,
99
      String term,
100
      School school,
101
      String instructorEmail,
102
      int numStudents,
103
      int numStaff,
104
      boolean hideBasePermissionWarning) {
105
106
    // Creates view from Course entity
107
    public InstructorCourseView(Course c) {
108
      this(
109
          c.getId(),
110
          c.getInstallationId(),
111
          c.getOrgName(),
112
          c.getCourseName(),
113
          c.getTerm(),
114
          c.getSchool(),
115
          c.getInstructorEmail(),
116 1 1. <init> : negated conditional → KILLED
          c.getRosterStudents() != null ? c.getRosterStudents().size() : 0,
117 1 1. <init> : negated conditional → KILLED
          c.getCourseStaff() != null ? c.getCourseStaff().size() : 0,
118
          c.getHideBasePermissionWarning());
119
    }
120
  }
121
122
  /**
123
   * This method returns a list of courses.
124
   *
125
   * @return a list of all courses for an instructor.
126
   */
127
  @Operation(summary = "List all courses for an instructor")
128
  @PreAuthorize("hasRole('ROLE_INSTRUCTOR')")
129
  @GetMapping("/allForInstructors")
130
  public Iterable<InstructorCourseView> allForInstructors() {
131
    CurrentUser currentUser = getCurrentUser();
132
    String instructorEmail = currentUser.getUser().getEmail();
133
    List<Course> courses = courseRepository.findByInstructorEmail(instructorEmail);
134
135
    List<InstructorCourseView> courseViews =
136
        courses.stream().map(InstructorCourseView::new).collect(Collectors.toList());
137 1 1. allForInstructors : replaced return value with Collections.emptyList for edu/ucsb/cs156/frontiers/controllers/CoursesController::allForInstructors → KILLED
    return courseViews;
138
  }
139
140
  /**
141
   * This method returns a list of courses.
142
   *
143
   * @return a list of all courses for an admin.
144
   */
145
  @Operation(summary = "List all courses for an admin")
146
  @PreAuthorize("hasRole('ROLE_ADMIN')")
147
  @GetMapping("/allForAdmins")
148
  public Iterable<InstructorCourseView> allForAdmins() {
149
    List<Course> courses = courseRepository.findAll();
150
151
    List<InstructorCourseView> courseViews =
152
        courses.stream().map(InstructorCourseView::new).collect(Collectors.toList());
153 1 1. allForAdmins : replaced return value with Collections.emptyList for edu/ucsb/cs156/frontiers/controllers/CoursesController::allForAdmins → KILLED
    return courseViews;
154
  }
155
156
  /**
157
   * This method returns single course by its id
158
   *
159
   * @return a course
160
   */
161
  @Operation(summary = "Get course by id")
162
  @PreAuthorize("@CourseSecurity.hasManagePermissions(#root, #id)")
163
  @GetMapping("/{id}")
164
  public InstructorCourseView getCourseById(@Parameter(name = "id") @PathVariable Long id) {
165
    Course course =
166
        courseRepository
167
            .findById(id)
168 1 1. lambda$getCourseById$0 : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::lambda$getCourseById$0 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Course.class, id));
169
    // Convert to InstructorCourseView
170
    InstructorCourseView courseView = new InstructorCourseView(course);
171 1 1. getCourseById : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::getCourseById → KILLED
    return courseView;
172
  }
173
174
  /**
175
   * This method returns the Canvas course ID and partially obscured Canvas token for a course by
176
   * its id. If the token is less than or equal to 3 characters long, it is returned in full.
177
   * Otherwise, all but the last three characters are replaced with asterisks. This is okay because
178
   * such short tokens are not generated by Canvas.
179
   *
180
   * @param courseId the id of the course
181
   * @return a map with courseId, canvasCourseId, and obscured canvasApiToken
182
   */
183
  @Operation(summary = "Get course Canvas course ID and Canvas token (partially obscured)")
184
  @PreAuthorize("@CourseSecurity.hasManagePermissions(#root, #courseId)")
185
  @GetMapping("getCanvasInfo")
186
  public Map<String, String> getCourseCanvasInfo(
187
      @Parameter(name = "courseId") @RequestParam Long courseId) {
188
    Course course =
189
        courseRepository
190
            .findById(courseId)
191 1 1. lambda$getCourseCanvasInfo$1 : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::lambda$getCourseCanvasInfo$1 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Course.class, courseId));
192
193
    String obscuredToken = null;
194
195 1 1. getCourseCanvasInfo : negated conditional → KILLED
    if (course.getCanvasApiToken() != null) {
196
      String token = course.getCanvasApiToken();
197 2 1. getCourseCanvasInfo : changed conditional boundary → KILLED
2. getCourseCanvasInfo : negated conditional → KILLED
      if (token.length() < 4) {
198
        obscuredToken = token;
199
      } else {
200 1 1. getCourseCanvasInfo : Replaced integer subtraction with addition → KILLED
        String lastThree = token.substring(token.length() - 3);
201 1 1. getCourseCanvasInfo : Replaced integer subtraction with addition → KILLED
        obscuredToken = "*".repeat(token.length() - 3) + lastThree;
202
      }
203
    }
204 1 1. getCourseCanvasInfo : replaced return value with Collections.emptyMap for edu/ucsb/cs156/frontiers/controllers/CoursesController::getCourseCanvasInfo → KILLED
    return Map.of(
205
        "courseId", course.getId().toString(),
206 1 1. getCourseCanvasInfo : negated conditional → KILLED
        "canvasCourseId", course.getCanvasCourseId() != null ? course.getCanvasCourseId() : "",
207 1 1. getCourseCanvasInfo : negated conditional → KILLED
        "canvasApiToken", obscuredToken != null ? obscuredToken : "");
208
  }
209
210
  /**
211
   * This is the outgoing method, redirecting from Frontiers to GitHub to allow a Course to be
212
   * linked to a GitHub Organization. It redirects from Frontiers to the GitHub app installation
213
   * process, and will return with the {@link #addInstallation(Optional, String, String, Long)
214
   * addInstallation()} endpoint
215
   *
216
   * @param courseId id of the course to be linked to
217
   * @return dynamically loaded url to install Frontiers to a Github Organization, with the courseId
218
   *     marked as the state parameter, which GitHub will return.
219
   */
220
  @Operation(summary = "Authorize Frontiers to a Github Course")
221
  @PreAuthorize("@CourseSecurity.hasManagePermissions(#root, #courseId)")
222
  @GetMapping("/redirect")
223
  public ResponseEntity<Void> linkCourse(@Parameter Long courseId)
224
      throws JsonProcessingException, NoSuchAlgorithmException, InvalidKeySpecException {
225
    String newUrl = linkerService.getRedirectUrl();
226
    newUrl += "/installations/new?state=" + courseId;
227
    // found this convenient solution here:
228
    // https://stackoverflow.com/questions/29085295/spring-mvc-restcontroller-and-redirect
229 1 1. linkCourse : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::linkCourse → KILLED
    return ResponseEntity.status(HttpStatus.MOVED_PERMANENTLY)
230
        .header(HttpHeaders.LOCATION, newUrl)
231
        .build();
232
  }
233
234
  /**
235
   * @param installation_id id of the incoming GitHub Organization installation
236
   * @param setup_action whether the permissions are installed or updated. Required RequestParam but
237
   *     not used by the method.
238
   * @param code token to be exchanged with GitHub to ensure the request is legitimate and not
239
   *     spoofed.
240
   * @param state id of the Course to be linked with the GitHub installation.
241
   * @return ResponseEntity, returning /success if the course was successfully linked or /noperms if
242
   *     the user does not have the permission to install the application on GitHub. Alternately
243
   *     returns 403 Forbidden if the user is not the creator.
244
   */
245
  @Operation(summary = "Link a Course to a Github Organization by installing Github App")
246
  @PreAuthorize("hasRole('ROLE_ADMIN') || hasRole('ROLE_INSTRUCTOR')")
247
  @GetMapping("link")
248
  public ResponseEntity<Void> addInstallation(
249
      @Parameter(name = "installationId") @RequestParam Optional<String> installation_id,
250
      @Parameter(name = "setupAction") @RequestParam String setup_action,
251
      @Parameter(name = "code") @RequestParam String code,
252
      @Parameter(name = "state") @RequestParam Long state)
253
      throws NoSuchAlgorithmException, InvalidKeySpecException, JsonProcessingException {
254 1 1. addInstallation : negated conditional → KILLED
    if (installation_id.isEmpty()) {
255 1 1. addInstallation : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::addInstallation → KILLED
      return ResponseEntity.status(HttpStatus.MOVED_PERMANENTLY)
256
          .header(HttpHeaders.LOCATION, "/courses/nopermissions")
257
          .build();
258
    } else {
259
      Course course =
260
          courseRepository
261
              .findById(state)
262 1 1. lambda$addInstallation$2 : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::lambda$addInstallation$2 → KILLED
              .orElseThrow(() -> new EntityNotFoundException(Course.class, state));
263 1 1. addInstallation : negated conditional → KILLED
      if (!isCurrentUserAdmin()
264 1 1. addInstallation : negated conditional → KILLED
          && !course.getInstructorEmail().equals(getCurrentUser().getUser().getEmail())) {
265 1 1. addInstallation : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::addInstallation → KILLED
        return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
266
      } else {
267
        String orgName = linkerService.getOrgName(installation_id.get());
268 1 1. addInstallation : removed call to edu/ucsb/cs156/frontiers/entities/Course::setInstallationId → KILLED
        course.setInstallationId(installation_id.get());
269 1 1. addInstallation : removed call to edu/ucsb/cs156/frontiers/entities/Course::setOrgName → KILLED
        course.setOrgName(orgName);
270
        course
271
            .getRosterStudents()
272 1 1. addInstallation : removed call to java/util/List::forEach → KILLED
            .forEach(
273
                rs -> {
274 1 1. lambda$addInstallation$3 : removed call to edu/ucsb/cs156/frontiers/entities/RosterStudent::setOrgStatus → KILLED
                  rs.setOrgStatus(OrgStatus.JOINCOURSE);
275
                });
276
        course
277
            .getCourseStaff()
278 1 1. addInstallation : removed call to java/util/List::forEach → KILLED
            .forEach(
279
                cs -> {
280 1 1. lambda$addInstallation$4 : removed call to edu/ucsb/cs156/frontiers/entities/CourseStaff::setOrgStatus → KILLED
                  cs.setOrgStatus(OrgStatus.JOINCOURSE);
281
                });
282
        courseRepository.save(course);
283 1 1. addInstallation : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::addInstallation → KILLED
        return ResponseEntity.status(HttpStatus.MOVED_PERMANENTLY)
284
            .header(HttpHeaders.LOCATION, "/login/success")
285
            .build();
286
      }
287
    }
288
  }
289
290
  /**
291
   * This method handles the InvalidInstallationTypeException.
292
   *
293
   * @param e the exception
294
   * @return a map with the type and message of the exception
295
   */
296
  @ExceptionHandler({InvalidInstallationTypeException.class})
297
  @ResponseStatus(HttpStatus.BAD_REQUEST)
298
  public Object handleInvalidInstallationType(Throwable e) {
299 1 1. handleInvalidInstallationType : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::handleInvalidInstallationType → KILLED
    return Map.of(
300
        "type", e.getClass().getSimpleName(),
301
        "message", e.getMessage());
302
  }
303
304
  public record RosterStudentCoursesDTO(
305
      Long id,
306
      String installationId,
307
      String orgName,
308
      String courseName,
309
      String term,
310
      String school,
311
      OrgStatus studentStatus,
312
      Long rosterStudentId) {}
313
314
  /**
315
   * This method returns a list of courses that the current user is enrolled.
316
   *
317
   * @return a list of courses in the DTO form along with the student status in the organization.
318
   */
319
  @Operation(summary = "List all courses for the current student, including their org status")
320
  @PreAuthorize("hasRole('ROLE_USER')")
321
  @GetMapping("/list")
322
  public List<RosterStudentCoursesDTO> listCoursesForCurrentUser() {
323
    String email = getCurrentUser().getUser().getEmail();
324
    Iterable<RosterStudent> rosterStudentsIterable = rosterStudentRepository.findAllByEmail(email);
325
    List<RosterStudent> rosterStudents = new ArrayList<>();
326 1 1. listCoursesForCurrentUser : removed call to java/lang/Iterable::forEach → KILLED
    rosterStudentsIterable.forEach(rosterStudents::add);
327 1 1. listCoursesForCurrentUser : replaced return value with Collections.emptyList for edu/ucsb/cs156/frontiers/controllers/CoursesController::listCoursesForCurrentUser → KILLED
    return rosterStudents.stream()
328
        .map(
329
            rs -> {
330
              Course course = rs.getCourse();
331
              RosterStudentCoursesDTO rsDto =
332
                  new RosterStudentCoursesDTO(
333
                      course.getId(),
334
                      course.getInstallationId(),
335
                      course.getOrgName(),
336
                      course.getCourseName(),
337
                      course.getTerm(),
338
                      course.getSchool().getDisplayName(),
339
                      rs.getOrgStatus(),
340
                      rs.getId());
341 1 1. lambda$listCoursesForCurrentUser$5 : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::lambda$listCoursesForCurrentUser$5 → KILLED
              return rsDto;
342
            })
343
        .collect(Collectors.toList());
344
  }
345
346
  public record StaffCoursesDTO(
347
      Long id,
348
      String installationId,
349
      String orgName,
350
      String courseName,
351
      String term,
352
      School school,
353
      OrgStatus studentStatus,
354
      Long staffId) {}
355
356
  /**
357
   * student see what courses they appear as staff in
358
   *
359
   * @param studentId the id of the student making request
360
   * @return a list of all courses student is staff in
361
   */
362
  @Operation(summary = "Student see what courses they appear as staff in")
363
  @PreAuthorize("hasRole('ROLE_USER')")
364
  @GetMapping("/staffCourses")
365
  public List<StaffCoursesDTO> staffCourses() {
366
    CurrentUser currentUser = getCurrentUser();
367
    User user = currentUser.getUser();
368
369
    String email = user.getEmail();
370
371
    List<CourseStaff> staffMembers = courseStaffRepository.findAllByEmail(email);
372 1 1. staffCourses : replaced return value with Collections.emptyList for edu/ucsb/cs156/frontiers/controllers/CoursesController::staffCourses → KILLED
    return staffMembers.stream()
373
        .map(
374
            s -> {
375
              Course course = s.getCourse();
376
              StaffCoursesDTO sDto =
377
                  new StaffCoursesDTO(
378
                      course.getId(),
379
                      course.getInstallationId(),
380
                      course.getOrgName(),
381
                      course.getCourseName(),
382
                      course.getTerm(),
383
                      course.getSchool(),
384
                      s.getOrgStatus(),
385
                      s.getId());
386 1 1. lambda$staffCourses$6 : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::lambda$staffCourses$6 → KILLED
              return sDto;
387
            })
388
        .collect(Collectors.toList());
389
  }
390
391
  @Operation(summary = "Update instructor email for a course (admin only)")
392
  @PreAuthorize("hasRole('ROLE_ADMIN')")
393
  @PutMapping("/updateInstructor")
394
  public InstructorCourseView updateInstructorEmail(
395
      @Parameter(name = "courseId") @RequestParam Long courseId,
396
      @Parameter(name = "instructorEmail") @RequestParam String instructorEmail) {
397
398
    instructorEmail = instructorEmail.strip();
399
400
    Course course =
401
        courseRepository
402
            .findById(courseId)
403 1 1. lambda$updateInstructorEmail$7 : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::lambda$updateInstructorEmail$7 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Course.class, courseId));
404
405
    // Validate that the email exists in either instructor or admin table
406
    boolean isInstructor = instructorRepository.existsByEmail(instructorEmail);
407
    boolean isAdmin = adminRepository.existsByEmail(instructorEmail);
408
409 2 1. updateInstructorEmail : negated conditional → KILLED
2. updateInstructorEmail : negated conditional → KILLED
    if (!isInstructor && !isAdmin) {
410
      throw new IllegalArgumentException("Email must belong to either an instructor or admin");
411
    }
412
413 1 1. updateInstructorEmail : removed call to edu/ucsb/cs156/frontiers/entities/Course::setInstructorEmail → KILLED
    course.setInstructorEmail(instructorEmail);
414
    Course savedCourse = courseRepository.save(course);
415
416 1 1. updateInstructorEmail : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::updateInstructorEmail → KILLED
    return new InstructorCourseView(savedCourse);
417
  }
418
419
  @Operation(summary = "Delete a course")
420
  @PreAuthorize("hasRole('ROLE_ADMIN')")
421
  @DeleteMapping("")
422
  public Object deleteCourse(@RequestParam Long courseId)
423
      throws NoSuchAlgorithmException, InvalidKeySpecException {
424
    Course course =
425
        courseRepository
426
            .findById(courseId)
427 1 1. lambda$deleteCourse$8 : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::lambda$deleteCourse$8 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Course.class, courseId));
428
429
    // Check if course has roster students or staff
430 2 1. deleteCourse : negated conditional → KILLED
2. deleteCourse : negated conditional → KILLED
    if (!course.getRosterStudents().isEmpty() || !course.getCourseStaff().isEmpty()) {
431
      throw new IllegalArgumentException("Cannot delete course with students or staff");
432
    }
433
434 1 1. deleteCourse : removed call to edu/ucsb/cs156/frontiers/services/OrganizationLinkerService::unenrollOrganization → KILLED
    linkerService.unenrollOrganization(course);
435 1 1. deleteCourse : removed call to edu/ucsb/cs156/frontiers/repositories/CourseRepository::delete → KILLED
    courseRepository.delete(course);
436 1 1. deleteCourse : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::deleteCourse → KILLED
    return genericMessage("Course with id %s deleted".formatted(course.getId()));
437
  }
438
439
  /**
440
   * This method updates an existing course.
441
   *
442
   * @param courseId the id of the course to update
443
   * @param courseName the new name of the course
444
   * @param term the new term of the course
445
   * @param school the new school of the course
446
   * @return the updated course
447
   */
448
  @Operation(summary = "Update an existing course")
449
  @PreAuthorize("@CourseSecurity.hasManagePermissions(#root, #courseId)")
450
  @PutMapping("")
451
  public InstructorCourseView updateCourse(
452
      @Parameter(name = "courseId") @RequestParam Long courseId,
453
      @Parameter(name = "courseName") @RequestParam String courseName,
454
      @Parameter(name = "term") @RequestParam String term,
455
      @Parameter(name = "school") @RequestParam School school) {
456
    Course course =
457
        courseRepository
458
            .findById(courseId)
459 1 1. lambda$updateCourse$9 : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::lambda$updateCourse$9 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Course.class, courseId));
460
461 1 1. updateCourse : removed call to edu/ucsb/cs156/frontiers/entities/Course::setCourseName → KILLED
    course.setCourseName(courseName);
462 1 1. updateCourse : removed call to edu/ucsb/cs156/frontiers/entities/Course::setTerm → KILLED
    course.setTerm(term);
463 1 1. updateCourse : removed call to edu/ucsb/cs156/frontiers/entities/Course::setSchool → KILLED
    course.setSchool(school);
464
465
    Course savedCourse = courseRepository.save(course);
466
467 1 1. updateCourse : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::updateCourse → KILLED
    return new InstructorCourseView(savedCourse);
468
  }
469
470
  /**
471
   * This method updates an existing course.
472
   *
473
   * @param courseId the id of the course to update
474
   * @param courseName the new name of the course
475
   * @param term the new term of the course
476
   * @param school the new school of the course
477
   * @param canvasApiToken the new Canvas API token for the course
478
   * @param canvasCourseId the new Canvas course ID
479
   * @return the updated course
480
   */
481
  @Operation(summary = "Update an existing course with Canvas token and course ID")
482
  @PreAuthorize("@CourseSecurity.hasManagePermissions(#root, #courseId)")
483
  @PutMapping("/updateCourseCanvasToken")
484
  public InstructorCourseView updateCourseWithCanvasToken(
485
      @Parameter(name = "courseId") @RequestParam Long courseId,
486
      @Parameter(name = "canvasApiToken") @RequestParam(required = false) String canvasApiToken,
487
      @Parameter(name = "canvasCourseId") @RequestParam(required = false) String canvasCourseId) {
488
    Course course =
489
        courseRepository
490
            .findById(courseId)
491 1 1. lambda$updateCourseWithCanvasToken$10 : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::lambda$updateCourseWithCanvasToken$10 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Course.class, courseId));
492
493 1 1. updateCourseWithCanvasToken : negated conditional → KILLED
    if (canvasApiToken != null
494 1 1. updateCourseWithCanvasToken : negated conditional → KILLED
        && !canvasApiToken.isEmpty()
495 1 1. updateCourseWithCanvasToken : negated conditional → KILLED
        && !canvasApiToken.equals(course.getCanvasApiToken())) {
496 1 1. updateCourseWithCanvasToken : removed call to edu/ucsb/cs156/frontiers/entities/Course::setCanvasApiToken → KILLED
      course.setCanvasApiToken(canvasApiToken);
497
    }
498
499 1 1. updateCourseWithCanvasToken : negated conditional → KILLED
    if (canvasCourseId != null
500 1 1. updateCourseWithCanvasToken : negated conditional → KILLED
        && !canvasCourseId.isEmpty()
501 1 1. updateCourseWithCanvasToken : negated conditional → KILLED
        && !canvasCourseId.equals(course.getCanvasCourseId())) {
502 1 1. updateCourseWithCanvasToken : removed call to edu/ucsb/cs156/frontiers/entities/Course::setCanvasCourseId → KILLED
      course.setCanvasCourseId(canvasCourseId);
503
    }
504
505
    Course savedCourse = courseRepository.save(course);
506
507 1 1. updateCourseWithCanvasToken : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::updateCourseWithCanvasToken → KILLED
    return new InstructorCourseView(savedCourse);
508
  }
509
510
  @GetMapping("/warnings/{courseId}")
511
  @PreAuthorize("@CourseSecurity.hasManagePermissions(#root, #courseId)")
512
  public CourseWarning warnings(@PathVariable Long courseId) throws Exception {
513
    Course course =
514
        courseRepository
515
            .findById(courseId)
516 1 1. lambda$warnings$11 : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::lambda$warnings$11 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Course.class, courseId));
517 1 1. warnings : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::warnings → KILLED
    return linkerService.checkCourseWarnings(course);
518
  }
519
520
  @Operation(summary = "Hide base permission warning for a course")
521
  @PostMapping("/warnings/hideBasePermissionWarning/{courseId}")
522
  @PreAuthorize("@CourseSecurity.hasManagePermissions(#root, #courseId)")
523
  public Course hideBasePermissionWarning(@PathVariable Long courseId) {
524
    Course course =
525
        courseRepository
526
            .findById(courseId)
527 1 1. lambda$hideBasePermissionWarning$12 : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::lambda$hideBasePermissionWarning$12 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Course.class, courseId));
528
529 1 1. hideBasePermissionWarning : removed call to edu/ucsb/cs156/frontiers/entities/Course::setHideBasePermissionWarning → KILLED
    course.setHideBasePermissionWarning(true);
530 1 1. hideBasePermissionWarning : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::hideBasePermissionWarning → KILLED
    return courseRepository.save(course);
531
  }
532
}

Mutations

90

1.1
Location : postCourse
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testPostCourse_byInstructor()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::postCourse → KILLED

116

1.1
Location : <init>
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testInstructorCourseView_withBothCollectionsNull()]
negated conditional → KILLED

117

1.1
Location : <init>
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testInstructorCourseView_withBothCollectionsNull()]
negated conditional → KILLED

137

1.1
Location : allForInstructors
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testAllCourses_ROLE_INSTRUCTOR()]
replaced return value with Collections.emptyList for edu/ucsb/cs156/frontiers/controllers/CoursesController::allForInstructors → KILLED

153

1.1
Location : allForAdmins
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testAllCourses_ROLE_ADMIN()]
replaced return value with Collections.emptyList for edu/ucsb/cs156/frontiers/controllers/CoursesController::allForAdmins → KILLED

168

1.1
Location : lambda$getCourseById$0
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testGetCourseById_courseDoesNotExist()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::lambda$getCourseById$0 → KILLED

171

1.1
Location : getCourseById
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testGetCourseById()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::getCourseById → KILLED

191

1.1
Location : lambda$getCourseCanvasInfo$1
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testGetCanvasInfo_courseDoesNotExist()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::lambda$getCourseCanvasInfo$1 → KILLED

195

1.1
Location : getCourseCanvasInfo
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:getCanvasInfo_obscuresCorrectlyForNoChars()]
negated conditional → KILLED

197

1.1
Location : getCourseCanvasInfo
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:getCanvasInfo_obscuresCorrectlyForFourCharacters()]
changed conditional boundary → KILLED

2.2
Location : getCourseCanvasInfo
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:getCanvasInfo_obscuresCorrectlyForLessThanThreeCharacters()]
negated conditional → KILLED

200

1.1
Location : getCourseCanvasInfo
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:getCanvasInfo_returnsCorrectOutput()]
Replaced integer subtraction with addition → KILLED

201

1.1
Location : getCourseCanvasInfo
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:getCanvasInfo_returnsCorrectOutput()]
Replaced integer subtraction with addition → KILLED

204

1.1
Location : getCourseCanvasInfo
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:getCanvasInfo_obscuresCorrectlyForNoChars()]
replaced return value with Collections.emptyMap for edu/ucsb/cs156/frontiers/controllers/CoursesController::getCourseCanvasInfo → KILLED

206

1.1
Location : getCourseCanvasInfo
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:getCanvasInfo_obscuresCorrectlyForNoChars()]
negated conditional → KILLED

207

1.1
Location : getCourseCanvasInfo
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:getCanvasInfo_obscuresCorrectlyForNoChars()]
negated conditional → KILLED

229

1.1
Location : linkCourse
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testRedirect()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::linkCourse → KILLED

254

1.1
Location : addInstallation
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testNoPerms()]
negated conditional → KILLED

255

1.1
Location : addInstallation
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testNoPerms()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::addInstallation → KILLED

262

1.1
Location : lambda$addInstallation$2
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testCourseLinkNotFound()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::lambda$addInstallation$2 → KILLED

263

1.1
Location : addInstallation
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testNotCreator()]
negated conditional → KILLED

264

1.1
Location : addInstallation
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testNotCreator()]
negated conditional → KILLED

265

1.1
Location : addInstallation
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testNotCreator()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::addInstallation → KILLED

268

1.1
Location : addInstallation
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testLinkCourseSuccessfullyProfessorCreator()]
removed call to edu/ucsb/cs156/frontiers/entities/Course::setInstallationId → KILLED

269

1.1
Location : addInstallation
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testLinkCourseSuccessfullyProfessorCreator()]
removed call to edu/ucsb/cs156/frontiers/entities/Course::setOrgName → KILLED

272

1.1
Location : addInstallation
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testLinkCourseSuccessfully()]
removed call to java/util/List::forEach → KILLED

274

1.1
Location : lambda$addInstallation$3
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testLinkCourseSuccessfully()]
removed call to edu/ucsb/cs156/frontiers/entities/RosterStudent::setOrgStatus → KILLED

278

1.1
Location : addInstallation
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testLinkCourseSuccessfully()]
removed call to java/util/List::forEach → KILLED

280

1.1
Location : lambda$addInstallation$4
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testLinkCourseSuccessfully()]
removed call to edu/ucsb/cs156/frontiers/entities/CourseStaff::setOrgStatus → KILLED

283

1.1
Location : addInstallation
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testLinkCourseSuccessfullyProfessorCreator()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::addInstallation → KILLED

299

1.1
Location : handleInvalidInstallationType
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testNotOrganization()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::handleInvalidInstallationType → KILLED

326

1.1
Location : listCoursesForCurrentUser
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testListCoursesForCurrentUser()]
removed call to java/lang/Iterable::forEach → KILLED

327

1.1
Location : listCoursesForCurrentUser
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testListCoursesForCurrentUser()]
replaced return value with Collections.emptyList for edu/ucsb/cs156/frontiers/controllers/CoursesController::listCoursesForCurrentUser → KILLED

341

1.1
Location : lambda$listCoursesForCurrentUser$5
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testListCoursesForCurrentUser()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::lambda$listCoursesForCurrentUser$5 → KILLED

372

1.1
Location : staffCourses
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testStudenIsStaffInCourse()]
replaced return value with Collections.emptyList for edu/ucsb/cs156/frontiers/controllers/CoursesController::staffCourses → KILLED

386

1.1
Location : lambda$staffCourses$6
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testStudenIsStaffInCourse()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::lambda$staffCourses$6 → KILLED

403

1.1
Location : lambda$updateInstructorEmail$7
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testUpdateInstructorEmail_courseDoesNotExist()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::lambda$updateInstructorEmail$7 → KILLED

409

1.1
Location : updateInstructorEmail
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testUpdateInstructorEmail_emailNotFound()]
negated conditional → KILLED

2.2
Location : updateInstructorEmail
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testUpdateInstructorEmail_emailNotFound()]
negated conditional → KILLED

413

1.1
Location : updateInstructorEmail
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testUpdateInstructorEmail_byAdmin_email_is_admin()]
removed call to edu/ucsb/cs156/frontiers/entities/Course::setInstructorEmail → KILLED

416

1.1
Location : updateInstructorEmail
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testUpdateInstructorEmail_byAdmin_email_is_instructor()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::updateInstructorEmail → KILLED

427

1.1
Location : lambda$deleteCourse$8
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:delete_not_found_returns_not_found()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::lambda$deleteCourse$8 → KILLED

430

1.1
Location : deleteCourse
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:delete_success_returns_ok()]
negated conditional → KILLED

2.2
Location : deleteCourse
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:delete_success_returns_ok()]
negated conditional → KILLED

434

1.1
Location : deleteCourse
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:delete_success_returns_ok()]
removed call to edu/ucsb/cs156/frontiers/services/OrganizationLinkerService::unenrollOrganization → KILLED

435

1.1
Location : deleteCourse
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:delete_success_returns_ok()]
removed call to edu/ucsb/cs156/frontiers/repositories/CourseRepository::delete → KILLED

436

1.1
Location : deleteCourse
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:delete_success_returns_ok()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::deleteCourse → KILLED

459

1.1
Location : lambda$updateCourse$9
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:update_course_not_found_returns_not_found()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::lambda$updateCourse$9 → KILLED

461

1.1
Location : updateCourse
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:admin_can_update_course_created_by_someone_else()]
removed call to edu/ucsb/cs156/frontiers/entities/Course::setCourseName → KILLED

462

1.1
Location : updateCourse
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:admin_can_update_course_created_by_someone_else()]
removed call to edu/ucsb/cs156/frontiers/entities/Course::setTerm → KILLED

463

1.1
Location : updateCourse
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:admin_can_update_course_created_by_someone_else()]
removed call to edu/ucsb/cs156/frontiers/entities/Course::setSchool → KILLED

467

1.1
Location : updateCourse
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:updateCourse_success_admin()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::updateCourse → KILLED

491

1.1
Location : lambda$updateCourseWithCanvasToken$10
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:updateCourseCanvasToken_not_found_returns_not_found()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::lambda$updateCourseWithCanvasToken$10 → KILLED

493

1.1
Location : updateCourseWithCanvasToken
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:updateCourseCanvasToken_null_params_no_change()]
negated conditional → KILLED

494

1.1
Location : updateCourseWithCanvasToken
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:admin_can_updateCourseCanvasToken_created_by_someone_else()]
negated conditional → KILLED

495

1.1
Location : updateCourseWithCanvasToken
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:admin_can_updateCourseCanvasToken_created_by_someone_else()]
negated conditional → KILLED

496

1.1
Location : updateCourseWithCanvasToken
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:admin_can_updateCourseCanvasToken_created_by_someone_else()]
removed call to edu/ucsb/cs156/frontiers/entities/Course::setCanvasApiToken → KILLED

499

1.1
Location : updateCourseWithCanvasToken
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:updateCourseCanvasToken_null_params_no_change()]
negated conditional → KILLED

500

1.1
Location : updateCourseWithCanvasToken
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:admin_can_updateCourseCanvasToken_created_by_someone_else()]
negated conditional → KILLED

501

1.1
Location : updateCourseWithCanvasToken
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:admin_can_updateCourseCanvasToken_created_by_someone_else()]
negated conditional → KILLED

502

1.1
Location : updateCourseWithCanvasToken
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:admin_can_updateCourseCanvasToken_created_by_someone_else()]
removed call to edu/ucsb/cs156/frontiers/entities/Course::setCanvasCourseId → KILLED

507

1.1
Location : updateCourseWithCanvasToken
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:updateCourseCanvasToken_success_admin()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::updateCourseWithCanvasToken → KILLED

516

1.1
Location : lambda$warnings$11
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:test_warnings_not_found()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::lambda$warnings$11 → KILLED

517

1.1
Location : warnings
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:calls_org_service_for_warnings()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::warnings → KILLED

527

1.1
Location : lambda$hideBasePermissionWarning$12
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:hide_base_permission_warning_not_found()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::lambda$hideBasePermissionWarning$12 → KILLED

529

1.1
Location : hideBasePermissionWarning
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:hide_base_permission_warning_sets_field_to_true()]
removed call to edu/ucsb/cs156/frontiers/entities/Course::setHideBasePermissionWarning → KILLED

530

1.1
Location : hideBasePermissionWarning
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:hide_base_permission_warning_sets_field_to_true()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::hideBasePermissionWarning → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0