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

Use test class with database #132

Merged
merged 10 commits into from
Jun 24, 2024
37 changes: 37 additions & 0 deletions .github/workflows/maven.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven

# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

name: Java CI with Maven

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
cache: maven


- name: Build with Maven
run: cd server/eclipse-project; mvn -B -DskipTests package --file pom.xml

- name: Classic Tests
run: cd server/eclipse-project; mvn -Dtest='us.freeandfair.corla.**' test
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ public static synchronized void setProperties(final Properties the_properties) {
if (env.containsKey("HIBERNATE_URL")) {
system_properties.setProperty("hibernate.url", env.get("HIBERNATE_URL"));
}

// Properties have changed, we need to reinitialize our DB connectors
session_info.remove();
session_factory = null;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ public static int updateCVRContestInfos(final Long countyId, final Long contestI
newChoice = (newChoice.substring(0, newChoice.length() - 1));
newChoice = (newChoice.substring(1, newChoice.length()));

String escapedOldChoice = oldChoice.replaceAll("\"", Matcher.quoteReplacement("\\\\\""));
String escapedOldChoice = oldChoice.replaceAll("\"", Matcher.quoteReplacement("\\\\\"")).replace("?", "\\?");

final Query q = s
// this will only fix the first match, which is what we want, because
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,13 @@
import au.org.democracydevelopers.corla.model.ContestType;
import au.org.democracydevelopers.corla.model.IRVComparisonAudit;
import au.org.democracydevelopers.corla.model.vote.IRVParsingException;
import au.org.democracydevelopers.corla.util.TestClassWithDatabase;
import au.org.democracydevelopers.corla.util.testUtils;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.ext.ScriptUtils;
import org.testcontainers.jdbc.JdbcDatabaseDelegate;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
Expand All @@ -37,6 +40,7 @@
import us.freeandfair.corla.model.*;
import us.freeandfair.corla.persistence.Persistence;

import javax.transaction.Transactional;
import java.io.IOException;
import java.io.Reader;
import java.math.BigDecimal;
Expand All @@ -61,7 +65,8 @@
* - an all-plurality contest is made into a (plain, plurality) ComparisonAudit,
* - a mixed-type contest, or a contest that is neither plurality nor IRV, throws an error.
*/
public class ComparisonAuditControllerTests {
@Transactional
public class ComparisonAuditControllerTests extends TestClassWithDatabase {

/**
* Class-wide logger
Expand All @@ -71,14 +76,7 @@ public class ComparisonAuditControllerTests {
/**
* Container for the mock-up database.
*/
static PostgreSQLContainer<?> postgres
= new PostgreSQLContainer<>("postgres:15-alpine")
// None of these actually have to be the same as the real database (except its name), but this
// makes it easy to match the setup scripts.
.withDatabaseName("corla")
.withUsername("corlaadmin")
.withPassword("corlasecret")
.withInitScript("SQL/corla-three-candidates-ten-votes-inconsistent-types.sql");
static PostgreSQLContainer<?> postgres = createTestContainer();

/**
* Blank properties for submitting to the DominionCVRExportParser instance.
Expand All @@ -103,14 +101,13 @@ public class ComparisonAuditControllerTests {
@BeforeClass
public static void beforeAll() {
postgres.start();
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.driver", "org.postgresql.Driver");
hibernateProperties.setProperty("hibernate.url", postgres.getJdbcUrl());
hibernateProperties.setProperty("hibernate.user", postgres.getUsername());
hibernateProperties.setProperty("hibernate.pass", postgres.getPassword());
hibernateProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQL9Dialect");
Persistence.setProperties(hibernateProperties);
Persistence.beginTransaction();
Persistence.setProperties(createHibernateProperties(postgres));

var containerDelegate = new JdbcDatabaseDelegate(postgres, "");
ScriptUtils.runInitScript(containerDelegate,
"SQL/co-counties.sql");
ScriptUtils.runInitScript(containerDelegate,
"SQL/corla-three-candidates-ten-votes-inconsistent-types.sql");

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@
package au.org.democracydevelopers.corla.csv;

import au.org.democracydevelopers.corla.model.ContestType;
import au.org.democracydevelopers.corla.util.TestClassWithDatabase;
import au.org.democracydevelopers.corla.util.testUtils;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.ext.ScriptUtils;
import org.testcontainers.jdbc.JdbcDatabaseDelegate;
import us.freeandfair.corla.csv.DominionCVRExportParser;
import us.freeandfair.corla.model.*;
import us.freeandfair.corla.persistence.Persistence;
Expand All @@ -44,6 +47,8 @@

import org.testng.annotations.*;

import javax.transaction.Transactional;

import static au.org.democracydevelopers.corla.util.testUtils.*;
import static org.testng.Assert.*;

Expand All @@ -62,7 +67,7 @@
* plus a bad plurality "Vote For= " with a non-integer.
* - an examples to test the broader class of Write In strings.
*/
public class DominionCVRExportParserTests {
public class DominionCVRExportParserTests extends TestClassWithDatabase {

/**
* Class-wide logger
Expand All @@ -72,14 +77,7 @@ public class DominionCVRExportParserTests {
/**
* Container for the mock-up database.
*/
static PostgreSQLContainer<?> postgres
= new PostgreSQLContainer<>("postgres:15-alpine")
// None of these actually have to be the same as the real database (except its name), but this
// makes it easy to match the setup scripts.
.withDatabaseName("corla")
.withUsername("corlaadmin")
.withPassword("corlasecret")
.withInitScript("SQL/corlaInit.sql");
static PostgreSQLContainer<?> postgres = createTestContainer();

/**
* Error message to match.
Expand All @@ -94,15 +92,10 @@ public class DominionCVRExportParserTests {
@BeforeClass
public static void beforeAll() {
postgres.start();
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.driver", "org.postgresql.Driver");
hibernateProperties.setProperty("hibernate.url", postgres.getJdbcUrl());
hibernateProperties.setProperty("hibernate.user", postgres.getUsername());
hibernateProperties.setProperty("hibernate.pass", postgres.getPassword());
hibernateProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQL9Dialect");
Persistence.setProperties(hibernateProperties);
Persistence.beginTransaction();
Persistence.setProperties(createHibernateProperties(postgres));

var containerDelegate = new JdbcDatabaseDelegate(postgres, "");
ScriptUtils.runInitScript(containerDelegate, "SQL/co-counties.sql");
}

@AfterClass
Expand All @@ -116,22 +109,22 @@ public static void afterAll() {
* @throws IOException never.
*/
@Test
@Transactional
public void parseThreeCandidatesTenVotesSucceeds() throws IOException {
testUtils.log(LOGGER, "parseThreeCandidatesTenVotesSucceeds");
Path path = Paths.get(TINY_CSV_PATH + "ThreeCandidatesTenVotes.csv");
Reader reader = Files.newBufferedReader(path);
County saguache = fromString("Saguache");

final List<String> ABC = List.of("Alice","Bob","Chuan");
final List<String> ACB = List.of("Alice","Chuan","Bob");
final List<String> BAC = List.of("Bob","Alice","Chuan");
final List<String> CAB = List.of("Chuan","Alice","Bob");

DominionCVRExportParser parser = new DominionCVRExportParser(reader, saguache, blank, true);
DominionCVRExportParser parser = new DominionCVRExportParser(reader, fromString("Saguache"), blank, true);
assertTrue(parser.parse().success);

// There should be one contest, the one we just read in.
List<Contest> contests = forCounties(Set.of(saguache));
List<Contest> contests = forCounties(Set.of(fromString("Saguache")));
assertEquals(1, contests.size());
Contest contest = contests.get(0);

Expand All @@ -155,8 +148,8 @@ public void parseThreeCandidatesTenVotesSucceeds() throws IOException {
BAC,
CAB, CAB, CAB);

List<CVRContestInfo> cvrs = getMatching(saguache.id(), CastVoteRecord.RecordType.UPLOADED)
.map(cvr -> cvr.contestInfoForContest(contest)).collect(Collectors.toList());
List<CVRContestInfo> cvrs = getMatching(fromString("Saguache").id(), CastVoteRecord.RecordType.UPLOADED)
.map(cvr -> cvr.contestInfoForContest(contest)).toList();
assertEquals(10, cvrs.size());
for(int i=0 ; i < expectedChoices.size() ; i++) {
assertEquals(cvrs.get(i).choices(), expectedChoices.get(i));
Expand All @@ -169,17 +162,18 @@ public void parseThreeCandidatesTenVotesSucceeds() throws IOException {
* @throws IOException never.
*/
@Test
@Transactional
public void parseGuideToRaireExample3() throws IOException {
testUtils.log(LOGGER, "parseGuideToRaireExample3");
Path path = Paths.get(TINY_CSV_PATH + "GuideToRAIREExample3.csv");
Reader reader = Files.newBufferedReader(path);
County montezuma = fromString("Montezuma");

DominionCVRExportParser parser = new DominionCVRExportParser(reader, montezuma, blank, true);
DominionCVRExportParser parser = new DominionCVRExportParser(reader, fromString("Montezuma"),
blank, true);
assertTrue(parser.parse().success);

// There should be one contest, the one we just read in.
List<Contest> contests = forCounties(Set.of(montezuma));
List<Contest> contests = forCounties(Set.of(fromString("Montezuma")));
assertEquals(1, contests.size());
Contest contest = contests.get(0);

Expand All @@ -194,8 +188,8 @@ public void parseGuideToRaireExample3() throws IOException {
assertEquals(contest.winnersAllowed().intValue(), 1);

// There are 225 votes. Spot-check some of them.
List<CVRContestInfo> cvrs = getMatching(montezuma.id(), CastVoteRecord.RecordType.UPLOADED)
.map(cvr -> cvr.contestInfoForContest(contest)).collect(Collectors.toList());
List<CVRContestInfo> cvrs = getMatching(fromString("Montezuma").id(), CastVoteRecord.RecordType.UPLOADED)
.map(cvr -> cvr.contestInfoForContest(contest)).toList();
assertEquals(225, cvrs.size());
assertEquals(List.of("C", "D"), cvrs.get(0).choices());
assertEquals(List.of("C", "D"), cvrs.get(44).choices());
Expand All @@ -219,17 +213,17 @@ public void parseGuideToRaireExample3() throws IOException {
* @throws IOException if there are file I/O issues.
*/
@Test
@Transactional
public void parseThreeCandidatesTenInvalidVotesSucceeds() throws IOException {
testUtils.log(LOGGER, "parseThreeCandidatesTenInvalidVotesSucceeds");
Path path = Paths.get(TINY_CSV_PATH + "ThreeCandidatesTenInvalidVotes.csv");
Reader reader = Files.newBufferedReader(path);
County gilpin = fromString("Gilpin");

DominionCVRExportParser parser = new DominionCVRExportParser(reader, gilpin, blank, true);
DominionCVRExportParser parser = new DominionCVRExportParser(reader, fromString("Gilpin"), blank, true);
assertTrue(parser.parse().success);

// There should be one contest, the one we just read in.
List<Contest> contests = forCounties(Set.of(gilpin));
List<Contest> contests = forCounties(Set.of(fromString("Gilpin")));
assertEquals(1, contests.size());
Contest contest = contests.get(0);

Expand All @@ -244,8 +238,8 @@ public void parseThreeCandidatesTenInvalidVotesSucceeds() throws IOException {
assertEquals(contest.winnersAllowed().intValue(), 1);

// There are 10 votes, with respective valid interpretations as below:
List<CVRContestInfo> cvrs = getMatching(gilpin.id(), CastVoteRecord.RecordType.UPLOADED)
.map(cvr -> cvr.contestInfoForContest(contest)).collect(Collectors.toList());
List<CVRContestInfo> cvrs = getMatching(fromString("Gilpin").id(), CastVoteRecord.RecordType.UPLOADED)
.map(cvr -> cvr.contestInfoForContest(contest)).toList();
assertEquals(10, cvrs.size());

// Raw: "Alice(1),Alice(2),Bob(2),Chuan(3)
Expand Down Expand Up @@ -276,17 +270,18 @@ public void parseThreeCandidatesTenInvalidVotesSucceeds() throws IOException {
* Check correct parsing of the first vote.
*/
@Test
@Transactional
public void parseBoulder23Succeeds() throws IOException {
testUtils.log(LOGGER, "parseBoulder23Succeeds");
Path path = Paths.get(BOULDER_CSV_PATH + "Boulder-2023-Coordinated-CVR-Redactions-removed.csv");
Reader reader = Files.newBufferedReader(path);
County boulder = fromString("Boulder");

DominionCVRExportParser parser = new DominionCVRExportParser(reader, boulder, blank, true);
DominionCVRExportParser parser = new DominionCVRExportParser(reader, fromString("Boulder"),
blank, true);
assertTrue(parser.parse().success);

// There should be 38 contests. Check their metadata.
List<Contest> contests = forCounties(Set.of(boulder));
List<Contest> contests = forCounties(Set.of(fromString("Boulder")));
assertEquals(38, contests.size());

Contest boulderMayoral = contests.get(0);
Expand Down Expand Up @@ -496,8 +491,8 @@ public void parseBoulder23Succeeds() throws IOException {

// Check that the number of cvrs is correct. We have redacted CVRs, so the total is slightly
// less than the actual official count of 119757.
List<CastVoteRecord> cvrs = getMatching(boulder.id(),
CastVoteRecord.RecordType.UPLOADED).collect(Collectors.toList());
List<CastVoteRecord> cvrs = getMatching(fromString("Boulder").id(),
CastVoteRecord.RecordType.UPLOADED).toList();
assertEquals(cvrs.size(), 118669);
CastVoteRecord cvr1 = cvrs.get(0);

Expand Down Expand Up @@ -539,13 +534,14 @@ public void parseBoulder23Succeeds() throws IOException {
*/
@Test(expectedExceptions = RuntimeException.class,
expectedExceptionsMessageRegExp = badNumsRegexp)
@Transactional
public void parseBadVoteForPluralityError() throws IOException {
testUtils.log(LOGGER, "parseBadVoteForPluralityError");
Path path = Paths.get(BAD_CSV_PATH + "badVoteForPlurality.csv");
Reader reader = Files.newBufferedReader(path);
County sedgwick = fromString("Sedgwick");
// County sedgwick = fromString("Sedgwick");

DominionCVRExportParser parser = new DominionCVRExportParser(reader, sedgwick, blank, true);
DominionCVRExportParser parser = new DominionCVRExportParser(reader, fromString("Sedgwick"), blank, true);
assertTrue(parser.parse().success);
}

Expand All @@ -556,18 +552,18 @@ public void parseBadVoteForPluralityError() throws IOException {
* by -, _, space or no space, followed by any capitalization of "in", followed by any whitespace.
*/
@Test
@Transactional
public void parseWriteIns() throws IOException {
testUtils.log(LOGGER, "parseWriteIns");
Path path = Paths.get(WRITEIN_CSV_PATH + "WriteIns.csv");
Reader reader = Files.newBufferedReader(path);
County lasAnimas = fromString("Las Animas");

DominionCVRExportParser parser = new DominionCVRExportParser(reader, lasAnimas, blank, true);
DominionCVRExportParser parser = new DominionCVRExportParser(reader, fromString("Las Animas"), blank, true);
assertTrue(parser.parse().success);

// There should be seven contests, one for each example way of writing write-in:
// "Write-in", "Write-In", "Write in", "Write_in", "writeIn", "WRITEIN", "WRITE_IN"
List<Contest> contests = forCounties(Set.of(lasAnimas));
List<Contest> contests = forCounties(Set.of(fromString("Las Animas")));
assertEquals(7, contests.size());

for(int i=0 ; i < contests.size() ; i++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,12 @@
import us.freeandfair.corla.model.ContestResult;
import us.freeandfair.corla.persistence.Persistence;

import javax.transaction.Transactional;

/**
* This class contains tests for the functionality present in IRVComparisonAudit.
*/
@Transactional
public class IRVComparisonAuditTests extends TestClassWithDatabase {

private static final Logger LOGGER = LogManager.getLogger(IRVComparisonAuditTests.class);
Expand Down
Loading
Loading