/** {@inheritDoc} */
  @Override
  public Response getAllReports(HttpServletRequest request, String tickId, String crsid) {
    String myCrsid = (String) request.getSession().getAttribute("RavenRemoteUser");

    /* The user operated on is the caller */
    if (crsid.equals("")) {
      crsid = myCrsid;
    }

    /* Get the fork object, returning if not found */
    Fork fork = db.getFork(Fork.generateForkId(crsid, tickId));
    if (fork == null) {
      log.error(
          "User "
              + myCrsid
              + " requested fork "
              + Fork.generateForkId(crsid, tickId)
              + " to get testing status, but it couldn't be found");
      return Response.status(Status.NOT_FOUND).entity(Strings.MISSING).build();
    }

    /* Check permissions */
    if (!(permissions.forkCreator(myCrsid, crsid, tickId)
        || permissions.tickRole(myCrsid, tickId, Role.MARKER))) {
      log.warn(
          "User "
              + myCrsid
              + " tried to access fork "
              + Fork.generateForkId(crsid, tickId)
              + " but was denied permission");
      return Response.status(Status.FORBIDDEN).entity(Strings.INVALIDROLE).build();
    }

    /* Call the test service */
    List<Report> status;
    try {
      status = testServiceProxy.getAllReports(config.getConfig().getSecurityToken(), crsid, tickId);

    } catch (InternalServerErrorException e) {
      RemoteFailureHandler h = new RemoteFailureHandler();
      SerializableException s = h.readException(e);

      log.error(
          "User "
              + myCrsid
              + " failed getting all reports for "
              + crsid
              + " "
              + tickId
              + "\nCause: "
              + s.toString());
      return Response.status(Status.NOT_FOUND).entity(Strings.MISSING).build();

    } catch (UserNotInDBException | TickNotInDBException e) {
      log.error("User " + myCrsid + " failed getting all reports for " + crsid + " " + tickId, e);
      return Response.status(Status.INTERNAL_SERVER_ERROR).entity(e).build();
    }

    /*
     * Set the fork's last report date from the last report and
     * save it
     */
    fork.setLastReport(new DateTime(status.get(status.size() - 1).getCreationDate()));
    db.saveFork(fork);

    /* Return all of the reports */
    return Response.ok(status).build();
  }
  /** {@inheritDoc} */
  @Override
  public Response submit(HttpServletRequest request, String tickId)
      throws RepositoryNotFoundException {
    String crsid = (String) request.getSession().getAttribute("RavenRemoteUser");

    /* Get the fork object, returning if not found */
    Tick tick = db.getTick(tickId);
    Fork fork = db.getFork(Fork.generateForkId(crsid, tickId));
    if (fork == null) {
      log.error(
          "User "
              + crsid
              + " requested fork "
              + Fork.generateForkId(crsid, tickId)
              + " to submission, but it couldn't be found");
      return Response.status(Status.NOT_FOUND).entity(Strings.MISSING).build();
    }

    /*
     * Check if the deadline has passed, replace the deadline if an
     * extension exists for this user
     */
    DateTime extension = tick.getExtensions().get(crsid);

    if (extension != null) {
      tick.setDeadline(extension);
    }

    if (tick.getDeadline() != null && tick.getDeadline().isBeforeNow()) {
      return Response.status(Status.NOT_FOUND).entity(Strings.DEADLINE).build();
    }

    /* Parse id for the git service via the test service */
    String repoName = Tick.replaceDelimeter(tickId);

    String forkRepoName = crsid + "/" + repoName;

    /* Call the git service */
    try {
      testServiceProxy.runNewTest(
          config.getConfig().getSecurityToken(), crsid, tickId, forkRepoName);

    } catch (InternalServerErrorException e) {
      RemoteFailureHandler h = new RemoteFailureHandler();
      SerializableException s = h.readException(e);

      if (s.getClassName().equals(IOException.class.getName())) {
        log.error(
            "User "
                + crsid
                + " failed to start new test on "
                + repoName
                + "\nCause: "
                + s.toString());
        return Response.status(Status.INTERNAL_SERVER_ERROR)
            .entity(Strings.IDEMPOTENTRETRY)
            .build();
      }

      if (s.getClassName().equals(TestStillRunningException.class.getName())) {
        log.error(
            "User "
                + crsid
                + " failed to start new test on "
                + repoName
                + "\nCause: "
                + s.toString());
        return Response.status(Status.SERVICE_UNAVAILABLE).entity(Strings.TESTRUNNING).build();
      }

      if (s.getClassName().equals(TestIDNotFoundException.class.getName())) {
        log.error(
            "User "
                + crsid
                + " failed to start new test on "
                + repoName
                + "\nCause: "
                + s.toString());
        return Response.status(Status.NOT_FOUND).entity(Strings.MISSING).build();
      }

      if (s.getClassName().equals(NoCommitsToRepoException.class.getName())) {
        log.error(
            "User "
                + crsid
                + " failed to start new test on "
                + repoName
                + "\nCause: "
                + s.toString());
        return Response.status(Status.BAD_REQUEST).entity(Strings.NOCOMMITS).build();

      } else {
        log.error(
            "User "
                + crsid
                + " failed to start new test on "
                + repoName
                + "\nCause: "
                + s.toString());
        return Response.status(Status.INTERNAL_SERVER_ERROR)
            .entity(Strings.IDEMPOTENTRETRY)
            .build();
      }

    } catch (IOException
        | TestStillRunningException
        | TestIDNotFoundException
        | NoCommitsToRepoException e) {
      log.error("User " + crsid + " failed to start new test on " + repoName, e);
      return Response.status(Status.INTERNAL_SERVER_ERROR).entity(e).build();
    }

    /* The fork is not submitted for testing */
    fork.setTesting(true);

    /* Save and return the fork object */
    db.saveFork(fork);
    return Response.status(Status.CREATED).build();
  }
  /** {@inheritDoc} */
  @Override
  public Response getStatus(HttpServletRequest request, String tickId) {
    String crsid = (String) request.getSession().getAttribute("RavenRemoteUser");

    /* Get the fork object, returning if not found */
    Fork fork = db.getFork(Fork.generateForkId(crsid, tickId));
    if (fork == null) {
      log.error(
          "User "
              + crsid
              + " requested fork "
              + Fork.generateForkId(crsid, tickId)
              + " to get testing status, but it couldn't be found");
      return Response.status(Status.NOT_FOUND).entity(Strings.MISSING).build();
    }

    /* Call the test service */
    publicinterfaces.Status status;
    try {
      status = testServiceProxy.pollStatus(config.getConfig().getSecurityToken(), crsid, tickId);
    } catch (InternalServerErrorException e) {
      RemoteFailureHandler h = new RemoteFailureHandler();
      SerializableException s = h.readException(e);

      log.error(
          "User "
              + crsid
              + " failed getting the running status of "
              + crsid
              + " "
              + tickId
              + "\nCause: "
              + s.toString());
      return Response.status(Status.NOT_FOUND).entity(Strings.MISSING).build();
    } catch (NoSuchTestException e) {
      log.error(
          "User " + crsid + " failed getting the running status of " + crsid + " " + tickId, e);
      return Response.status(Status.INTERNAL_SERVER_ERROR).entity(e).build();
    }

    /* Check if the tests are complete */
    if ((status.getProgress() == status.getMaxProgress())
        && (status.getCurrentPositionInQueue() == 0)) {

      /* The fork has finished testing and the report is available */
      fork.setTesting(false);
      fork.setReportAvailable(true);

      /*
       * Get all of the groups this tick is in and set whether the user
       * can sign up for ticking in them or not
       */
      List<String> groupIds = db.getTick(tickId).getGroups();

      boolean unitPass = status.getInfo().equals("PASS");
      if (unitPass) {
        for (String groupId : groupIds) {
          tickSignupService.allowSignup(crsid, groupId, tickId);
        }
        fork.incrementUnitPasses();
      } else {
        for (String groupId : groupIds) {
          tickSignupService.disallowSignup(crsid, groupId, tickId);
        }
        fork.incrementUnitFails();
      }

      /* Set whether the fork passed and save it */
      fork.setUnitPass(unitPass);
      db.saveFork(fork);
    }

    /* Return the status object */
    return Response.ok(status).build();
  }