RepositoryService.java

1
package edu.ucsb.cs156.frontiers.services;
2
3
import com.fasterxml.jackson.core.JsonProcessingException;
4
import com.fasterxml.jackson.databind.JsonNode;
5
import com.fasterxml.jackson.databind.ObjectMapper;
6
import edu.ucsb.cs156.frontiers.entities.Course;
7
import edu.ucsb.cs156.frontiers.entities.CourseStaff;
8
import edu.ucsb.cs156.frontiers.entities.RosterStudent;
9
import edu.ucsb.cs156.frontiers.entities.Team;
10
import edu.ucsb.cs156.frontiers.enums.RepositoryPermissions;
11
import edu.ucsb.cs156.frontiers.repositories.TeamRepository;
12
import java.security.NoSuchAlgorithmException;
13
import java.security.spec.InvalidKeySpecException;
14
import java.util.ArrayList;
15
import java.util.HashMap;
16
import java.util.List;
17
import java.util.Map;
18
import lombok.extern.slf4j.Slf4j;
19
import org.springframework.boot.web.client.RestTemplateBuilder;
20
import org.springframework.http.*;
21
import org.springframework.stereotype.Service;
22
import org.springframework.web.client.HttpClientErrorException;
23
import org.springframework.web.client.RestTemplate;
24
25
@Service
26
@Slf4j
27
public class RepositoryService {
28
  private static final String GITHUB_ACCEPT = "application/vnd.github+json";
29
  private static final String GITHUB_API_VERSION = "2022-11-28";
30
31
  private final JwtService jwtService;
32
  private final GithubTeamService githubTeamService;
33
  private final TeamRepository teamRepository;
34
  private final RestTemplate restTemplate;
35
  private final ObjectMapper mapper;
36
37
  /**
38
   * Creates a GitHub repository for a user (student or staff), given only their GitHub login.
39
   *
40
   * <p>This helper method contains the shared logic used by both {@link
41
   * #createStudentRepository(Course, RosterStudent, String, Boolean, RepositoryPermissions)} and
42
   * {@link #createStaffRepository(Course, CourseStaff, String, Boolean, RepositoryPermissions)}.
43
   *
44
   * <ul>
45
   *   <li>Checks whether the repository already exists.
46
   *   <li>If not, creates a new repository under the course's organization.
47
   *   <li>Adds the user as a collaborator with the given permission level.
48
   * </ul>
49
   *
50
   * @param course the course whose organization the repo belongs to
51
   * @param githubLogin GitHub username of the student or staff member
52
   * @param repoPrefix prefix for the repository name (repoPrefix-githubLogin)
53
   * @param isPrivate whether the created repository should be private
54
   * @param permissions collaborator permissions to grant the user
55
   * @throws NoSuchAlgorithmException if signing fails
56
   * @throws InvalidKeySpecException if signing fails
57
   * @throws JsonProcessingException if JSON serialization fails
58
   */
59
  private void createRepositoryForStudentOrStaff(
60
      Course course,
61
      String githubLogin,
62
      String repoPrefix,
63
      Boolean isPrivate,
64
      RepositoryPermissions permissions)
65
      throws NoSuchAlgorithmException, InvalidKeySpecException, JsonProcessingException {
66
67
    String newRepoName = repoPrefix + "-" + githubLogin;
68
    String token = jwtService.getInstallationToken(course);
69
70
    String existenceEndpoint =
71
        "https://api.github.com/repos/" + course.getOrgName() + "/" + newRepoName;
72
    String createEndpoint = "https://api.github.com/orgs/" + course.getOrgName() + "/repos";
73
    String provisionEndpoint =
74
        "https://api.github.com/repos/"
75
            + course.getOrgName()
76
            + "/"
77
            + newRepoName
78
            + "/collaborators/"
79
            + githubLogin;
80
81
    HttpHeaders existenceHeaders = githubHeaders(token);
82
83
    HttpEntity<String> existenceEntity = new HttpEntity<>(existenceHeaders);
84
85
    try {
86
      restTemplate.exchange(existenceEndpoint, HttpMethod.GET, existenceEntity, String.class);
87
    } catch (HttpClientErrorException e) {
88 1 1. createRepositoryForStudentOrStaff : negated conditional → KILLED
      if (e.getStatusCode().equals(HttpStatus.NOT_FOUND)) {
89
        Map<String, Object> body = new HashMap<>();
90
        body.put("name", newRepoName);
91
        body.put("private", isPrivate);
92
        String bodyAsJson = mapper.writeValueAsString(body);
93
94
        HttpEntity<String> createEntity = new HttpEntity<>(bodyAsJson, githubHeaders(token));
95
96
        restTemplate.exchange(createEndpoint, HttpMethod.POST, createEntity, String.class);
97
      } else {
98
        log.warn(
99
            "Unexpected response code {} when checking for existence of repository {}",
100
            e.getStatusCode(),
101
            newRepoName);
102
        return;
103
      }
104
    }
105
106
    try {
107
      Map<String, Object> provisionBody = new HashMap<>();
108
      provisionBody.put("permission", permissions.getApiName());
109
      String provisionAsJson = mapper.writeValueAsString(provisionBody);
110
111
      HttpEntity<String> provisionEntity = new HttpEntity<>(provisionAsJson, existenceHeaders);
112
      restTemplate.exchange(provisionEndpoint, HttpMethod.PUT, provisionEntity, String.class);
113
    } catch (HttpClientErrorException ignored) {
114
      // silently ignore if provisioning fails (same as before)
115
    }
116
  }
117
118
  public RepositoryService(
119
      JwtService jwtService,
120
      GithubTeamService githubTeamService,
121
      TeamRepository teamRepository,
122
      RestTemplateBuilder restTemplateBuilder,
123
      ObjectMapper mapper) {
124
    this.jwtService = jwtService;
125
    this.githubTeamService = githubTeamService;
126
    this.teamRepository = teamRepository;
127
    this.restTemplate = restTemplateBuilder.build();
128
    this.mapper = mapper;
129
  }
130
131
  /**
132
   * Creates a single student repository if it doesn't already exist, and provisions access to the
133
   * repository by that student
134
   *
135
   * @param course The Course in question
136
   * @param student RosterStudent of the student the repository should be created for
137
   * @param repoPrefix Name of the project or assignment. Used to title the repository, in the
138
   *     format repoPrefix-githubLogin
139
   * @param isPrivate Whether the repository is private or not
140
   */
141
  public void createStudentRepository(
142
      Course course,
143
      RosterStudent student,
144
      String repoPrefix,
145
      Boolean isPrivate,
146
      RepositoryPermissions permissions)
147
      throws NoSuchAlgorithmException, InvalidKeySpecException, JsonProcessingException {
148 1 1. createStudentRepository : removed call to edu/ucsb/cs156/frontiers/services/RepositoryService::createRepositoryForStudentOrStaff → KILLED
    createRepositoryForStudentOrStaff(
149
        course, student.getGithubLogin(), repoPrefix, isPrivate, permissions);
150
  }
151
152
  /**
153
   * Creates a single staff repository if it doesn't already exist, and provisions access to the
154
   * repository by that staff member
155
   *
156
   * @param course The Course in question
157
   * @param staff CourseStaff of the staff the repository should be created for
158
   * @param repoPrefix Name of the project or assignment. Used to title the repository, in the
159
   *     format repoPrefix-githubLogin
160
   * @param isPrivate Whether the repository is private or not
161
   */
162
  public void createStaffRepository(
163
      Course course,
164
      CourseStaff staff,
165
      String repoPrefix,
166
      Boolean isPrivate,
167
      RepositoryPermissions permissions)
168
      throws NoSuchAlgorithmException, InvalidKeySpecException, JsonProcessingException {
169
170 1 1. createStaffRepository : removed call to edu/ucsb/cs156/frontiers/services/RepositoryService::createRepositoryForStudentOrStaff → KILLED
    createRepositoryForStudentOrStaff(
171
        course, staff.getGithubLogin(), repoPrefix, isPrivate, permissions);
172
  }
173
174
  /**
175
   * Creates a GitHub repository for a team (student or staff), given only their team name.
176
   *
177
   * <ul>
178
   *   <li>Checks whether the repository already exists.
179
   *   <li>If not, creates a new repository under the course's organization.
180
   *   <li>Adds all team members as collaborators with the given permission level.
181
   * </ul>
182
   *
183
   * @param course the course whose organization the repo belongs to
184
   * @param team the team for which the repo is being created
185
   * @param repoPrefix prefix for the repository name (repoPrefix-teamSlug)
186
   * @param isPrivate whether the created repository should be private
187
   * @param permissions collaborator permissions to grant the user
188
   * @param orgId GitHub organization ID used for team-based repo provisioning
189
   * @throws NoSuchAlgorithmException if signing fails
190
   * @throws InvalidKeySpecException if signing fails
191
   * @throws JsonProcessingException if JSON serialization fails
192
   */
193
  public void createTeamRepository(
194
      Course course,
195
      Team team,
196
      String repoPrefix,
197
      Boolean isPrivate,
198
      RepositoryPermissions permissions,
199
      Integer orgId)
200
      throws NoSuchAlgorithmException, InvalidKeySpecException, JsonProcessingException {
201
    String teamSlug = getOrFetchTeamSlug(course, team, orgId);
202
    String newRepoName = repoPrefix + "-" + teamSlug;
203
    String token = jwtService.getInstallationToken(course);
204
205
    String existenceEndpoint =
206
        "https://api.github.com/repos/" + course.getOrgName() + "/" + newRepoName;
207
    String createEndpoint = "https://api.github.com/orgs/" + course.getOrgName() + "/repos";
208
    String provisionEndpoint =
209
        "https://api.github.com/organizations/"
210
            + orgId
211
            + "/team/"
212
            + team.getGithubTeamId()
213
            + "/repos/"
214
            + course.getOrgName()
215
            + "/"
216
            + newRepoName;
217
218
    HttpHeaders existenceHeaders = githubHeaders(token);
219
220
    HttpEntity<String> existenceEntity = new HttpEntity<>(existenceHeaders);
221
222
    try {
223
      restTemplate.exchange(existenceEndpoint, HttpMethod.GET, existenceEntity, String.class);
224
    } catch (HttpClientErrorException e) {
225 1 1. createTeamRepository : negated conditional → KILLED
      if (e.getStatusCode().equals(HttpStatus.NOT_FOUND)) {
226
        Map<String, Object> body = new HashMap<>();
227
        body.put("name", newRepoName);
228
        body.put("private", isPrivate);
229
        String bodyAsJson = mapper.writeValueAsString(body);
230
231
        HttpEntity<String> createEntity = new HttpEntity<>(bodyAsJson, githubHeaders(token));
232
233
        restTemplate.exchange(createEndpoint, HttpMethod.POST, createEntity, String.class);
234
      } else {
235
        log.warn(
236
            "Unexpected response code {} when checking for existence of repository {}",
237
            e.getStatusCode(),
238
            newRepoName);
239
        return;
240
      }
241
    }
242
    try {
243
      Map<String, Object> provisionBody = new HashMap<>();
244
      provisionBody.put("permission", permissions.getApiName());
245
      String provisionAsJson = mapper.writeValueAsString(provisionBody);
246
247
      HttpEntity<String> provisionEntity = new HttpEntity<>(provisionAsJson, existenceHeaders);
248
      restTemplate.exchange(provisionEndpoint, HttpMethod.PUT, provisionEntity, String.class);
249
    } catch (HttpClientErrorException ignored) {
250
      // silently ignore if provisioning fails (same as before)
251
    }
252
  }
253
254
  private String getOrFetchTeamSlug(Course course, Team team, Integer orgId)
255
      throws JsonProcessingException, NoSuchAlgorithmException, InvalidKeySpecException {
256 2 1. getOrFetchTeamSlug : negated conditional → KILLED
2. getOrFetchTeamSlug : negated conditional → KILLED
    if (team.getGithubTeamSlug() != null && !team.getGithubTeamSlug().isBlank()) {
257 1 1. getOrFetchTeamSlug : replaced return value with "" for edu/ucsb/cs156/frontiers/services/RepositoryService::getOrFetchTeamSlug → KILLED
      return team.getGithubTeamSlug();
258
    }
259
260 1 1. getOrFetchTeamSlug : negated conditional → KILLED
    if (team.getGithubTeamId() == null) {
261
      throw new IllegalStateException(
262
          "Cannot create team repository without a GitHub team ID for team '"
263
              + team.getName()
264
              + "'");
265
    }
266
267
    GithubTeamService.GithubTeamInfo teamInfo =
268
        githubTeamService.getTeamInfoById(orgId, team.getGithubTeamId(), course);
269
270 3 1. getOrFetchTeamSlug : negated conditional → KILLED
2. getOrFetchTeamSlug : negated conditional → KILLED
3. getOrFetchTeamSlug : negated conditional → KILLED
    if (teamInfo == null || teamInfo.slug() == null || teamInfo.slug().isBlank()) {
271
      throw new IllegalStateException(
272
          "Cannot determine GitHub team slug for team '" + team.getName() + "'");
273
    }
274
275 1 1. getOrFetchTeamSlug : removed call to edu/ucsb/cs156/frontiers/entities/Team::setGithubTeamSlug → KILLED
    team.setGithubTeamSlug(teamInfo.slug());
276
    teamRepository.save(team);
277 1 1. getOrFetchTeamSlug : replaced return value with "" for edu/ucsb/cs156/frontiers/services/RepositoryService::getOrFetchTeamSlug → KILLED
    return teamInfo.slug();
278
  }
279
280
  public List<String> getRepositoryNamesWithPrefix(Course course, String prefix)
281
      throws NoSuchAlgorithmException, InvalidKeySpecException, JsonProcessingException {
282
    String token = jwtService.getInstallationToken(course);
283
    HttpEntity<String> requestEntity = new HttpEntity<>(githubHeaders(token));
284
285
    List<String> repoNames = new ArrayList<>();
286 1 1. getRepositoryNamesWithPrefix : Changed increment from 1 to -1 → KILLED
    for (int page = 1; ; page++) {
287
      String endpoint =
288
          "https://api.github.com/orgs/" + course.getOrgName() + "/repos?per_page=100&page=" + page;
289
      ResponseEntity<String> response =
290
          restTemplate.exchange(endpoint, HttpMethod.GET, requestEntity, String.class);
291
292
      JsonNode repos = mapper.readTree(response.getBody());
293 2 1. getRepositoryNamesWithPrefix : negated conditional → KILLED
2. getRepositoryNamesWithPrefix : negated conditional → KILLED
      if (!repos.isArray() || repos.isEmpty()) {
294
        break;
295
      }
296
297
      for (JsonNode repo : repos) {
298
        String repoName = repo.path("name").asText(null);
299 2 1. getRepositoryNamesWithPrefix : negated conditional → KILLED
2. getRepositoryNamesWithPrefix : negated conditional → KILLED
        if (repoName != null && repoName.startsWith(prefix)) {
300
          repoNames.add(repoName);
301
        }
302
      }
303
    }
304
305 1 1. getRepositoryNamesWithPrefix : replaced return value with Collections.emptyList for edu/ucsb/cs156/frontiers/services/RepositoryService::getRepositoryNamesWithPrefix → KILLED
    return repoNames;
306
  }
307
308
  public boolean repositoryHasCommits(Course course, String repoName)
309
      throws NoSuchAlgorithmException, InvalidKeySpecException, JsonProcessingException {
310
    String token = jwtService.getInstallationToken(course);
311
    String endpoint =
312
        "https://api.github.com/repos/"
313
            + course.getOrgName()
314
            + "/"
315
            + repoName
316
            + "/commits?per_page=1";
317
    HttpEntity<String> requestEntity = new HttpEntity<>(githubHeaders(token));
318
319
    try {
320
      restTemplate.exchange(endpoint, HttpMethod.GET, requestEntity, String.class);
321 1 1. repositoryHasCommits : replaced boolean return with false for edu/ucsb/cs156/frontiers/services/RepositoryService::repositoryHasCommits → KILLED
      return true;
322
    } catch (HttpClientErrorException e) {
323 1 1. repositoryHasCommits : negated conditional → KILLED
      if (e.getStatusCode().equals(HttpStatus.CONFLICT)) {
324 1 1. repositoryHasCommits : replaced boolean return with true for edu/ucsb/cs156/frontiers/services/RepositoryService::repositoryHasCommits → KILLED
        return false;
325
      }
326
      throw e;
327
    }
328
  }
329
330
  public void deleteRepository(Course course, String repoName)
331
      throws NoSuchAlgorithmException, InvalidKeySpecException, JsonProcessingException {
332
    String token = jwtService.getInstallationToken(course);
333
    String endpoint = "https://api.github.com/repos/" + course.getOrgName() + "/" + repoName;
334
    HttpEntity<String> requestEntity = new HttpEntity<>(githubHeaders(token));
335
    restTemplate.exchange(endpoint, HttpMethod.DELETE, requestEntity, String.class);
336
  }
337
338
  private HttpHeaders githubHeaders(String token) {
339
    HttpHeaders headers = new HttpHeaders();
340 1 1. githubHeaders : removed call to org/springframework/http/HttpHeaders::add → KILLED
    headers.add("Authorization", "Bearer " + token);
341 1 1. githubHeaders : removed call to org/springframework/http/HttpHeaders::add → KILLED
    headers.add("Accept", GITHUB_ACCEPT);
342 1 1. githubHeaders : removed call to org/springframework/http/HttpHeaders::add → KILLED
    headers.add("X-GitHub-Api-Version", GITHUB_API_VERSION);
343 1 1. githubHeaders : replaced return value with null for edu/ucsb/cs156/frontiers/services/RepositoryService::githubHeaders → KILLED
    return headers;
344
  }
345
}

Mutations

88

1.1
Location : createRepositoryForStudentOrStaff
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:exits_if_not_not_found()]
negated conditional → KILLED

148

1.1
Location : createStudentRepository
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:exits_if_not_not_found()]
removed call to edu/ucsb/cs156/frontiers/services/RepositoryService::createRepositoryForStudentOrStaff → KILLED

170

1.1
Location : createStaffRepository
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:successfully_creates_staff_repo_public()]
removed call to edu/ucsb/cs156/frontiers/services/RepositoryService::createRepositoryForStudentOrStaff → KILLED

225

1.1
Location : createTeamRepository
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:exits_if_team_repo_not_not_found()]
negated conditional → KILLED

256

1.1
Location : getOrFetchTeamSlug
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:team_repo_throws_when_slug_missing_and_team_id_missing()]
negated conditional → KILLED

2.2
Location : getOrFetchTeamSlug
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:exits_if_team_repo_not_not_found()]
negated conditional → KILLED

257

1.1
Location : getOrFetchTeamSlug
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:exits_if_team_repo_not_not_found()]
replaced return value with "" for edu/ucsb/cs156/frontiers/services/RepositoryService::getOrFetchTeamSlug → KILLED

260

1.1
Location : getOrFetchTeamSlug
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:team_repo_throws_when_slug_missing_and_team_id_missing()]
negated conditional → KILLED

270

1.1
Location : getOrFetchTeamSlug
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:team_repo_throws_when_fetched_team_slug_is_blank()]
negated conditional → KILLED

2.2
Location : getOrFetchTeamSlug
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:team_repo_throws_when_fetched_team_slug_is_null()]
negated conditional → KILLED

3.3
Location : getOrFetchTeamSlug
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:team_repo_throws_when_fetched_team_info_is_null()]
negated conditional → KILLED

275

1.1
Location : getOrFetchTeamSlug
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:team_repo_fetches_and_persists_slug_when_missing()]
removed call to edu/ucsb/cs156/frontiers/entities/Team::setGithubTeamSlug → KILLED

277

1.1
Location : getOrFetchTeamSlug
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:team_repo_fetches_and_persists_slug_when_missing()]
replaced return value with "" for edu/ucsb/cs156/frontiers/services/RepositoryService::getOrFetchTeamSlug → KILLED

286

1.1
Location : getRepositoryNamesWithPrefix
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:getRepositoryNamesWithPrefix_ignoresReposWithoutNames()]
Changed increment from 1 to -1 → KILLED

293

1.1
Location : getRepositoryNamesWithPrefix
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:getRepositoryNamesWithPrefix_stopsWhenResponseIsNotArray()]
negated conditional → KILLED

2.2
Location : getRepositoryNamesWithPrefix
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:getRepositoryNamesWithPrefix_ignoresReposWithoutNames()]
negated conditional → KILLED

299

1.1
Location : getRepositoryNamesWithPrefix
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:getRepositoryNamesWithPrefix_ignoresReposWithoutNames()]
negated conditional → KILLED

2.2
Location : getRepositoryNamesWithPrefix
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:getRepositoryNamesWithPrefix_ignoresReposWithoutNames()]
negated conditional → KILLED

305

1.1
Location : getRepositoryNamesWithPrefix
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:getRepositoryNamesWithPrefix_ignoresReposWithoutNames()]
replaced return value with Collections.emptyList for edu/ucsb/cs156/frontiers/services/RepositoryService::getRepositoryNamesWithPrefix → KILLED

321

1.1
Location : repositoryHasCommits
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:repositoryHasCommits_returnsTrueWhenCommitsEndpointSucceeds()]
replaced boolean return with false for edu/ucsb/cs156/frontiers/services/RepositoryService::repositoryHasCommits → KILLED

323

1.1
Location : repositoryHasCommits
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:repositoryHasCommits_rethrowsNonConflictErrors()]
negated conditional → KILLED

324

1.1
Location : repositoryHasCommits
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:repositoryHasCommits_returnsFalseWhenRepoIsEmpty()]
replaced boolean return with true for edu/ucsb/cs156/frontiers/services/RepositoryService::repositoryHasCommits → KILLED

340

1.1
Location : githubHeaders
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:getRepositoryNamesWithPrefix_stopsWhenResponseIsNotArray()]
removed call to org/springframework/http/HttpHeaders::add → KILLED

341

1.1
Location : githubHeaders
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:getRepositoryNamesWithPrefix_stopsWhenResponseIsNotArray()]
removed call to org/springframework/http/HttpHeaders::add → KILLED

342

1.1
Location : githubHeaders
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:getRepositoryNamesWithPrefix_stopsWhenResponseIsNotArray()]
removed call to org/springframework/http/HttpHeaders::add → KILLED

343

1.1
Location : githubHeaders
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:getRepositoryNamesWithPrefix_stopsWhenResponseIsNotArray()]
replaced return value with null for edu/ucsb/cs156/frontiers/services/RepositoryService::githubHeaders → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0