From 7ecaa53d6b4045e8d297b5c39df16e72ac8ae3c9 Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Wed, 19 Jun 2024 16:34:38 -0400 Subject: [PATCH 01/22] Testing ContestQueries.java --- .../corla/query/ContestQueries.java | 6 +- .../query/CastVoteRecordQueriesTest.java | 4 +- .../corla/query/ContestQueriesTest.java | 211 ++++++++++++++++++ 3 files changed, 215 insertions(+), 6 deletions(-) create mode 100644 server/eclipse-project/src/test/java/us/freeandfair/corla/query/ContestQueriesTest.java diff --git a/server/eclipse-project/src/main/java/us/freeandfair/corla/query/ContestQueries.java b/server/eclipse-project/src/main/java/us/freeandfair/corla/query/ContestQueries.java index 65d8a6ca..2dacdb12 100644 --- a/server/eclipse-project/src/main/java/us/freeandfair/corla/query/ContestQueries.java +++ b/server/eclipse-project/src/main/java/us/freeandfair/corla/query/ContestQueries.java @@ -109,10 +109,8 @@ public static Set forCounty(final County the_county) { public static void deleteForCounty(final Long the_county_id) { final Set contests = forCounty(Persistence.getByID(the_county_id, County.class)); - if (contests != null) { - for (final Contest c : contests) { - Persistence.delete(c); - } + for (final Contest c : contests) { + Persistence.delete(c); } Persistence.flush(); } diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java index 40ee4955..998ca2d4 100644 --- a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java @@ -33,8 +33,8 @@ public class CastVoteRecordQueriesTest { private static PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:15-alpine") .withDatabaseName("corla") .withUsername("corlaadmin") - .withPassword("corlasecret") - .withInitScript("SQL/corlaInitEmpty.sql"); + .withPassword("corlasecret") + .withInitScript("SQL/corlaInitEmpty.sql"); @BeforeClass public static void beforeAll() { diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ContestQueriesTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ContestQueriesTest.java new file mode 100644 index 00000000..7c0e163b --- /dev/null +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ContestQueriesTest.java @@ -0,0 +1,211 @@ +package us.freeandfair.corla.query; + +import net.sf.ehcache.search.aggregator.Count; +import org.hibernate.TransientObjectException; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testng.annotations.*; +import us.freeandfair.corla.model.*; +import us.freeandfair.corla.model.Choice; +import us.freeandfair.corla.persistence.Persistence; + +import java.awt.*; +import java.util.*; +import java.util.List; + +import static org.testng.Assert.*; + +@Test(groups = {"integration"}) +public class ContestQueriesTest { + + /** + * Container for the mock-up database. + */ + private static PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:15-alpine") + .withDatabaseName("corla") + .withUsername("corlaadmin") + .withPassword("corlasecret") + .withInitScript("SQL/corlaInitEmpty.sql"); + + @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); + } + @BeforeMethod + public static void beforeEach() { + Persistence.beginTransaction(); + } + + @AfterMethod + public static void afterEach() { + Persistence.rollbackTransaction(); + } + + @AfterClass + public static void afterall() { + postgres.stop(); + } + + public County countySetup() { + return countySetup(1L); + } + + public County countySetup(long county_id) { + return new County("test" + county_id, county_id); + } + + public List setupChoices() { + List choices = new ArrayList<>(); + Choice choice = new Choice("Test Choice 1", "Test choice description", false, false); + choices.add(choice); + choice = new Choice("Test Choice 2", "Test choice description", false, false); + choices.add(choice); + return choices; + } + + @Test + public void testForCountiesSimple() { + County county = countySetup(); + List choices = setupChoices(); + Contest contest = new Contest("Test Contest", county, "Test description", choices, 1, 1, 1); + + Persistence.saveOrUpdate(county); + Persistence.saveOrUpdate(contest); + + Set countySet = new HashSet<>(); + countySet.add(county); + + List expected = new ArrayList<>(); + expected.add(contest); + assertEquals(ContestQueries.forCounties(countySet), expected); + + } + + @Test + public void testForCountiesMulti() { + + List counties = new ArrayList<>(); + List contests = new ArrayList<>(); + + List choices = setupChoices(); + for(int i = 0; i < 10; i++) { + County county = countySetup(i); + counties.add(county); + Persistence.saveOrUpdate(county); + + + Contest contest = new Contest("Test", county, "Description", choices, 1, 1, 1); + Persistence.saveOrUpdate(contest); + contests.add(contest); + }; + + // See what happens if we only want the first 5 counties + Set countySet = new HashSet<>(counties.subList(0, 5)); + + List expected = contests.subList(0, 5); + assertEquals(ContestQueries.forCounties(countySet), expected); + + } + + @Test + public void testForCountySimple() { + + County county = countySetup(); + List choices = setupChoices(); + Contest contest = new Contest("Test Contest", county, "Test description", choices, 1, 1, 1); + + Persistence.saveOrUpdate(county); + Persistence.saveOrUpdate(contest); + + List expected = new ArrayList<>(); + expected.add(contest); + assertEquals(ContestQueries.forCounty(county), expected); + } + + @Test + public void testForCountyMulti() { + + County county = countySetup(); + Persistence.saveOrUpdate(county); + + List contests = new ArrayList<>(); + List choices = setupChoices(); + for(int i = 0; i < 10; i++) { + Contest contest = new Contest("Test" + i, county, "Description", choices, 1, 1, 1); + Persistence.saveOrUpdate(contest); + contests.add(contest); + }; + + Set expected = new HashSet<>(contests); + assertEquals(ContestQueries.forCounty(county), expected); + + } + + @Test(expectedExceptions = IllegalStateException.class) + public void testForCountyNull() { + // We are intentionally not persisting this county to see what happens. + County county = countySetup(); + + ContestQueries.forCounty(county); + fail("An error should have been thrown for a non-existent county!"); + + } + + @Test + public void testDBError() { + // Close the database + Persistence.commitTransaction(); + + // TODO: is there a way to check that the right message is logged? + County county = countySetup(); + assertNull(ContestQueries.forCounty(county)); + + Set counties = new HashSet<>(); + counties.add(county); + assertNull(ContestQueries.forCounties(counties)); + + Persistence.beginTransaction(); + ContestQueries.deleteForCounty(-1L); + + } + + @Test + public void testDeleteForCounty() { + + County county = countySetup(); + Persistence.saveOrUpdate(county); + + Set expected = new HashSet<>(); + assertEquals(ContestQueries.forCounty(county), expected); + + List contests = new ArrayList<>(); + List choices = setupChoices(); + for(int i = 0; i < 10; i++) { + Contest contest = new Contest("Test" + i, county, "Description", choices, 1, 1, 1); + Persistence.saveOrUpdate(contest); + contests.add(contest); + }; + + expected = new HashSet<>(contests); + assertEquals(ContestQueries.forCounty(county), expected); + + ContestQueries.deleteForCounty(county.id()); + + expected = new HashSet<>(); + assertEquals(ContestQueries.forCounty(county), expected); + + // This should be a no-op as county.contests is null + ContestQueries.deleteForCounty(county.id()); + assertEquals(ContestQueries.forCounty(county), expected); + + // See what happens when we pass an invalid county + ContestQueries.deleteForCounty(-1L); + } + +} From 5a7788d149b786200712e3de62542fb0663c5786 Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Wed, 19 Jun 2024 16:57:40 -0400 Subject: [PATCH 02/22] AdministratorQueries is now fully tested, though some code changed might be needed --- .../corla/query/AdministratorQueries.java | 3 +- .../corla/query/AdministratorQueriesTest.java | 127 ++++++++++++++++++ 2 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 server/eclipse-project/src/test/java/us/freeandfair/corla/query/AdministratorQueriesTest.java diff --git a/server/eclipse-project/src/main/java/us/freeandfair/corla/query/AdministratorQueries.java b/server/eclipse-project/src/main/java/us/freeandfair/corla/query/AdministratorQueries.java index b58322e5..1b902353 100644 --- a/server/eclipse-project/src/main/java/us/freeandfair/corla/query/AdministratorQueries.java +++ b/server/eclipse-project/src/main/java/us/freeandfair/corla/query/AdministratorQueries.java @@ -63,7 +63,8 @@ public static Administrator byUsername(final String the_username) { // if there's exactly one result, return that if (query_results.size() == 1) { result = query_results.get(0); - } + } + // TODO: if two admins have the same username, null gets returned. This seems like a bug? } catch (final PersistenceException e) { Main.LOGGER.error("could not query database for administrator"); } diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/AdministratorQueriesTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/AdministratorQueriesTest.java new file mode 100644 index 00000000..84307d94 --- /dev/null +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/AdministratorQueriesTest.java @@ -0,0 +1,127 @@ +package us.freeandfair.corla.query; + +import org.testcontainers.containers.PostgreSQLContainer; +import org.testng.annotations.*; +import us.freeandfair.corla.model.Administrator; +import us.freeandfair.corla.model.County; +import us.freeandfair.corla.persistence.Persistence; + +import java.util.Properties; + +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertNull; + +@Test +public class AdministratorQueriesTest { + + /** + * Container for the mock-up database. + */ + private static PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:15-alpine") + .withDatabaseName("corla") + .withUsername("corlaadmin") + .withPassword("corlasecret") + .withInitScript("SQL/corlaInitEmpty.sql"); + + @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); + } + @BeforeMethod + public static void beforeEach() { + Persistence.beginTransaction(); + } + + @AfterMethod + public static void afterEach() { + Persistence.rollbackTransaction(); + } + + @AfterClass + public static void afterall() { + postgres.stop(); + } + + @Test + public void testByUsernameState() { + + Administrator admin = new Administrator("testname", + Administrator.AdministratorType.STATE, + "fulltestname", + null); + + Persistence.saveOrUpdate(admin); + assertEquals(AdministratorQueries.byUsername("testname"), admin); + } + + + @Test + public void testByUsernameCounty() { + + County county = new County("test", 1L); + Persistence.saveOrUpdate(county); + Administrator admin = new Administrator("testname", + Administrator.AdministratorType.COUNTY, + "fulltestname", + county + ); + + Persistence.saveOrUpdate(admin); + assertEquals(AdministratorQueries.byUsername("testname"), admin); + } + + @Test + public void testByUsernameStateAndCounty() { + County county = new County("test", 1L); + Persistence.saveOrUpdate(county); + Administrator countyadmin = new Administrator("county", + Administrator.AdministratorType.COUNTY, + "countyfull", + county); + + Persistence.saveOrUpdate(countyadmin); + + Administrator stateadmin = new Administrator("state", + Administrator.AdministratorType.STATE, + "statefull", + null); + + Persistence.saveOrUpdate(stateadmin); + + assertEquals(AdministratorQueries.byUsername("county"),countyadmin); + assertEquals(AdministratorQueries.byUsername("state"), stateadmin); + } + + @Test + public void testSameName() { + + Administrator first = new Administrator("state", + Administrator.AdministratorType.STATE, + "first", + null); + + Administrator second = new Administrator("state", + Administrator.AdministratorType.STATE, + "second", + null); + Persistence.saveOrUpdate(first); + Persistence.saveOrUpdate(second); + + assertNull(AdministratorQueries.byUsername("state")); + } + + @Test + public void testDBError() { + // Close the DB + Persistence.commitTransaction(); + assertNull(AdministratorQueries.byUsername("username")); + Persistence.beginTransaction(); + } +} From f627c7df02745611610f556d00cdcaaedef31c80 Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Wed, 19 Jun 2024 20:15:41 -0400 Subject: [PATCH 03/22] revising pom.xml to include integration tests in maven --- server/eclipse-project/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/eclipse-project/pom.xml b/server/eclipse-project/pom.xml index 414a779f..d85699d5 100644 --- a/server/eclipse-project/pom.xml +++ b/server/eclipse-project/pom.xml @@ -25,7 +25,7 @@ 11 UTF-8 - integration + true true false From 7f275d77d5157222484e61b949845e90167d433f Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Wed, 19 Jun 2024 20:26:34 -0400 Subject: [PATCH 04/22] make sure maven includes integration tests --- server/eclipse-project/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/eclipse-project/pom.xml b/server/eclipse-project/pom.xml index d85699d5..eb72f5eb 100644 --- a/server/eclipse-project/pom.xml +++ b/server/eclipse-project/pom.xml @@ -25,7 +25,7 @@ 11 UTF-8 - + true true false From 82e0348e78a5f2c19dbb0ece368f3c1f1ebd5da4 Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Wed, 19 Jun 2024 22:31:31 -0400 Subject: [PATCH 05/22] Adding tests for BMIQuery, also fixing an edge case --- .../query/BallotManifestInfoQueries.java | 4 +- .../query/BallotManifestInfoQueriesTest.java | 267 ++++++++++++++++++ 2 files changed, 270 insertions(+), 1 deletion(-) create mode 100644 server/eclipse-project/src/test/java/us/freeandfair/corla/query/BallotManifestInfoQueriesTest.java diff --git a/server/eclipse-project/src/main/java/us/freeandfair/corla/query/BallotManifestInfoQueries.java b/server/eclipse-project/src/main/java/us/freeandfair/corla/query/BallotManifestInfoQueries.java index ee4e8473..fbd257ef 100644 --- a/server/eclipse-project/src/main/java/us/freeandfair/corla/query/BallotManifestInfoQueries.java +++ b/server/eclipse-project/src/main/java/us/freeandfair/corla/query/BallotManifestInfoQueries.java @@ -28,6 +28,7 @@ import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; +import javax.swing.text.html.Option; import org.hibernate.Session; import org.hibernate.query.Query; @@ -204,8 +205,9 @@ Find the batch(bmi) that would hold the sequence number given. cq.select(root).where(cb.and(disjuncts.toArray(new Predicate[disjuncts.size()]))); final TypedQuery query = s.createQuery(cq); result = new HashSet(query.getResultList()); - } catch (final PersistenceException e) { + } catch (PersistenceException e) { Main.LOGGER.error("Exception when reading ballot manifests from database: ", e); + return Optional.empty(); } return result.stream().findFirst(); } diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/BallotManifestInfoQueriesTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/BallotManifestInfoQueriesTest.java new file mode 100644 index 00000000..6886749c --- /dev/null +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/BallotManifestInfoQueriesTest.java @@ -0,0 +1,267 @@ +package us.freeandfair.corla.query; + +import org.apache.velocity.util.ArrayListWrapper; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.shaded.org.checkerframework.checker.units.qual.A; +import org.testng.annotations.*; +import us.freeandfair.corla.model.*; +import us.freeandfair.corla.persistence.Persistence; + +import javax.swing.text.html.Option; +import java.util.*; +import java.util.Optional; + +import static org.testng.AssertJUnit.assertEquals; + +@Test +public class BallotManifestInfoQueriesTest { + + /** + * Container for the mock-up database. + */ + private static PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:15-alpine") + .withDatabaseName("corla") + .withUsername("corlaadmin") + .withPassword("corlasecret") + .withInitScript("SQL/corlaInitEmpty.sql"); + + @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); + } + @BeforeMethod + public static void beforeEach() { + Persistence.beginTransaction(); + } + + @AfterMethod + public static void afterEach() { + Persistence.rollbackTransaction(); + } + + @AfterClass + public static void afterall() { + postgres.stop(); + } + + public BallotManifestInfo setupBMI() { + return new BallotManifestInfo(1L, + 1, + "batch1", + 64, + "storage1", + 0L, + 64L); + } + + @Test + public void testGetMatching() { + BallotManifestInfo bmi = setupBMI(); + Persistence.saveOrUpdate(bmi); + + Set counties = new HashSet<>(); + counties.add(1L); + + Set expected = new HashSet<>(); + expected.add(bmi); + assertEquals(BallotManifestInfoQueries.getMatching(counties), expected); + + } + + @Test + public void testGetMatchingDBError() { + Set counties = new HashSet<>(); + counties.add(1L); + + Persistence.rollbackTransaction(); + // This code should return an empty set in the event of a PersistenceException + assertEquals(new TreeSet<>(), BallotManifestInfoQueries.getMatching(counties)); + Persistence.beginTransaction(); + } + + @Test + public void testLocationForURI() { + BallotManifestInfo bmi = setupBMI(); + Persistence.saveOrUpdate(bmi); + + String uri = String.format("%s:%s:%s-%s", + "bmi", + 1L, + 1, + "batch1"); + + Set uris = new HashSet(); + uris.add(uri); + + List expected = new ArrayList<>(); + expected.add(bmi); + assertEquals(BallotManifestInfoQueries.locationFor(uris), expected); + + } + + @Test + public void testLocationForURIEmpty() { + assertEquals(BallotManifestInfoQueries.locationFor(new HashSet<>()), new ArrayList<>()); + } + + @Test + public void testLocationForCVR() { + BallotManifestInfo bmi = setupBMI(); + Persistence.saveOrUpdate(bmi); + + County county = new County("Test County", 1L); + Persistence.saveOrUpdate(county); + + List choices = new ArrayList(); + Choice choice = new Choice("Bob Anderson", "Esquire", false, false); + choices.add(choice); + + List votes = new ArrayList<>(); + votes.add("Bob Anderson"); + votes.add("Bob Anderson"); + + Contest contest = new Contest("test", county, "description", choices, 1, 1, 1); + Persistence.saveOrUpdate(contest); + List contest_info = new ArrayList<>(); + contest_info.add( new CVRContestInfo(contest, "comment", null, votes)); + + CastVoteRecord cvr = new CastVoteRecord(CastVoteRecord.RecordType.UPLOADED, + null, + 1L, + 1, + 1, + 1, + "batch1", + 1, + "1", + "a", + contest_info); + + Persistence.saveOrUpdate(cvr); + + Optional expected = Optional.of(bmi); + assertEquals(expected, BallotManifestInfoQueries.locationFor(cvr)); + + CastVoteRecord bad = new CastVoteRecord(CastVoteRecord.RecordType.UPLOADED, + null, + 1L, + 1, + 1, + 1, + "batchthatdoesntexist", + 1, + "1", + "a", + contest_info); + Persistence.saveOrUpdate(bad); + + assertEquals(Optional.empty(), BallotManifestInfoQueries.locationFor(bad)); + + } + + @Test + public void testLocationForCVRDBError() { + Persistence.commitTransaction(); + assertEquals(Optional.empty(), BallotManifestInfoQueries.locationFor(new CastVoteRecord())); + Persistence.beginTransaction(); + } + + @Test + public void testDeleteMatching() { + BallotManifestInfo bmi = setupBMI(); + Persistence.saveOrUpdate(bmi); + String uri = String.format("%s:%s:%s-%s", + "bmi", + 1L, + 1, + "batch1"); + + Set uris = new HashSet(); + uris.add(uri); + + List expected = new ArrayList<>(); + expected.add(bmi); + assertEquals(BallotManifestInfoQueries.locationFor(uris), expected); + + // Now delete everything for county 1L + int num_deleted = BallotManifestInfoQueries.deleteMatching(1L); + + assertEquals(1, num_deleted); + assertEquals(new ArrayList<>(), BallotManifestInfoQueries.locationFor(uris)); + + } + + @Test + public void testCount() { + BallotManifestInfo bmi = setupBMI(); + Persistence.saveOrUpdate(bmi); + + assertEquals(OptionalLong.of(1L), BallotManifestInfoQueries.count()); + } + + @Test + public void testCountDBError() { + Persistence.commitTransaction(); + assertEquals(OptionalLong.empty(), BallotManifestInfoQueries.count()); + Persistence.beginTransaction(); + } + + @Test + public void testHoldingSequencePosition() { + BallotManifestInfo bmi = setupBMI(); + Persistence.saveOrUpdate(bmi); + + assertEquals(Optional.of(bmi), BallotManifestInfoQueries.holdingSequencePosition(1L, 1L)); + } + + @Test + public void testHoldingSequencePositionDBError() { + Persistence.commitTransaction(); + assertEquals(Optional.empty(), BallotManifestInfoQueries.holdingSequencePosition(1L, 1L)); + Persistence.beginTransaction(); + } + + @Test + public void testMaxSequence() { + BallotManifestInfo bmi = setupBMI(); + Persistence.saveOrUpdate(bmi); + + assertEquals(OptionalLong.of(64L), BallotManifestInfoQueries.maxSequence(1L)); + } + + + @Test + public void testMaxSequenceDBError() { + Persistence.commitTransaction(); + assertEquals(OptionalLong.empty(), BallotManifestInfoQueries.maxSequence(1L)); + Persistence.beginTransaction(); + } + + @Test + public void testTotalBallots() { + BallotManifestInfo bmi = setupBMI(); + Persistence.saveOrUpdate(bmi); + Persistence.flush(); + + Set countyIDs = new HashSet<>(); + assertEquals((Long)0L,BallotManifestInfoQueries.totalBallots(countyIDs)); + + countyIDs.add(1L); + assertEquals((Long)64L,BallotManifestInfoQueries.totalBallots(countyIDs)); + + // Make sure that non-existing counties don't impact others + countyIDs.add(2L); + assertEquals((Long)64L,BallotManifestInfoQueries.totalBallots(countyIDs)); + + countyIDs.remove(1L); + assertEquals((Long)0L,BallotManifestInfoQueries.totalBallots(countyIDs)); + } + +} From 7422e9ac302e1dade71bd48c69fc743c11140933 Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Wed, 19 Jun 2024 22:32:40 -0400 Subject: [PATCH 06/22] removing unneeded import --- .../us/freeandfair/corla/query/BallotManifestInfoQueries.java | 1 - 1 file changed, 1 deletion(-) diff --git a/server/eclipse-project/src/main/java/us/freeandfair/corla/query/BallotManifestInfoQueries.java b/server/eclipse-project/src/main/java/us/freeandfair/corla/query/BallotManifestInfoQueries.java index fbd257ef..ac7ffad5 100644 --- a/server/eclipse-project/src/main/java/us/freeandfair/corla/query/BallotManifestInfoQueries.java +++ b/server/eclipse-project/src/main/java/us/freeandfair/corla/query/BallotManifestInfoQueries.java @@ -28,7 +28,6 @@ import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; -import javax.swing.text.html.Option; import org.hibernate.Session; import org.hibernate.query.Query; From 78a3b99193fc60b5fd6bbbfbfc47d3d0f3873b4c Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Thu, 20 Jun 2024 17:02:57 -0400 Subject: [PATCH 07/22] Working on CVR query tests --- .../corla/query/CastVoteRecordQueries.java | 8 +- .../query/CastVoteRecordQueriesTest.java | 148 ++++++++++++++++-- 2 files changed, 144 insertions(+), 12 deletions(-) diff --git a/server/eclipse-project/src/main/java/us/freeandfair/corla/query/CastVoteRecordQueries.java b/server/eclipse-project/src/main/java/us/freeandfair/corla/query/CastVoteRecordQueries.java index 0b83d2fe..56849335 100644 --- a/server/eclipse-project/src/main/java/us/freeandfair/corla/query/CastVoteRecordQueries.java +++ b/server/eclipse-project/src/main/java/us/freeandfair/corla/query/CastVoteRecordQueries.java @@ -149,7 +149,7 @@ public static OptionalLong countMatching(final RecordType the_type) { } catch (final PersistenceException e) { Main.LOGGER.error(COULD_NOT_QUERY_DATABASE); } - if (result == null) { + if (result == OptionalLong.empty()) { Main.LOGGER.debug("found no CVRs for type " + the_type); } else { Main.LOGGER.debug("query succeeded, returning CVR stream"); @@ -222,7 +222,7 @@ public static OptionalLong countMatching(final Long the_county, final RecordType } catch (final PersistenceException e) { Main.LOGGER.error(COULD_NOT_QUERY_DATABASE); } - if (result == null) { + if (result == OptionalLong.empty()) { Main.LOGGER.debug("found no CVRs for county " + the_county + ", type " + the_type); } else { Main.LOGGER.debug("query succeeded, returning CVR stream"); @@ -323,6 +323,7 @@ public static CastVoteRecord get(final Long the_county_id, final RecordType the_ final TypedQuery query = s.createQuery(cq); final List query_results = query.getResultList(); // if there's exactly one result, return that + // TODO the else branch here shouldn't return null? if (query_results.size() == 1) { result = query_results.get(0); } @@ -352,6 +353,9 @@ public static CastVoteRecord get(final Long the_county_id, final RecordType the_ public static Map get(final Long the_county_id, final RecordType the_type, final List the_sequence_numbers) { + + // TODO: this doesn't handle the case where two CVRs in the same county have the same sequence number. + // TODO: There should probably be a DB error or something if that happens. Map result = null; final Set unique_numbers = new HashSet<>(the_sequence_numbers); diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java index 998ca2d4..d6229758 100644 --- a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java @@ -1,26 +1,24 @@ package us.freeandfair.corla.query; import java.lang.reflect.Method; -import java.util.List; -import java.util.ArrayList; +import java.util.*; import org.hibernate.LockMode; import org.hibernate.Session; +import org.hibernate.query.Query; +import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.PostgreSQLContainer; import org.testng.annotations.*; import static org.testng.Assert.*; import java.time.Instant; -import java.util.Properties; +import java.util.Optional; +import java.util.function.*; +import java.util.stream.*; import us.freeandfair.corla.asm.PersistentASMState; -import us.freeandfair.corla.model.CastVoteRecord; -import us.freeandfair.corla.model.Contest; -import us.freeandfair.corla.model.Choice; -import us.freeandfair.corla.model.County; -import us.freeandfair.corla.model.CVRContestInfo; -import us.freeandfair.corla.model.CVRAuditInfo; +import us.freeandfair.corla.model.*; import us.freeandfair.corla.persistence.Persistence; import us.freeandfair.corla.query.Setup; @@ -54,7 +52,11 @@ public static void beforeEach() { @AfterMethod public static void afterEach() { - Persistence.rollbackTransaction(); + try { + Persistence.rollbackTransaction(); + } catch (IllegalStateException e) { + // Sometimes our tests intentionally kill the DB. + } } @AfterClass @@ -190,4 +192,130 @@ public void activityReportTest() { assertEquals(acvrs, result); } + @Test + public void testGetMatching() { + Set expected = new HashSet<>(); + + assertEquals(expected, CastVoteRecordQueries.getMatching(CastVoteRecord.RecordType.UPLOADED).collect(Collectors.toSet())); + assertEquals(expected, CastVoteRecordQueries.getMatching(1L, CastVoteRecord.RecordType.UPLOADED).collect(Collectors.toSet())); + + CastVoteRecord cvr = noisyCVRSetup(); + expected.add(cvr); + + assertEquals(expected, CastVoteRecordQueries.getMatching(CastVoteRecord.RecordType.UPLOADED).collect(Collectors.toSet())); + assertEquals(expected, CastVoteRecordQueries.getMatching(1L, CastVoteRecord.RecordType.UPLOADED).collect(Collectors.toSet())); + } + + @Test(expectedExceptions = IllegalStateException.class) + public void testGetMatchingDBError() { + Persistence.commitTransaction(); + CastVoteRecordQueries.getMatching(CastVoteRecord.RecordType.UPLOADED); + } + + @Test + public void testGetMatchingNoCVRTable (){ + // Drop the CVR table, which should cause a different error in getMatching + Query q = Persistence.currentSession().createNativeQuery("DROP TABLE cast_vote_record"); + q.executeUpdate(); + assertNull(CastVoteRecordQueries.getMatching(CastVoteRecord.RecordType.UPLOADED)); + assertNull(CastVoteRecordQueries.getMatching(1L, CastVoteRecord.RecordType.UPLOADED)); + } + + @Test + public void testCountMatching() { + assertEquals(OptionalLong.of(0), CastVoteRecordQueries.countMatching(CastVoteRecord.RecordType.UPLOADED)); + assertEquals(OptionalLong.of(0), CastVoteRecordQueries.countMatching(1L, CastVoteRecord.RecordType.UPLOADED)); + CastVoteRecord cvr = noisyCVRSetup(); + assertEquals(OptionalLong.of(1), CastVoteRecordQueries.countMatching(CastVoteRecord.RecordType.UPLOADED)); + assertEquals(OptionalLong.of(1), CastVoteRecordQueries.countMatching(1L, CastVoteRecord.RecordType.UPLOADED)); + } + + @Test(expectedExceptions = IllegalStateException.class) + public void testCountMatchingDBError() { + Persistence.commitTransaction(); + CastVoteRecordQueries.countMatching(CastVoteRecord.RecordType.UPLOADED); + } + + @Test + public void testCountMatchingNoCVRTable (){ + // Drop the CVR table, which should cause a different error in getMatching + Query q = Persistence.currentSession().createNativeQuery("DROP TABLE cast_vote_record"); + q.executeUpdate(); + assertEquals(OptionalLong.empty(), CastVoteRecordQueries.countMatching(CastVoteRecord.RecordType.UPLOADED)); + assertEquals(OptionalLong.empty(), CastVoteRecordQueries.countMatching(1L, CastVoteRecord.RecordType.UPLOADED)); + } + + @Test + public void testGetSingle() { + // We don't have any records yet + assertNull(CastVoteRecordQueries.get(1L, CastVoteRecord.RecordType.UPLOADED, 1)); + + CastVoteRecord expected = noisyCVRSetup(); + assertEquals(expected, CastVoteRecordQueries.get(1L, CastVoteRecord.RecordType.UPLOADED, 1)); + + // Now we have two CVRs with the same county, recordType, and sequenceNumber + noisyCVRSetup(2); + assertNull(CastVoteRecordQueries.get(1L, CastVoteRecord.RecordType.UPLOADED, 1)); + } + + @Test + public void testGetSingleDBError() { + // Drop the CVR table, which should cause a different error in getMatching + Query q = Persistence.currentSession().createNativeQuery("DROP TABLE cast_vote_record"); + q.executeUpdate(); + assertNull(CastVoteRecordQueries.get(1L, CastVoteRecord.RecordType.UPLOADED, 1)); + + } + + @Test + public void testGetMulti() { + List sequence_numbers = new ArrayList<>(); + Map expected = new HashMap<>(); + // We don't have any records yet + assertEquals(expected, CastVoteRecordQueries.get(1L, CastVoteRecord.RecordType.UPLOADED, sequence_numbers)); + + List contest_info = noisyContestSetup(); + + CastVoteRecord cvr = new CastVoteRecord(CastVoteRecord.RecordType.UPLOADED, + null, + 1L, + 1, + 1, + 1, + "1", + 1, + "1", + "a", + contest_info); + Persistence.save(cvr); + expected.put(1, cvr); + sequence_numbers.add(1); + assertEquals(expected, CastVoteRecordQueries.get(1L, CastVoteRecord.RecordType.UPLOADED, sequence_numbers)); + + // Now we have two CVRs with the same county, recordType, and sequenceNumber + CastVoteRecord second = new CastVoteRecord(CastVoteRecord.RecordType.UPLOADED, + null, + 1L, + 1, + 2, + 1, + "1", + 1, + "1", + "a", + contest_info); + Persistence.saveOrUpdate(second); + + sequence_numbers.add(2); + expected.put(2, second); + assertEquals(expected, CastVoteRecordQueries.get(1L, CastVoteRecord.RecordType.UPLOADED, sequence_numbers)); + } + + @Test + public void testGetMultiDBError() { + Query q = Persistence.currentSession().createNativeQuery("DROP TABLE cast_vote_record"); + q.executeUpdate(); + assertNull(CastVoteRecordQueries.get(1L, CastVoteRecord.RecordType.UPLOADED, new ArrayList<>())); + } } + From 4ffc9e1f7c4e2362741abe9b02cd147557540b5f Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Thu, 20 Jun 2024 18:40:24 -0400 Subject: [PATCH 08/22] Still working on CVRQueries. There's a lot of stuff in here! --- .../corla/query/CastVoteRecordQueries.java | 2 +- .../query/CastVoteRecordQueriesTest.java | 148 ++++++++++++++++++ 2 files changed, 149 insertions(+), 1 deletion(-) diff --git a/server/eclipse-project/src/main/java/us/freeandfair/corla/query/CastVoteRecordQueries.java b/server/eclipse-project/src/main/java/us/freeandfair/corla/query/CastVoteRecordQueries.java index 56849335..29f35cbc 100644 --- a/server/eclipse-project/src/main/java/us/freeandfair/corla/query/CastVoteRecordQueries.java +++ b/server/eclipse-project/src/main/java/us/freeandfair/corla/query/CastVoteRecordQueries.java @@ -415,7 +415,7 @@ public static List get(final List the_ids) { } catch (final PersistenceException e) { Main.LOGGER.error(COULD_NOT_QUERY_DATABASE); } - if (result == null) { + if (result.isEmpty()) { Main.LOGGER.debug("found no CVRs with ids " + the_ids); return new ArrayList<>(); } else { diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java index d6229758..919e18d1 100644 --- a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java @@ -3,11 +3,13 @@ import java.lang.reflect.Method; import java.util.*; +import org.antlr.v4.runtime.misc.Array2DHashSet; import org.hibernate.LockMode; import org.hibernate.Session; import org.hibernate.query.Query; import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.shaded.org.checkerframework.checker.units.qual.A; import org.testng.annotations.*; import static org.testng.Assert.*; @@ -317,5 +319,151 @@ public void testGetMultiDBError() { q.executeUpdate(); assertNull(CastVoteRecordQueries.get(1L, CastVoteRecord.RecordType.UPLOADED, new ArrayList<>())); } + + @Test + public void testGetIDs() { + List ids = new ArrayList<>(); + List expected = new ArrayList<>(); + // We don't have any records yet + assertEquals(expected, CastVoteRecordQueries.get(ids)); + + // Make sure that a bad lookup also returns nothing + ids.add(4L); + assertEquals(expected, CastVoteRecordQueries.get(ids)); + ids.clear(); + + List contest_info = noisyContestSetup(); + + CastVoteRecord cvr = new CastVoteRecord(CastVoteRecord.RecordType.UPLOADED, + null, + 1L, + 1, + 1, + 1, + "1", + 1, + "1", + "a", + contest_info); + Persistence.save(cvr); + expected.add(cvr); + ids.add(cvr.id()); + assertEquals(expected, CastVoteRecordQueries.get(ids)); + + // Now we have two CVRs with the same county, recordType, and sequenceNumber + CastVoteRecord second = new CastVoteRecord(CastVoteRecord.RecordType.UPLOADED, + null, + 1L, + 1, + 2, + 1, + "1", + 1, + "1", + "a", + contest_info); + Persistence.saveOrUpdate(second); + + ids.add(second.id()); + expected.add(second); + assertEquals(expected, CastVoteRecordQueries.get(ids)); + + } + @Test + public void testGetIDsDBError() { + Persistence.commitTransaction(); + List list = new ArrayList<>(); + list.add(1L); + assertEquals(new ArrayList<>(), CastVoteRecordQueries.get(list)); + } + + @Test + public void testAtPositionTribute() { + Tribute tribute = new Tribute(); + tribute.countyId = 1L; + tribute.scannerId = 1; + tribute.batchId = "1"; + tribute.ballotPosition = 1; + + CastVoteRecord expected = noisyCVRSetup(); + + assertEquals(expected, CastVoteRecordQueries.atPosition(tribute)); + } + + + @Test + public void testAtPositionTributes() { + List contest_info = noisyContestSetup(); + Tribute tribute = new Tribute(); + tribute.countyId = 1L; + tribute.scannerId = 1; + tribute.batchId = "1"; + tribute.ballotPosition = 1; + + tribute.setUri(); + + List tributes = new ArrayList<>(); + + List expected = new ArrayList<>(); + assertEquals(expected, CastVoteRecordQueries.atPosition(tributes)); + + tributes.add(tribute); + CastVoteRecord cvr = noisyCVRSetup(1, contest_info); + expected.add(cvr); + + assertEquals(expected, CastVoteRecordQueries.atPosition(tributes)); + + // Create a fake tribute to cause a branch at line 504 in CastVoteRecordQueries.java + tribute = new Tribute(); + tribute.countyId = 1L; + tribute.scannerId = 1; + tribute.batchId = "1"; + tribute.ballotPosition = 5; + + tribute.setUri(); + tributes.add(tribute); + + System.out.println(expected); + assertEquals(CastVoteRecordQueries.atPosition(tributes), expected); + } + + @Test + public void testPositionAtLots() { + List contest_info = noisyContestSetup(); + Tribute tribute; + CastVoteRecord cvr; + + List tributes = new ArrayList<>(); + List expected = new ArrayList<>(); + + // Now we need to test chunking, so add a whole lotta tributes + // These are not the greatest CVRs in the world, they are just a tribute + for (int i = 0; i < 2000; i++) { + tribute = new Tribute(); + tribute.countyId = 1L; + tribute.scannerId = 1; + tribute.batchId = "1"; + tribute.ballotPosition = i; + tribute.setUri(); + + tributes.add(tribute); + + cvr = new CastVoteRecord(CastVoteRecord.RecordType.UPLOADED, + null, + 1L, + i, + 1, + 1, + "1", + i, + "1", + "a", + contest_info); + Persistence.save(cvr); + expected.add(cvr); + } + + assertEquals(CastVoteRecordQueries.atPosition(tributes), expected); + } } From 65803d2a88cbda23f820f2594c6c01d062010797 Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Thu, 20 Jun 2024 18:47:00 -0400 Subject: [PATCH 09/22] Making drops cascade to prevent weird side effects --- .../corla/query/CastVoteRecordQueriesTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java index 919e18d1..1a9853b1 100644 --- a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java @@ -217,7 +217,7 @@ public void testGetMatchingDBError() { @Test public void testGetMatchingNoCVRTable (){ // Drop the CVR table, which should cause a different error in getMatching - Query q = Persistence.currentSession().createNativeQuery("DROP TABLE cast_vote_record"); + Query q = Persistence.currentSession().createNativeQuery("DROP TABLE cast_vote_record CASCADE"); q.executeUpdate(); assertNull(CastVoteRecordQueries.getMatching(CastVoteRecord.RecordType.UPLOADED)); assertNull(CastVoteRecordQueries.getMatching(1L, CastVoteRecord.RecordType.UPLOADED)); @@ -241,7 +241,7 @@ public void testCountMatchingDBError() { @Test public void testCountMatchingNoCVRTable (){ // Drop the CVR table, which should cause a different error in getMatching - Query q = Persistence.currentSession().createNativeQuery("DROP TABLE cast_vote_record"); + Query q = Persistence.currentSession().createNativeQuery("DROP TABLE cast_vote_record CASCADE"); q.executeUpdate(); assertEquals(OptionalLong.empty(), CastVoteRecordQueries.countMatching(CastVoteRecord.RecordType.UPLOADED)); assertEquals(OptionalLong.empty(), CastVoteRecordQueries.countMatching(1L, CastVoteRecord.RecordType.UPLOADED)); @@ -263,7 +263,7 @@ public void testGetSingle() { @Test public void testGetSingleDBError() { // Drop the CVR table, which should cause a different error in getMatching - Query q = Persistence.currentSession().createNativeQuery("DROP TABLE cast_vote_record"); + Query q = Persistence.currentSession().createNativeQuery("DROP TABLE cast_vote_record CASCADE"); q.executeUpdate(); assertNull(CastVoteRecordQueries.get(1L, CastVoteRecord.RecordType.UPLOADED, 1)); @@ -315,7 +315,7 @@ public void testGetMulti() { @Test public void testGetMultiDBError() { - Query q = Persistence.currentSession().createNativeQuery("DROP TABLE cast_vote_record"); + Query q = Persistence.currentSession().createNativeQuery("DROP TABLE cast_vote_record CASCADE"); q.executeUpdate(); assertNull(CastVoteRecordQueries.get(1L, CastVoteRecord.RecordType.UPLOADED, new ArrayList<>())); } From 7e23559d31e8bc6b20316e0cf2bb422dfda9be52 Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Thu, 20 Jun 2024 20:38:44 -0400 Subject: [PATCH 10/22] Changing deleteForCounty to return a count --- .../freeandfair/corla/query/ContestQueries.java | 15 +++++++++++---- .../corla/query/ContestQueriesTest.java | 13 ++++++++++--- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/server/eclipse-project/src/main/java/us/freeandfair/corla/query/ContestQueries.java b/server/eclipse-project/src/main/java/us/freeandfair/corla/query/ContestQueries.java index 2dacdb12..54469421 100644 --- a/server/eclipse-project/src/main/java/us/freeandfair/corla/query/ContestQueries.java +++ b/server/eclipse-project/src/main/java/us/freeandfair/corla/query/ContestQueries.java @@ -104,14 +104,21 @@ public static Set forCounty(final County the_county) { /** * Deletes all the contests for the county with the specified ID. * - * @param the_id The county ID. + * @param the_county_id The county ID. + * @return the number of contests that were deleted, or -1 if an error occured. */ - public static void deleteForCounty(final Long the_county_id) { + public static int deleteForCounty(final Long the_county_id) { final Set contests = forCounty(Persistence.getByID(the_county_id, County.class)); - for (final Contest c : contests) { - Persistence.delete(c); + + int retval = -1; + if (contests != null) { + retval = contests.size(); + for (final Contest c : contests) { + Persistence.delete(c); + } } Persistence.flush(); + return retval; } } diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ContestQueriesTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ContestQueriesTest.java index 7c0e163b..8264487e 100644 --- a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ContestQueriesTest.java +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ContestQueriesTest.java @@ -2,6 +2,7 @@ import net.sf.ehcache.search.aggregator.Count; import org.hibernate.TransientObjectException; +import org.hibernate.query.Query; import org.testcontainers.containers.PostgreSQLContainer; import org.testng.annotations.*; import us.freeandfair.corla.model.*; @@ -195,17 +196,23 @@ public void testDeleteForCounty() { expected = new HashSet<>(contests); assertEquals(ContestQueries.forCounty(county), expected); - ContestQueries.deleteForCounty(county.id()); + assertEquals(ContestQueries.deleteForCounty(county.id()), 10); expected = new HashSet<>(); assertEquals(ContestQueries.forCounty(county), expected); // This should be a no-op as county.contests is null - ContestQueries.deleteForCounty(county.id()); + assertEquals(ContestQueries.deleteForCounty(county.id()), 0); assertEquals(ContestQueries.forCounty(county), expected); // See what happens when we pass an invalid county - ContestQueries.deleteForCounty(-1L); + assertEquals(ContestQueries.deleteForCounty(-1L), 0); + + // Test what happens when forCounty returns null + Query q = Persistence.currentSession().createNativeQuery("DROP TABLE contest"); + q.executeUpdate(); + assertEquals(ContestQueries.deleteForCounty(1L), -1); } + } From ec8c22abca12dcd26394988b09d98ac5ddb5e550 Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Thu, 20 Jun 2024 20:40:33 -0400 Subject: [PATCH 11/22] putting back a final descriptor --- .../us/freeandfair/corla/query/BallotManifestInfoQueries.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/eclipse-project/src/main/java/us/freeandfair/corla/query/BallotManifestInfoQueries.java b/server/eclipse-project/src/main/java/us/freeandfair/corla/query/BallotManifestInfoQueries.java index ac7ffad5..38ef77b7 100644 --- a/server/eclipse-project/src/main/java/us/freeandfair/corla/query/BallotManifestInfoQueries.java +++ b/server/eclipse-project/src/main/java/us/freeandfair/corla/query/BallotManifestInfoQueries.java @@ -204,7 +204,7 @@ Find the batch(bmi) that would hold the sequence number given. cq.select(root).where(cb.and(disjuncts.toArray(new Predicate[disjuncts.size()]))); final TypedQuery query = s.createQuery(cq); result = new HashSet(query.getResultList()); - } catch (PersistenceException e) { + } catch (final PersistenceException e) { Main.LOGGER.error("Exception when reading ballot manifests from database: ", e); return Optional.empty(); } From 15e9423597b5541ccb6f7238cc521395abd4d3a2 Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Thu, 20 Jun 2024 21:12:39 -0400 Subject: [PATCH 12/22] making some lambdas more clear, adding a TODO about unreachable code --- .../us/freeandfair/corla/query/CastVoteRecordQueries.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/eclipse-project/src/main/java/us/freeandfair/corla/query/CastVoteRecordQueries.java b/server/eclipse-project/src/main/java/us/freeandfair/corla/query/CastVoteRecordQueries.java index 29f35cbc..61b574fe 100644 --- a/server/eclipse-project/src/main/java/us/freeandfair/corla/query/CastVoteRecordQueries.java +++ b/server/eclipse-project/src/main/java/us/freeandfair/corla/query/CastVoteRecordQueries.java @@ -478,7 +478,7 @@ public static List atPosition(final List tributes) { return t.getUri(); })) // is it faster to let the db do this with an except query? - .filter(t -> !foundUris.contains(t.getUri())).map(t -> phantomRecord(t)) + .filter(t -> !foundUris.contains(t.getUri())).map(CastVoteRecordQueries::phantomRecord) .map(Persistence::persist).collect(Collectors.toSet()); results.addAll(phantomRecords); @@ -500,8 +500,10 @@ public static List atPosition(final List tributes) { } final List returnList = - randomOrder.stream().filter(cvr -> null != cvr).collect(Collectors.toList()); + randomOrder.stream().filter(Objects::nonNull).collect(Collectors.toList()); if (returnList.size() != uris.size()) { + // TODO: I'm pretty sure this code is unreachable, since any time |URIs| < |return|, we + // TODO: make phantoms until they equal. Maybe take this out? // we got a problem here Main.LOGGER .error("something went wrong with atPosition - returnList.size() != uris.size()"); From 6d995c92b1d95184698a4a2ceb6f6b304359d3fd Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Fri, 21 Jun 2024 15:28:43 -0400 Subject: [PATCH 13/22] Calling CastVoteRecordQueriesTest good for now. Not 100% coverage, but close. --- .../corla/query/CastVoteRecordQueries.java | 12 +- .../query/CastVoteRecordQueriesTest.java | 165 +++++++++++++++++- 2 files changed, 171 insertions(+), 6 deletions(-) diff --git a/server/eclipse-project/src/main/java/us/freeandfair/corla/query/CastVoteRecordQueries.java b/server/eclipse-project/src/main/java/us/freeandfair/corla/query/CastVoteRecordQueries.java index 61b574fe..de877266 100644 --- a/server/eclipse-project/src/main/java/us/freeandfair/corla/query/CastVoteRecordQueries.java +++ b/server/eclipse-project/src/main/java/us/freeandfair/corla/query/CastVoteRecordQueries.java @@ -576,13 +576,15 @@ public static Long maxRevision(final CastVoteRecord cvr) { q.setLong("countyId", cvr.countyID()); q.setString("imprintedId", cvr.imprintedID()); - final Long result = (Long) q.getSingleResult(); - - if (null == result) { + try { + return (Long) q.getSingleResult(); + } catch (final PersistenceException e) { + // the DB had a problem! + // TODO: Technically this should probably be an error code? + // TODO: Otherwise there's no way to discern this from a CVR with no revisions? return 0L; - } else { - return result; } + } /** diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java index 1a9853b1..3cedbd67 100644 --- a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java @@ -194,6 +194,54 @@ public void activityReportTest() { assertEquals(acvrs, result); } + @Test() + public void activityReportTestBig() { + List contest_info = noisyContestSetup(); + Tribute tribute; + CastVoteRecord cvr; + + List expected = new ArrayList<>(); + + List acvrs = new ArrayList(); + List contestCVRIds = new ArrayList(); + // Now we need to test chunking, so add a whole lotta tributes + // These are not the greatest CVRs in the world, they are just a tribute + for (int i = 0; i < 2000; i++) { + cvr = new CastVoteRecord(CastVoteRecord.RecordType.UPLOADED, + null, + 1L, + i, + 1, + 1, + "1", + i, + "1", + "a", + contest_info); + + Persistence.save(cvr); + CastVoteRecord acvr = new CastVoteRecord(CastVoteRecord.RecordType.AUDITOR_ENTERED, Instant.now(), + cvr.countyID(), cvr.cvrNumber(), null, cvr.scannerID(), + cvr.batchID(), cvr.recordID(), cvr.imprintedID(), + cvr.ballotType(), cvr.contestInfo()); + + acvr.setComment("testing"); + acvr.setAuditBoardIndex(14); + acvr.setCvrId(cvr.id()); + + CVRAuditInfo cai = new CVRAuditInfo(cvr); + cai.setACVR(acvr); + + Persistence.save(acvr); + Persistence.save(cai); + + acvrs.add(acvr); + contestCVRIds.add(cvr.id()); + } + List result = CastVoteRecordQueries.activityReport(contestCVRIds); + assertEquals(acvrs, result); + } + @Test public void testGetMatching() { Set expected = new HashSet<>(); @@ -414,6 +462,7 @@ public void testAtPositionTributes() { assertEquals(expected, CastVoteRecordQueries.atPosition(tributes)); // Create a fake tribute to cause a branch at line 504 in CastVoteRecordQueries.java + // This causes a phantom to get created tribute = new Tribute(); tribute.countyId = 1L; tribute.scannerId = 1; @@ -423,7 +472,8 @@ public void testAtPositionTributes() { tribute.setUri(); tributes.add(tribute); - System.out.println(expected); + CastVoteRecord phantom = CastVoteRecordQueries.phantomRecord(tribute); + expected.add(phantom); assertEquals(CastVoteRecordQueries.atPosition(tributes), expected); } @@ -465,5 +515,118 @@ public void testPositionAtLots() { assertEquals(CastVoteRecordQueries.atPosition(tributes), expected); } + + @Test + public void testAtPositionRawData() { + CastVoteRecord expected = noisyCVRSetup(); + assertEquals(expected, CastVoteRecordQueries.atPosition(1L, 1, "1", 1)); + + // Make sure it creates a phantom if I ask for an invalid record: + CastVoteRecord actual = CastVoteRecordQueries.atPosition(2L, 3, "blah", 9); + expected = CastVoteRecordQueries.phantomRecord(2L, 3, "blah", 9); + assertEquals(expected, actual); + + } + + + @Test + public void testPhantomRecordTribute() { + Tribute tribute = new Tribute(); + tribute.countyId = 1L; + tribute.scannerId = 1; + tribute.batchId = "1"; + tribute.ballotPosition = 5; + tribute.setUri(); + + CastVoteRecord expected = new CastVoteRecord(CastVoteRecord.RecordType.PHANTOM_RECORD, + null, + 1L, + 0, + 0, + 1, + "1", + 5, + "1-1-5", + "PHANTOM RECORD", + null); + + CastVoteRecord phantom = CastVoteRecordQueries.phantomRecord(tribute); + assertEquals(phantom, expected); + + } + + @Test + public void testPhantomRecordRawData() { + CastVoteRecord expected = new CastVoteRecord(CastVoteRecord.RecordType.PHANTOM_RECORD, + null, + 1L, + 0, + 0, + 1, + "1", + 5, + "1-1-5", + "PHANTOM RECORD", + null); + + CastVoteRecord phantom = CastVoteRecordQueries.phantomRecord(1L, 1, "1", 5); + assertEquals(phantom, expected); + } + + @Test + public void testMaxRevisionDBError() { + CastVoteRecord cvr = noisyCVRSetup(); + Query q = Persistence.currentSession().createNativeQuery("DROP TABLE cast_vote_record CASCADE"); + q.executeUpdate(); + assertEquals((Long)0L, CastVoteRecordQueries.maxRevision(cvr)); + } + + @Test() + public void testResultsReportBig() { + List contest_info = noisyContestSetup(); + Tribute tribute; + CastVoteRecord cvr; + + List expected = new ArrayList<>(); + + List acvrs = new ArrayList(); + List contestCVRIds = new ArrayList(); + // Now we need to test chunking, so add a whole lotta tributes + // These are not the greatest CVRs in the world, they are just a tribute + for (int i = 0; i < 2000; i++) { + cvr = new CastVoteRecord(CastVoteRecord.RecordType.UPLOADED, + null, + 1L, + i, + 1, + 1, + "1", + i, + "1", + "a", + contest_info); + + Persistence.save(cvr); + CastVoteRecord acvr = new CastVoteRecord(CastVoteRecord.RecordType.AUDITOR_ENTERED, Instant.now(), + cvr.countyID(), cvr.cvrNumber(), null, cvr.scannerID(), + cvr.batchID(), cvr.recordID(), cvr.imprintedID(), + cvr.ballotType(), cvr.contestInfo()); + + acvr.setComment("testing"); + acvr.setAuditBoardIndex(14); + acvr.setCvrId(cvr.id()); + + CVRAuditInfo cai = new CVRAuditInfo(cvr); + cai.setACVR(acvr); + + Persistence.save(acvr); + Persistence.save(cai); + + acvrs.add(acvr); + contestCVRIds.add(cvr.id()); + } + List result = CastVoteRecordQueries.resultsReport(contestCVRIds); + assertEquals(acvrs, result); + } } From ed31a9fc462c030e5d2e14b821b2261aa16fe9c8 Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Fri, 21 Jun 2024 15:30:56 -0400 Subject: [PATCH 14/22] Fixing missing CASCADE statement --- .../java/us/freeandfair/corla/query/ContestQueriesTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ContestQueriesTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ContestQueriesTest.java index 8264487e..7a79e592 100644 --- a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ContestQueriesTest.java +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ContestQueriesTest.java @@ -209,7 +209,7 @@ public void testDeleteForCounty() { assertEquals(ContestQueries.deleteForCounty(-1L), 0); // Test what happens when forCounty returns null - Query q = Persistence.currentSession().createNativeQuery("DROP TABLE contest"); + Query q = Persistence.currentSession().createNativeQuery("DROP TABLE contest CASCADE"); q.executeUpdate(); assertEquals(ContestQueries.deleteForCounty(1L), -1); } From bcb32a8470e1e9105e4fb18a92eecc2aa98e734f Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Fri, 21 Jun 2024 16:27:36 -0400 Subject: [PATCH 15/22] disabling big tests to see if it unborks GitHub --- .../freeandfair/corla/query/CastVoteRecordQueriesTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java index 3cedbd67..042fb256 100644 --- a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java @@ -194,7 +194,7 @@ public void activityReportTest() { assertEquals(acvrs, result); } - @Test() + @Test(groups = {"localonly"}) public void activityReportTestBig() { List contest_info = noisyContestSetup(); Tribute tribute; @@ -477,7 +477,7 @@ public void testAtPositionTributes() { assertEquals(CastVoteRecordQueries.atPosition(tributes), expected); } - @Test + @Test(enabled = false) public void testPositionAtLots() { List contest_info = noisyContestSetup(); Tribute tribute; @@ -581,7 +581,7 @@ public void testMaxRevisionDBError() { assertEquals((Long)0L, CastVoteRecordQueries.maxRevision(cvr)); } - @Test() + @Test(enabled = false) public void testResultsReportBig() { List contest_info = noisyContestSetup(); Tribute tribute; From 7781cb8c0356b4bdd63ac5f51d9fb08c33420269 Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Fri, 21 Jun 2024 16:41:32 -0400 Subject: [PATCH 16/22] Trying to see if a different os makes a difference --- .github/workflows/maven.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 0d1372e1..a00b12c3 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -17,7 +17,7 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: macos-latest steps: - uses: actions/checkout@v4 From 448c373b9926882eb368766e82b565dda9a22b48 Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Fri, 21 Jun 2024 16:47:11 -0400 Subject: [PATCH 17/22] nope, that wasn't it --- .github/workflows/maven.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index a00b12c3..0d1372e1 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -17,7 +17,7 @@ on: jobs: build: - runs-on: macos-latest + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 From db25f1263333fec25927cda6d3ec87cea51d461a Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Fri, 21 Jun 2024 16:53:50 -0400 Subject: [PATCH 18/22] somehow StartAuditRoundTest got reverted to non testcontainers? --- .../corla/endpoint/StartAuditRoundTest.java | 51 ++++++++++++++----- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/endpoint/StartAuditRoundTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/endpoint/StartAuditRoundTest.java index 50fc2d73..7801265d 100644 --- a/server/eclipse-project/src/test/java/us/freeandfair/corla/endpoint/StartAuditRoundTest.java +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/endpoint/StartAuditRoundTest.java @@ -2,9 +2,8 @@ import static org.testng.Assert.assertEquals; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testng.annotations.*; import us.freeandfair.corla.asm.CountyDashboardASM; import us.freeandfair.corla.model.County; @@ -12,24 +11,52 @@ import us.freeandfair.corla.persistence.Persistence; import us.freeandfair.corla.query.Setup; +import java.util.Properties; + @Test(groups = {"integration"}) public class StartAuditRoundTest { private StartAuditRoundTest() {}; + /** + * 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") + // .withInitScripts("corlaInit.sql","contest.sql"); + .withInitScript("SQL/corlaInitEmpty.sql"); + + @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(); + } + + @AfterClass + public static void afterAll() { + postgres.stop(); + } - @BeforeTest() - public void setUp() { - Setup.setProperties(); + @BeforeMethod + public static void beforeEach() { Persistence.beginTransaction(); } - @AfterTest() - public void tearDown() { - try { - Persistence.rollbackTransaction(); - } catch (Exception e) { - } + @AfterMethod + public static void afterEach() { + Persistence.rollbackTransaction(); } // this test doesn't do much yet From cd456ddd2f22465cff19ba50cfb3c31ac5e496f0 Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Sat, 22 Jun 2024 17:38:38 -0400 Subject: [PATCH 19/22] Moving to @michelleblom's abstraction for db setup --- .../controllers/BallotSelectionTest.java | 45 +------- .../corla/endpoint/StartAuditRoundTest.java | 44 +------ .../corla/query/AdministratorQueriesTest.java | 38 +----- .../query/BallotManifestInfoQueriesTest.java | 38 +----- .../query/CastVoteRecordQueriesTest.java | 42 +------ .../corla/query/ContestQueriesTest.java | 40 +------ .../corla/query/ContestResultQueriesTest.java | 43 +------ .../corla/query/ExportQueriesTest.java | 48 +------- .../corla/util/TestClassWithDatabase.java | 109 ++++++++++++++++++ 9 files changed, 128 insertions(+), 319 deletions(-) create mode 100644 server/eclipse-project/src/test/java/us/freeandfair/corla/util/TestClassWithDatabase.java diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/controllers/BallotSelectionTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/controllers/BallotSelectionTest.java index cdf6dee5..0998ba1b 100644 --- a/server/eclipse-project/src/test/java/us/freeandfair/corla/controllers/BallotSelectionTest.java +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/controllers/BallotSelectionTest.java @@ -31,56 +31,15 @@ import org.hibernate.Session; import us.freeandfair.corla.query.CastVoteRecordQueries; import us.freeandfair.corla.query.Setup; +import us.freeandfair.corla.util.TestClassWithDatabase; @Test(groups = {"integration"}) -public class BallotSelectionTest { +public class BallotSelectionTest extends TestClassWithDatabase { private BallotSelectionTest() {}; private Boolean return_cvr = true; - /** - * 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") - // .withInitScripts("corlaInit.sql","contest.sql"); - .withInitScript("SQL/corlaInitEmpty.sql"); - - @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(); - - } - - @AfterClass - public static void afterAll() { - postgres.stop(); - } - - @BeforeMethod - public static void beforeEach() { - Persistence.beginTransaction(); - } - - @AfterMethod - public static void afterEach() { - Persistence.rollbackTransaction(); - } - @Test() public void testAuditedPrefixLengthWithNone() { List cvrIds = new ArrayList<>(); diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/endpoint/StartAuditRoundTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/endpoint/StartAuditRoundTest.java index 7801265d..b645ff0d 100644 --- a/server/eclipse-project/src/test/java/us/freeandfair/corla/endpoint/StartAuditRoundTest.java +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/endpoint/StartAuditRoundTest.java @@ -10,54 +10,14 @@ import us.freeandfair.corla.model.CountyDashboard; import us.freeandfair.corla.persistence.Persistence; import us.freeandfair.corla.query.Setup; +import us.freeandfair.corla.util.TestClassWithDatabase; import java.util.Properties; @Test(groups = {"integration"}) -public class StartAuditRoundTest { +public class StartAuditRoundTest extends TestClassWithDatabase { private StartAuditRoundTest() {}; - /** - * 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") - // .withInitScripts("corlaInit.sql","contest.sql"); - .withInitScript("SQL/corlaInitEmpty.sql"); - - @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(); - - } - - @AfterClass - public static void afterAll() { - postgres.stop(); - } - - @BeforeMethod - public static void beforeEach() { - Persistence.beginTransaction(); - } - - @AfterMethod - public static void afterEach() { - Persistence.rollbackTransaction(); - } // this test doesn't do much yet @Test() diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/AdministratorQueriesTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/AdministratorQueriesTest.java index 84307d94..241ee9c0 100644 --- a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/AdministratorQueriesTest.java +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/AdministratorQueriesTest.java @@ -5,6 +5,7 @@ import us.freeandfair.corla.model.Administrator; import us.freeandfair.corla.model.County; import us.freeandfair.corla.persistence.Persistence; +import us.freeandfair.corla.util.TestClassWithDatabase; import java.util.Properties; @@ -12,42 +13,7 @@ import static org.testng.AssertJUnit.assertNull; @Test -public class AdministratorQueriesTest { - - /** - * Container for the mock-up database. - */ - private static PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:15-alpine") - .withDatabaseName("corla") - .withUsername("corlaadmin") - .withPassword("corlasecret") - .withInitScript("SQL/corlaInitEmpty.sql"); - - @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); - } - @BeforeMethod - public static void beforeEach() { - Persistence.beginTransaction(); - } - - @AfterMethod - public static void afterEach() { - Persistence.rollbackTransaction(); - } - - @AfterClass - public static void afterall() { - postgres.stop(); - } +public class AdministratorQueriesTest extends TestClassWithDatabase { @Test public void testByUsernameState() { diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/BallotManifestInfoQueriesTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/BallotManifestInfoQueriesTest.java index 6886749c..c51a0812 100644 --- a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/BallotManifestInfoQueriesTest.java +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/BallotManifestInfoQueriesTest.java @@ -6,6 +6,7 @@ import org.testng.annotations.*; import us.freeandfair.corla.model.*; import us.freeandfair.corla.persistence.Persistence; +import us.freeandfair.corla.util.TestClassWithDatabase; import javax.swing.text.html.Option; import java.util.*; @@ -14,42 +15,7 @@ import static org.testng.AssertJUnit.assertEquals; @Test -public class BallotManifestInfoQueriesTest { - - /** - * Container for the mock-up database. - */ - private static PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:15-alpine") - .withDatabaseName("corla") - .withUsername("corlaadmin") - .withPassword("corlasecret") - .withInitScript("SQL/corlaInitEmpty.sql"); - - @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); - } - @BeforeMethod - public static void beforeEach() { - Persistence.beginTransaction(); - } - - @AfterMethod - public static void afterEach() { - Persistence.rollbackTransaction(); - } - - @AfterClass - public static void afterall() { - postgres.stop(); - } +public class BallotManifestInfoQueriesTest extends TestClassWithDatabase { public BallotManifestInfo setupBMI() { return new BallotManifestInfo(1L, diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java index 042fb256..d2e26ee1 100644 --- a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java @@ -23,48 +23,10 @@ import us.freeandfair.corla.model.*; import us.freeandfair.corla.persistence.Persistence; import us.freeandfair.corla.query.Setup; +import us.freeandfair.corla.util.TestClassWithDatabase; @Test(groups = {"integration"}) -public class CastVoteRecordQueriesTest { - - /** - * Container for the mock-up database. - */ - private static PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:15-alpine") - .withDatabaseName("corla") - .withUsername("corlaadmin") - .withPassword("corlasecret") - .withInitScript("SQL/corlaInitEmpty.sql"); - - @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); - } - @BeforeMethod - public static void beforeEach() { - Persistence.beginTransaction(); - } - - @AfterMethod - public static void afterEach() { - try { - Persistence.rollbackTransaction(); - } catch (IllegalStateException e) { - // Sometimes our tests intentionally kill the DB. - } - } - - @AfterClass - public static void afterall() { - postgres.stop(); - } +public class CastVoteRecordQueriesTest extends TestClassWithDatabase { public List noisyContestSetup(){ return noisyContestSetup(1L); diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ContestQueriesTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ContestQueriesTest.java index 7a79e592..504ba437 100644 --- a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ContestQueriesTest.java +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ContestQueriesTest.java @@ -8,6 +8,7 @@ import us.freeandfair.corla.model.*; import us.freeandfair.corla.model.Choice; import us.freeandfair.corla.persistence.Persistence; +import us.freeandfair.corla.util.TestClassWithDatabase; import java.awt.*; import java.util.*; @@ -16,42 +17,7 @@ import static org.testng.Assert.*; @Test(groups = {"integration"}) -public class ContestQueriesTest { - - /** - * Container for the mock-up database. - */ - private static PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:15-alpine") - .withDatabaseName("corla") - .withUsername("corlaadmin") - .withPassword("corlasecret") - .withInitScript("SQL/corlaInitEmpty.sql"); - - @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); - } - @BeforeMethod - public static void beforeEach() { - Persistence.beginTransaction(); - } - - @AfterMethod - public static void afterEach() { - Persistence.rollbackTransaction(); - } - - @AfterClass - public static void afterall() { - postgres.stop(); - } +public class ContestQueriesTest extends TestClassWithDatabase { public County countySetup() { return countySetup(1L); @@ -158,7 +124,7 @@ public void testForCountyNull() { } - @Test + @Test() public void testDBError() { // Close the database Persistence.commitTransaction(); diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ContestResultQueriesTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ContestResultQueriesTest.java index 15bff298..963cc926 100644 --- a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ContestResultQueriesTest.java +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ContestResultQueriesTest.java @@ -14,49 +14,10 @@ import us.freeandfair.corla.model.ContestResult; import us.freeandfair.corla.model.Contest; import us.freeandfair.corla.model.County; +import us.freeandfair.corla.util.TestClassWithDatabase; @Test(groups = {"integration"}) -public class ContestResultQueriesTest { - - /** - * 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") - // .withInitScripts("corlaInit.sql","contest.sql"); - .withInitScript("SQL/corlaInitEmpty.sql"); - - @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); - - } - - @AfterClass - public static void afterAll() { - postgres.stop(); - } - - @BeforeMethod - public static void beforeEach() { - Persistence.beginTransaction(); - } - - @AfterMethod - public static void afterEach() { Persistence.rollbackTransaction(); } - +public class ContestResultQueriesTest extends TestClassWithDatabase { @Test() public void findOrCreateTest() { diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ExportQueriesTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ExportQueriesTest.java index 2d10a145..2c02f183 100644 --- a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ExportQueriesTest.java +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ExportQueriesTest.java @@ -4,67 +4,27 @@ import java.util.*; -import org.junit.Ignore; import org.testcontainers.containers.PostgreSQLContainer; import org.testng.annotations.*; import static org.testng.Assert.*; import us.freeandfair.corla.persistence.Persistence; -import us.freeandfair.corla.query.Setup; -import us.freeandfair.corla.query.ExportQueries; - -import org.hibernate.Session; -import org.hibernate.query.Query; -@Test(groups = {"integration"}) -public class ExportQueriesTest { - - /** - * 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") - // .withInitScripts("corlaInit.sql","contest.sql"); - .withInitScript("SQL/corlaInitEmpty.sql"); - - @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(); +import org.hibernate.Session; +import us.freeandfair.corla.util.TestClassWithDatabase; - } - @AfterClass - public static void afterAll() { - postgres.stop(); - } +@Test(groups = {"integration"}) +public class ExportQueriesTest extends TestClassWithDatabase { @BeforeMethod public static void beforeEach() { - Persistence.beginTransaction(); insertSeed(); } - @AfterMethod - public static void afterEach() { - Persistence.rollbackTransaction(); - } - private static void insertSeed() { final Session s = Persistence.currentSession(); String query = "insert into dos_dashboard (id,seed) values (99,'1234');"; diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/util/TestClassWithDatabase.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/util/TestClassWithDatabase.java new file mode 100644 index 00000000..a3c8ae94 --- /dev/null +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/util/TestClassWithDatabase.java @@ -0,0 +1,109 @@ +/* +Democracy Developers IRV extensions to colorado-rla. + +@copyright 2024 Colorado Department of State + +These IRV extensions are designed to connect to a running instance of the raire +service (https://github.com/DemocracyDevelopers/raire-service), in order to +generate assertions that can be audited using colorado-rla. + +The colorado-rla IRV extensions are free software: you can redistribute it and/or modify it under the terms +of the GNU Affero General Public License as published by the Free Software Foundation, either +version 3 of the License, or (at your option) any later version. + +The colorado-rla IRV extensions are distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +See the GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License along with +raire-service. If not, see . +*/ + +package us.freeandfair.corla.util; + +import java.util.Properties; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testng.annotations.AfterClass; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import us.freeandfair.corla.persistence.Persistence; + +/** + * This class is designed to be extended by any test class that needs to interact with a test + * instantiation of the colorado-rla database. It provides convenience methods for instantiating + * a postgres container (initialised with one a given SQL script) and hibernate properties. + */ +public abstract class TestClassWithDatabase { + + + /** + * Container for the mock-up database. + */ + static PostgreSQLContainer postgres = createTestContainer(); + + + @BeforeClass + public static void beforeAll() { + postgres.start(); + Persistence.setProperties(createHibernateProperties(postgres)); + } + + @AfterClass + public static void afterAll() { + postgres.stop(); + } + + /** + * Begin a new transaction before each test method in the class is run. + */ + @BeforeMethod + public static void beforeTest(){ + Persistence.beginTransaction(); + } + + /** + * Rollback any changes to the (test) database after each test method is run. + */ + @AfterMethod + public static void afterTest(){ + try { + Persistence.rollbackTransaction(); + } catch (Exception ignored) { + } + } + + /** + * Create and return a postgres test container for the purposes of testing functionality that + * interacts with the database. + * @return a postgres test container representing a test database. + */ + public static PostgreSQLContainer createTestContainer() { + return 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. + // .withExposedPorts(port) + .withDatabaseName("corla") + .withUsername("corlaadmin") + .withPassword("corlasecret") + .withInitScript("SQL/corla.sql"); + } + + /** + * Create and return a hibernate properties object for use in testing functionality that + * interacts with the database. + * @param postgres Postgres test container representing a test version of the database. + * @return Hibernate persistence properties. + */ + public static Properties createHibernateProperties(PostgreSQLContainer postgres) { + 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"); + + return hibernateProperties; + } + +} \ No newline at end of file From 48d079f2c0e63b193bd48646168c8ca433075a32 Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Sat, 22 Jun 2024 17:41:58 -0400 Subject: [PATCH 20/22] adding big tests back in since they weren't the problem --- .../us/freeandfair/corla/query/CastVoteRecordQueriesTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java index d2e26ee1..1d06e9c1 100644 --- a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/CastVoteRecordQueriesTest.java @@ -439,7 +439,7 @@ public void testAtPositionTributes() { assertEquals(CastVoteRecordQueries.atPosition(tributes), expected); } - @Test(enabled = false) + @Test public void testPositionAtLots() { List contest_info = noisyContestSetup(); Tribute tribute; @@ -543,7 +543,7 @@ public void testMaxRevisionDBError() { assertEquals((Long)0L, CastVoteRecordQueries.maxRevision(cvr)); } - @Test(enabled = false) + @Test public void testResultsReportBig() { List contest_info = noisyContestSetup(); Tribute tribute; From 940ac000a8d9cb991d316ea2fe7d4b8a41ceb7c7 Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Sat, 22 Jun 2024 17:58:57 -0400 Subject: [PATCH 21/22] starting a stub for this for now, gonna need to write all the other tests then come back for this one... --- .../query/ComparisonAuditQueriesTest.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 server/eclipse-project/src/test/java/us/freeandfair/corla/query/ComparisonAuditQueriesTest.java diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ComparisonAuditQueriesTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ComparisonAuditQueriesTest.java new file mode 100644 index 00000000..2ccf69bb --- /dev/null +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ComparisonAuditQueriesTest.java @@ -0,0 +1,36 @@ +package us.freeandfair.corla.query; + +import org.testng.annotations.Test; +import us.freeandfair.corla.math.Audit; +import us.freeandfair.corla.model.AuditReason; +import us.freeandfair.corla.model.ComparisonAudit; +import us.freeandfair.corla.model.ContestResult; +import us.freeandfair.corla.persistence.Persistence; +import us.freeandfair.corla.util.TestClassWithDatabase; + +import java.math.BigDecimal; +import java.util.ArrayList; + +import static org.testng.Assert.assertEquals; + +public class ComparisonAuditQueriesTest extends TestClassWithDatabase { + + @Test + public void testSortedList() { + + ContestResult contestResult = new ContestResult("Test Contest"); + BigDecimal riskLimit = new BigDecimal(0.05); + BigDecimal dilutedMargin = new BigDecimal(.2); + + contestResult.setDilutedMargin(dilutedMargin); + + Persistence.saveOrUpdate(contestResult); + ComparisonAudit ca = new ComparisonAudit(contestResult, riskLimit, dilutedMargin, Audit.GAMMA, AuditReason.STATE_WIDE_CONTEST); + + Persistence.saveOrUpdate(ca); + assertEquals(new ArrayList<>(), ComparisonAuditQueries.sortedList()); + + + + } +} From c334d6071eb28b7b5182a69d7ef051538dc48363 Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Sun, 23 Jun 2024 21:09:12 -0400 Subject: [PATCH 22/22] Disabling ComparisonAuditQueriesTest because it's unfinished --- .../us/freeandfair/corla/query/ComparisonAuditQueriesTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ComparisonAuditQueriesTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ComparisonAuditQueriesTest.java index 2ccf69bb..aaeb7d5d 100644 --- a/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ComparisonAuditQueriesTest.java +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/query/ComparisonAuditQueriesTest.java @@ -15,7 +15,7 @@ public class ComparisonAuditQueriesTest extends TestClassWithDatabase { - @Test + @Test(enabled = false) public void testSortedList() { ContestResult contestResult = new ContestResult("Test Contest");