Problem Description
Multiple tests are passing on macOS but failing on Windows due to differences in operating system timestamp resolution. The tests use isAfter() assertions to verify that updated timestamps are strictly greater than created or previous updated timestamps. On Windows, the timestamp resolution may be coarser, causing both timestamps to have the same value and making isAfter() assertions fail.
Affected Tests
Failing on Windows (5 tests):
-
IdentifiedObjectTest.java:144
- Test:
shouldUpdateModificationTimestampOnEntityUpdate
- Location:
src/test/java/org/greenbuttonalliance/espi/common/domain/common/IdentifiedObjectTest.java
- Assertion:
assertThat(updated.getUpdated()).isAfter(originalUpdated);
-
UsageSummaryRepositoryTest.java:539
- Test:
shouldUpdateTimestampsOnModification
- Location:
src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/UsageSummaryRepositoryTest.java
- Assertion:
assertThat(retrieved.get().getUpdated()).isAfter(retrieved.get().getCreated());
-
ResourceRepositoryTest.java:238
- Test: Timestamp update verification in resource test
- Location:
src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/ResourceRepositoryTest.java
- Assertion:
assertThat(retrievedResource.getUpdated()).isAfter(retrievedResource.getCreated());
-
CustomerAccountRepositoryTest.java:593
- Test:
shouldUpdateTimestampsOnModification
- Location:
src/test/java/org/greenbuttonalliance/espi/common/repositories/customer/CustomerAccountRepositoryTest.java
- Assertion:
assertThat(retrieved.get().getUpdated()).isAfter(retrieved.get().getCreated());
-
MeterRepositoryTest.java:615
- Test:
shouldUpdateTimestampsOnModification
- Location:
src/test/java/org/greenbuttonalliance/espi/common/repositories/customer/MeterRepositoryTest.java
- Assertion:
assertThat(retrieved.get().getUpdated()).isAfter(retrieved.get().getCreated());
Potentially Affected (date range logic, needs verification):
-
StatementRepositoryTest.java:200
- Test: Date range filtering with
isAfter() on business logic fields
- Assertion:
assertThat(results).allMatch(s -> s.getIssueDateTime().isAfter(cutoffDate));
-
StatementRepositoryTest.java:267
- Test: Date range filtering
- Assertion:
s.getIssueDateTime().isAfter(startDate) && s.getIssueDateTime().isBefore(endDate)
Root Cause
Timestamp Resolution Differences:
- macOS/Linux: Typically microsecond or nanosecond resolution
- Windows: May have millisecond resolution or use time slicing that results in identical timestamps
Test Pattern:
// Current problematic pattern:
UsageSummaryEntity saved = persistAndFlush(summary);
saved.setDescription("Updated");
UsageSummaryEntity updated = usageSummaryRepository.save(saved);
flushAndClear();
// Fails on Windows when both timestamps are identical:
assertThat(retrieved.get().getUpdated()).isAfter(retrieved.get().getCreated());
On Windows, both created and updated timestamps may have identical values when operations occur within the same time slice, causing isAfter() to return false.
Recommended Solution
Replace isAfter() with isAfterOrEqualTo() in all timestamp update tests. This change:
- ✅ Maintains the test's intent (verifying timestamp is set/updated)
- ✅ Works consistently across all platforms
- ✅ Still validates that JPA
@PreUpdate callbacks are functioning
- ✅ Accounts for the reality that updates can occur within the same timestamp unit
Example Fix:
// Before (fails on Windows):
assertThat(updated.getUpdated()).isAfter(originalUpdated);
// After (works on all platforms):
assertThat(updated.getUpdated()).isAfterOrEqualTo(originalUpdated);
For tests comparing updated vs created:
// Before (fails on Windows):
assertThat(retrieved.get().getUpdated()).isAfter(retrieved.get().getCreated());
// After (works on all platforms):
assertThat(retrieved.get().getUpdated()).isAfterOrEqualTo(retrieved.get().getCreated());
Implementation Plan
-
Update IdentifiedObjectTest.java (highest priority - base class test)
- Line 144: Change
isAfter(originalUpdated) to isAfterOrEqualTo(originalUpdated)
-
Update Repository Tests (timestamp update verification tests)
- UsageSummaryRepositoryTest.java:539
- ResourceRepositoryTest.java:238
- CustomerAccountRepositoryTest.java:593
- MeterRepositoryTest.java:615
- Change all
isAfter(created) to isAfterOrEqualTo(created)
-
Review StatementRepositoryTest.java (date range tests)
- Lines 200, 267: Analyze if these business logic tests are affected
- These may be testing different behavior (date filtering) and might not need changes
-
Verify All Tests Pass
- Run tests on macOS:
mvn test
- Run tests on Windows:
mvn test
- Confirm all 533 tests pass on both platforms
Additional Context
- Test framework: JUnit 5 with AssertJ assertions
- ORM: Hibernate 6.x with JPA
@PrePersist and @PreUpdate callbacks
- Database: H2 (test), MySQL 8.0+, PostgreSQL 15+
- All affected entities extend
IdentifiedObject which has automatic timestamp management
Acceptance Criteria
Related Information
- Similar cross-platform issues: This is a common problem with timestamp-based tests across operating systems
- AssertJ documentation: https://assertj.github.io/doc/#assertj-core-time
- Hibernate timestamp management:
@CreationTimestamp, @UpdateTimestamp annotations
Problem Description
Multiple tests are passing on macOS but failing on Windows due to differences in operating system timestamp resolution. The tests use
isAfter()assertions to verify thatupdatedtimestamps are strictly greater thancreatedor previousupdatedtimestamps. On Windows, the timestamp resolution may be coarser, causing both timestamps to have the same value and makingisAfter()assertions fail.Affected Tests
Failing on Windows (5 tests):
IdentifiedObjectTest.java:144
shouldUpdateModificationTimestampOnEntityUpdatesrc/test/java/org/greenbuttonalliance/espi/common/domain/common/IdentifiedObjectTest.javaassertThat(updated.getUpdated()).isAfter(originalUpdated);UsageSummaryRepositoryTest.java:539
shouldUpdateTimestampsOnModificationsrc/test/java/org/greenbuttonalliance/espi/common/repositories/usage/UsageSummaryRepositoryTest.javaassertThat(retrieved.get().getUpdated()).isAfter(retrieved.get().getCreated());ResourceRepositoryTest.java:238
src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/ResourceRepositoryTest.javaassertThat(retrievedResource.getUpdated()).isAfter(retrievedResource.getCreated());CustomerAccountRepositoryTest.java:593
shouldUpdateTimestampsOnModificationsrc/test/java/org/greenbuttonalliance/espi/common/repositories/customer/CustomerAccountRepositoryTest.javaassertThat(retrieved.get().getUpdated()).isAfter(retrieved.get().getCreated());MeterRepositoryTest.java:615
shouldUpdateTimestampsOnModificationsrc/test/java/org/greenbuttonalliance/espi/common/repositories/customer/MeterRepositoryTest.javaassertThat(retrieved.get().getUpdated()).isAfter(retrieved.get().getCreated());Potentially Affected (date range logic, needs verification):
StatementRepositoryTest.java:200
isAfter()on business logic fieldsassertThat(results).allMatch(s -> s.getIssueDateTime().isAfter(cutoffDate));StatementRepositoryTest.java:267
s.getIssueDateTime().isAfter(startDate) && s.getIssueDateTime().isBefore(endDate)Root Cause
Timestamp Resolution Differences:
Test Pattern:
On Windows, both
createdandupdatedtimestamps may have identical values when operations occur within the same time slice, causingisAfter()to return false.Recommended Solution
Replace
isAfter()withisAfterOrEqualTo()in all timestamp update tests. This change:@PreUpdatecallbacks are functioningExample Fix:
For tests comparing
updatedvscreated:Implementation Plan
Update IdentifiedObjectTest.java (highest priority - base class test)
isAfter(originalUpdated)toisAfterOrEqualTo(originalUpdated)Update Repository Tests (timestamp update verification tests)
isAfter(created)toisAfterOrEqualTo(created)Review StatementRepositoryTest.java (date range tests)
Verify All Tests Pass
mvn testmvn testAdditional Context
@PrePersistand@PreUpdatecallbacksIdentifiedObjectwhich has automatic timestamp managementAcceptance Criteria
Related Information
@CreationTimestamp,@UpdateTimestampannotations