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

Mutations

89

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

114

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

115

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

134

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

150

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

165

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

168

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

188

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

192

1.1
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

194

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

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()]
Replaced integer subtraction with addition → KILLED

198

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()]
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_obscuresCorrectlyForLessThanThreeCharacters()]
replaced return value with Collections.emptyMap for edu/ucsb/cs156/frontiers/controllers/CoursesController::getCourseCanvasInfo → KILLED

203

1.1
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

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_obscuresCorrectlyForLessThanThreeCharacters()]
negated conditional → KILLED

226

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

251

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

252

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

259

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

260

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

261

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

262

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

265

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

266

1.1
Location : addInstallation
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:testCourseLinkSuccessWhenAdminNotCreator()]
removed call to edu/ucsb/cs156/frontiers/entities/Course::setOrgName → 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:testLinkCourseSuccessfully()]
removed call to java/util/List::forEach → KILLED

271

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

275

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

277

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

280

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

296

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

323

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

324

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

338

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

369

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

383

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

400

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

406

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

410

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

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_instructor()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CoursesController::updateInstructorEmail → KILLED

424

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

427

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

431

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

432

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

433

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

456

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

458

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

459

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

460

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

464

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

488

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

490

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

491

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

492

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

493

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

496

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

497

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

498

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

499

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

504

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

513

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

514

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

Active mutators

Tests examined


Report generated by PIT 1.17.0