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

Mutations

83

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

108

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

109

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

128

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

144

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

159

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

162

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

184

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

209

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

210

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

217

1.1
Location : lambda$addInstallation$1
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$1 → KILLED

218

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

219

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

220

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

223

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

224

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

227

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

229

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:testLinkCourseSuccessfully()]
removed call to edu/ucsb/cs156/frontiers/entities/RosterStudent::setOrgStatus → KILLED

233

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

235

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/CourseStaff::setOrgStatus → KILLED

238

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

254

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

281

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

282

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

296

1.1
Location : lambda$listCoursesForCurrentUser$4
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$4 → KILLED

327

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

341

1.1
Location : lambda$staffCourses$5
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$5 → KILLED

358

1.1
Location : lambda$updateInstructorEmail$6
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$6 → KILLED

364

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

368

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

371

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

382

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

385

1.1
Location : deleteCourse
Killed by : edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CoursesControllerTests]/[method:delete_course_with_staff_throws_illegal_argument()]
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_course_with_students_throws_illegal_argument()]
negated conditional → KILLED

389

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

390

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

391

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

414

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

416

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

417

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

418

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

422

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

Active mutators

Tests examined


Report generated by PIT 1.17.0