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

Mutations

92

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

119

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

120

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

139

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

155

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

170

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

173

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

193

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

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

199

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

202

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

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

208

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

209

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

231

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

256

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

257

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

264

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

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

267

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

270

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

271

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

274

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

276

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

280

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

282

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

285

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

301

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

328

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

329

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

343

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

385

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

399

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

416

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

422

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

426

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_sanitized()]
removed call to edu/ucsb/cs156/frontiers/entities/Course::setInstructorEmail → KILLED

429

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

451

1.1
Location : lambda$getCourseEmails$8
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:getCourseEmails_students_blank_team_is_unfiltered()]
negated conditional → KILLED

2.2
Location : lambda$getCourseEmails$8
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:getCourseEmails_students_filtered_by_team()]
replaced boolean return with true for edu/ucsb/cs156/frontiers/controllers/CoursesController::lambda$getCourseEmails$8 → KILLED

3.3
Location : lambda$getCourseEmails$8
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:getCourseEmails_students_filtered_by_team()]
negated conditional → KILLED

4.4
Location : lambda$getCourseEmails$8
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:getCourseEmails_defaults_to_students_one_per_line()]
negated conditional → KILLED

458

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

460

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

465

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

466

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

477

1.1
Location : lambda$deleteCourse$9
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$9 → KILLED

480

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

484

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

485

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

486

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

509

1.1
Location : lambda$updateCourse$10
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$10 → KILLED

511

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

512

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

513

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

517

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

541

1.1
Location : lambda$updateCourseWithCanvasToken$11
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$11 → KILLED

543

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

544

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

545

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

546

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

549

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

550

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

551

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

552

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

557

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

567

1.1
Location : lambda$warnings$12
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$12 → KILLED

568

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

578

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

580

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

583

1.1
Location : hideBasePermissionWarning
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:hideBasePermissionWarning_setsFieldTrue()]
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