Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Complete GenerateAssertionsService tests. Added some explicit checks … #95

Merged
merged 2 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,16 @@ public RaireResultOrError generateAssertions(GenerateAssertionsRequest request)
prefix, request.contestName, request.candidates, request.totalAuditableBallots,
request.timeLimitSeconds));

// Check that the contest exists and is all IRV. Otherwise this is an internal error because
// it should be caught before here.
if(contestRepository.findFirstByName(request.contestName).isEmpty()
|| !contestRepository.isAllIRV(request.contestName)) {
final String msg = String.format("%s Contest %s does not exist or is not all IRV", prefix,
request.contestName);
logger.error(msg + "Throwing a RaireServiceException.");
throw new RaireServiceException(msg, RaireErrorCode.INTERNAL_ERROR);
}

// Use raire-java to consolidate the votes, collecting all votes with the same ranking
// together and representing that collection as a single ranking with an associated number
// denoting how many votes with that ranking exist.
Expand All @@ -103,14 +113,21 @@ public RaireResultOrError generateAssertions(GenerateAssertionsRequest request)
c -> cvrContestInfoRepository.getCVRs(c.getContestID(), c.getCountyID())).
flatMap(List::stream).toList();

if(votes.size() > request.totalAuditableBallots){
if(votes.size() > request.totalAuditableBallots) {
final String msg = String.format("%s %d votes present for contest %s but a universe size of "
+ "%d specified in the assertion generation request. Throwing a RaireServiceException.",
prefix, votes.size(), request.contestName, request.totalAuditableBallots);
logger.error(msg);
throw new RaireServiceException(msg, RaireErrorCode.INVALID_TOTAL_AUDITABLE_BALLOTS);
}

if(votes.isEmpty()) {
final String msg = String.format("%s No votes present for contest %s.", prefix,
request.contestName);
logger.error(msg + " Throwing a RaireServiceException.");
throw new RaireServiceException(msg, RaireErrorCode.NO_VOTES_PRESENT);
}

logger.debug(String.format("%s Adding all extracted rankings to a consolidator to identify " +
"unique rankings and their number.", prefix));
votes.forEach(consolidator::addVoteNames);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,16 @@ public enum RaireErrorCode {
WRONG_CANDIDATE_NAMES,

/**
* The user has request to retrieve assertions for a contest for which no assertions have
* The user has requested to retrieve assertions for a contest for which no assertions have
* been generated.
*/
NO_ASSERTIONS_PRESENT,

/**
* The user has requested to generate assertions for a contest for which no votes are present.
*/
NO_VOTES_PRESENT,

// Internal errors (that the user can do nothing about)

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ the raire assertion generation engine (https://github.com/DemocracyDevelopers/ra
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import au.org.democracydevelopers.raireservice.request.GenerateAssertionsRequest;
import au.org.democracydevelopers.raireservice.testUtils;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
Expand All @@ -47,10 +49,11 @@ the raire assertion generation engine (https://github.com/DemocracyDevelopers/ra

/**
* Tests for appropriate responses to bad requests to the generate-assertions endpoint. This class
* automatically fires up the RAIRE Microservice on a random port. Currently, we check for
* proper input validation.
* automatically fires up the RAIRE Microservice on a random port. These sorts of errors are _not_
* supposed to happen - they indicate programming errors or problems with databases etc.
* Currently, we check for proper input validation and inconsistent input.
* The list of tests is similar to GenerateAssertionsRequestTests.java, and also to
* GetAssertionsAPITests.java when the same test is relevant to both endpoints.
* GetAssertionsAPIErrorTests.java when the same test is relevant to both endpoints.
* Contests which will be used for validity testing are
* preloaded into the database using src/test/resources/data.sql.
* Tests include:
Expand All @@ -74,6 +77,8 @@ public class GenerateAssertionsAPIErrorTests {
private final static String baseURL = "http://localhost:";
private final static String generateAssertionsEndpoint = "/raire/generate-assertions";

private final static List<String> aliceAndBob = List.of("Alice","Bob");

@LocalServerPort
private int port;

Expand Down Expand Up @@ -132,17 +137,32 @@ public void generateAssertionsWithNonExistentContestIsAnError() {
testUtils.log(logger, "generateAssertionsWithNonExistentContestIsAnError");
String url = baseURL + port + generateAssertionsEndpoint;

String requestAsJson =
"{\"timeLimitSeconds\":10.0,\"totalAuditableBallots\":100,"
+"\"contestName\":\"NonExistentContest\",\"candidates\":[\"Alice\",\"Bob\"]}";

HttpEntity<String> request = new HttpEntity<>(requestAsJson, httpHeaders);
GenerateAssertionsRequest request = new GenerateAssertionsRequest("NonExistentContest",
100, 10, aliceAndBob);
ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);

assertTrue(response.getStatusCode().is4xxClientError());
assertTrue(StringUtils.containsIgnoreCase(response.getBody(), "No such contest"));
}

/**
* The generateAssertions endpoint, called with a valid IRV contest for which no votes are present,
* returns a meaningful error.
*/
@Test
public void generateAssertionsFromNoVotesIsAnError() {
testUtils.log(logger, "generateAssertionsFromNoVotesIsAnError");
String url = baseURL + port + generateAssertionsEndpoint;

GenerateAssertionsRequest request = new GenerateAssertionsRequest("No CVR Mayoral", 100,
10, aliceAndBob);

ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);

assertTrue(response.getStatusCode().is5xxServerError());
assertTrue(StringUtils.containsIgnoreCase(response.getBody(), "No votes present for contest"));
}

/**
* The generateAssertions endpoint, called with a valid plurality contest,
* returns a meaningful error.
Expand All @@ -152,11 +172,8 @@ public void generateAssertionsWithPluralityContestIsAnError() {
testUtils.log(logger, "generateAssertionsWithPluralityContestIsAnError");
String url = baseURL + port + generateAssertionsEndpoint;

String requestAsJson =
"{\"timeLimitSeconds\":10.0,\"totalAuditableBallots\":100,"
+"\"contestName\":\"Valid Plurality Contest\",\"candidates\":[\"Alice\",\"Bob\"]}";

HttpEntity<String> request = new HttpEntity<>(requestAsJson, httpHeaders);
GenerateAssertionsRequest request = new GenerateAssertionsRequest(
"Valid Plurality Contest", 100, 10, aliceAndBob);
ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);

assertTrue(response.getStatusCode().is4xxClientError());
Expand All @@ -172,11 +189,8 @@ public void generateAssertionsWithMixedIRVPluralityContestIsAnError() {
testUtils.log(logger, "generateAssertionsWithMixedIRVPluralityContestIsAnError");
String url = baseURL + port + generateAssertionsEndpoint;

String requestAsJson =
"{\"timeLimitSeconds\":10.0,\"totalAuditableBallots\":100,"
+"\"contestName\":\"Invalid Mixed Contest\",\"candidates\":[\"Alice\",\"Bob\"]}";

HttpEntity<String> request = new HttpEntity<>(requestAsJson, httpHeaders);
GenerateAssertionsRequest request = new GenerateAssertionsRequest("Invalid Mixed Contest",
100,10,aliceAndBob);
ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);

assertTrue(response.getStatusCode().is4xxClientError());
Expand Down Expand Up @@ -400,8 +414,6 @@ public void generateAssertionsWithNegativeAuditableBallotsIsAnError() {
"Non-positive total auditable ballots"));
}



/**
* The generateAssertions endpoint, called with null/missing time limit, returns a meaningful error.
*/
Expand Down Expand Up @@ -478,6 +490,8 @@ public void wrongCandidatesIsAnError() {
assertTrue(response.getStatusCode().is5xxServerError());
assertEquals(WRONG_CANDIDATE_NAMES.toString(),
response.getHeaders().getFirst("error_code"));
assertTrue(StringUtils.containsIgnoreCase(response.getBody(),
"was not on the list of candidates"));
}

}
Loading
Loading