fix(lastleg): route all relabel pickups directly to outbound and verify final schemas
This commit is contained in:
+16
@@ -56,6 +56,22 @@ class Phase5OverseasUpgradeSqlContractTest {
|
||||
assertThat(overseas).doesNotContain("\nUPDATE ", "\nDELETE ", "ALTER TABLE ll_");
|
||||
}
|
||||
|
||||
@Test
|
||||
void transferFinalMeasurementColumnsAreCreatedBeforeAnyBusinessWrite() throws Exception {
|
||||
for (String path : List.of("lastleg-service/src/main/resources/db/lastleg.sql",
|
||||
"deploy/sql/海外区域统一增量-MySQL8.sql")) {
|
||||
String sql = repositoryFile(path);
|
||||
String carton = createStatement(sql, "ll_warehouse_transfer_carton");
|
||||
for (String column : List.of("customer_carton_mark", "length_cm", "width_cm", "height_cm",
|
||||
"gross_weight_kg", "counted_quantity", "count_idempotency_key", "count_request_hash",
|
||||
"counted_by", "counted_at", "discrepancy_reason", "transfer_pallet_id", "sku_items_json")) {
|
||||
assertThat(carton).as(path + ":" + column).contains(column);
|
||||
}
|
||||
assertThat(createStatement(sql, "ll_warehouse_transfer_item")).contains("declared_quantity");
|
||||
assertThat(createStatement(sql, "ll_cargo_discard_item")).contains("idx_ll_cargo_discard_return_carton");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void overseasFixedEntryAndModuleBaselineBothDefineCompletePhase5Tables() throws Exception {
|
||||
String baseline = repositoryFile("lastleg-service/src/main/resources/db/lastleg.sql");
|
||||
|
||||
+3
-2
@@ -26,7 +26,7 @@ class SchemaChineseCommentContractTest {
|
||||
private static final Pattern CHINESE = Pattern.compile("[\\u4e00-\\u9fff]");
|
||||
private static final Pattern TABLE = Pattern.compile(
|
||||
"(?is)CREATE\\s+TABLE\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?(ll_[a-z0-9_]+)"
|
||||
+ "\\s*\\((.*?)\\)\\s*ENGINE\\s*=\\s*InnoDB[^;]*?COMMENT\\s*=\\s*'([^']*)'\\s*;");
|
||||
+ "\\s*\\((.*?)\\)\\s*ENGINE\\s*=\\s*InnoDB([^;]*);");
|
||||
|
||||
@Test
|
||||
void allLastlegTablesAndColumnsHaveChineseComments() throws Exception {
|
||||
@@ -104,7 +104,8 @@ class SchemaChineseCommentContractTest {
|
||||
Matcher matcher = TABLE.matcher(sql);
|
||||
while (matcher.find()) {
|
||||
String table = matcher.group(1);
|
||||
assertTrue(CHINESE.matcher(matcher.group(3)).find(), source + " 表备注必须包含中文: " + table);
|
||||
assertTrue(Pattern.compile("(?is)\\bCOMMENT\\s*=\\s*'[^']*[\\u4e00-\\u9fff][^']*'")
|
||||
.matcher(matcher.group(3)).find(), source + " 表备注必须包含中文: " + table);
|
||||
List<String> columns = splitTopLevel(matcher.group(2));
|
||||
assertFalse(columns.isEmpty(), source + " 表定义没有字段: " + table);
|
||||
List<String> columnNames = new ArrayList<>();
|
||||
|
||||
+52
-12
@@ -6,35 +6,62 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class SelfoperatedAnalyticsSchemaContractTest {
|
||||
private static final Pattern TABLE = Pattern.compile(
|
||||
"(?s)CREATE TABLE (ll_selfop_\\w+) \\(.*?\\) ENGINE=InnoDB[^;]+;");
|
||||
"(?s)CREATE TABLE (?:IF NOT EXISTS )?(ll_selfop_\\w+) \\(.*?\\) ENGINE=InnoDB[^;]+;");
|
||||
private static final Set<String> EXPECTED_TABLES = Set.of(
|
||||
"ll_selfop_outbound_discovery_scope", "ll_selfop_sync_scope", "ll_selfop_sync_task",
|
||||
"ll_selfop_sync_run", "ll_selfop_sync_stage", "ll_selfop_outbound_order",
|
||||
"ll_selfop_outbound_item", "ll_selfop_inventory_snapshot", "ll_selfop_sku_daily_sales",
|
||||
"ll_selfop_warehouse_daily_sales", "ll_selfop_sku_profile", "ll_selfop_owner",
|
||||
"ll_selfop_product", "ll_selfop_inventory_flow", "ll_selfop_inventory_day_version",
|
||||
"ll_selfop_inventory_daily");
|
||||
|
||||
private static Map<String, String> tables(String sql) {
|
||||
var result = new LinkedHashMap<String, String>();
|
||||
var matcher = TABLE.matcher(sql);
|
||||
var matcher = TABLE.matcher(sql.replace("\r\n", "\n"));
|
||||
while (matcher.find()) {
|
||||
assertNull(result.put(matcher.group(1), matcher.group()), "duplicate table definition");
|
||||
assertNull(result.put(matcher.group(1), matcher.group().replaceFirst("CREATE TABLE IF NOT EXISTS", "CREATE TABLE")),
|
||||
"duplicate table definition");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Test
|
||||
void sourceAndEmptyDatabaseBaselineContainIdenticalElevenIndependentTables() throws Exception {
|
||||
void sourceAndEmptyDatabaseBaselineContainAllCurrentIndependentTables() throws Exception {
|
||||
var source = tables(Files.readString(Path.of("src/main/resources/db/lastleg.sql")));
|
||||
var deployment = tables(Files.readString(Path.of("../deploy/sql/海外区域统一增量-MySQL8.sql")));
|
||||
assertEquals(11, source.size());
|
||||
assertEquals(EXPECTED_TABLES, source.keySet());
|
||||
assertEquals(source, deployment);
|
||||
for (var entry : source.entrySet()) {
|
||||
var ddl = entry.getValue();
|
||||
assertTrue(ddl.contains("COLLATE=utf8mb4_bin"), entry.getKey());
|
||||
assertTrue(ddl.contains("id BIGINT NOT NULL"));
|
||||
assertTrue(ddl.contains("created_at DATETIME(3) NOT NULL"));
|
||||
assertTrue(ddl.contains("updated_at DATETIME(3) NOT NULL"));
|
||||
boolean master = Set.of("ll_selfop_owner", "ll_selfop_product").contains(entry.getKey());
|
||||
boolean history = Set.of("ll_selfop_inventory_flow", "ll_selfop_inventory_day_version",
|
||||
"ll_selfop_inventory_daily").contains(entry.getKey());
|
||||
if (master) {
|
||||
assertTrue(ddl.contains("source_account VARCHAR(64) COLLATE utf8mb4_bin NOT NULL"));
|
||||
assertTrue(ddl.contains("customer_code VARCHAR(64) COLLATE utf8mb4_bin NOT NULL"));
|
||||
assertTrue(ddl.contains("observed_at_utc DATETIME(6) NOT NULL"));
|
||||
} else {
|
||||
assertTrue(ddl.contains("COLLATE=utf8mb4_bin"), entry.getKey());
|
||||
}
|
||||
if (!master && !history) {
|
||||
assertTrue(ddl.contains("created_at DATETIME(3) NOT NULL"), entry.getKey());
|
||||
assertTrue(ddl.contains("updated_at DATETIME(3) NOT NULL"), entry.getKey());
|
||||
}
|
||||
assertTrue(ddl.contains("PRIMARY KEY ("), entry.getKey());
|
||||
if (entry.getKey().equals("ll_selfop_inventory_daily")) {
|
||||
assertTrue(ddl.contains("PRIMARY KEY (day_version_id,customer_code,sku_code,product_quality)"));
|
||||
} else if (entry.getKey().equals("ll_selfop_warehouse_daily_sales")) {
|
||||
assertTrue(ddl.contains("PRIMARY KEY (sales_run_id,source_account,warehouse_code,business_date)"));
|
||||
} else {
|
||||
assertTrue(ddl.contains("\n id BIGINT NOT NULL"), entry.getKey());
|
||||
}
|
||||
assertFalse(ddl.contains("AUTO_INCREMENT"));
|
||||
assertFalse(ddl.contains("tenant_id"));
|
||||
assertFalse(ddl.contains("FOREIGN KEY"));
|
||||
@@ -107,21 +134,34 @@ class SelfoperatedAnalyticsSchemaContractTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void readAndSyncPermissionsAreIndependentWithThreeAuthorizedAdminMenus() throws Exception {
|
||||
void readSyncAndExportPermissionsAreIndependentWithFourAuthorizedAdminMenus() throws Exception {
|
||||
var seed = Files.readString(Path.of("../iam/src/main/resources/db/selfoperated-analytics-permissions.sql"));
|
||||
var unified = Files.readString(Path.of("../deploy/sql/头程数据库统一增量-MySQL8.sql"));
|
||||
assertTrue(unified.contains(seed.strip()));
|
||||
// Compare executable seed statements; explanatory comments and line endings are not SQL facts.
|
||||
String seedSql = seed.replace("\r\n", "\n").replaceAll("(?m)^\\s*--[^\n]*", "");
|
||||
String unifiedSql = unified.replace("\r\n", "\n").replaceAll("(?m)^\\s*--[^\n]*", "");
|
||||
int comparedStatements = 0;
|
||||
for (String statement : seedSql.split(";")) {
|
||||
if (statement.strip().startsWith("INSERT INTO")) {
|
||||
assertTrue(unifiedSql.contains(statement.strip() + ";"), statement);
|
||||
comparedStatements++;
|
||||
}
|
||||
}
|
||||
assertEquals(2, comparedStatements, "permission and role grant INSERT statements must both be checked");
|
||||
assertTrue(seed.contains("platform:self-operated-analytics:list"));
|
||||
assertTrue(seed.contains("platform:self-operated-analytics:sync"));
|
||||
for (var route : new String[]{"/self-operated/sales", "/self-operated/inventory", "/self-operated/collection"}) {
|
||||
assertTrue(seed.contains("platform:self-operated-analytics:export"));
|
||||
for (var route : new String[]{"/self-operated/overview", "/self-operated/sales", "/self-operated/inventory", "/self-operated/collection"}) {
|
||||
assertTrue(seed.contains(route));
|
||||
}
|
||||
assertTrue(seed.contains("'API',5118100,'platform:self-operated-analytics:list'"));
|
||||
assertTrue(seed.contains("'API',5118130,'platform:self-operated-analytics:sync'"));
|
||||
assertTrue(seed.contains("'API',5118120,'platform:self-operated-analytics:export'"));
|
||||
assertFalse(seed.contains("'MERCHANT'"));
|
||||
assertFalse(seed.contains("INSERT IGNORE"));
|
||||
assertFalse(seed.contains("ON DUPLICATE KEY"));
|
||||
assertTrue(seed.contains("(4000001,5118000,0,"));
|
||||
assertTrue(seed.contains("(4000001,5118001,0,"));
|
||||
assertTrue(seed.contains("(4000001,5118002,0,"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user