diff --git a/src/main/java/de/avatic/lcc/repositories/packaging/PackagingRepository.java b/src/main/java/de/avatic/lcc/repositories/packaging/PackagingRepository.java index 58079eb..a0c47b3 100644 --- a/src/main/java/de/avatic/lcc/repositories/packaging/PackagingRepository.java +++ b/src/main/java/de/avatic/lcc/repositories/packaging/PackagingRepository.java @@ -61,7 +61,7 @@ public class PackagingRepository { Object[] paginationParams = dialectProvider.getPaginationParameters(pagination.getLimit(), pagination.getOffset()); var params = new ArrayList(); - params.add(excludeDeprecated); + // Note: excludeDeprecated is not added as parameter - it's inserted as boolean literal in buildQuery() if (materialId != null) { params.add(materialId); } diff --git a/src/test/java/de/avatic/lcc/repositories/MaterialRepositoryIntegrationTest.java b/src/test/java/de/avatic/lcc/repositories/MaterialRepositoryIntegrationTest.java new file mode 100644 index 0000000..350fdb2 --- /dev/null +++ b/src/test/java/de/avatic/lcc/repositories/MaterialRepositoryIntegrationTest.java @@ -0,0 +1,351 @@ +package de.avatic.lcc.repositories; + +import de.avatic.lcc.model.db.materials.Material; +import de.avatic.lcc.repositories.pagination.SearchQueryPagination; +import de.avatic.lcc.repositories.pagination.SearchQueryResult; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Integration tests for MaterialRepository. + *

+ * Tests critical functionality across both MySQL and MSSQL: + * - CRUD operations (Create, Read, Update, Delete) + * - Pagination with ORDER BY (MSSQL requirement) + * - Search with filters (name and part_number) + * - Boolean literal compatibility (deprecated filtering) + * - Bulk operations (getByPartNumbers, deleteByIds, findMissingIds) + *

+ * Run with: + *

+ * mvn test -Dspring.profiles.active=test,mysql -Dtest=MaterialRepositoryIntegrationTest
+ * mvn test -Dspring.profiles.active=test,mssql -Dtest=MaterialRepositoryIntegrationTest
+ * 
+ */ +class MaterialRepositoryIntegrationTest extends AbstractRepositoryIntegrationTest { + + @Autowired + private MaterialRepository materialRepository; + + @Test + void testInsertAndRetrieve() { + // Given: Create material + Material material = createTestMaterial("TEST-001", "Test Material 1"); + + // When: Insert + materialRepository.insert(material); + + // When: Retrieve by part number + Optional retrieved = materialRepository.getByPartNumber("TEST-001"); + + // Then: Should retrieve successfully + assertTrue(retrieved.isPresent(), "Material should be retrievable after insert"); + assertEquals("TEST-001", retrieved.get().getPartNumber()); + assertEquals("Test Material 1", retrieved.get().getName()); + assertFalse(retrieved.get().getDeprecated()); + } + + @Test + void testUpdate() { + // Given: Insert material + Material material = createTestMaterial("TEST-002", "Original Name"); + materialRepository.insert(material); + + // When: Update material + Material toUpdate = materialRepository.getByPartNumber("TEST-002").orElseThrow(); + toUpdate.setName("Updated Name"); + toUpdate.setHsCode("12345678901"); + materialRepository.update(toUpdate); + + // Then: Verify update + Material updated = materialRepository.getById(toUpdate.getId()).orElseThrow(); + assertEquals("Updated Name", updated.getName()); + assertEquals("12345678901", updated.getHsCode()); + } + + @Test + void testUpdateByPartNumber() { + // Given: Insert material + Material material = createTestMaterial("TEST-003", "Original Name"); + materialRepository.insert(material); + + // When: Update by part number + Material toUpdate = materialRepository.getByPartNumber("TEST-003").orElseThrow(); + toUpdate.setName("Updated via PartNumber"); + materialRepository.updateByPartNumber(toUpdate); + + // Then: Verify update + Material updated = materialRepository.getByPartNumber("TEST-003").orElseThrow(); + assertEquals("Updated via PartNumber", updated.getName()); + } + + @Test + void testSetDeprecatedById() { + // Given: Insert material + Material material = createTestMaterial("TEST-004", "Material to Deprecate"); + materialRepository.insert(material); + Integer materialId = materialRepository.getByPartNumber("TEST-004").orElseThrow().getId(); + + // When: Deprecate + Optional result = materialRepository.setDeprecatedById(materialId); + + // Then: Should be deprecated + assertTrue(result.isPresent()); + + // getById() excludes deprecated + Optional deprecated = materialRepository.getById(materialId); + assertFalse(deprecated.isPresent(), "getById() should exclude deprecated materials"); + + // But getByIdIncludeDeprecated() should find it + Optional includingDeprecated = materialRepository.getByIdIncludeDeprecated(materialId); + assertTrue(includingDeprecated.isPresent(), "getByIdIncludeDeprecated() should find deprecated materials"); + assertTrue(includingDeprecated.get().getDeprecated()); + } + + @Test + void testDeleteById() { + // Given: Insert material + Material material = createTestMaterial("TEST-005", "Material to Delete"); + materialRepository.insert(material); + Integer materialId = materialRepository.getByPartNumber("TEST-005").orElseThrow().getId(); + + // When: Delete (soft delete - sets deprecated) + materialRepository.deleteById(materialId); + + // Then: Should be deprecated + Optional deleted = materialRepository.getById(materialId); + assertFalse(deleted.isPresent(), "Deleted material should not be retrievable via getById()"); + + Optional includingDeleted = materialRepository.getByIdIncludeDeprecated(materialId); + assertTrue(includingDeleted.isPresent()); + assertTrue(includingDeleted.get().getDeprecated()); + } + + @Test + void testListMaterialsWithPagination() { + // Given: Insert multiple materials + for (int i = 1; i <= 5; i++) { + Material material = createTestMaterial("PAGE-" + String.format("%03d", i), "Pagination Material " + i); + materialRepository.insert(material); + } + + // When: List with pagination (page 1, size 3) + SearchQueryPagination pagination = new SearchQueryPagination(1, 3); + SearchQueryResult result = materialRepository.listMaterials( + Optional.empty(), false, pagination + ); + + // Then: Verify pagination works + assertNotNull(result); + assertNotNull(result.toList()); + assertTrue(result.toList().size() <= 3, "Should return at most 3 materials per page"); + assertTrue(result.getTotalElements() >= 5, "Should have at least 5 materials total"); + } + + @Test + void testListMaterialsWithFilter() { + // Given: Insert materials with different names + Material material1 = createTestMaterial("FILTER-001", "Special Widget"); + materialRepository.insert(material1); + + Material material2 = createTestMaterial("FILTER-002", "Normal Component"); + materialRepository.insert(material2); + + Material material3 = createTestMaterial("FILTER-003", "Special Gadget"); + materialRepository.insert(material3); + + // When: Search for "Special" + SearchQueryPagination pagination = new SearchQueryPagination(1, 10); + SearchQueryResult result = materialRepository.listMaterials( + Optional.of("SPECIAL"), false, pagination + ); + + // Then: Should find materials with "Special" in name + assertNotNull(result); + assertTrue(result.toList().size() >= 2, "Should find at least 2 materials with 'Special'"); + + for (Material m : result.toList()) { + boolean matches = m.getName().toUpperCase().contains("SPECIAL") || + m.getPartNumber().toUpperCase().contains("SPECIAL"); + assertTrue(matches, "Material should match filter"); + } + } + + @Test + void testListMaterialsExcludeDeprecated() { + // Given: Insert deprecated and active materials + Material deprecated = createTestMaterial("DEPR-001", "Deprecated Material"); + deprecated.setDeprecated(true); + materialRepository.insert(deprecated); + + Material active = createTestMaterial("ACTIVE-001", "Active Material"); + materialRepository.insert(active); + + // When: List excluding deprecated + SearchQueryPagination pagination = new SearchQueryPagination(1, 10); + SearchQueryResult result = materialRepository.listMaterials( + Optional.empty(), true, pagination + ); + + // Then: Should not include deprecated materials + assertNotNull(result); + for (Material m : result.toList()) { + assertFalse(m.getDeprecated(), "Should not include deprecated materials"); + } + } + + @Test + void testListAllMaterials() { + // Given: Insert materials + Material material1 = createTestMaterial("ALL-001", "Material 1"); + materialRepository.insert(material1); + + Material material2 = createTestMaterial("ALL-002", "Material 2"); + materialRepository.insert(material2); + + // When: List all + List materials = materialRepository.listAllMaterials(); + + // Then: Should return all materials ordered by normalized_part_number + assertNotNull(materials); + assertFalse(materials.isEmpty()); + + // Verify ordering + for (int i = 1; i < materials.size(); i++) { + String prev = materials.get(i - 1).getNormalizedPartNumber(); + String current = materials.get(i).getNormalizedPartNumber(); + assertTrue(prev.compareTo(current) <= 0, + "Materials should be ordered by normalized_part_number"); + } + } + + @Test + void testGetByPartNumber() { + // Given: Insert material + Material material = createTestMaterial("BYPART-001", "Get By Part"); + materialRepository.insert(material); + + // When: Get by part number + Optional result = materialRepository.getByPartNumber("BYPART-001"); + + // Then: Should find material + assertTrue(result.isPresent()); + assertEquals("BYPART-001", result.get().getPartNumber()); + assertEquals("Get By Part", result.get().getName()); + } + + @Test + void testGetByPartNumberNotFound() { + // When: Get by non-existent part number + Optional result = materialRepository.getByPartNumber("NONEXISTENT-999"); + + // Then: Should return empty + assertFalse(result.isPresent(), "Should not find material with non-existent part number"); + } + + @Test + void testGetByPartNumbers() { + // Given: Insert multiple materials + Material material1 = createTestMaterial("BULK-001", "Bulk Material 1"); + materialRepository.insert(material1); + + Material material2 = createTestMaterial("BULK-002", "Bulk Material 2"); + materialRepository.insert(material2); + + Material material3 = createTestMaterial("BULK-003", "Bulk Material 3"); + materialRepository.insert(material3); + + // When: Get by part numbers + List partNumbers = List.of("BULK-001", "BULK-002", "NONEXISTENT"); + List materials = materialRepository.getByPartNumbers(partNumbers); + + // Then: Should find existing materials (2 out of 3 part numbers) + assertNotNull(materials); + assertTrue(materials.size() >= 2, "Should find at least 2 materials"); + + List foundPartNumbers = materials.stream() + .map(Material::getPartNumber) + .toList(); + assertTrue(foundPartNumbers.contains("BULK-001")); + assertTrue(foundPartNumbers.contains("BULK-002")); + } + + @Test + void testGetByPartNumbersEmptyList() { + // When: Get by empty list + List materials = materialRepository.getByPartNumbers(List.of()); + + // Then: Should return empty list + assertNotNull(materials); + assertTrue(materials.isEmpty()); + } + + @Test + void testDeleteByIds() { + // Given: Insert multiple materials + Material material1 = createTestMaterial("DELETE-001", "To Delete 1"); + materialRepository.insert(material1); + Integer id1 = materialRepository.getByPartNumber("DELETE-001").orElseThrow().getId(); + + Material material2 = createTestMaterial("DELETE-002", "To Delete 2"); + materialRepository.insert(material2); + Integer id2 = materialRepository.getByPartNumber("DELETE-002").orElseThrow().getId(); + + // When: Delete by IDs + materialRepository.deleteByIds(List.of(id1, id2)); + + // Then: Should be deprecated + assertFalse(materialRepository.getById(id1).isPresent()); + assertFalse(materialRepository.getById(id2).isPresent()); + + // But should exist with deprecated flag + assertTrue(materialRepository.getByIdIncludeDeprecated(id1).orElseThrow().getDeprecated()); + assertTrue(materialRepository.getByIdIncludeDeprecated(id2).orElseThrow().getDeprecated()); + } + + @Test + void testFindMissingIds() { + // Given: Insert some materials + Material material1 = createTestMaterial("MISSING-001", "Material 1"); + materialRepository.insert(material1); + Integer existingId = materialRepository.getByPartNumber("MISSING-001").orElseThrow().getId(); + + // When: Check for missing IDs + List idsToCheck = List.of(existingId, 99999, 99998); + List missingIds = materialRepository.findMissingIds(idsToCheck); + + // Then: Should return only non-existent IDs + assertNotNull(missingIds); + assertEquals(2, missingIds.size(), "Should find 2 missing IDs"); + assertTrue(missingIds.contains(99999)); + assertTrue(missingIds.contains(99998)); + assertFalse(missingIds.contains(existingId), "Existing ID should not be in missing list"); + } + + @Test + void testFindMissingIdsEmptyList() { + // When: Check empty list + List missingIds = materialRepository.findMissingIds(List.of()); + + // Then: Should return empty list + assertNotNull(missingIds); + assertTrue(missingIds.isEmpty()); + } + + // ========== Helper Methods ========== + + private Material createTestMaterial(String partNumber, String name) { + Material material = new Material(); + material.setPartNumber(partNumber); + material.setNormalizedPartNumber(partNumber.toUpperCase()); + material.setName(name); + material.setHsCode(null); + material.setDeprecated(false); + return material; + } +} diff --git a/src/test/java/de/avatic/lcc/repositories/NomenclatureRepositoryIntegrationTest.java b/src/test/java/de/avatic/lcc/repositories/NomenclatureRepositoryIntegrationTest.java new file mode 100644 index 0000000..171be77 --- /dev/null +++ b/src/test/java/de/avatic/lcc/repositories/NomenclatureRepositoryIntegrationTest.java @@ -0,0 +1,159 @@ +package de.avatic.lcc.repositories; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Integration tests for NomenclatureRepository. + *

+ * Tests critical functionality across both MySQL and MSSQL: + * - Search with LIKE and CONCAT functions + * - Pagination (LIMIT/OFFSET vs OFFSET/FETCH) + * - ORDER BY compatibility + *

+ * Run with: + *

+ * mvn test -Dspring.profiles.active=test,mysql -Dtest=NomenclatureRepositoryIntegrationTest
+ * mvn test -Dspring.profiles.active=test,mssql -Dtest=NomenclatureRepositoryIntegrationTest
+ * 
+ */ +class NomenclatureRepositoryIntegrationTest extends AbstractRepositoryIntegrationTest { + + @Autowired + private NomenclatureRepository nomenclatureRepository; + + @BeforeEach + void setupTestData() { + // Insert test HS codes into the nomenclature table + String sql = "INSERT INTO nomenclature (hs_code, description) VALUES (?, ?)"; + + executeRawSql(sql, "8471300000", "Portable automatic data processing machines, weighing not more than 10 kg"); + executeRawSql(sql, "8471410000", "Comprising in the same housing at least a central processing unit"); + executeRawSql(sql, "8471420000", "Other, presented in the form of systems"); + executeRawSql(sql, "8471490000", "Other automatic data processing machines"); + executeRawSql(sql, "8471500000", "Processing units other than those of subheading 8471.41 or 8471.49"); + executeRawSql(sql, "8471600000", "Input or output units"); + executeRawSql(sql, "8471700000", "Storage units"); + executeRawSql(sql, "8471800000", "Other units of automatic data processing machines"); + executeRawSql(sql, "9403200000", "Other metal furniture"); + executeRawSql(sql, "9403300000", "Wooden furniture of a kind used in offices"); + } + + @Test + void testSearchHsCodeWithExactMatch() { + // Given: Search for exact HS code prefix + String search = "847130"; + + // When: Search + List results = nomenclatureRepository.searchHsCode(search); + + // Then: Should find matching HS codes starting with 847130 + assertNotNull(results); + assertFalse(results.isEmpty(), "Should find HS codes starting with 847130"); + assertTrue(results.stream().anyMatch(code -> code.startsWith("847130")), + "Results should contain codes starting with 847130"); + } + + @Test + void testSearchHsCodeWithPartialMatch() { + // Given: Search for partial HS code + String search = "8471"; + + // When: Search + List results = nomenclatureRepository.searchHsCode(search); + + // Then: Should find all HS codes starting with 8471 + assertNotNull(results); + assertTrue(results.size() >= 7, "Should find at least 7 codes starting with 8471"); + + // Verify all results start with the search term + for (String code : results) { + assertTrue(code.startsWith(search), + "All results should start with search term: " + code); + } + } + + @Test + void testSearchHsCodeOrdering() { + // Given: Search for codes + String search = "8471"; + + // When: Search + List results = nomenclatureRepository.searchHsCode(search); + + // Then: Should be ordered by hs_code + assertNotNull(results); + assertFalse(results.isEmpty()); + + // Verify ordering + for (int i = 1; i < results.size(); i++) { + assertTrue(results.get(i - 1).compareTo(results.get(i)) <= 0, + "Results should be ordered by hs_code"); + } + } + + @Test + void testSearchHsCodeWithPagination() { + // Given: Search that returns many results + String search = "8471"; + + // When: Search (limit is 10 as per repository implementation) + List results = nomenclatureRepository.searchHsCode(search); + + // Then: Should respect pagination limit + assertNotNull(results); + assertTrue(results.size() <= 10, "Should return at most 10 results (pagination limit)"); + } + + @Test + void testSearchHsCodeNotFound() { + // Given: Search for non-existent HS code + String search = "9999"; + + // When: Search + List results = nomenclatureRepository.searchHsCode(search); + + // Then: Should return empty list + assertNotNull(results); + assertTrue(results.isEmpty(), "Should return empty list for non-existent HS code"); + } + + @Test + void testSearchHsCodeDifferentPrefix() { + // Given: Search for different category (furniture) + String search = "9403"; + + // When: Search + List results = nomenclatureRepository.searchHsCode(search); + + // Then: Should find furniture codes + assertNotNull(results); + assertTrue(results.size() >= 2, "Should find at least 2 codes starting with 9403"); + assertTrue(results.stream().allMatch(code -> code.startsWith("9403")), + "All results should start with 9403"); + } + + @Test + void testSearchHsCodeConcatFunction() { + // This test verifies that the CONCAT function works across both databases + // MySQL: CONCAT(?, '%') + // MSSQL: ? + '%' + + // Given: Search with single digit + String search = "8"; + + // When: Search + List results = nomenclatureRepository.searchHsCode(search); + + // Then: Should find all codes starting with 8 + assertNotNull(results); + assertTrue(results.size() >= 7, "Should find at least 7 codes starting with 8"); + assertTrue(results.stream().allMatch(code -> code.startsWith("8")), + "All results should start with 8"); + } +} diff --git a/src/test/java/de/avatic/lcc/repositories/PackagingDimensionRepositoryIntegrationTest.java b/src/test/java/de/avatic/lcc/repositories/PackagingDimensionRepositoryIntegrationTest.java new file mode 100644 index 0000000..2f08e41 --- /dev/null +++ b/src/test/java/de/avatic/lcc/repositories/PackagingDimensionRepositoryIntegrationTest.java @@ -0,0 +1,183 @@ +package de.avatic.lcc.repositories; + +import de.avatic.lcc.model.db.packaging.PackagingDimension; +import de.avatic.lcc.model.db.packaging.PackagingType; +import de.avatic.lcc.model.db.utils.DimensionUnit; +import de.avatic.lcc.model.db.utils.WeightUnit; +import de.avatic.lcc.repositories.packaging.PackagingDimensionRepository; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Integration tests for PackagingDimensionRepository. + *

+ * Tests critical functionality across both MySQL and MSSQL: + * - CRUD operations + * - Boolean literal compatibility (deprecated filtering) + * - Enum handling (PackagingType, DimensionUnit, WeightUnit) + *

+ * Run with: + *

+ * mvn test -Dspring.profiles.active=test,mysql -Dtest=PackagingDimensionRepositoryIntegrationTest
+ * mvn test -Dspring.profiles.active=test,mssql -Dtest=PackagingDimensionRepositoryIntegrationTest
+ * 
+ */ +class PackagingDimensionRepositoryIntegrationTest extends AbstractRepositoryIntegrationTest { + + @Autowired + private PackagingDimensionRepository packagingDimensionRepository; + + @Test + void testInsertAndRetrieve() { + // Given: Create packaging dimension + PackagingDimension dimension = createTestDimension(1000, 500, 300, 10000, 1); + + // When: Insert + Optional dimensionId = packagingDimensionRepository.insert(dimension); + + // Then: Should be inserted successfully + assertTrue(dimensionId.isPresent(), "Dimension ID should be present"); + assertTrue(dimensionId.get() > 0, "Dimension ID should be positive"); + + // When: Retrieve by ID + Optional retrieved = packagingDimensionRepository.getById(dimensionId.get()); + + // Then: Should retrieve successfully + assertTrue(retrieved.isPresent(), "Dimension should be retrievable after insert"); + assertEquals(1000, retrieved.get().getLength()); + assertEquals(500, retrieved.get().getWidth()); + assertEquals(300, retrieved.get().getHeight()); + assertEquals(10000, retrieved.get().getWeight()); + assertEquals(1, retrieved.get().getContentUnitCount()); + assertEquals(PackagingType.HU, retrieved.get().getType()); + assertEquals(DimensionUnit.CM, retrieved.get().getDimensionUnit()); + assertEquals(WeightUnit.KG, retrieved.get().getWeightUnit()); + assertFalse(retrieved.get().getDeprecated()); + } + + @Test + void testUpdate() { + // Given: Insert dimension + PackagingDimension dimension = createTestDimension(1000, 500, 300, 10000, 1); + Integer dimensionId = packagingDimensionRepository.insert(dimension).orElseThrow(); + + // When: Update dimension + PackagingDimension toUpdate = packagingDimensionRepository.getById(dimensionId).orElseThrow(); + toUpdate.setLength(1200); + toUpdate.setWidth(600); + toUpdate.setHeight(400); + toUpdate.setWeight(15000); + toUpdate.setContentUnitCount(2); + packagingDimensionRepository.update(toUpdate); + + // Then: Verify update + PackagingDimension updated = packagingDimensionRepository.getById(dimensionId).orElseThrow(); + assertEquals(1200, updated.getLength()); + assertEquals(600, updated.getWidth()); + assertEquals(400, updated.getHeight()); + assertEquals(15000, updated.getWeight()); + assertEquals(2, updated.getContentUnitCount()); + } + + @Test + void testSetDeprecatedById() { + // Given: Insert dimension + PackagingDimension dimension = createTestDimension(1000, 500, 300, 10000, 1); + Integer dimensionId = packagingDimensionRepository.insert(dimension).orElseThrow(); + + // When: Deprecate + Optional result = packagingDimensionRepository.setDeprecatedById(dimensionId); + + // Then: Should be deprecated + assertTrue(result.isPresent()); + + // getById() excludes deprecated dimensions + Optional deprecated = packagingDimensionRepository.getById(dimensionId); + assertFalse(deprecated.isPresent(), "Deprecated dimension should not be retrievable via getById()"); + } + + @Test + void testGetByIdNotFound() { + // When: Get by non-existent ID + Optional result = packagingDimensionRepository.getById(99999); + + // Then: Should return empty + assertFalse(result.isPresent(), "Should not find dimension with non-existent ID"); + } + + @Test + void testDifferentPackagingTypes() { + // Given: Insert dimensions with different types + PackagingDimension hu = createTestDimension(1000, 500, 300, 10000, 1); + hu.setType(PackagingType.HU); + Integer huId = packagingDimensionRepository.insert(hu).orElseThrow(); + + PackagingDimension shu = createTestDimension(500, 300, 200, 5000, 1); + shu.setType(PackagingType.SHU); + Integer shuId = packagingDimensionRepository.insert(shu).orElseThrow(); + + // When: Retrieve both + PackagingDimension retrievedHu = packagingDimensionRepository.getById(huId).orElseThrow(); + PackagingDimension retrievedShu = packagingDimensionRepository.getById(shuId).orElseThrow(); + + // Then: Should have correct types + assertEquals(PackagingType.HU, retrievedHu.getType()); + assertEquals(PackagingType.SHU, retrievedShu.getType()); + } + + @Test + void testDifferentUnits() { + // Given: Insert dimension with different units (meters and grams instead of cm and kg) + PackagingDimension dimension = createTestDimension(1, 1, 1, 1000, 1); // meters and grams + dimension.setDimensionUnit(DimensionUnit.M); + dimension.setWeightUnit(WeightUnit.G); + Integer dimensionId = packagingDimensionRepository.insert(dimension).orElseThrow(); + + // When: Retrieve + PackagingDimension retrieved = packagingDimensionRepository.getById(dimensionId).orElseThrow(); + + // Then: Should have correct units + assertEquals(DimensionUnit.M, retrieved.getDimensionUnit()); + assertEquals(WeightUnit.G, retrieved.getWeightUnit()); + assertEquals(1, retrieved.getLength()); + assertEquals(1, retrieved.getWidth()); + assertEquals(1, retrieved.getHeight()); + assertEquals(1000, retrieved.getWeight()); + } + + @Test + void testUpdateWithDeprecation() { + // Given: Insert dimension + PackagingDimension dimension = createTestDimension(1000, 500, 300, 10000, 1); + Integer dimensionId = packagingDimensionRepository.insert(dimension).orElseThrow(); + + // When: Update and deprecate + PackagingDimension toUpdate = packagingDimensionRepository.getById(dimensionId).orElseThrow(); + toUpdate.setDeprecated(true); + packagingDimensionRepository.update(toUpdate); + + // Then: Should not be retrievable via getById() (which filters deprecated) + Optional deprecated = packagingDimensionRepository.getById(dimensionId); + assertFalse(deprecated.isPresent()); + } + + // ========== Helper Methods ========== + + private PackagingDimension createTestDimension(Integer length, Integer width, Integer height, Integer weight, Integer contentUnitCount) { + PackagingDimension dimension = new PackagingDimension(); + dimension.setType(PackagingType.HU); + dimension.setLength(length); + dimension.setWidth(width); + dimension.setHeight(height); + dimension.setDimensionUnit(DimensionUnit.CM); + dimension.setWeight(weight); + dimension.setWeightUnit(WeightUnit.KG); + dimension.setContentUnitCount(contentUnitCount); + dimension.setDeprecated(false); + return dimension; + } +} diff --git a/src/test/java/de/avatic/lcc/repositories/PackagingRepositoryIntegrationTest.java b/src/test/java/de/avatic/lcc/repositories/PackagingRepositoryIntegrationTest.java index 4e4fc73..59bca4f 100644 --- a/src/test/java/de/avatic/lcc/repositories/PackagingRepositoryIntegrationTest.java +++ b/src/test/java/de/avatic/lcc/repositories/PackagingRepositoryIntegrationTest.java @@ -37,9 +37,16 @@ class PackagingRepositoryIntegrationTest extends AbstractRepositoryIntegrationTe private Integer testMaterialId2; private Integer testNodeId1; private Integer testNodeId2; + private Integer testDimensionId1; + private Integer testDimensionId2; @BeforeEach void setupTestData() { + // Create test packaging dimensions (required by foreign key) + // Dimensions: length, width, height in mm, weight in g, content_unit_count + testDimensionId1 = createTestDimension(1000, 500, 300, 10000, 1); + testDimensionId2 = createTestDimension(1200, 600, 400, 15000, 1); + // Create test materials (required by foreign key) testMaterialId1 = createTestMaterial("TEST-MAT-001", "Test Material 1"); testMaterialId2 = createTestMaterial("TEST-MAT-002", "Test Material 2"); @@ -55,8 +62,8 @@ class PackagingRepositoryIntegrationTest extends AbstractRepositoryIntegrationTe Packaging packaging = new Packaging(); packaging.setMaterialId(testMaterialId1); packaging.setSupplierId(testNodeId1); - packaging.setHuId(1); // Handling unit dimension - packaging.setShuId(1); // Shipping handling unit dimension + packaging.setHuId(testDimensionId1); // Handling unit dimension + packaging.setShuId(testDimensionId1); // Shipping handling unit dimension packaging.setDeprecated(false); // When: Insert @@ -81,25 +88,25 @@ class PackagingRepositoryIntegrationTest extends AbstractRepositoryIntegrationTe @Test void testUpdate() { // Given: Create and insert packaging - Packaging packaging = createTestPackaging(testMaterialId1, testNodeId1, 1, 1, false); + Packaging packaging = createTestPackaging(testMaterialId1, testNodeId1, testDimensionId1, testDimensionId1, false); Integer packagingId = packagingRepository.insert(packaging).orElseThrow(); // When: Update packaging Packaging toUpdate = packagingRepository.getById(packagingId).orElseThrow(); - toUpdate.setHuId(2); - toUpdate.setShuId(2); + toUpdate.setHuId(testDimensionId2); + toUpdate.setShuId(testDimensionId2); packagingRepository.update(toUpdate); // Then: Verify update Packaging updated = packagingRepository.getById(packagingId).orElseThrow(); - assertEquals(2, updated.getHuId()); - assertEquals(2, updated.getShuId()); + assertEquals(testDimensionId2, updated.getHuId()); + assertEquals(testDimensionId2, updated.getShuId()); } @Test void testSetDeprecatedById() { // Given: Create packaging - Packaging packaging = createTestPackaging(1, 1, 1, 1, false); + Packaging packaging = createTestPackaging(testMaterialId1, testNodeId1, testDimensionId1, testDimensionId1, false); Integer packagingId = packagingRepository.insert(packaging).orElseThrow(); // When: Deprecate @@ -115,7 +122,7 @@ class PackagingRepositoryIntegrationTest extends AbstractRepositoryIntegrationTe void testListPackagingWithPagination() { // Given: Create multiple packagings for (int i = 0; i < 5; i++) { - Packaging packaging = createTestPackaging(1, 1, 1, 1, false); + Packaging packaging = createTestPackaging(testMaterialId1, testNodeId1, testDimensionId1, testDimensionId1, false); packagingRepository.insert(packaging); } @@ -134,54 +141,54 @@ class PackagingRepositoryIntegrationTest extends AbstractRepositoryIntegrationTe @Test void testListPackagingFilterByMaterialId() { // Given: Create packagings with different materials - Packaging packaging1 = createTestPackaging(1, 1, 1, 1, false); + Packaging packaging1 = createTestPackaging(testMaterialId1, testNodeId1, testDimensionId1, testDimensionId1, false); packagingRepository.insert(packaging1); - Packaging packaging2 = createTestPackaging(2, 1, 1, 1, false); + Packaging packaging2 = createTestPackaging(testMaterialId2, testNodeId1, testDimensionId1, testDimensionId1, false); packagingRepository.insert(packaging2); - // When: Filter by materialId=1 + // When: Filter by materialId SearchQueryPagination pagination = new SearchQueryPagination(1, 10); SearchQueryResult result = packagingRepository.listPackaging( - 1, null, false, pagination + testMaterialId1, null, false, pagination ); // Then: Should only return material 1 packagings assertNotNull(result); for (Packaging p : result.toList()) { - assertEquals(1, p.getMaterialId(), "Should only return packagings with materialId=1"); + assertEquals(testMaterialId1, p.getMaterialId(), "Should only return packagings with correct materialId"); } } @Test void testListPackagingFilterBySupplierId() { // Given: Create packagings with different suppliers - Packaging packaging1 = createTestPackaging(1, 1, 1, 1, false); + Packaging packaging1 = createTestPackaging(testMaterialId1, testNodeId1, testDimensionId1, testDimensionId1, false); packagingRepository.insert(packaging1); - Packaging packaging2 = createTestPackaging(1, 2, 1, 1, false); + Packaging packaging2 = createTestPackaging(testMaterialId1, testNodeId2, testDimensionId1, testDimensionId1, false); packagingRepository.insert(packaging2); - // When: Filter by supplierId=1 + // When: Filter by supplierId SearchQueryPagination pagination = new SearchQueryPagination(1, 10); SearchQueryResult result = packagingRepository.listPackaging( - null, 1, false, pagination + null, testNodeId1, false, pagination ); // Then: Should only return supplier 1 packagings assertNotNull(result); for (Packaging p : result.toList()) { - assertEquals(1, p.getSupplierId(), "Should only return packagings with supplierId=1"); + assertEquals(testNodeId1, p.getSupplierId(), "Should only return packagings with correct supplierId"); } } @Test void testListPackagingExcludeDeprecated() { // Given: Create deprecated and non-deprecated packagings - Packaging deprecated = createTestPackaging(1, 1, 1, 1, true); + Packaging deprecated = createTestPackaging(testMaterialId1, testNodeId1, testDimensionId1, testDimensionId1, true); packagingRepository.insert(deprecated); - Packaging active = createTestPackaging(1, 1, 1, 1, false); + Packaging active = createTestPackaging(testMaterialId1, testNodeId1, testDimensionId1, testDimensionId1, false); packagingRepository.insert(active); // When: List excluding deprecated @@ -200,37 +207,37 @@ class PackagingRepositoryIntegrationTest extends AbstractRepositoryIntegrationTe @Test void testGetByMaterialId() { // Given: Create packagings for specific material - Packaging packaging1 = createTestPackaging(10, 1, 1, 1, false); + Packaging packaging1 = createTestPackaging(testMaterialId1, testNodeId1, testDimensionId1, testDimensionId1, false); packagingRepository.insert(packaging1); - Packaging packaging2 = createTestPackaging(10, 2, 1, 1, false); + Packaging packaging2 = createTestPackaging(testMaterialId1, testNodeId2, testDimensionId1, testDimensionId1, false); packagingRepository.insert(packaging2); // When: Get by materialId - List packagings = packagingRepository.getByMaterialId(10); + List packagings = packagingRepository.getByMaterialId(testMaterialId1); // Then: Should return all packagings for that material assertNotNull(packagings); - assertTrue(packagings.size() >= 2, "Should find at least 2 packagings for material 10"); + assertTrue(packagings.size() >= 2, "Should find at least 2 packagings for material"); for (Packaging p : packagings) { - assertEquals(10, p.getMaterialId()); + assertEquals(testMaterialId1, p.getMaterialId()); } } @Test void testGetByMaterialIdAndSupplierId() { // Given: Create packaging with specific material and supplier - Packaging packaging = createTestPackaging(15, 15, 1, 1, false); + Packaging packaging = createTestPackaging(testMaterialId1, testNodeId1, testDimensionId1, testDimensionId1, false); Integer packagingId = packagingRepository.insert(packaging).orElseThrow(); // When: Get by materialId and supplierId - Optional result = packagingRepository.getByMaterialIdAndSupplierId(15, 15); + Optional result = packagingRepository.getByMaterialIdAndSupplierId(testMaterialId1, testNodeId1); // Then: Should find the packaging - assertTrue(result.isPresent(), "Should find packaging with materialId=15 and supplierId=15"); + assertTrue(result.isPresent(), "Should find packaging with correct IDs"); assertEquals(packagingId, result.get().getId()); - assertEquals(15, result.get().getMaterialId()); - assertEquals(15, result.get().getSupplierId()); + assertEquals(testMaterialId1, result.get().getMaterialId()); + assertEquals(testNodeId1, result.get().getSupplierId()); } @Test @@ -245,10 +252,10 @@ class PackagingRepositoryIntegrationTest extends AbstractRepositoryIntegrationTe @Test void testListAllPackaging() { // Given: Create packagings - Packaging packaging1 = createTestPackaging(1, 1, 1, 1, false); + Packaging packaging1 = createTestPackaging(testMaterialId1, testNodeId1, testDimensionId1, testDimensionId1, false); packagingRepository.insert(packaging1); - Packaging packaging2 = createTestPackaging(2, 2, 1, 1, false); + Packaging packaging2 = createTestPackaging(testMaterialId2, testNodeId2, testDimensionId1, testDimensionId1, false); packagingRepository.insert(packaging2); // When: List all @@ -267,6 +274,37 @@ class PackagingRepositoryIntegrationTest extends AbstractRepositoryIntegrationTe // ========== Helper Methods ========== + private Integer createTestDimension(Integer length, Integer width, Integer height, Integer weight, Integer contentUnitCount) { + String sql = "INSERT INTO packaging_dimension (length, width, height, weight, content_unit_count) VALUES (?, ?, ?, ?, ?)"; + executeRawSql(sql, length, width, height, weight, contentUnitCount); + + // Get last inserted ID + String selectSql = isMysql() ? "SELECT LAST_INSERT_ID()" : "SELECT CAST(@@IDENTITY AS INT)"; + return jdbcTemplate.queryForObject(selectSql, Integer.class); + } + + private Integer createTestMaterial(String partNumber, String name) { + String sql = "INSERT INTO material (part_number, normalized_part_number, name, is_deprecated) VALUES (?, ?, ?, " + + dialectProvider.getBooleanFalse() + ")"; + executeRawSql(sql, partNumber, partNumber.toUpperCase(), name); + + // Get last inserted ID (works for both MySQL and MSSQL after ServiceConnection auto-config) + String selectSql = isMysql() ? "SELECT LAST_INSERT_ID()" : "SELECT CAST(@@IDENTITY AS INT)"; + return jdbcTemplate.queryForObject(selectSql, Integer.class); + } + + private Integer createTestNode(String name, Integer countryId) { + String sql = "INSERT INTO node (name, address, geo_lat, geo_lng, is_deprecated, is_destination, is_source, is_intermediate, country_id, predecessor_required) " + + "VALUES (?, ?, ?, ?, " + dialectProvider.getBooleanFalse() + ", " + + dialectProvider.getBooleanTrue() + ", " + dialectProvider.getBooleanTrue() + ", " + + dialectProvider.getBooleanFalse() + ", ?, " + dialectProvider.getBooleanFalse() + ")"; + executeRawSql(sql, name, "Test Address", 50.0, 10.0, countryId); + + // Get last inserted ID + String selectSql = isMysql() ? "SELECT LAST_INSERT_ID()" : "SELECT CAST(@@IDENTITY AS INT)"; + return jdbcTemplate.queryForObject(selectSql, Integer.class); + } + private Packaging createTestPackaging(Integer materialId, Integer supplierId, Integer huId, Integer shuId, boolean deprecated) { Packaging packaging = new Packaging(); packaging.setMaterialId(materialId); diff --git a/src/test/java/de/avatic/lcc/repositories/users/UserNodeRepositoryIntegrationTest.java b/src/test/java/de/avatic/lcc/repositories/users/UserNodeRepositoryIntegrationTest.java new file mode 100644 index 0000000..6f69cf2 --- /dev/null +++ b/src/test/java/de/avatic/lcc/repositories/users/UserNodeRepositoryIntegrationTest.java @@ -0,0 +1,284 @@ +package de.avatic.lcc.repositories.users; + +import de.avatic.lcc.model.db.nodes.Node; +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.math.BigDecimal; +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Integration tests for UserNodeRepository. + *

+ * Tests critical functionality across both MySQL and MSSQL: + * - CRUD operations (Create, Read) + * - Search with filtering and pagination + * - Boolean literal compatibility (is_deprecated filtering) + * - Bulk operations (getByIds, checkOwner) + *

+ * Run with: + *

+ * mvn test -Dspring.profiles.active=test,mysql -Dtest=UserNodeRepositoryIntegrationTest
+ * mvn test -Dspring.profiles.active=test,mssql -Dtest=UserNodeRepositoryIntegrationTest
+ * 
+ */ +class UserNodeRepositoryIntegrationTest extends AbstractRepositoryIntegrationTest { + + @Autowired + private UserNodeRepository userNodeRepository; + + private Integer testUserId1; + private Integer testUserId2; + private Integer testCountryId; + + @BeforeEach + void setupTestData() { + // Create test users + testUserId1 = createTestUser("user1@test.com", "WORKDAY001"); + testUserId2 = createTestUser("user2@test.com", "WORKDAY002"); + + // Use existing country (id=1 should exist from Flyway migrations) + testCountryId = 1; + } + + @Test + void testAddAndRetrieve() { + // Given: Create user node + Node node = createTestNode("Test Supplier Berlin", "Berlin, Germany", 52.5200, 13.4050); + + // When: Add + Integer nodeId = userNodeRepository.add(testUserId1, node); + + // Then: Should be inserted successfully + assertNotNull(nodeId); + assertTrue(nodeId > 0); + + // When: Retrieve by ID + Optional retrieved = userNodeRepository.getById(nodeId); + + // Then: Should retrieve successfully + assertTrue(retrieved.isPresent(), "User node should be retrievable after insert"); + assertEquals("Test Supplier Berlin", retrieved.get().getName()); + assertEquals("Berlin, Germany", retrieved.get().getAddress()); + assertEquals(new BigDecimal("52.5200"), retrieved.get().getGeoLat()); + assertEquals(new BigDecimal("13.4050"), retrieved.get().getGeoLng()); + assertFalse(retrieved.get().getDeprecated()); + assertEquals(testCountryId, retrieved.get().getCountryId()); + assertTrue(retrieved.get().isUserNode()); + } + + @Test + void testGetByIdNotFound() { + // When: Get by non-existent ID + Optional result = userNodeRepository.getById(99999); + + // Then: Should return empty + assertFalse(result.isPresent(), "Should not find user node with non-existent ID"); + } + + @Test + void testSearchNodeWithFilter() { + // Given: Insert multiple user nodes + Node node1 = createTestNode("Berlin Supplier", "Berlin", 52.5200, 13.4050); + userNodeRepository.add(testUserId1, node1); + + Node node2 = createTestNode("Munich Supplier", "Munich", 48.1351, 11.5820); + userNodeRepository.add(testUserId1, node2); + + Node node3 = createTestNode("Hamburg Distribution", "Hamburg", 53.5511, 9.9937); + userNodeRepository.add(testUserId1, node3); + + // When: Search for "Supplier" + Collection results = userNodeRepository.searchNode("Supplier", 10, testUserId1, false); + + // Then: Should find nodes with "Supplier" in name + assertNotNull(results); + assertTrue(results.size() >= 2, "Should find at least 2 nodes with 'Supplier'"); + + for (Node node : results) { + assertTrue(node.getName().contains("Supplier") || node.getAddress().contains("Supplier"), + "Results should match filter"); + } + } + + @Test + void testSearchNodeWithPagination() { + // Given: Insert multiple user nodes + for (int i = 1; i <= 5; i++) { + Node node = createTestNode("Supplier " + i, "Address " + i, 50.0 + i, 10.0 + i); + userNodeRepository.add(testUserId1, node); + } + + // When: Search with limit 3 + Collection results = userNodeRepository.searchNode(null, 3, testUserId1, false); + + // Then: Should respect pagination limit + assertNotNull(results); + assertTrue(results.size() <= 3, "Should return at most 3 nodes"); + } + + @Test + void testSearchNodeExcludeDeprecated() { + // Given: Insert deprecated and non-deprecated user nodes + Node deprecated = createTestNode("Deprecated Supplier", "Old Address", 50.0, 10.0); + deprecated.setDeprecated(true); + userNodeRepository.add(testUserId1, deprecated); + + Node active = createTestNode("Active Supplier", "New Address", 51.0, 11.0); + userNodeRepository.add(testUserId1, active); + + // When: Search excluding deprecated + Collection results = userNodeRepository.searchNode(null, 10, testUserId1, true); + + // Then: Should not include deprecated nodes + assertNotNull(results); + for (Node node : results) { + assertFalse(node.getDeprecated(), "Should not include deprecated nodes"); + } + } + + @Test + void testSearchNodeByUserId() { + // Given: Insert nodes for different users + Node user1Node = createTestNode("User 1 Supplier", "User 1 Address", 50.0, 10.0); + userNodeRepository.add(testUserId1, user1Node); + + Node user2Node = createTestNode("User 2 Supplier", "User 2 Address", 51.0, 11.0); + userNodeRepository.add(testUserId2, user2Node); + + // When: Search for user1 nodes + Collection user1Results = userNodeRepository.searchNode(null, 10, testUserId1, false); + + // Then: Should only return user1 nodes + assertNotNull(user1Results); + // Can't assert exact count because other tests might have created nodes + // Just verify all returned nodes belong to user1 + for (Node node : user1Results) { + // Note: We can't directly verify userId in the Node object since it's not stored there + // The verification happens implicitly through the WHERE clause + assertNotNull(node.getName()); + } + } + + @Test + void testGetByIds() { + // Given: Insert multiple user nodes + Node node1 = createTestNode("Bulk Node 1", "Address 1", 50.0, 10.0); + Integer id1 = userNodeRepository.add(testUserId1, node1); + + Node node2 = createTestNode("Bulk Node 2", "Address 2", 51.0, 11.0); + Integer id2 = userNodeRepository.add(testUserId1, node2); + + Node node3 = createTestNode("Bulk Node 3", "Address 3", 52.0, 12.0); + Integer id3 = userNodeRepository.add(testUserId1, node3); + + // When: Get by IDs + List ids = List.of(id1, id2, id3); + Collection nodes = userNodeRepository.getByIds(ids); + + // Then: Should return all requested nodes + assertNotNull(nodes); + assertEquals(3, nodes.size(), "Should return exactly 3 nodes"); + + List retrievedIds = nodes.stream().map(Node::getId).toList(); + assertTrue(retrievedIds.contains(id1)); + assertTrue(retrievedIds.contains(id2)); + assertTrue(retrievedIds.contains(id3)); + } + + @Test + void testGetByIdsEmptyList() { + // When: Get by empty list + Collection nodes = userNodeRepository.getByIds(List.of()); + + // Then: Should return empty collection + assertNotNull(nodes); + assertTrue(nodes.isEmpty()); + } + + @Test + void testGetOwnerById() { + // Given: Insert user node + Node node = createTestNode("Owner Test Node", "Address", 50.0, 10.0); + Integer nodeId = userNodeRepository.add(testUserId1, node); + + // When: Get owner + Optional owner = userNodeRepository.getOwnerById(nodeId); + + // Then: Should return correct user ID + assertTrue(owner.isPresent()); + assertEquals(testUserId1, owner.get()); + } + + @Test + void testGetOwnerByIdNotFound() { + // When: Get owner of non-existent node + Optional owner = userNodeRepository.getOwnerById(99999); + + // Then: Should return empty + assertFalse(owner.isPresent()); + } + + @Test + void testCheckOwnerValid() { + // Given: Insert user nodes + Node node1 = createTestNode("Owner Check 1", "Address 1", 50.0, 10.0); + Integer id1 = userNodeRepository.add(testUserId1, node1); + + Node node2 = createTestNode("Owner Check 2", "Address 2", 51.0, 11.0); + Integer id2 = userNodeRepository.add(testUserId1, node2); + + // When/Then: Should not throw exception for valid owner + assertDoesNotThrow(() -> + userNodeRepository.checkOwner(List.of(id1, id2), testUserId1) + ); + } + + @Test + void testCheckOwnerInvalid() { + // Given: Insert user node for user1 + Node node = createTestNode("Owner Violation", "Address", 50.0, 10.0); + Integer nodeId = userNodeRepository.add(testUserId1, node); + + // When/Then: Should throw exception when user2 tries to access user1's node + assertThrows(Exception.class, () -> + userNodeRepository.checkOwner(List.of(nodeId), testUserId2) + ); + } + + @Test + void testCheckOwnerEmptyList() { + // When/Then: Should not throw exception for empty list + assertDoesNotThrow(() -> + userNodeRepository.checkOwner(List.of(), testUserId1) + ); + } + + // ========== Helper Methods ========== + + private Integer createTestUser(String email, String workdayId) { + String sql = "INSERT INTO sys_user (email, workday_id, firstname, lastname, is_active) VALUES (?, ?, ?, ?, " + + dialectProvider.getBooleanTrue() + ")"; + executeRawSql(sql, email, workdayId, "Test", "User"); + + String selectSql = isMysql() ? "SELECT LAST_INSERT_ID()" : "SELECT CAST(@@IDENTITY AS INT)"; + return jdbcTemplate.queryForObject(selectSql, Integer.class); + } + + private Node createTestNode(String name, String address, double geoLat, double geoLng) { + Node node = new Node(); + node.setName(name); + node.setAddress(address); + node.setGeoLat(new BigDecimal(String.valueOf(geoLat))); + node.setGeoLng(new BigDecimal(String.valueOf(geoLng))); + node.setDeprecated(false); + node.setCountryId(testCountryId); + return node; + } +}