From 5c8165c60e024c5388b46373cac9da5a19461c7d Mon Sep 17 00:00:00 2001 From: Jan Date: Wed, 28 Jan 2026 09:12:27 +0100 Subject: [PATCH] Added `MatrixRateRepositoryIntegrationTest` for MySQL and MSSQL; ensured data cleanup, improved test coverage for rate operations, and adjusted SQL logic for validation and copying between periods. --- .../rates/MatrixRateRepository.java | 12 +- .../MatrixRateRepositoryIntegrationTest.java | 298 ++++++++++++++++++ 2 files changed, 303 insertions(+), 7 deletions(-) create mode 100644 src/test/java/de/avatic/lcc/repositories/rates/MatrixRateRepositoryIntegrationTest.java diff --git a/src/main/java/de/avatic/lcc/repositories/rates/MatrixRateRepository.java b/src/main/java/de/avatic/lcc/repositories/rates/MatrixRateRepository.java index 966e4df..bf8a954 100644 --- a/src/main/java/de/avatic/lcc/repositories/rates/MatrixRateRepository.java +++ b/src/main/java/de/avatic/lcc/repositories/rates/MatrixRateRepository.java @@ -191,22 +191,20 @@ public class MatrixRateRepository { @Transactional public void copyCurrentToDraft() { - String limitClause = dialectProvider.buildPaginationClause(1, 0); - Object[] paginationParams = dialectProvider.getPaginationParameters(1, 0); - - String sql = String.format(""" + // Note: No pagination needed for the DRAFT subquery - there should only be one DRAFT period + String sql = """ INSERT INTO country_matrix_rate (from_country_id, to_country_id, rate, validity_period_id) SELECT cmr.from_country_id, cmr.to_country_id, cmr.rate, - (SELECT id FROM validity_period WHERE state = 'DRAFT' %s) AS validity_period_id + (SELECT id FROM validity_period WHERE state = 'DRAFT') AS validity_period_id FROM country_matrix_rate cmr INNER JOIN validity_period vp ON cmr.validity_period_id = vp.id WHERE vp.state = 'VALID' - """, limitClause); + """; - jdbcTemplate.update(sql, paginationParams); + jdbcTemplate.update(sql); } /** diff --git a/src/test/java/de/avatic/lcc/repositories/rates/MatrixRateRepositoryIntegrationTest.java b/src/test/java/de/avatic/lcc/repositories/rates/MatrixRateRepositoryIntegrationTest.java new file mode 100644 index 0000000..c398fd2 --- /dev/null +++ b/src/test/java/de/avatic/lcc/repositories/rates/MatrixRateRepositoryIntegrationTest.java @@ -0,0 +1,298 @@ +package de.avatic.lcc.repositories.rates; + +import de.avatic.lcc.model.db.rates.MatrixRate; +import de.avatic.lcc.model.db.rates.ValidityPeriodState; +import de.avatic.lcc.repositories.AbstractRepositoryIntegrationTest; +import de.avatic.lcc.repositories.pagination.SearchQueryPagination; +import de.avatic.lcc.repositories.pagination.SearchQueryResult; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Integration tests for MatrixRateRepository. + *

+ * Tests critical functionality across both MySQL and MSSQL: + * - Pagination (LIMIT/OFFSET vs OFFSET/FETCH) + * - UPSERT operations (ON DUPLICATE KEY UPDATE vs MERGE) + * - Complex JOIN queries with filtering + * - Copy operations between validity periods + *

+ * Run with: + *

+ * mvn test -Dspring.profiles.active=test,mysql -Dtest=MatrixRateRepositoryIntegrationTest
+ * mvn test -Dspring.profiles.active=test,mssql -Dtest=MatrixRateRepositoryIntegrationTest
+ * 
+ */ +class MatrixRateRepositoryIntegrationTest extends AbstractRepositoryIntegrationTest { + + @Autowired + private MatrixRateRepository matrixRateRepository; + + private Integer testValidPeriodId; + private Integer testDraftPeriodId; + private Integer testCountryDeId; + private Integer testCountryUsId; + private Integer testCountryFrId; + + @BeforeEach + void setupTestData() { + // Clean up in correct order (foreign key constraints) + jdbcTemplate.update("DELETE FROM country_matrix_rate"); + jdbcTemplate.update("DELETE FROM country_property"); + jdbcTemplate.update("DELETE FROM container_rate"); + jdbcTemplate.update("DELETE FROM validity_period"); + + // Use existing countries from migrations (country table has initial data) + // Query for countries by ISO code to get IDs + testCountryDeId = jdbcTemplate.queryForObject( + "SELECT id FROM country WHERE iso_code = 'DE'", Integer.class); + testCountryUsId = jdbcTemplate.queryForObject( + "SELECT id FROM country WHERE iso_code = 'US'", Integer.class); + testCountryFrId = jdbcTemplate.queryForObject( + "SELECT id FROM country WHERE iso_code = 'FR'", Integer.class); + + // Create test validity periods + testValidPeriodId = createTestValidityPeriod(ValidityPeriodState.VALID, + LocalDateTime.now().minusDays(1), null); + testDraftPeriodId = createTestValidityPeriod(ValidityPeriodState.DRAFT, + LocalDateTime.now(), null); + + // Create test matrix rates + createTestMatrixRate(testCountryDeId, testCountryUsId, new BigDecimal("1.50"), testValidPeriodId); + createTestMatrixRate(testCountryDeId, testCountryFrId, new BigDecimal("0.80"), testValidPeriodId); + createTestMatrixRate(testCountryUsId, testCountryDeId, new BigDecimal("1.20"), testValidPeriodId); + } + + @Test + void testListRates() { + // Given: Pagination + SearchQueryPagination pagination = new SearchQueryPagination(1, 10); + + // When: List all rates + SearchQueryResult result = matrixRateRepository.listRates(pagination); + + // Then: Should return rates with pagination + assertNotNull(result); + assertFalse(result.toList().isEmpty()); + assertEquals(3, result.getTotalElements()); + assertTrue(result.toList().size() <= 10); + } + + @Test + void testListRatesPagination() { + // Given: Pagination with limit 1 + SearchQueryPagination pagination = new SearchQueryPagination(1, 1); + + // When: List rates + SearchQueryResult result = matrixRateRepository.listRates(pagination); + + // Then: Should respect limit + assertNotNull(result); + assertEquals(1, result.toList().size()); + assertEquals(3, result.getTotalElements()); + } + + @Test + void testListRatesByPeriodId() { + // Given: Valid period ID + SearchQueryPagination pagination = new SearchQueryPagination(1, 10); + + // When: List rates by period + SearchQueryResult result = matrixRateRepository.listRatesByPeriodId(null, pagination, testValidPeriodId); + + // Then: Should return rates for this period + assertNotNull(result); + assertEquals(3, result.getTotalElements()); + assertTrue(result.toList().stream() + .allMatch(rate -> rate.getValidityPeriodId().equals(testValidPeriodId))); + } + + @Test + void testListRatesByPeriodIdWithFilter() { + // Given: Filter for "Germany" + SearchQueryPagination pagination = new SearchQueryPagination(1, 10); + + // When: List rates with filter + SearchQueryResult result = matrixRateRepository.listRatesByPeriodId("Germany", pagination, testValidPeriodId); + + // Then: Should return rates involving Germany + assertNotNull(result); + assertTrue(result.getTotalElements() >= 2, "Should find at least 2 rates with Germany"); + } + + @Test + void testListRatesByPeriodIdWithIsoCodeFilter() { + // Given: Filter for "US" + SearchQueryPagination pagination = new SearchQueryPagination(1, 10); + + // When: List rates with ISO code filter + SearchQueryResult result = matrixRateRepository.listRatesByPeriodId("US", pagination, testValidPeriodId); + + // Then: Should return rates involving US + assertNotNull(result); + assertTrue(result.getTotalElements() >= 2, "Should find at least 2 rates with US"); + } + + @Test + void testGetById() { + // Given: Get first rate ID + SearchQueryPagination pagination = new SearchQueryPagination(1, 1); + SearchQueryResult result = matrixRateRepository.listRates(pagination); + Integer rateId = result.toList().getFirst().getId(); + + // When: Get by ID + MatrixRate rate = matrixRateRepository.getById(rateId); + + // Then: Should retrieve correct rate + assertNotNull(rate); + assertEquals(rateId, rate.getId()); + assertNotNull(rate.getRate()); + assertNotNull(rate.getFromCountry()); + assertNotNull(rate.getToCountry()); + } + + @Test + void testListAllRatesByPeriodId() { + // When: List all rates for valid period + List rates = matrixRateRepository.listAllRatesByPeriodId(testValidPeriodId); + + // Then: Should return all 3 rates + assertNotNull(rates); + assertEquals(3, rates.size()); + assertTrue(rates.stream().allMatch(rate -> rate.getValidityPeriodId().equals(testValidPeriodId))); + } + + @Test + void testGetByCountryIds() { + // When: Get rate from DE to US + Optional rate = matrixRateRepository.getByCountryIds(testCountryDeId, testCountryUsId); + + // Then: Should find rate + assertTrue(rate.isPresent()); + assertEquals(testCountryDeId, rate.get().getFromCountry()); + assertEquals(testCountryUsId, rate.get().getToCountry()); + assertEquals(new BigDecimal("1.50"), rate.get().getRate()); + } + + @Test + void testGetByCountryIdsNotFound() { + // Given: Non-existent country combination + Integer nonExistentCountryId = 99999; + + // When: Get rate + Optional rate = matrixRateRepository.getByCountryIds(nonExistentCountryId, testCountryUsId); + + // Then: Should not find + assertFalse(rate.isPresent()); + } + + @Test + void testGetByCountryIdsWithPeriodId() { + // When: Get rate from DE to US in valid period + Optional rate = matrixRateRepository.getByCountryIds(testCountryDeId, testCountryUsId, testValidPeriodId); + + // Then: Should find rate + assertTrue(rate.isPresent()); + assertEquals(testCountryDeId, rate.get().getFromCountry()); + assertEquals(testCountryUsId, rate.get().getToCountry()); + assertEquals(testValidPeriodId, rate.get().getValidityPeriodId()); + } + + @Test + void testGetByCountryIdsWithWrongPeriodId() { + // When: Get rate with wrong period ID + Optional rate = matrixRateRepository.getByCountryIds(testCountryDeId, testCountryUsId, testDraftPeriodId); + + // Then: Should not find + assertFalse(rate.isPresent()); + } + + @Test + void testInsertNewRate() { + // Given: New matrix rate + MatrixRate newRate = new MatrixRate(); + newRate.setFromCountry(testCountryFrId); + newRate.setToCountry(testCountryUsId); + newRate.setRate(new BigDecimal("2.50")); + newRate.setValidityPeriodId(testDraftPeriodId); + + // When: Insert + matrixRateRepository.insert(newRate); + + // Then: Should be inserted + Optional inserted = matrixRateRepository.getByCountryIds(testCountryFrId, testCountryUsId, testDraftPeriodId); + assertTrue(inserted.isPresent()); + assertEquals(new BigDecimal("2.50"), inserted.get().getRate()); + } + + @Test + void testInsertUpsertExisting() { + // Given: Existing rate DE -> US + MatrixRate updateRate = new MatrixRate(); + updateRate.setFromCountry(testCountryDeId); + updateRate.setToCountry(testCountryUsId); + updateRate.setRate(new BigDecimal("3.00")); // Different rate + updateRate.setValidityPeriodId(testValidPeriodId); + + // When: Insert (should upsert) + matrixRateRepository.insert(updateRate); + + // Then: Rate should be updated + Optional updated = matrixRateRepository.getByCountryIds(testCountryDeId, testCountryUsId, testValidPeriodId); + assertTrue(updated.isPresent()); + assertEquals(new BigDecimal("3.00"), updated.get().getRate()); + + // Should still have only 3 rates total + List allRates = matrixRateRepository.listAllRatesByPeriodId(testValidPeriodId); + assertEquals(3, allRates.size()); + } + + @Test + void testCopyCurrentToDraft() { + // Given: Valid period has 3 rates, draft has 0 + List draftRatesBefore = matrixRateRepository.listAllRatesByPeriodId(testDraftPeriodId); + assertEquals(0, draftRatesBefore.size()); + + // When: Copy current to draft + matrixRateRepository.copyCurrentToDraft(); + + // Then: Draft should have copies of all valid rates + List draftRatesAfter = matrixRateRepository.listAllRatesByPeriodId(testDraftPeriodId); + assertEquals(3, draftRatesAfter.size()); + + // Verify rates are copied with correct values + Optional copiedRate = matrixRateRepository.getByCountryIds(testCountryDeId, testCountryUsId, testDraftPeriodId); + assertTrue(copiedRate.isPresent()); + assertEquals(new BigDecimal("1.50"), copiedRate.get().getRate()); + + // Original rates should still exist + List validRates = matrixRateRepository.listAllRatesByPeriodId(testValidPeriodId); + assertEquals(3, validRates.size()); + } + + // ========== Helper Methods ========== + + private Integer createTestValidityPeriod(ValidityPeriodState state, LocalDateTime startDate, LocalDateTime endDate) { + String sql = "INSERT INTO validity_period (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); + } + + private void createTestMatrixRate(Integer fromCountryId, Integer toCountryId, BigDecimal rate, Integer validityPeriodId) { + String sql = "INSERT INTO country_matrix_rate (from_country_id, to_country_id, rate, validity_period_id) VALUES (?, ?, ?, ?)"; + executeRawSql(sql, fromCountryId, toCountryId, rate, validityPeriodId); + } +}