fix: complete phase5 overseas deployment contract
This commit is contained in:
+3
-2
@@ -65,8 +65,9 @@ class DeliverySchemaContractTest {
|
||||
String overseas = Files.readString(Path.of("..", "deploy", "sql", "海外区域统一增量-MySQL8.sql"),
|
||||
StandardCharsets.UTF_8).toLowerCase();
|
||||
assertTrue(overseas.contains("空库先执行 lastleg-service/src/main/resources/db/lastleg.sql 基线"));
|
||||
assertFalse(overseas.contains(createToken));
|
||||
assertFalse(overseas.contains("alter table ll_delivery_order"));
|
||||
assertTrue(overseas.contains(createToken));
|
||||
assertTrue(overseas.indexOf(createToken) < overseas.indexOf("assert_phase5_lastleg_identifiers_migratable"));
|
||||
assertTrue(overseas.contains("alter table ll_delivery_order add column overseas_warehouse_name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
package com.baiyue.lastleg.dropship.controller;
|
||||
|
||||
import com.baiyue.lastleg.dropship.dto.DropshipRequests;
|
||||
import com.baiyue.lastleg.dropship.service.DropshipOrderService;
|
||||
import com.baiyue.lastleg.security.InternalServiceTokenGuard;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InOrder;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class DropshipOrderControllerContractTest {
|
||||
@Test
|
||||
void routeRequiresTheFrozenInternalTokenHeaderBeforeCallingTheService() throws Exception {
|
||||
RequestMapping base = DropshipOrderController.class.getAnnotation(RequestMapping.class);
|
||||
Method receive = DropshipOrderController.class.getMethod("receive", String.class,
|
||||
DropshipRequests.Receive.class);
|
||||
RequestHeader token = receive.getParameters()[0].getAnnotation(RequestHeader.class);
|
||||
|
||||
assertThat(base.value()).containsExactly("/internal/v1/lastleg/dropship");
|
||||
assertThat(receive.getAnnotation(PostMapping.class).value()).containsExactly("/receive");
|
||||
assertThat(token.value()).isEqualTo("X-Internal-Service-Token");
|
||||
|
||||
InternalServiceTokenGuard guard = mock(InternalServiceTokenGuard.class);
|
||||
DropshipOrderService service = mock(DropshipOrderService.class);
|
||||
DropshipOrderController controller = new DropshipOrderController();
|
||||
ReflectionTestUtils.setField(controller, "tokenGuard", guard);
|
||||
ReflectionTestUtils.setField(controller, "service", service);
|
||||
DropshipRequests.Receive request = request();
|
||||
when(service.receive(request)).thenReturn(1);
|
||||
|
||||
assertThat(controller.receive("internal-token", request).data()).isEqualTo(1);
|
||||
InOrder order = inOrder(guard, service);
|
||||
order.verify(guard).verify("internal-token");
|
||||
order.verify(service).receive(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
void versionedOpenApiMatchesTheControllerAndDtoContract() throws Exception {
|
||||
String yaml = Files.readString(
|
||||
Path.of("src/main/resources/openapi/lastleg-internal-dropship-v1.yaml"),
|
||||
StandardCharsets.UTF_8);
|
||||
|
||||
assertThat(yaml).contains(
|
||||
"version: 1.0.0",
|
||||
"/internal/v1/lastleg/dropship/receive:",
|
||||
"name: X-Internal-Service-Token",
|
||||
"required: [customerId, orderId, warehouseId, idempotencyKey, pallets]",
|
||||
"required: [palletNo, skuPackages]",
|
||||
"required: [skuCode, packageCount, productQuantity, cartonMarks]",
|
||||
"data: { type: integer, format: int32, minimum: 0 }");
|
||||
assertThat(yaml.indexOf("name: X-Internal-Service-Token"))
|
||||
.isLessThan(yaml.indexOf("requestBody:"));
|
||||
}
|
||||
|
||||
private static DropshipRequests.Receive request() {
|
||||
return new DropshipRequests.Receive("100", 10L, 20L, "event-1",
|
||||
List.of(new DropshipRequests.Pallet("PALLET-1",
|
||||
List.of(new DropshipRequests.SkuPackages("SKU-1", "Product", 1, 2,
|
||||
List.of("CARTON-1"))))));
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.baiyue.lastleg.schema;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class Phase5ExceptionVisitSchemaContractTest {
|
||||
@Test
|
||||
void baselineAndOfficialOverseasEntryDefineExceptionAndVisitPersistence() throws Exception {
|
||||
String baseline = Files.readString(Path.of("src/main/resources/db/lastleg.sql"), StandardCharsets.UTF_8);
|
||||
String overseas = Files.readString(Path.of("..", "deploy", "sql", "海外区域统一增量-MySQL8.sql"),
|
||||
StandardCharsets.UTF_8);
|
||||
|
||||
for (String sql : new String[] { baseline, overseas }) {
|
||||
assertThat(sql).contains(
|
||||
"CREATE TABLE IF NOT EXISTS ll_exception_order",
|
||||
"UNIQUE KEY uk_ll_exception_no(customer_id,exception_no)",
|
||||
"UNIQUE KEY uk_ll_exception_delivery_return(customer_id,delivery_order_id,exception_type)",
|
||||
"KEY idx_ll_exception_inbound(customer_id,inbound_code)",
|
||||
"CREATE TABLE IF NOT EXISTS ll_customer_visit",
|
||||
"UNIQUE KEY uk_ll_customer_visit_delivery(customer_id,delivery_order_id)",
|
||||
"KEY idx_ll_customer_visit_list(customer_id,status,delivery_completed_at)",
|
||||
"COMMENT='海外仓异常订单'",
|
||||
"COMMENT='派送单维度客户回访记录'");
|
||||
}
|
||||
}
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
package com.baiyue.lastleg.schema;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class Phase5OverseasUpgradeSqlContractTest {
|
||||
private static final List<String> PHASE5_TABLES = List.of(
|
||||
"ll_dispatch_pda_task", "ll_dispatch_pda_pick", "ll_dispatch_pda_operation",
|
||||
"ll_dispatch_pda_pick_order", "ll_outbound_order", "ll_outbound_pallet_item",
|
||||
"ll_outbound_pda_task", "ll_outbound_pda_pick", "ll_delivery_order", "ll_delivery_fee",
|
||||
"ll_delivery_operation", "ll_dropship_order_profile", "ll_dropship_carton",
|
||||
"ll_dropship_sku_inventory", "ll_dropship_carton_ledger", "ll_exception_order",
|
||||
"ll_customer_visit");
|
||||
|
||||
@Test
|
||||
void overseasFixedEntryCreatesEveryPhase5TableBeforeAnyPhase5AlterOrAssertion() throws Exception {
|
||||
String overseas = repositoryFile("deploy/sql/海外区域统一增量-MySQL8.sql");
|
||||
int phase5Gate = overseas.indexOf("-- 五期排车、出库与海外PDA正式业务号/幂等约束");
|
||||
assertThat(phase5Gate).isPositive();
|
||||
|
||||
for (String table : PHASE5_TABLES) {
|
||||
String create = "CREATE TABLE IF NOT EXISTS " + table + " (";
|
||||
assertThat(overseas.indexOf(create)).as(table).isBetween(0, phase5Gate - 1);
|
||||
assertThat(count(overseas, create)).as(table).isEqualTo(1);
|
||||
assertThat(overseas.indexOf(table)).as(table + " first reference")
|
||||
.isEqualTo(overseas.indexOf(create) + "CREATE TABLE IF NOT EXISTS ".length());
|
||||
}
|
||||
assertThat(overseas).contains("<> 22", "五期完整业务表集合未部署");
|
||||
}
|
||||
|
||||
@Test
|
||||
void overseasFixedEntryConvergesKnownIntermediatePhase5ShapesWithoutBackfill() throws Exception {
|
||||
String overseas = repositoryFile("deploy/sql/海外区域统一增量-MySQL8.sql");
|
||||
|
||||
assertThat(overseas).contains(
|
||||
"ALTER TABLE ll_outbound_order RENAME COLUMN tenant_id TO customer_id",
|
||||
"ALTER TABLE ll_outbound_pallet_item RENAME COLUMN tenant_id TO customer_id",
|
||||
"ALTER TABLE ll_delivery_order RENAME COLUMN tenant_id TO customer_id",
|
||||
"ALTER TABLE ll_outbound_order ADD COLUMN outbound_type",
|
||||
"ALTER TABLE ll_outbound_pallet_item ADD COLUMN picked_package_count",
|
||||
"ALTER TABLE ll_delivery_order ADD COLUMN overseas_warehouse_name",
|
||||
"ALTER TABLE ll_delivery_order ADD COLUMN waybill_no",
|
||||
"ALTER TABLE ll_dropship_order_profile ADD COLUMN source_pallet_no",
|
||||
"ADD UNIQUE KEY uk_ll_dropship_sku_inventory(profile_id,sku_code)",
|
||||
"MODIFY COLUMN source_pallet_no VARCHAR(128) NOT NULL");
|
||||
int start = overseas.indexOf("-- 五期正式结构必须由海外固定入口自行补齐");
|
||||
int end = overseas.indexOf("USE baiyue_lastleg;", start);
|
||||
String phase5 = overseas.substring(start, end);
|
||||
assertThat(phase5).doesNotContain("\nUPDATE ", "\nDELETE ", "\nINSERT ");
|
||||
}
|
||||
|
||||
@Test
|
||||
void overseasFixedEntryUsesTheCurrentBaselineDefinitions() throws Exception {
|
||||
String baseline = repositoryFile("lastleg-service/src/main/resources/db/lastleg.sql");
|
||||
String overseas = repositoryFile("deploy/sql/海外区域统一增量-MySQL8.sql");
|
||||
|
||||
for (String table : PHASE5_TABLES) {
|
||||
assertThat(normalize(createStatement(overseas, table)))
|
||||
.as(table)
|
||||
.isEqualTo(normalize(createStatement(baseline, table)));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void domesticFixedEntryKeepsIamPermissionsButOwnsNoLastlegBusinessSchema() throws Exception {
|
||||
String domestic = repositoryFile("deploy/sql/头程数据库统一增量-MySQL8.sql");
|
||||
|
||||
assertThat(domestic).contains(
|
||||
"platform:overseas-warehouse:dispatch:list",
|
||||
"platform:overseas-warehouse:exception-order:list",
|
||||
"platform:overseas-warehouse:customer-visit:list");
|
||||
assertThat(domestic).doesNotContain(
|
||||
"USE baiyue_lastleg",
|
||||
"CREATE DATABASE IF NOT EXISTS baiyue_lastleg",
|
||||
"CREATE TABLE IF NOT EXISTS ll_",
|
||||
"ALTER TABLE ll_");
|
||||
}
|
||||
|
||||
private static String createStatement(String sql, String table) {
|
||||
String token = "CREATE TABLE IF NOT EXISTS " + table + " (";
|
||||
int start = sql.indexOf(token);
|
||||
int end = sql.indexOf(';', start);
|
||||
assertThat(start).as(table).isGreaterThanOrEqualTo(0);
|
||||
assertThat(end).as(table).isGreaterThan(start);
|
||||
return sql.substring(start, end + 1);
|
||||
}
|
||||
|
||||
private static String normalize(String sql) {
|
||||
return sql.toLowerCase(Locale.ROOT).replaceAll("\\s+", "");
|
||||
}
|
||||
|
||||
private static int count(String source, String token) {
|
||||
return source.split(java.util.regex.Pattern.quote(token), -1).length - 1;
|
||||
}
|
||||
|
||||
private static String repositoryFile(String relative) throws Exception {
|
||||
Path path = Path.of(relative);
|
||||
if (!Files.exists(path)) path = Path.of("..").resolve(relative);
|
||||
return Files.readString(path, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user