package com.baiyue.lastleg.procurement; import com.baiyue.lastleg.procurement.dto.OverseasProcurementRequests; import com.baiyue.lastleg.procurement.dto.OverseasProcurementResponses.*; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import java.math.BigDecimal; import java.util.List; import static org.assertj.core.api.Assertions.*; class ProcurementRequestBoundaryTest { @Test void purchaseCategoryMayBeOmittedOrBlankButCannotExceedLimit() throws Exception { var json = new ObjectMapper(); try (var factory = jakarta.validation.Validation.buildDefaultValidatorFactory()) { var validator = factory.getValidator(); for (String category : List.of("null", "\"\"", "\"sample\"")) { var request = json.readValue("{\"purchaseCategory\":" + category + "}", OverseasProcurementRequests.Create.class); assertThat(validator.validate(request)).noneMatch(v -> "purchaseCategory".equals(v.getPropertyPath().toString())); } var oversized = json.readValue("{\"purchaseCategory\":\"" + "x".repeat(129) + "\"}", OverseasProcurementRequests.Create.class); assertThat(validator.validate(oversized)).anyMatch(v -> "purchaseCategory".equals(v.getPropertyPath().toString())); } } @Test void createRejectsMultiProductAndBatchExtensionsWithApplicationJacksonDefaults() { new org.springframework.boot.test.context.runner.ApplicationContextRunner() .withConfiguration(org.springframework.boot.autoconfigure.AutoConfigurations.of( org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration.class)) .withUserConfiguration(com.baiyue.common.web.LongIdJacksonConfiguration.class) .run(context -> { var json=context.getBean(ObjectMapper.class); assertThat(json.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)).isFalse(); String valid=""" {"purchaseCategory":"sample","productName":"Sample","productSpecification":"A", "purchaseQuantity":1,"budgetUnitPrice":10,"currency":"CNY","receivingAddress":"Address", "contactName":"Buyer","contactPhone":"13800000000","idempotencyKey":"one"} """; assertThat(json.readValue(valid,OverseasProcurementRequests.Create.class).productName()).isEqualTo("Sample"); for(String field:List.of("items","products","batches","purchaseBatches","shipmentBatches")) { var extended=(com.fasterxml.jackson.databind.node.ObjectNode)json.readTree(valid); extended.putArray(field).addObject().put("productName","Second product"); assertThatThrownBy(()->json.treeToValue(extended,OverseasProcurementRequests.Create.class)) .as(field).hasMessageContaining("单商品"); } }); } @Test void missingCommandVersionCannotDefaultToZero() throws Exception { var json=new ObjectMapper().findAndRegisterModules(); try(var factory=jakarta.validation.Validation.buildDefaultValidatorFactory()) { var validator=factory.getValidator(); for(var type:List.of(OverseasProcurementRequests.Review.class, OverseasProcurementRequests.Update.class)) { assertThat(validator.validate(json.readValue("{}",type))).anyMatch(v->"version".equals(v.getPropertyPath().toString())); } } } @Test void legacyGenericFieldsCannotSilentlyBypassDedicatedNodes() { var json=new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); for(String field:List.of("inboundQuantity","outboundDetails","productName","purchaseWarehouseId","domesticTrackingNo")) { assertThatThrownBy(()->json.readValue("{\"version\":0,\""+field+"\":null}",OverseasProcurementRequests.Update.class)) .hasMessageContaining("采购通用编辑仅支持备注"); } } @Test void customerProjectionExcludesInternalFilesAndFinancialDiagnostics() throws Exception { var fee=new com.baiyue.lastleg.financefee.entity.OverseasFeeSource(); fee.setSourceFeeId(1L);fee.setFeeItemName("关税");fee.setAmount(BigDecimal.TEN);fee.setCurrency("CNY");fee.setWalletStatus("FREEZE_FAILED"); fee.setPaymentVoucherFileIds(List.of("private.pdf"));fee.setWalletErrorMessage("database error");fee.setReviewReason("internal note");fee.setCreatedBy("operator"); var output=new ObjectMapper().writeValueAsString(com.baiyue.lastleg.procurement.service.ProcurementQueryService.CustomerFee.fromManual(fee)); assertThat(output).contains("FREEZE_FAILED").doesNotContain("private.pdf","voucherFileId","internal note","database error","operator","private-ledger"); } }