feat: complete phase 5 overseas fulfillment contracts
This commit is contained in:
+139
@@ -0,0 +1,139 @@
|
||||
package com.baiyue.lastleg.delivery.controller;
|
||||
|
||||
import com.baiyue.common.security.AuthenticatedUser;
|
||||
import com.baiyue.lastleg.delivery.dto.DeliveryRequests;
|
||||
import com.baiyue.lastleg.delivery.service.DeliveryTransferService;
|
||||
import com.baiyue.lastleg.security.OverseasPermissions;
|
||||
import jakarta.validation.Validation;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
class MerchantDeliveryControllerTest {
|
||||
private final DeliveryTransferService service = mock(DeliveryTransferService.class);
|
||||
private final MerchantDeliveryController controller = controller();
|
||||
private final DeliveryRequests.Query query = new DeliveryRequests.Query(
|
||||
1, 20, null, null, null, null, null, null, null, null, null, null);
|
||||
|
||||
@Test
|
||||
void fixedRoutesUseIndependentListAndCreatePermissionsWithoutCustomerParameter() throws Exception {
|
||||
RequestMapping mapping = MerchantDeliveryController.class.getAnnotation(RequestMapping.class);
|
||||
assertEquals(List.of("/api/merchant/v1/last-leg/delivery-orders"), List.of(mapping.value()));
|
||||
|
||||
Method page = MerchantDeliveryController.class.getMethod("page", AuthenticatedUser.class,
|
||||
DeliveryRequests.Query.class);
|
||||
Method create = MerchantDeliveryController.class.getMethod("create", AuthenticatedUser.class,
|
||||
String.class, DeliveryRequests.Transfer.class);
|
||||
Method detail = MerchantDeliveryController.class.getMethod("detail", AuthenticatedUser.class, String.class);
|
||||
|
||||
assertPermission(page, OverseasPermissions.MERCHANT_DELIVERY_LIST);
|
||||
assertPermission(detail, OverseasPermissions.MERCHANT_DELIVERY_LIST);
|
||||
assertPermission(create, OverseasPermissions.MERCHANT_DELIVERY_CREATE);
|
||||
assertNotEquals(OverseasPermissions.MERCHANT_DELIVERY_LIST,
|
||||
OverseasPermissions.MERCHANT_DELIVERY_CREATE);
|
||||
assertTrue(Arrays.stream(page.getParameters()).noneMatch(this::isCustomerParameter));
|
||||
assertTrue(Arrays.stream(detail.getParameters()).noneMatch(this::isCustomerParameter));
|
||||
assertTrue(Arrays.stream(create.getParameters()).noneMatch(this::isCustomerParameter));
|
||||
assertTrue(Arrays.stream(DeliveryRequests.Transfer.class.getRecordComponents())
|
||||
.noneMatch(component -> "customerId".equals(component.getName())));
|
||||
}
|
||||
|
||||
@Test
|
||||
void allOperationsUseOnlyAuthenticatedCustomerScope() {
|
||||
AuthenticatedUser merchant = user(100L, "MERCHANT");
|
||||
DeliveryRequests.Transfer request = transferRequest();
|
||||
|
||||
controller.page(merchant, query);
|
||||
controller.detail(merchant, "DEL-100");
|
||||
controller.create(merchant, "DSP-100", request);
|
||||
|
||||
verify(service).deliveryPage("100", query);
|
||||
verify(service).deliveryDetail("100", "DEL-100");
|
||||
verify(service).merchantTransfer("100", "DSP-100", "1", request);
|
||||
}
|
||||
|
||||
@Test
|
||||
void merchantEndpointsRejectPlatformIdentity() {
|
||||
AuthenticatedUser platform = user(0L, "PLATFORM");
|
||||
|
||||
assertThrows(ResponseStatusException.class, () -> controller.page(platform, query));
|
||||
assertThrows(ResponseStatusException.class, () -> controller.detail(platform, "DEL-100"));
|
||||
assertThrows(ResponseStatusException.class, () -> controller.create(platform, "DSP-100", transferRequest()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void invalidCreatePayloadIsRejectedByBeanValidationContract() {
|
||||
DeliveryRequests.Transfer invalid = new DeliveryRequests.Transfer(" ", null, " ", null, null,
|
||||
" ", " ", null, null, null, null, null, null, null, null, null);
|
||||
try (var factory = Validation.buildDefaultValidatorFactory()) {
|
||||
assertTrue(factory.getValidator().validate(invalid).size() >= 6);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void openApiPublishesOnlyTokenScopedMerchantDeliveryProtocol() throws Exception {
|
||||
String yaml = Files.readString(Path.of("src/main/resources/openapi/overseas-business-v1.yaml"),
|
||||
StandardCharsets.UTF_8);
|
||||
String contract = slice(yaml, " /api/merchant/v1/last-leg/delivery-orders:",
|
||||
" /api/merchant/v1/last-leg/overseas-procurements:");
|
||||
|
||||
assertTrue(contract.contains("merchant:warehouse:delivery:list"));
|
||||
assertTrue(contract.contains("merchant:warehouse:delivery:create"));
|
||||
assertFalse(contract.contains("name: customerId"));
|
||||
}
|
||||
|
||||
private boolean isCustomerParameter(java.lang.reflect.Parameter parameter) {
|
||||
RequestParam annotation = parameter.getAnnotation(RequestParam.class);
|
||||
return annotation != null && ("customerId".equals(annotation.name())
|
||||
|| "customerId".equals(annotation.value()));
|
||||
}
|
||||
|
||||
private static void assertPermission(Method method, String permission) {
|
||||
PreAuthorize annotation = method.getAnnotation(PreAuthorize.class);
|
||||
assertEquals("hasAuthority('" + permission + "')", annotation.value());
|
||||
}
|
||||
|
||||
private static String slice(String source, String start, String end) {
|
||||
int from = source.indexOf(start);
|
||||
int to = source.indexOf(end, from + start.length());
|
||||
assertTrue(from >= 0 && to > from);
|
||||
return source.substring(from, to);
|
||||
}
|
||||
|
||||
private static AuthenticatedUser user(long tenantId, String appCode) {
|
||||
return new AuthenticatedUser(1L, tenantId, tenantId, 9L, "merchant", appCode, "WEB", Set.of());
|
||||
}
|
||||
|
||||
private MerchantDeliveryController controller() {
|
||||
MerchantDeliveryController value = new MerchantDeliveryController();
|
||||
ReflectionTestUtils.setField(value, "service", service);
|
||||
return value;
|
||||
}
|
||||
|
||||
private static DeliveryRequests.Transfer transferRequest() {
|
||||
return new DeliveryRequests.Transfer("merchant-key", LocalDateTime.now(), "SELF_DELIVERY", null, null,
|
||||
"ONE_PICKUP_ONE_DROP", "PALLET", null, null, null, null, null, null, null, null,
|
||||
List.of(new DeliveryRequests.FeeItem("DELIVERY_FEE", "USD", BigDecimal.ZERO, null)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user