From 47806409d968703eac241ccbdab3ed1bd5217ee2 Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Wed, 2 Oct 2024 22:41:40 -0400 Subject: [PATCH 1/8] Starting work on testing the data model --- .../corla/model/AdministratorTest.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 server/eclipse-project/src/test/java/us/freeandfair/corla/model/AdministratorTest.java diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AdministratorTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AdministratorTest.java new file mode 100644 index 00000000..acaa13f0 --- /dev/null +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AdministratorTest.java @@ -0,0 +1,38 @@ +package us.freeandfair.corla.model; + +import org.testng.annotations.Test; +import us.freeandfair.corla.persistence.Persistence; +import us.freeandfair.corla.query.AdministratorQueries; +import us.freeandfair.corla.util.TestClassWithDatabase; + +import static org.testng.AssertJUnit.*; + +@Test +public class AdministratorTest extends TestClassWithDatabase { + + @Test + public static void testGettersAndSetters() { + String expectedUsername = "testname"; + Administrator.AdministratorType expectedType = Administrator.AdministratorType.STATE; + String expectedFullname = "fulltestname"; + Administrator admin = new Administrator(expectedUsername, + expectedType, + expectedFullname, + null); + + Persistence.saveOrUpdate(admin); + + // this is a database constraint + assertNotNull(admin.id()); + + Long expectedID = 42L; + admin.setID(expectedID); + assertEquals(expectedID, admin.id()); + + assertEquals(expectedUsername, admin.username()); + assertEquals(expectedFullname, admin.fullName()); + assertEquals(expectedType, admin.type()); + assertNull(admin.county()); + + } +} From 7e33542bafde85dd6ddabdd8a41c9b601599bcdc Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Thu, 3 Oct 2024 16:29:10 -0400 Subject: [PATCH 2/8] Continuing to work on models testing, but docker doens't like me anymore --- .../corla/model/Administrator.java | 30 +++++++++++++++++-- .../corla/model/AdministratorTest.java | 26 +++++++++++++++- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/server/eclipse-project/src/main/java/us/freeandfair/corla/model/Administrator.java b/server/eclipse-project/src/main/java/us/freeandfair/corla/model/Administrator.java index d3b23737..c9461117 100644 --- a/server/eclipse-project/src/main/java/us/freeandfair/corla/model/Administrator.java +++ b/server/eclipse-project/src/main/java/us/freeandfair/corla/model/Administrator.java @@ -14,6 +14,7 @@ import static us.freeandfair.corla.util.EqualsHashcodeHelper.*; import java.io.Serializable; +import java.time.Clock; import java.time.Instant; import javax.persistence.Cacheable; @@ -106,6 +107,11 @@ public class Administrator implements PersistentEntity, Serializable { * The last logout time. */ private Instant my_last_logout_time; + + /** + * A clock that is only used for testing + */ + private Clock my_clock; /** * Constructs a new Administrator with default values. @@ -133,7 +139,25 @@ public Administrator(final String the_username, my_full_name = the_full_name; my_county = the_county; } - + + /** + * Same as above, but injects a clock for use with testing. + * + * @param the_username + * @param the_type + * @param the_full_name + * @param the_county + * @param clock + */ + public Administrator(final String the_username, + final AdministratorType the_type, + final String the_full_name, + final County the_county, + Clock clock) { + this(the_username, the_type, the_full_name, the_county); + my_clock = clock; + } + /** * {@inheritDoc} */ @@ -197,7 +221,7 @@ public Instant lastLoginTime() { * Updates the last login time to the current time. */ public void updateLastLoginTime() { - my_last_login_time = Instant.now(); + my_last_login_time = Instant.now(my_clock); } /** @@ -211,7 +235,7 @@ public Instant lastLogoutTime() { * Updates the last logout time to the current time. */ public void updateLastLogoutTime() { - my_last_logout_time = Instant.now(); + my_last_logout_time = Instant.now(my_clock); } /** diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AdministratorTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AdministratorTest.java index acaa13f0..cbd0fe89 100644 --- a/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AdministratorTest.java +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AdministratorTest.java @@ -5,6 +5,9 @@ import us.freeandfair.corla.query.AdministratorQueries; import us.freeandfair.corla.util.TestClassWithDatabase; +import java.time.Clock; +import java.time.Instant; + import static org.testng.AssertJUnit.*; @Test @@ -15,11 +18,16 @@ public static void testGettersAndSetters() { String expectedUsername = "testname"; Administrator.AdministratorType expectedType = Administrator.AdministratorType.STATE; String expectedFullname = "fulltestname"; + + // Make the clock stuck + Clock testClock = Clock.fixed(Instant.now(), Clock.systemDefaultZone().getZone()); Administrator admin = new Administrator(expectedUsername, expectedType, expectedFullname, - null); + null, + testClock); + assertNull(admin.id()); Persistence.saveOrUpdate(admin); // this is a database constraint @@ -32,7 +40,23 @@ public static void testGettersAndSetters() { assertEquals(expectedUsername, admin.username()); assertEquals(expectedFullname, admin.fullName()); assertEquals(expectedType, admin.type()); + assertNull(admin.version()); assertNull(admin.county()); + // Because we're using a stopped clock, this time will be the same for both + Instant expectedTime = testClock.instant(); + assertNull(admin.lastLoginTime()); + admin.updateLastLoginTime(); + assertEquals(expectedTime, admin.lastLoginTime()); + + assertNull(admin.lastLogoutTime()); + admin.updateLastLogoutTime(); + assertEquals(expectedTime, admin.lastLogoutTime()); + + String expected_string = "Administrator [username=" + expectedUsername + ", type=" + + expectedType+ ", full_name=" + expectedFullname+ ", county=" + + null + ", last_login_time=" + expectedTime + + ", last_logout_time=" + expectedTime + "]"; + } } From e3e1468cbcd4b7df2e9eba3f2a5a0e61f5b63e80 Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Tue, 3 Dec 2024 14:46:32 -0500 Subject: [PATCH 3/8] Administrator model has 100% coverage --- .../corla/model/Administrator.java | 19 ++------- .../corla/model/AdministratorTest.java | 40 ++++++++++++++++++- 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/server/eclipse-project/src/main/java/us/freeandfair/corla/model/Administrator.java b/server/eclipse-project/src/main/java/us/freeandfair/corla/model/Administrator.java index c9461117..2f54b9e3 100644 --- a/server/eclipse-project/src/main/java/us/freeandfair/corla/model/Administrator.java +++ b/server/eclipse-project/src/main/java/us/freeandfair/corla/model/Administrator.java @@ -17,21 +17,7 @@ import java.time.Clock; import java.time.Instant; -import javax.persistence.Cacheable; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; -import javax.persistence.Version; +import javax.persistence.*; import us.freeandfair.corla.persistence.PersistentEntity; @@ -111,6 +97,7 @@ public class Administrator implements PersistentEntity, Serializable { /** * A clock that is only used for testing */ + @Transient private Clock my_clock; /** @@ -153,7 +140,7 @@ public Administrator(final String the_username, final AdministratorType the_type, final String the_full_name, final County the_county, - Clock clock) { + final Clock clock) { this(the_username, the_type, the_full_name, the_county); my_clock = clock; } diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AdministratorTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AdministratorTest.java index cbd0fe89..fdb1ccb7 100644 --- a/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AdministratorTest.java +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AdministratorTest.java @@ -1,5 +1,7 @@ package us.freeandfair.corla.model; +import com.github.tomakehurst.wiremock.core.Admin; +import org.testng.AssertJUnit; import org.testng.annotations.Test; import us.freeandfair.corla.persistence.Persistence; import us.freeandfair.corla.query.AdministratorQueries; @@ -8,7 +10,9 @@ import java.time.Clock; import java.time.Instant; +import static org.testng.Assert.assertNotEquals; import static org.testng.AssertJUnit.*; +import static us.freeandfair.corla.util.EqualsHashcodeHelper.nullableHashCode; @Test public class AdministratorTest extends TestClassWithDatabase { @@ -27,7 +31,9 @@ public static void testGettersAndSetters() { null, testClock); + assertNull(admin.id()); + assertNull(admin.version()); Persistence.saveOrUpdate(admin); // this is a database constraint @@ -40,7 +46,6 @@ public static void testGettersAndSetters() { assertEquals(expectedUsername, admin.username()); assertEquals(expectedFullname, admin.fullName()); assertEquals(expectedType, admin.type()); - assertNull(admin.version()); assertNull(admin.county()); // Because we're using a stopped clock, this time will be the same for both @@ -58,5 +63,38 @@ public static void testGettersAndSetters() { null + ", last_login_time=" + expectedTime + ", last_logout_time=" + expectedTime + "]"; + assertEquals(expected_string, admin.toString()); + } + + @Test + public static void testEquality() { + String firstAdminUsername = "first"; + String secondAdminUsername = "second"; + Administrator.AdministratorType expectedType = Administrator.AdministratorType.STATE; + String expectedFullname = "fulltestname"; + + Administrator firstAdmin = new Administrator(firstAdminUsername, expectedType, expectedFullname, null); + Administrator secondAdmin = new Administrator(secondAdminUsername, expectedType, expectedFullname, null); + + assertTrue(firstAdmin.equals(firstAdmin)); + assertFalse(firstAdmin.equals(secondAdmin)); + + // Now test that non-admin objects result in false + assertFalse(firstAdmin.equals(firstAdminUsername)); + } + + @Test + public static void testHash() { + String expectedUsername = "testname"; + Administrator.AdministratorType expectedType = Administrator.AdministratorType.STATE; + String expectedFullname = "fulltestname"; + + Administrator admin = new Administrator(expectedUsername, + expectedType, + expectedFullname, + null); + + assertEquals(admin.hashCode(), nullableHashCode(expectedUsername)); + } } From 40fbef146a90d171e6b9945a8c07514ad293ca19 Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Tue, 3 Dec 2024 16:11:14 -0500 Subject: [PATCH 4/8] AuditBoard tests - also removing some unneeded imports --- .../corla/query/ExportQueries.java | 2 - .../corla/model/AdministratorTest.java | 4 -- .../corla/model/AuditBoardTest.java | 59 +++++++++++++++++++ 3 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 server/eclipse-project/src/test/java/us/freeandfair/corla/model/AuditBoardTest.java diff --git a/server/eclipse-project/src/main/java/us/freeandfair/corla/query/ExportQueries.java b/server/eclipse-project/src/main/java/us/freeandfair/corla/query/ExportQueries.java index d7772a84..a290ca67 100644 --- a/server/eclipse-project/src/main/java/us/freeandfair/corla/query/ExportQueries.java +++ b/server/eclipse-project/src/main/java/us/freeandfair/corla/query/ExportQueries.java @@ -11,10 +11,8 @@ import java.nio.charset.StandardCharsets; import java.util.*; -import java.util.stream.Collectors; import java.util.stream.Stream; -import au.org.democracydevelopers.corla.endpoint.AbstractAllIrvEndpoint; import au.org.democracydevelopers.corla.model.ContestType; import au.org.democracydevelopers.corla.model.GenerateAssertionsSummary; import au.org.democracydevelopers.corla.model.IRVComparisonAudit; diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AdministratorTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AdministratorTest.java index fdb1ccb7..899b1c28 100644 --- a/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AdministratorTest.java +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AdministratorTest.java @@ -1,16 +1,12 @@ package us.freeandfair.corla.model; -import com.github.tomakehurst.wiremock.core.Admin; -import org.testng.AssertJUnit; import org.testng.annotations.Test; import us.freeandfair.corla.persistence.Persistence; -import us.freeandfair.corla.query.AdministratorQueries; import us.freeandfair.corla.util.TestClassWithDatabase; import java.time.Clock; import java.time.Instant; -import static org.testng.Assert.assertNotEquals; import static org.testng.AssertJUnit.*; import static us.freeandfair.corla.util.EqualsHashcodeHelper.nullableHashCode; diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AuditBoardTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AuditBoardTest.java new file mode 100644 index 00000000..6e8a3633 --- /dev/null +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AuditBoardTest.java @@ -0,0 +1,59 @@ +package us.freeandfair.corla.model; + +import org.testng.annotations.Test; +import us.freeandfair.corla.util.TestClassWithDatabase; + +import java.lang.reflect.Array; +import java.time.Instant; + +import java.util.ArrayList; + +import static org.testng.AssertJUnit.*; +import static us.freeandfair.corla.util.EqualsHashcodeHelper.nullableHashCode; + +public class AuditBoardTest extends TestClassWithDatabase { + + @Test + public static void testGettersAndSetters () { + + AuditBoard defaultAB = new AuditBoard(); + assertEquals(new ArrayList(), defaultAB.members()); + assertNull(defaultAB.signInTime()); + assertNull(defaultAB.signOutTime()); + + ArrayList electors = new ArrayList<>(); + + electors.add(new Elector("firstname", "lastname", "party")); + + // TODO: do we want a stopped clock here too? + Instant sign_in_time = Instant.now(); + + AuditBoard ab = new AuditBoard(electors, sign_in_time); + + assertEquals(electors, ab.members()); + + assertEquals(sign_in_time, ab.signInTime()); + + Instant sign_out_time = Instant.now(); + + ab.setSignOutTime(sign_out_time); + assertEquals(sign_out_time, ab.signOutTime()); + + String expectedToString = "AuditBoard [members=" + electors + ", sign_in_time=" + + sign_in_time + ", sign_out_time=" + sign_out_time + "]"; + + assertEquals(expectedToString, ab.toString()); + + assertEquals(nullableHashCode(sign_in_time), ab.hashCode()); + + assertFalse(ab.equals("Not an auditboard")); + ArrayList otherElectors = new ArrayList<>(); + otherElectors.add(new Elector("otherfirstname", "otherlastname", "otherparty")); + + AuditBoard otherAB = new AuditBoard(otherElectors, sign_in_time); + + assertFalse(ab.equals(otherAB)); + assertTrue(ab.equals(ab)); + + } +} From 76351853bc90614bcae6d84336c0dc32881608e6 Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Tue, 3 Dec 2024 16:33:20 -0500 Subject: [PATCH 5/8] Starting work on revamping AuditInfoTest --- .../java/us/freeandfair/corla/model/AuditInfoTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AuditInfoTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AuditInfoTest.java index 7ddad3be..147cffd2 100644 --- a/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AuditInfoTest.java +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AuditInfoTest.java @@ -23,8 +23,9 @@ import us.freeandfair.corla.csv.ContestNameParser; import us.freeandfair.corla.json.VersionExclusionStrategy; +import us.freeandfair.corla.util.TestClassWithDatabase; -public class AuditInfoTest { +public class AuditInfoTest extends TestClassWithDatabase { public Gson gson; @BeforeClass @@ -33,7 +34,7 @@ public void AuditInfoTest() { } @Test() - private void canonicalContestsTest() { + private void testCanonicalContests() { Map> contestMap = new TreeMap>(); Set contests = new HashSet(); @@ -79,7 +80,7 @@ public String contestsFromJSON(final String json) { // @SuppressWarnings("PMD.DoNotUseThreads") // @SuppressFBWarnings(value = {"URF_UNREAD_FIELD"}, // justification = "JSON blobs are big") - public void parsingTest() { + public void testParsing() { final String json = "{\"election_date\":\"2018-07-31T06:00:00.000Z\",\"election_type\":\"coordinated\",\"public_meeting_date\":\"2018-08-07T06:00:00.000Z\",\"risk_limit\":0.05,\"upload_file\":[{\"preview\":\"blob:http://localhost:3000/54a4f865-9d2a-b442-881a-8630050977dc\",\"contents\":'\"CountyName\",\"ContestName\"\n\"Boulder\",\"Kombucha - DEM\"\n\"Boulder\",\"Kale - DEM\"\n\"Denver\",\"IPA - DEM\"\n\"Denver\",\"Porter - REP\"\n\"Chaffee\",\"Hooligan Race - DEM\"\n\"Chaffee\",\"Pole Pedal Paddle - DEM\"\n\"Chaffee\",\"Gunbarrel Challenge - REP\"\n'}]}"; String contests = contestsFromJSON(json); From 0ebf4b798807cdff13ce64272671009a4e1ea4fe Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Thu, 5 Dec 2024 14:08:49 -0500 Subject: [PATCH 6/8] Handling null clock case --- .../us/freeandfair/corla/model/Administrator.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/server/eclipse-project/src/main/java/us/freeandfair/corla/model/Administrator.java b/server/eclipse-project/src/main/java/us/freeandfair/corla/model/Administrator.java index 2f54b9e3..3129b458 100644 --- a/server/eclipse-project/src/main/java/us/freeandfair/corla/model/Administrator.java +++ b/server/eclipse-project/src/main/java/us/freeandfair/corla/model/Administrator.java @@ -208,7 +208,11 @@ public Instant lastLoginTime() { * Updates the last login time to the current time. */ public void updateLastLoginTime() { - my_last_login_time = Instant.now(my_clock); + if (my_clock == null) { + my_last_login_time = Instant.now(); + } else { + my_last_login_time = Instant.now(my_clock); + } } /** @@ -222,7 +226,11 @@ public Instant lastLogoutTime() { * Updates the last logout time to the current time. */ public void updateLastLogoutTime() { - my_last_logout_time = Instant.now(my_clock); + if (my_clock == null) { + my_last_login_time = Instant.now(); + } else { + my_last_login_time = Instant.now(my_clock); + } } /** From b1dbb99d213f86e96f121486463cb4f9caaf46e2 Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Thu, 5 Dec 2024 14:20:40 -0500 Subject: [PATCH 7/8] Fixing admin test so it has full coverage again --- .../corla/model/Administrator.java | 4 ++-- .../corla/model/AdministratorTest.java | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/server/eclipse-project/src/main/java/us/freeandfair/corla/model/Administrator.java b/server/eclipse-project/src/main/java/us/freeandfair/corla/model/Administrator.java index 3129b458..ab1ea86f 100644 --- a/server/eclipse-project/src/main/java/us/freeandfair/corla/model/Administrator.java +++ b/server/eclipse-project/src/main/java/us/freeandfair/corla/model/Administrator.java @@ -227,9 +227,9 @@ public Instant lastLogoutTime() { */ public void updateLastLogoutTime() { if (my_clock == null) { - my_last_login_time = Instant.now(); + my_last_logout_time = Instant.now(); } else { - my_last_login_time = Instant.now(my_clock); + my_last_logout_time = Instant.now(my_clock); } } diff --git a/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AdministratorTest.java b/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AdministratorTest.java index 899b1c28..66e4723c 100644 --- a/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AdministratorTest.java +++ b/server/eclipse-project/src/test/java/us/freeandfair/corla/model/AdministratorTest.java @@ -7,6 +7,7 @@ import java.time.Clock; import java.time.Instant; +import static org.testng.Assert.assertNotEquals; import static org.testng.AssertJUnit.*; import static us.freeandfair.corla.util.EqualsHashcodeHelper.nullableHashCode; @@ -60,6 +61,24 @@ public static void testGettersAndSetters() { ", last_logout_time=" + expectedTime + "]"; assertEquals(expected_string, admin.toString()); + + /** + * Now test an admin without a clock set. This means we can't test timing-specific things, + * but we can at least test that the code paths behave the way we expect. + */ + admin = new Administrator(expectedUsername, + expectedType, + expectedFullname, + null); + + assertNull(admin.lastLoginTime()); + assertNull(admin.lastLogoutTime()); + + admin.updateLastLoginTime(); + assertNotNull(admin.lastLoginTime()); + admin.updateLastLogoutTime(); + assertNotNull(admin.lastLogoutTime()); + assert(admin.lastLoginTime() != admin.lastLogoutTime()); } @Test From 0ddaa4f6aaf00ff4ca39fe35838fe1e9f408f8d8 Mon Sep 17 00:00:00 2001 From: Matt Bernhard Date: Tue, 10 Dec 2024 12:49:14 -0500 Subject: [PATCH 8/8] Adding more comments about the clock in admin --- .../src/main/java/us/freeandfair/corla/model/Administrator.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/eclipse-project/src/main/java/us/freeandfair/corla/model/Administrator.java b/server/eclipse-project/src/main/java/us/freeandfair/corla/model/Administrator.java index ab1ea86f..47084bb3 100644 --- a/server/eclipse-project/src/main/java/us/freeandfair/corla/model/Administrator.java +++ b/server/eclipse-project/src/main/java/us/freeandfair/corla/model/Administrator.java @@ -208,6 +208,7 @@ public Instant lastLoginTime() { * Updates the last login time to the current time. */ public void updateLastLoginTime() { + // If we're not testing, there will be no clock if (my_clock == null) { my_last_login_time = Instant.now(); } else { @@ -226,6 +227,7 @@ public Instant lastLogoutTime() { * Updates the last logout time to the current time. */ public void updateLastLogoutTime() { + // If we're not testing, there will be no clock if (my_clock == null) { my_last_logout_time = Instant.now(); } else {