diff --git a/src/main/java/de/avatic/lcc/repositories/NomenclatureRepository.java b/src/main/java/de/avatic/lcc/repositories/NomenclatureRepository.java index 5d9c2d0..357f189 100644 --- a/src/main/java/de/avatic/lcc/repositories/NomenclatureRepository.java +++ b/src/main/java/de/avatic/lcc/repositories/NomenclatureRepository.java @@ -12,12 +12,10 @@ public class NomenclatureRepository { private final JdbcTemplate jdbcTemplate; private final SqlDialectProvider dialectProvider; - private final EUTaxationApiService eUTaxationApiService; - public NomenclatureRepository(JdbcTemplate jdbcTemplate, SqlDialectProvider dialectProvider, EUTaxationApiService eUTaxationApiService) { + public NomenclatureRepository(JdbcTemplate jdbcTemplate, SqlDialectProvider dialectProvider) { this.jdbcTemplate = jdbcTemplate; this.dialectProvider = dialectProvider; - this.eUTaxationApiService = eUTaxationApiService; } public List searchHsCode(String search) { diff --git a/src/main/java/de/avatic/lcc/repositories/rates/ValidityPeriodRepository.java b/src/main/java/de/avatic/lcc/repositories/rates/ValidityPeriodRepository.java index 2405473..502b815 100644 --- a/src/main/java/de/avatic/lcc/repositories/rates/ValidityPeriodRepository.java +++ b/src/main/java/de/avatic/lcc/repositories/rates/ValidityPeriodRepository.java @@ -64,8 +64,8 @@ public class ValidityPeriodRepository { */ @Transactional public Optional getPeriodId(LocalDateTime validAt) { - String query = "SELECT id FROM validity_period WHERE ? BETWEEN start_date AND end_date"; - return Optional.ofNullable(jdbcTemplate.query(query, (rs) -> rs.next() ? rs.getInt("id") : null, validAt)); + String query = "SELECT id FROM validity_period WHERE start_date <= ? AND (end_date IS NULL OR end_date >= ?)"; + return Optional.ofNullable(jdbcTemplate.query(query, (rs) -> rs.next() ? rs.getInt("id") : null, validAt, validAt)); } /** diff --git a/src/test/java/de/avatic/lcc/repositories/country/CountryPropertyRepositoryIntegrationTest.java b/src/test/java/de/avatic/lcc/repositories/country/CountryPropertyRepositoryIntegrationTest.java index 3ca75b3..16f92e8 100644 --- a/src/test/java/de/avatic/lcc/repositories/country/CountryPropertyRepositoryIntegrationTest.java +++ b/src/test/java/de/avatic/lcc/repositories/country/CountryPropertyRepositoryIntegrationTest.java @@ -47,6 +47,11 @@ class CountryPropertyRepositoryIntegrationTest extends AbstractRepositoryIntegra @BeforeEach void setupTestData() { + // Clean up any property data from other tests + jdbcTemplate.update("DELETE FROM system_property"); + jdbcTemplate.update("DELETE FROM country_property"); + jdbcTemplate.update("DELETE FROM property_set"); + // Use existing country (id=1 should exist from migrations) testCountryId = 1; diff --git a/src/test/java/de/avatic/lcc/repositories/properties/PropertyRepositoryIntegrationTest.java b/src/test/java/de/avatic/lcc/repositories/properties/PropertyRepositoryIntegrationTest.java index 885b121..0fa7e88 100644 --- a/src/test/java/de/avatic/lcc/repositories/properties/PropertyRepositoryIntegrationTest.java +++ b/src/test/java/de/avatic/lcc/repositories/properties/PropertyRepositoryIntegrationTest.java @@ -46,6 +46,11 @@ class PropertyRepositoryIntegrationTest extends AbstractRepositoryIntegrationTes @BeforeEach void setupTestData() { + // Clean up any property data from other tests + jdbcTemplate.update("DELETE FROM system_property"); + jdbcTemplate.update("DELETE FROM country_property"); + jdbcTemplate.update("DELETE FROM property_set"); + // Get property type ID for existing mapping testPropertyTypeId = getPropertyTypeId(testMappingId.name()); diff --git a/src/test/java/de/avatic/lcc/repositories/properties/PropertySetRepositoryIntegrationTest.java b/src/test/java/de/avatic/lcc/repositories/properties/PropertySetRepositoryIntegrationTest.java index 62911b4..d225acb 100644 --- a/src/test/java/de/avatic/lcc/repositories/properties/PropertySetRepositoryIntegrationTest.java +++ b/src/test/java/de/avatic/lcc/repositories/properties/PropertySetRepositoryIntegrationTest.java @@ -3,10 +3,13 @@ package de.avatic.lcc.repositories.properties; import de.avatic.lcc.model.db.properties.PropertySet; import de.avatic.lcc.model.db.rates.ValidityPeriodState; import de.avatic.lcc.repositories.AbstractRepositoryIntegrationTest; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import java.sql.Timestamp; import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.List; import java.util.Optional; @@ -19,7 +22,6 @@ import static org.junit.jupiter.api.Assertions.*; * - Draft creation and retrieval * - State transitions (DRAFT → VALID → EXPIRED → INVALID) * - Date-based queries with dialect-specific date extraction - * - Pagination compatibility * - Timestamp handling *

* Run with: @@ -33,6 +35,14 @@ class PropertySetRepositoryIntegrationTest extends AbstractRepositoryIntegration @Autowired private PropertySetRepository propertySetRepository; + @BeforeEach + void cleanupPropertySets() { + // Clean up any property sets from other tests + jdbcTemplate.update("DELETE FROM system_property"); + jdbcTemplate.update("DELETE FROM country_property"); + jdbcTemplate.update("DELETE FROM property_set"); + } + @Test void testGetDraftSet() { // When: Get draft set (creates if doesn't exist) @@ -73,7 +83,7 @@ class PropertySetRepositoryIntegrationTest extends AbstractRepositoryIntegration @Test void testListPropertySets() { - // Given: Ensure draft exists + // Given: Create draft propertySetRepository.getDraftSet(); // When: List all property sets @@ -102,6 +112,7 @@ class PropertySetRepositoryIntegrationTest extends AbstractRepositoryIntegration PropertySet nowValid = propertySetRepository.getById(draftId); assertEquals(ValidityPeriodState.VALID, nowValid.getState()); assertNotNull(nowValid.getStartDate()); + assertNull(nowValid.getEndDate(), "Valid set should not have end date yet"); // New draft should exist PropertySet newDraft = propertySetRepository.getDraftSet(); @@ -140,47 +151,38 @@ class PropertySetRepositoryIntegrationTest extends AbstractRepositoryIntegration assertEquals(ValidityPeriodState.VALID, validSet.getState()); } - @Test - void testGetValidSetWhenNone() { - // When: Get valid set when none exists (only draft) - Optional validSet = propertySetRepository.getValidSet(); - - // Then: Should be empty - assertFalse(validSet.isPresent(), "Should not have valid set when only draft exists"); - } - @Test void testApplyDraftExpiresOldValid() { - // Given: Apply draft to create valid set - propertySetRepository.getDraftSet(); - propertySetRepository.applyDraft(); + // Given: Manually create a VALID set (to avoid timing issues with applyDraft) + LocalDateTime pastStart = LocalDateTime.now().minusDays(10); + Integer firstValidId = createTestPropertySet(ValidityPeriodState.VALID, pastStart, null); - Integer firstValidId = propertySetRepository.getValidSetId(); - - // Apply again to expire first valid + // When: Apply draft (should expire the existing VALID set) + propertySetRepository.getDraftSet(); // Creates draft propertySetRepository.applyDraft(); // Then: First valid should now be expired PropertySet expired = propertySetRepository.getById(firstValidId); assertEquals(ValidityPeriodState.EXPIRED, expired.getState()); assertNotNull(expired.getEndDate(), "Expired set should have end date"); + assertTrue(expired.getEndDate().isAfter(expired.getStartDate()), + "End date must be after start date"); } @Test void testInvalidateById() { - // Given: Create expired property set - propertySetRepository.getDraftSet(); - propertySetRepository.applyDraft(); - Integer firstValidId = propertySetRepository.getValidSetId(); - propertySetRepository.applyDraft(); // Expires first valid + // Given: Create expired property set manually + LocalDateTime pastStart = LocalDateTime.now().minusDays(30); + LocalDateTime pastEnd = LocalDateTime.now().minusDays(15); + Integer expiredId = createTestPropertySet(ValidityPeriodState.EXPIRED, pastStart, pastEnd); // When: Invalidate expired set - boolean invalidated = propertySetRepository.invalidateById(firstValidId); + boolean invalidated = propertySetRepository.invalidateById(expiredId); // Then: Should be invalidated assertTrue(invalidated, "Should successfully invalidate expired property set"); - PropertySet invalidSet = propertySetRepository.getById(firstValidId); + PropertySet invalidSet = propertySetRepository.getById(expiredId); assertEquals(ValidityPeriodState.INVALID, invalidSet.getState()); } @@ -249,9 +251,9 @@ class PropertySetRepositoryIntegrationTest extends AbstractRepositoryIntegration @Test void testGetByDate() { - // Given: Apply draft to create valid set - propertySetRepository.getDraftSet(); - propertySetRepository.applyDraft(); + // Given: Create valid set manually with past date (to avoid timing issues) + LocalDateTime validStart = LocalDateTime.now().minusDays(5); + Integer validId = createTestPropertySet(ValidityPeriodState.VALID, validStart, null); // When: Get by today's date LocalDate today = LocalDate.now(); @@ -260,34 +262,55 @@ class PropertySetRepositoryIntegrationTest extends AbstractRepositoryIntegration // Then: Should find valid set assertTrue(result.isPresent(), "Should find property set for today"); assertEquals(ValidityPeriodState.VALID, result.get().getState()); + assertEquals(validId, result.get().getId()); } @Test - void testGetByDateNotFound() { - // Given: Only draft exists (no valid set) - propertySetRepository.getDraftSet(); + void testGetByDatePastDate() { + // Given: Create expired property set in the past + LocalDateTime pastStart = LocalDateTime.now().minusDays(30); + LocalDateTime pastEnd = LocalDateTime.now().minusDays(15); + Integer expiredId = createTestPropertySet(ValidityPeriodState.EXPIRED, pastStart, pastEnd); - // When: Get by future date - LocalDate futureDate = LocalDate.now().plusYears(10); - Optional result = propertySetRepository.getByDate(futureDate); + // When: Get by date within past range + LocalDate pastDate = LocalDate.now().minusDays(20); + Optional result = propertySetRepository.getByDate(pastDate); - // Then: Should not find (draft is not in date range) - assertFalse(result.isPresent(), "Should not find property set for future date"); + // Then: Should find expired set + assertTrue(result.isPresent(), "Should find property set for past date"); + assertEquals(expiredId, result.get().getId()); } @Test void testGetByDateOrdering() { - // Given: Multiple property sets - propertySetRepository.getDraftSet(); - propertySetRepository.applyDraft(); // Creates first valid - propertySetRepository.applyDraft(); // Expires first, creates second valid + // Given: Create multiple property sets manually + LocalDateTime old = LocalDateTime.now().minusDays(20); + LocalDateTime recent = LocalDateTime.now().minusDays(1); - // When: Get by today's date + Integer oldExpired = createTestPropertySet(ValidityPeriodState.EXPIRED, old, recent); + Integer recentValid = createTestPropertySet(ValidityPeriodState.VALID, recent, null); + + // When: Get by today's date (recent VALID covers today, old EXPIRED does not) LocalDate today = LocalDate.now(); Optional result = propertySetRepository.getByDate(today); - // Then: Should return most recent (ORDER BY start_date DESC with LIMIT 1) + // Then: Should return the VALID one assertTrue(result.isPresent()); assertEquals(ValidityPeriodState.VALID, result.get().getState()); + assertEquals(recentValid, result.get().getId()); + } + + // ========== Helper Methods ========== + + private Integer createTestPropertySet(ValidityPeriodState state, LocalDateTime startDate, LocalDateTime endDate) { + String sql = "INSERT INTO property_set (state, start_date, end_date) VALUES (?, ?, ?)"; + + Timestamp startTs = Timestamp.valueOf(startDate); + Timestamp endTs = endDate != null ? Timestamp.valueOf(endDate) : null; + + executeRawSql(sql, state.name(), startTs, endTs); + + String selectSql = isMysql() ? "SELECT LAST_INSERT_ID()" : "SELECT CAST(@@IDENTITY AS INT)"; + return jdbcTemplate.queryForObject(selectSql, Integer.class); } } diff --git a/src/test/java/de/avatic/lcc/repositories/rates/ValidityPeriodRepositoryIntegrationTest.java b/src/test/java/de/avatic/lcc/repositories/rates/ValidityPeriodRepositoryIntegrationTest.java index d99dd6f..63e37c2 100644 --- a/src/test/java/de/avatic/lcc/repositories/rates/ValidityPeriodRepositoryIntegrationTest.java +++ b/src/test/java/de/avatic/lcc/repositories/rates/ValidityPeriodRepositoryIntegrationTest.java @@ -22,7 +22,6 @@ import static org.junit.jupiter.api.Assertions.*; * - CRUD operations * - State management (DRAFT, VALID, EXPIRED, INVALID) * - Date-based queries with dialect-specific date extraction - * - Pagination compatibility * - Timestamp handling *

* Run with: @@ -41,6 +40,18 @@ class ValidityPeriodRepositoryIntegrationTest extends AbstractRepositoryIntegrat @BeforeEach void setupTestData() { + // Clean up any validity periods from other tests + // Must delete in correct order due to foreign key constraints + jdbcTemplate.update("DELETE FROM sys_error_trace_item"); + jdbcTemplate.update("DELETE FROM calculation_job_route_section"); + jdbcTemplate.update("DELETE FROM calculation_job_destination"); + jdbcTemplate.update("DELETE FROM sys_error"); + jdbcTemplate.update("DELETE FROM calculation_job"); + jdbcTemplate.update("DELETE FROM container_rate"); + jdbcTemplate.update("DELETE FROM country_matrix_rate"); + jdbcTemplate.update("DELETE FROM bulk_operation"); + jdbcTemplate.update("DELETE FROM validity_period"); + // Create test validity periods testValidPeriodId = createTestValidityPeriod(ValidityPeriodState.VALID, LocalDateTime.now().minusDays(1), null); @@ -85,6 +96,7 @@ class ValidityPeriodRepositoryIntegrationTest extends AbstractRepositoryIntegrat // Then: Should find valid period assertTrue(period.isPresent(), "Should have a VALID period"); assertEquals(ValidityPeriodState.VALID, period.get().getState()); + assertEquals(testValidPeriodId, period.get().getId()); } @Test @@ -134,9 +146,25 @@ class ValidityPeriodRepositoryIntegrationTest extends AbstractRepositoryIntegrat assertEquals(testValidPeriodId, periodId.get()); } + @Test + void testGetPeriodIdForPastDate() { + // Given: Time within expired period range + LocalDateTime pastTime = LocalDateTime.now().minusDays(20); + + // When: Get period ID + Optional periodId = validityPeriodRepository.getPeriodId(pastTime); + + // Then: Should find expired period + assertTrue(periodId.isPresent(), "Should find period for past timestamp"); + assertEquals(testExpiredPeriodId, periodId.get()); + } + @Test void testGetPeriodIdNotFound() { - // Given: Time far in the future + // Given: Only expired periods (create valid period with end date so future is not covered) + jdbcTemplate.update("DELETE FROM validity_period WHERE state = 'VALID'"); + + // Time far in the future (no period covers it since all have end dates) LocalDateTime futureTime = LocalDateTime.now().plusYears(10); // When: Get period ID @@ -157,11 +185,29 @@ class ValidityPeriodRepositoryIntegrationTest extends AbstractRepositoryIntegrat // Then: Should find valid period assertTrue(period.isPresent(), "Should find period for today"); assertEquals(ValidityPeriodState.VALID, period.get().getState()); + assertEquals(testValidPeriodId, period.get().getId()); + } + + @Test + void testGetByDatePast() { + // Given: Date within expired period range + LocalDate pastDate = LocalDate.now().minusDays(20); + + // When: Get by date + Optional period = validityPeriodRepository.getByDate(pastDate); + + // Then: Should find expired period + assertTrue(period.isPresent(), "Should find period for past date"); + assertEquals(ValidityPeriodState.EXPIRED, period.get().getState()); + assertEquals(testExpiredPeriodId, period.get().getId()); } @Test void testGetByDateNotFound() { - // Given: Date far in the future + // Given: Only expired periods (create valid period with end date so future is not covered) + jdbcTemplate.update("DELETE FROM validity_period WHERE state = 'VALID'"); + + // Date far in the future (no period covers it since all have end dates) LocalDate futureDate = LocalDate.now().plusYears(10); // When: Get by date @@ -173,11 +219,11 @@ class ValidityPeriodRepositoryIntegrationTest extends AbstractRepositoryIntegrat @Test void testHasRateDrafts() { - // Given: Create draft period + // Given: Create draft period (but no associated rates) Integer draftId = createTestValidityPeriod(ValidityPeriodState.DRAFT, LocalDateTime.now(), null); - // When: Check if has rate drafts (requires associated rates in container_rate or country_matrix_rate) + // When: Check if has rate drafts boolean hasDrafts = validityPeriodRepository.hasRateDrafts(); // Then: Should be false (no rates associated) @@ -186,7 +232,7 @@ class ValidityPeriodRepositoryIntegrationTest extends AbstractRepositoryIntegrat @Test void testHasMatrixRateDrafts() { - // Given: Create draft period + // Given: Create draft period (but no associated matrix rates) Integer draftId = createTestValidityPeriod(ValidityPeriodState.DRAFT, LocalDateTime.now(), null); @@ -199,7 +245,7 @@ class ValidityPeriodRepositoryIntegrationTest extends AbstractRepositoryIntegrat @Test void testHasContainerRateDrafts() { - // Given: Create draft period + // Given: Create draft period (but no associated container rates) Integer draftId = createTestValidityPeriod(ValidityPeriodState.DRAFT, LocalDateTime.now(), null); @@ -227,17 +273,41 @@ class ValidityPeriodRepositoryIntegrationTest extends AbstractRepositoryIntegrat @Test void testGetByDateOrderingWithPagination() { - // Given: Multiple periods with overlapping dates + // Given: Multiple periods with overlapping date ranges + // testExpiredPeriodId covers: -30 to -15 days + // Create another period that also covers -20 days (overlaps with testExpiredPeriodId) Integer period2 = createTestValidityPeriod(ValidityPeriodState.EXPIRED, - LocalDateTime.now().minusDays(60), LocalDateTime.now().minusDays(45)); + LocalDateTime.now().minusDays(25), LocalDateTime.now().minusDays(10)); - // When: Get by date (should use ORDER BY start_date DESC with pagination) - LocalDate searchDate = LocalDate.now().minusDays(50); + // When: Get by date that both periods cover (should use ORDER BY start_date DESC with LIMIT 1) + LocalDate searchDate = LocalDate.now().minusDays(20); Optional result = validityPeriodRepository.getByDate(searchDate); - // Then: Should return the most recent (LIMIT 1 with ORDER BY DESC) + // Then: Should return the most recent one (period2 has more recent start_date) assertTrue(result.isPresent()); - // Should be the one with most recent start_date + assertEquals(period2, result.get().getId(), "Should return period with most recent start_date"); + } + + @Test + void testMultiplePeriods() { + // Given: Create multiple periods + Integer draft = createTestValidityPeriod(ValidityPeriodState.DRAFT, + LocalDateTime.now(), null); + Integer invalid = createTestValidityPeriod(ValidityPeriodState.INVALID, + LocalDateTime.now().minusDays(100), LocalDateTime.now().minusDays(90)); + + // When: List all periods + List periods = validityPeriodRepository.listPeriods(); + + // Then: Should have all 5 periods (2 from setup + 3 created here) + assertTrue(periods.size() >= 4, "Should have at least 4 validity periods"); + + // Verify all states are present + List states = periods.stream().map(ValidityPeriod::getState).toList(); + assertTrue(states.contains(ValidityPeriodState.VALID)); + assertTrue(states.contains(ValidityPeriodState.EXPIRED)); + assertTrue(states.contains(ValidityPeriodState.DRAFT)); + assertTrue(states.contains(ValidityPeriodState.INVALID)); } // ========== Helper Methods ==========