CourseStaffController.java

1
package edu.ucsb.cs156.frontiers.controllers;
2
3
import com.fasterxml.jackson.core.JsonProcessingException;
4
import com.opencsv.CSVReader;
5
import com.opencsv.exceptions.CsvException;
6
import edu.ucsb.cs156.frontiers.entities.*;
7
import edu.ucsb.cs156.frontiers.enums.OrgStatus;
8
import edu.ucsb.cs156.frontiers.errors.EntityNotFoundException;
9
import edu.ucsb.cs156.frontiers.repositories.CourseRepository;
10
import edu.ucsb.cs156.frontiers.repositories.CourseStaffRepository;
11
import edu.ucsb.cs156.frontiers.services.*;
12
import io.swagger.v3.oas.annotations.Operation;
13
import io.swagger.v3.oas.annotations.Parameter;
14
import io.swagger.v3.oas.annotations.tags.Tag;
15
import java.io.BufferedInputStream;
16
import java.io.IOException;
17
import java.io.InputStream;
18
import java.io.InputStreamReader;
19
import java.security.NoSuchAlgorithmException;
20
import java.security.spec.InvalidKeySpecException;
21
import java.util.Arrays;
22
import java.util.List;
23
import lombok.extern.slf4j.Slf4j;
24
import org.springframework.beans.factory.annotation.Autowired;
25
import org.springframework.http.HttpStatus;
26
import org.springframework.http.ResponseEntity;
27
import org.springframework.security.access.prepost.PreAuthorize;
28
import org.springframework.transaction.annotation.Transactional;
29
import org.springframework.web.bind.annotation.*;
30
import org.springframework.web.multipart.MultipartFile;
31
import org.springframework.web.server.ResponseStatusException;
32
33
@Tag(name = "CourseStaff")
34
@RequestMapping("/api/coursestaff")
35
@RestController
36
@Slf4j
37
public class CourseStaffController extends ApiController {
38
39
  @Autowired private OrganizationMemberService organizationMemberService;
40
41
  @Autowired private CourseStaffRepository courseStaffRepository;
42
43
  @Autowired private CourseRepository courseRepository;
44
45
  @Autowired private UpdateUserService updateUserService;
46
47
  @Autowired private CurrentUserService currentUserService;
48
49
  public static final String STAFF_CSV_HEADERS = "firstName,lastName,email";
50
51
  private CourseStaff buildCourseStaffForCourse(
52
      Course course, String firstName, String lastName, String email) {
53
    CourseStaff courseStaff =
54
        CourseStaff.builder()
55
            .firstName(firstName)
56
            .lastName(lastName)
57
            .email(email.strip())
58
            .course(course)
59
            .build();
60
61 1 1. buildCourseStaffForCourse : negated conditional → KILLED
    if (course.getInstallationId() != null) {
62 1 1. buildCourseStaffForCourse : removed call to edu/ucsb/cs156/frontiers/entities/CourseStaff::setOrgStatus → KILLED
      courseStaff.setOrgStatus(OrgStatus.JOINCOURSE);
63
    } else {
64 1 1. buildCourseStaffForCourse : removed call to edu/ucsb/cs156/frontiers/entities/CourseStaff::setOrgStatus → KILLED
      courseStaff.setOrgStatus(OrgStatus.PENDING);
65
    }
66
67 1 1. buildCourseStaffForCourse : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::buildCourseStaffForCourse → KILLED
    return courseStaff;
68
  }
69
70
  public static boolean hasStaffCSVHeaders(String[] headers) {
71
    String[] expectedHeaders = STAFF_CSV_HEADERS.split(",");
72
73 3 1. hasStaffCSVHeaders : changed conditional boundary → KILLED
2. hasStaffCSVHeaders : negated conditional → KILLED
3. hasStaffCSVHeaders : negated conditional → KILLED
    if (headers == null || headers.length < expectedHeaders.length) {
74 1 1. hasStaffCSVHeaders : replaced boolean return with true for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::hasStaffCSVHeaders → SURVIVED
      return false;
75
    }
76
77 2 1. hasStaffCSVHeaders : negated conditional → SURVIVED
2. hasStaffCSVHeaders : changed conditional boundary → KILLED
    for (int i = 0; i < expectedHeaders.length; i++) {
78 1 1. hasStaffCSVHeaders : negated conditional → KILLED
      if (!expectedHeaders[i].trim().equalsIgnoreCase(headers[i].trim())) {
79 1 1. hasStaffCSVHeaders : replaced boolean return with true for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::hasStaffCSVHeaders → NO_COVERAGE
        return false;
80
      }
81
    }
82
83 1 1. hasStaffCSVHeaders : replaced boolean return with false for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::hasStaffCSVHeaders → KILLED
    return true;
84
  }
85
86
  public static CourseStaff fromStaffCSVRow(String[] row) {
87 2 1. fromStaffCSVRow : changed conditional boundary → KILLED
2. fromStaffCSVRow : negated conditional → KILLED
    if (row.length < 3) {
88
      throw new ResponseStatusException(
89
          HttpStatus.BAD_REQUEST,
90
          String.format(
91
              "Staff CSV row does not have enough columns. Length = %d Row content = [%s]",
92
              row.length, Arrays.toString(row)));
93
    }
94
95 1 1. fromStaffCSVRow : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::fromStaffCSVRow → KILLED
    return CourseStaff.builder()
96
        .firstName(row[0].strip())
97
        .lastName(row[1].strip())
98
        .email(row[2].strip())
99
        .build();
100
  }
101
102
  /**
103
   * This method creates a new CourseStaff.
104
   *
105
   * @return the created CourseStaff
106
   */
107
  @Operation(summary = "Add a staff member to a course")
108
  @PreAuthorize("@CourseSecurity.hasInstructorPermissions(#root, #courseId)")
109
  @PostMapping("/post")
110
  public CourseStaff postCourseStaff(
111
      @Parameter(name = "firstName") @RequestParam String firstName,
112
      @Parameter(name = "lastName") @RequestParam String lastName,
113
      @Parameter(name = "email") @RequestParam String email,
114
      @Parameter(name = "courseId") @RequestParam Long courseId)
115
      throws EntityNotFoundException {
116
117
    // Get Course or else throw an error
118
119
    Course course =
120
        courseRepository
121
            .findById(courseId)
122 1 1. lambda$postCourseStaff$0 : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::lambda$postCourseStaff$0 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Course.class, courseId));
123
124
    CourseStaff courseStaff = buildCourseStaffForCourse(course, firstName, lastName, email);
125
126
    CourseStaff savedCourseStaff = courseStaffRepository.save(courseStaff);
127
128 1 1. postCourseStaff : removed call to edu/ucsb/cs156/frontiers/services/UpdateUserService::attachUserToCourseStaff → KILLED
    updateUserService.attachUserToCourseStaff(savedCourseStaff);
129
130 1 1. postCourseStaff : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::postCourseStaff → KILLED
    return savedCourseStaff;
131
  }
132
133
  @Operation(summary = "Upload course staff from CSV")
134
  @PreAuthorize("@CourseSecurity.hasInstructorPermissions(#root, #courseId)")
135
  @PostMapping(
136
      value = "/upload/csv",
137
      consumes = {"multipart/form-data"})
138
  public ResponseEntity<List<CourseStaff>> uploadCourseStaffCSV(
139
      @Parameter(name = "courseId") @RequestParam Long courseId,
140
      @Parameter(name = "file") @RequestParam("file") MultipartFile file)
141
      throws IOException, CsvException {
142
143
    Course course =
144
        courseRepository
145
            .findById(courseId)
146 1 1. lambda$uploadCourseStaffCSV$1 : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::lambda$uploadCourseStaffCSV$1 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(Course.class, courseId));
147
148
    List<CourseStaff> courseStaff;
149
    try (InputStream inputStream = new BufferedInputStream(file.getInputStream());
150
        InputStreamReader reader = new InputStreamReader(inputStream);
151
        CSVReader csvReader = new CSVReader(reader); ) {
152
153
      String[] headers = csvReader.readNext();
154 1 1. uploadCourseStaffCSV : negated conditional → KILLED
      if (!hasStaffCSVHeaders(headers)) {
155
        throw new ResponseStatusException(
156
            HttpStatus.BAD_REQUEST, "Unknown Course Staff CSV format");
157
      }
158
159
      courseStaff =
160
          csvReader.readAll().stream()
161
              .map(CourseStaffController::fromStaffCSVRow)
162
              .map(
163
                  staff ->
164 1 1. lambda$uploadCourseStaffCSV$2 : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::lambda$uploadCourseStaffCSV$2 → KILLED
                      buildCourseStaffForCourse(
165
                          course, staff.getFirstName(), staff.getLastName(), staff.getEmail()))
166
              .toList();
167
    }
168
169
    List<CourseStaff> savedCourseStaff = courseStaffRepository.saveAll(courseStaff);
170 1 1. uploadCourseStaffCSV : removed call to java/util/List::forEach → KILLED
    savedCourseStaff.forEach(updateUserService::attachUserToCourseStaff);
171
172 1 1. uploadCourseStaffCSV : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::uploadCourseStaffCSV → KILLED
    return ResponseEntity.ok(savedCourseStaff);
173
  }
174
175
  /**
176
   * This method returns a list of course staff for a given course.
177
   *
178
   * @return a list of all courses.
179
   */
180
  @Operation(summary = "List all course staff members for a course")
181
  @PreAuthorize("@CourseSecurity.hasManagePermissions(#root, #courseId)")
182
  @GetMapping("/course")
183
  public Iterable<CourseStaff> courseStaffForCourse(
184
      @Parameter(name = "courseId") @RequestParam Long courseId) throws EntityNotFoundException {
185
    courseRepository
186
        .findById(courseId)
187 1 1. lambda$courseStaffForCourse$3 : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::lambda$courseStaffForCourse$3 → KILLED
        .orElseThrow(() -> new EntityNotFoundException(Course.class, courseId));
188
    Iterable<CourseStaff> courseStaffs = courseStaffRepository.findByCourseId(courseId);
189 1 1. courseStaffForCourse : replaced return value with Collections.emptyList for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::courseStaffForCourse → KILLED
    return courseStaffs;
190
  }
191
192
  @Operation(
193
      summary =
194
          "Allow staff member to join a course by generating an invitation to the linked Github Org")
195
  @PreAuthorize("hasRole('ROLE_USER')")
196
  @PutMapping("/joinCourse")
197
  public ResponseEntity<String> joinCourseOnGitHub(
198
      @Parameter(name = "courseStaffId", description = "Staff Member joining a course on GitHub")
199
          @RequestParam
200
          Long courseStaffId)
201
      throws NoSuchAlgorithmException, InvalidKeySpecException, JsonProcessingException {
202
203
    User currentUser = currentUserService.getUser();
204
    CourseStaff courseStaff =
205
        courseStaffRepository
206
            .findById(courseStaffId)
207 1 1. lambda$joinCourseOnGitHub$4 : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::lambda$joinCourseOnGitHub$4 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(CourseStaff.class, courseStaffId));
208
209 2 1. joinCourseOnGitHub : negated conditional → KILLED
2. joinCourseOnGitHub : negated conditional → KILLED
    if (courseStaff.getUser() == null || currentUser.getId() != courseStaff.getUser().getId()) {
210
      throw new IllegalArgumentException(
211
          String.format(
212
              "This operation is restricted to the user associated with staff member with id %d",
213
              courseStaff.getId()));
214
    }
215
216 1 1. joinCourseOnGitHub : negated conditional → KILLED
    if (courseStaff.getGithubId() != null
217 1 1. joinCourseOnGitHub : negated conditional → KILLED
        && courseStaff.getGithubLogin() != null
218 1 1. joinCourseOnGitHub : negated conditional → KILLED
        && (courseStaff.getOrgStatus() == OrgStatus.MEMBER
219 1 1. joinCourseOnGitHub : negated conditional → KILLED
            || courseStaff.getOrgStatus() == OrgStatus.OWNER)) {
220 1 1. joinCourseOnGitHub : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::joinCourseOnGitHub → KILLED
      return ResponseEntity.badRequest()
221
          .body("You have already linked a Github account to this course.");
222
    }
223
224 1 1. joinCourseOnGitHub : negated conditional → KILLED
    if (courseStaff.getCourse().getOrgName() == null
225 1 1. joinCourseOnGitHub : negated conditional → KILLED
        || courseStaff.getCourse().getInstallationId() == null) {
226 1 1. joinCourseOnGitHub : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::joinCourseOnGitHub → KILLED
      return ResponseEntity.badRequest()
227
          .body("Course has not been set up. Please ask your instructor for help.");
228
    }
229 1 1. joinCourseOnGitHub : removed call to edu/ucsb/cs156/frontiers/entities/CourseStaff::setGithubId → KILLED
    courseStaff.setGithubId(currentUser.getGithubId());
230 1 1. joinCourseOnGitHub : removed call to edu/ucsb/cs156/frontiers/entities/CourseStaff::setGithubLogin → KILLED
    courseStaff.setGithubLogin(currentUser.getGithubLogin());
231
    OrgStatus status = organizationMemberService.inviteOrganizationOwner(courseStaff);
232 1 1. joinCourseOnGitHub : removed call to edu/ucsb/cs156/frontiers/entities/CourseStaff::setOrgStatus → KILLED
    courseStaff.setOrgStatus(status);
233
    courseStaffRepository.save(courseStaff);
234 1 1. joinCourseOnGitHub : negated conditional → KILLED
    if (status == OrgStatus.INVITED) {
235 1 1. joinCourseOnGitHub : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::joinCourseOnGitHub → KILLED
      return ResponseEntity.accepted().body("Successfully invited staff member to Organization");
236 2 1. joinCourseOnGitHub : negated conditional → KILLED
2. joinCourseOnGitHub : negated conditional → KILLED
    } else if (status == OrgStatus.MEMBER || status == OrgStatus.OWNER) {
237 1 1. joinCourseOnGitHub : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::joinCourseOnGitHub → KILLED
      return ResponseEntity.accepted()
238
          .body("Already in organization - set status to %s".formatted(status.toString()));
239
    } else {
240 1 1. joinCourseOnGitHub : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::joinCourseOnGitHub → KILLED
      return ResponseEntity.internalServerError()
241
          .body("Could not invite staff member to Organization");
242
    }
243
  }
244
245
  @Operation(summary = "Update a staff member")
246
  @PreAuthorize("@CourseSecurity.hasInstructorPermissions(#root, #courseId)")
247
  @PutMapping("")
248
  public CourseStaff updateStaffMember(
249
      @Parameter(name = "courseId") @RequestParam Long courseId,
250
      @Parameter(name = "id") @RequestParam Long id,
251
      @Parameter(name = "firstName") @RequestParam String firstName,
252
      @Parameter(name = "lastName") @RequestParam String lastName)
253
      throws EntityNotFoundException {
254
255
    CourseStaff staffMember =
256
        courseStaffRepository
257
            .findById(id)
258 1 1. lambda$updateStaffMember$5 : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::lambda$updateStaffMember$5 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(CourseStaff.class, id));
259
260 1 1. updateStaffMember : removed call to edu/ucsb/cs156/frontiers/entities/CourseStaff::setFirstName → KILLED
    staffMember.setFirstName(firstName.trim());
261 1 1. updateStaffMember : removed call to edu/ucsb/cs156/frontiers/entities/CourseStaff::setLastName → KILLED
    staffMember.setLastName(lastName.trim());
262 1 1. updateStaffMember : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::updateStaffMember → KILLED
    return courseStaffRepository.save(staffMember);
263
  }
264
265
  @Operation(summary = "Delete a staff member")
266
  @PreAuthorize("@CourseSecurity.hasInstructorPermissions(#root, #courseId)")
267
  @DeleteMapping("/delete")
268
  @Transactional
269
  public ResponseEntity<String> deleteStaffMember(
270
      @Parameter(name = "id") @RequestParam Long id,
271
      @Parameter(name = "courseId") @RequestParam Long courseId,
272
      @Parameter(
273
              name = "removeFromOrg",
274
              description = "Whether to remove course staff from GitHub organization")
275
          @RequestParam(defaultValue = "false")
276
          boolean removeFromOrg)
277
      throws EntityNotFoundException {
278
    CourseStaff staffMember =
279
        courseStaffRepository
280
            .findById(id)
281 1 1. lambda$deleteStaffMember$6 : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::lambda$deleteStaffMember$6 → KILLED
            .orElseThrow(() -> new EntityNotFoundException(CourseStaff.class, id));
282
    Course course = staffMember.getCourse();
283
284
    boolean orgRemovalAttempted = false;
285
    boolean orgRemovalSuccessful = false;
286
    String orgRemovalErrorMessage = null;
287
288
    // Try to remove the course staff from the organization if they have a GitHub login
289 1 1. deleteStaffMember : negated conditional → KILLED
    if (staffMember.getGithubLogin() != null
290 1 1. deleteStaffMember : negated conditional → KILLED
        && course.getOrgName() != null
291 2 1. deleteStaffMember : negated conditional → KILLED
2. deleteStaffMember : negated conditional → KILLED
        && course.getInstallationId() != null
292
        && removeFromOrg) {
293
      orgRemovalAttempted = true;
294
      try {
295 1 1. deleteStaffMember : removed call to edu/ucsb/cs156/frontiers/services/OrganizationMemberService::removeOrganizationMember → KILLED
        organizationMemberService.removeOrganizationMember(staffMember);
296
        orgRemovalSuccessful = true;
297
      } catch (Exception e) {
298
        log.error("Error removing course staff from organization: {}", e.getMessage());
299
        orgRemovalErrorMessage = e.getMessage();
300
        // Continue with deletion even if organization removal fails
301
      }
302
    }
303
304
    course.getCourseStaff().remove(staffMember);
305 1 1. deleteStaffMember : removed call to edu/ucsb/cs156/frontiers/entities/CourseStaff::setCourse → KILLED
    staffMember.setCourse(null);
306 1 1. deleteStaffMember : removed call to edu/ucsb/cs156/frontiers/repositories/CourseStaffRepository::delete → KILLED
    courseStaffRepository.delete(staffMember);
307
308 1 1. deleteStaffMember : negated conditional → KILLED
    if (!orgRemovalAttempted) {
309 1 1. deleteStaffMember : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::deleteStaffMember → KILLED
      return ResponseEntity.ok(
310
          "Successfully deleted staff member and removed them from the staff roster.");
311 1 1. deleteStaffMember : negated conditional → KILLED
    } else if (orgRemovalSuccessful) {
312 1 1. deleteStaffMember : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::deleteStaffMember → KILLED
      return ResponseEntity.ok(
313
          "Successfully deleted staff member and removed them from the staff roster and organization.");
314
    } else {
315 1 1. deleteStaffMember : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::deleteStaffMember → KILLED
      return ResponseEntity.ok(
316
          "Successfully deleted staff member but there was an error removing them from the course organization: "
317
              + orgRemovalErrorMessage);
318
    }
319
  }
320
}

Mutations

61

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

62

1.1
Location : buildCourseStaffForCourse
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:test_post_course_staff_join_course_status()]
removed call to edu/ucsb/cs156/frontiers/entities/CourseStaff::setOrgStatus → KILLED

64

1.1
Location : buildCourseStaffForCourse
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:testPostCourseStaffEmailSanitized()]
removed call to edu/ucsb/cs156/frontiers/entities/CourseStaff::setOrgStatus → KILLED

67

1.1
Location : buildCourseStaffForCourse
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:testPostCourseStaffEmailSanitized()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::buildCourseStaffForCourse → KILLED

73

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

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

3.3
Location : hasStaffCSVHeaders
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:instructor_can_upload_course_staff_csv_for_existing_course()]
negated conditional → KILLED

74

1.1
Location : hasStaffCSVHeaders
Killed by : none
replaced boolean return with true for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::hasStaffCSVHeaders → SURVIVED
Covering tests

77

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

2.2
Location : hasStaffCSVHeaders
Killed by : none
negated conditional → SURVIVED
Covering tests

78

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

79

1.1
Location : hasStaffCSVHeaders
Killed by : none
replaced boolean return with true for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::hasStaffCSVHeaders → NO_COVERAGE

83

1.1
Location : hasStaffCSVHeaders
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:instructor_can_upload_course_staff_csv_for_existing_course()]
replaced boolean return with false for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::hasStaffCSVHeaders → KILLED

87

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

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

95

1.1
Location : fromStaffCSVRow
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:instructor_can_upload_course_staff_csv_for_existing_course()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::fromStaffCSVRow → KILLED

122

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

128

1.1
Location : postCourseStaff
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:testPostCourseStaffEmailSanitized()]
removed call to edu/ucsb/cs156/frontiers/services/UpdateUserService::attachUserToCourseStaff → KILLED

130

1.1
Location : postCourseStaff
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:testPostCourseStaffEmailSanitized()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::postCourseStaff → KILLED

146

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

154

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

164

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

170

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

172

1.1
Location : uploadCourseStaffCSV
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:instructor_can_upload_course_staff_csv_for_existing_course()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::uploadCourseStaffCSV → KILLED

187

1.1
Location : lambda$courseStaffForCourse$3
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:admin_can_get_course_staff_for_a_non_existing_course()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::lambda$courseStaffForCourse$3 → KILLED

189

1.1
Location : courseStaffForCourse
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:testCourseStaffByCourse()]
replaced return value with Collections.emptyList for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::courseStaffForCourse → KILLED

207

1.1
Location : lambda$joinCourseOnGitHub$4
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:testLinkGitHub_notFound()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::lambda$joinCourseOnGitHub$4 → KILLED

209

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

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

216

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

217

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

218

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

219

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

220

1.1
Location : joinCourseOnGitHub
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:testJoinCourseOnGitHub_alreadyJoined()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::joinCourseOnGitHub → KILLED

224

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

225

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

226

1.1
Location : joinCourseOnGitHub
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:no_fire_on_no_installation_id()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::joinCourseOnGitHub → KILLED

229

1.1
Location : joinCourseOnGitHub
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:test_already_part_is_member()]
removed call to edu/ucsb/cs156/frontiers/entities/CourseStaff::setGithubId → KILLED

230

1.1
Location : joinCourseOnGitHub
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:test_already_part_is_member()]
removed call to edu/ucsb/cs156/frontiers/entities/CourseStaff::setGithubLogin → KILLED

232

1.1
Location : joinCourseOnGitHub
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:test_already_part_is_member()]
removed call to edu/ucsb/cs156/frontiers/entities/CourseStaff::setOrgStatus → KILLED

234

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

235

1.1
Location : joinCourseOnGitHub
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:test_fires_invite()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::joinCourseOnGitHub → KILLED

236

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

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

237

1.1
Location : joinCourseOnGitHub
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:test_already_part_is_member()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::joinCourseOnGitHub → KILLED

240

1.1
Location : joinCourseOnGitHub
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:cant_invite()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::joinCourseOnGitHub → KILLED

258

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

260

1.1
Location : updateStaffMember
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:testUpdateCourseStaff_success()]
removed call to edu/ucsb/cs156/frontiers/entities/CourseStaff::setFirstName → KILLED

261

1.1
Location : updateStaffMember
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:testUpdateCourseStaff_success()]
removed call to edu/ucsb/cs156/frontiers/entities/CourseStaff::setLastName → KILLED

262

1.1
Location : updateStaffMember
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:testUpdateCourseStaff_success()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::updateStaffMember → KILLED

281

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

289

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

290

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

291

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

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

295

1.1
Location : deleteStaffMember
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:testDeleteCourseStaff_withRemoveFromOrgTrue_success()]
removed call to edu/ucsb/cs156/frontiers/services/OrganizationMemberService::removeOrganizationMember → KILLED

305

1.1
Location : deleteStaffMember
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:testDeleteCourseStaff_withGithubLogin_noInstallationId_success()]
removed call to edu/ucsb/cs156/frontiers/entities/CourseStaff::setCourse → KILLED

306

1.1
Location : deleteStaffMember
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:testDeleteCourseStaff_withGithubLogin_noInstallationId_success()]
removed call to edu/ucsb/cs156/frontiers/repositories/CourseStaffRepository::delete → KILLED

308

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

309

1.1
Location : deleteStaffMember
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:testDeleteCourseStaff_withGithubLogin_noInstallationId_success()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::deleteStaffMember → KILLED

311

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

312

1.1
Location : deleteStaffMember
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:testDeleteCourseStaff_withRemoveFromOrgTrue_success()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::deleteStaffMember → KILLED

315

1.1
Location : deleteStaffMember
Killed by : edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.CourseStaffControllerTests]/[method:testDeleteCourseStaff_withGithubLogin_orgRemovalFails()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/CourseStaffController::deleteStaffMember → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0