fix(lastleg): route all relabel pickups directly to outbound and verify final schemas
This commit is contained in:
@@ -8,8 +8,16 @@ const domesticSchemas = ['baiyue_auth', 'baiyue_iam', 'baiyue_order', 'baiyue_wa
|
||||
'baiyue_firstleg', 'baiyue_file', 'baiyue_audit', 'baiyue_finance']
|
||||
const overseasSchemas = ['baiyue_lastleg', 'baiyue_lastlag_warehouse']
|
||||
|
||||
test('种子解析覆盖多行多值、函数括号及带逗号和引号的文本', () => {
|
||||
const sql = "INSERT INTO sample (id,label,stamp,note) VALUES\r\n (1,'甲,乙',CURRENT_TIMESTAMP(3),NULL),\r\n (2,'客户''名称',CURRENT_TIMESTAMP(3),'已确认');\r\n"
|
||||
assert.deepEqual(seedRows(sql, 'sample'), [
|
||||
{ id: '1', label: '甲,乙', stamp: 'CURRENT_TIMESTAMP(3)', note: null },
|
||||
{ id: '2', label: "客户'名称", stamp: 'CURRENT_TIMESTAMP(3)', note: '已确认' },
|
||||
])
|
||||
})
|
||||
|
||||
test('两个入口分别只操作八个主业务库与两个仓储业务库,包括限定名引用', () => {
|
||||
for (const [sql, allowed, other, count] of [[cn, domesticSchemas, overseasSchemas, 226], [overseas, overseasSchemas, domesticSchemas, 159]]) {
|
||||
for (const [sql, allowed, other, count] of [[cn, domesticSchemas, overseasSchemas, 227], [overseas, overseasSchemas, domesticSchemas, 208]]) {
|
||||
assert.deepEqual([...sql.matchAll(/^CREATE DATABASE IF NOT EXISTS (\w+)/gm)].map(m => m[1]), allowed)
|
||||
assert.deepEqual([...new Set([...sql.matchAll(/^USE (\w+);/gm)].map(m => m[1]))], allowed)
|
||||
const body = sql.replace(/^--.*$/gm, '')
|
||||
@@ -39,7 +47,10 @@ test('纯初始化禁止迁移、数据回填、清理、重复建表和提前
|
||||
assert.doesNotMatch(body.replace(/^CREATE TRIGGER [\s\S]*?\$\$/gm, ''), /^\s*(?:UPDATE|DELETE FROM|REPLACE INTO)\b/gmi)
|
||||
assert.doesNotMatch(body, /CREATE TABLE IF NOT EXISTS/i)
|
||||
assert.equal((body.match(/AS initialization_status/g) ?? []).length, 1)
|
||||
assert.match(body.trim(), /SELECT 'PASS' AS initialization_status, '(CN|OVERSEAS)' AS region, (226|159) AS initialized_tables;$/)
|
||||
const end = body.trim().match(/SELECT 'PASS' AS initialization_status, '(CN|OVERSEAS)' AS region, (\d+) AS initialized_tables;$/)
|
||||
assert.ok(end, 'only the final statement may announce successful initialization')
|
||||
assert.equal(Number(end[2]), tableDefinitions(sql).size)
|
||||
assert.equal(end[1], sql === cn ? 'CN' : 'OVERSEAS')
|
||||
}
|
||||
})
|
||||
|
||||
@@ -100,10 +111,11 @@ test('公司车辆费用隔离于客户账务并约束实际付款金额', () =>
|
||||
test('退货出库不可变流水限制重复扣减并保存数量守恒快照', () => {
|
||||
const journal = table(overseas, 'baiyue_lastleg', 'll_return_inventory_outbound_movement')
|
||||
assert.match(journal, /UNIQUE KEY uk_ll_return_outbound_pick_sku\(outbound_pick_id,sku_code\)/i)
|
||||
assert.match(journal, /UNIQUE KEY uk_ll_return_outbound_carton_sku\(customer_id,carton_id,sku_code\)/i)
|
||||
assert.match(journal, /KEY idx_ll_return_outbound_carton_sku\(customer_id,carton_id,sku_code\)/i)
|
||||
assert.doesNotMatch(journal, /UNIQUE KEY uk_ll_return_outbound_carton_sku/i)
|
||||
assert.match(journal, /CHECK\(quantity>0 AND total_after>=0 AND available_after>=0/i)
|
||||
assert.match(journal, /total_before-total_after=quantity AND available_before-available_after=quantity/i)
|
||||
assert.match(journal, /cartons_before-cartons_after=1 AND total_after>=available_after/i)
|
||||
assert.match(journal, /cartons_before-cartons_after IN \(0,1\) AND total_after>=available_after/i)
|
||||
})
|
||||
|
||||
test('平台管理员由外部bcrypt引导,强制首登改密且不创建客户', () => {
|
||||
@@ -121,13 +133,14 @@ test('平台管理员由外部bcrypt引导,强制首登改密且不创建客
|
||||
})
|
||||
|
||||
test('必要系统种子完整保留,但不写入示例资源与业务事实', () => {
|
||||
for (const [name, count] of Object.entries({ iam_role: 8, iam_permission: 419, iam_role_permission: 600,
|
||||
for (const [name, count] of Object.entries({ iam_role: 8, iam_permission: 433, iam_role_permission: 614,
|
||||
iam_data_scope: 7, wh_warehouse: 318, wh_warehouse_address: 318, wh_service_capability: 4,
|
||||
fl_fee_item: 112, fl_price_channel_catalog: 15, fl_price_origin_alias: 7, fl_price_warehouse_mapping: 4,
|
||||
file_export_task_guard: 1, fin_subject: 1 })) assert.equal(seedRows(cn, name).length, count, name)
|
||||
for (const name of ['wh_pallet', 'wh_location', 'wh_partner', 'wh_partner_type', 'wh_express_company',
|
||||
'ord_order', 'fl_inbound_order', 'fin_fee_source']) assert.equal(seedRows(cn, name).length, 0, name)
|
||||
assert.deepEqual([...new Set([...overseas.matchAll(/^INSERT INTO (\w+)/gm)].map(m => m[1]))], [])
|
||||
assert.deepEqual([...new Set([...overseas.matchAll(/^INSERT INTO (\w+)/gm)].map(m => m[1]))], ['ll_appointment_work_guard'])
|
||||
assert.deepEqual(seedRows(overseas, 'll_appointment_work_guard'), [{ id: '1', initialized: 'FALSE' }])
|
||||
assert.ok(seedRows(cn, 'wh_warehouse').every(r => r.tenant_id === '0'))
|
||||
assert.ok(seedRows(cn, 'wh_service_capability').every(r => r.service_type === 'RECEIVING' || r.capability_code === 'RECEIVING' || Object.values(r).includes('RECEIVING')))
|
||||
})
|
||||
@@ -135,6 +148,10 @@ test('必要系统种子完整保留,但不写入示例资源与业务事实',
|
||||
test('国内IAM权限与角色授权没有丢失或悬挂,客户模板仍完整', () => {
|
||||
const roles = seedRows(cn, 'iam_role'), permissions = seedRows(cn, 'iam_permission')
|
||||
const roleIds = new Set(roles.map(r => r.id)), permissionIds = new Set(permissions.map(r => r.id))
|
||||
assert.equal(permissionIds.size, permissions.length, 'duplicate permission ID')
|
||||
const grants = seedRows(cn, 'iam_role_permission')
|
||||
assert.equal(new Set(grants.map(r => `${r.role_id}:${r.permission_id}:${r.tenant_id}`)).size, grants.length,
|
||||
'duplicate role permission grant')
|
||||
assert.equal(new Set(permissions.map(r => r.permission_code)).size, permissions.length)
|
||||
for (const row of seedRows(cn, 'iam_role_permission')) {
|
||||
assert.ok(roleIds.has(row.role_id), 'missing role ' + row.role_id)
|
||||
|
||||
@@ -49,12 +49,15 @@ function splitValues(value) {
|
||||
}
|
||||
|
||||
export function seedRows(sql, name) {
|
||||
const pattern = new RegExp(`^INSERT INTO ${name} \\(([^\\n]+?)\\) VALUES \\(([^\\n]+)\\);$`, 'gm')
|
||||
return [...sql.matchAll(pattern)].map(match => {
|
||||
const pattern = new RegExp(`^INSERT INTO ${name} \\(([^\\n]+?)\\) VALUES\\s*([\\s\\S]*?);$`, 'gm')
|
||||
return [...sql.replaceAll('\r\n', '\n').matchAll(pattern)].flatMap(match => {
|
||||
const keys = match[1].split(',').map(key => key.trim())
|
||||
const values = splitValues(match[2])
|
||||
if (keys.length !== values.length) throw new Error(`Invalid seed columns: ${name}`)
|
||||
return Object.fromEntries(keys.map((key, i) => [key, values[i]]))
|
||||
return splitValues(match[2]).map(row => {
|
||||
if (!row.startsWith('(') || !row.endsWith(')')) throw new Error(`Invalid seed row: ${name}`)
|
||||
const values = splitValues(row.slice(1, -1))
|
||||
if (keys.length !== values.length) throw new Error(`Invalid seed columns: ${name}`)
|
||||
return Object.fromEntries(keys.map((key, i) => [key, values[i]]))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user