2026-08-13 09:49:55 +08:00
|
|
|
import assert from 'node:assert/strict'
|
|
|
|
|
import { readFileSync } from 'node:fs'
|
|
|
|
|
import test from 'node:test'
|
|
|
|
|
|
|
|
|
|
const sql = readFileSync(new URL('../../deploy/sql/头程数据库统一增量-MySQL8.sql', import.meta.url), 'utf8')
|
2026-08-14 09:46:50 +08:00
|
|
|
const iamSeed = readFileSync(new URL('../../iam/src/main/resources/db/phase1-admin-rbac.sql', import.meta.url), 'utf8')
|
2026-08-20 13:34:29 +08:00
|
|
|
const oceanBookingSchema = readFileSync(new URL('../../firstleg/src/main/resources/db/phase3-ocean-booking-schema.sql', import.meta.url), 'utf8')
|
|
|
|
|
const truckAppointmentSchema = readFileSync(new URL('../../firstleg/src/main/resources/db/phase3-truck-appointment-schema.sql', import.meta.url), 'utf8')
|
2026-08-13 09:49:55 +08:00
|
|
|
|
2026-08-26 11:10:42 +08:00
|
|
|
test('统一初始化 SQL 只保留平台管理员且不创建固定客户', () => {
|
|
|
|
|
assert.match(sql, /最小登录基线只保留平台系统管理员/)
|
2026-08-13 09:49:55 +08:00
|
|
|
assert.equal((sql.match(/'platform_admin'/g) || []).length, 1)
|
2026-08-26 11:10:42 +08:00
|
|
|
assert.doesNotMatch(sql, /'merchant_admin'|'IT_TEST'|'IT 测试客户'/)
|
|
|
|
|
assert.doesNotMatch(iamSeed, /'IT_TEST'|'IT 测试客户'|\(10001,/)
|
2026-08-24 20:14:40 +08:00
|
|
|
const accountSeed = sql.match(/INSERT INTO auth_account \([\s\S]*?deleted = 0;/)?.[0]
|
|
|
|
|
assert.ok(accountSeed, 'missing auth account seed')
|
|
|
|
|
assert.doesNotMatch(accountSeed, /ON DUPLICATE KEY UPDATE[\s\S]*?password_hash\s*=/,
|
|
|
|
|
're-running the unified SQL must not reset an existing account password')
|
2026-08-13 09:49:55 +08:00
|
|
|
})
|
|
|
|
|
|
2026-08-26 11:10:42 +08:00
|
|
|
test('统一初始化直接提供tenant0商户角色与权限模板', () => {
|
|
|
|
|
assert.match(sql, /4000100, 0, 'MERCHANT', 'MERCHANT_OWNER'/)
|
|
|
|
|
assert.match(sql, /5300000, 0, 'MERCHANT', 'MENU',[\s\S]*?'merchant:enterprise'/)
|
|
|
|
|
assert.match(sql, /5301024, 0, 'MERCHANT', 'BUTTON',[\s\S]*?'merchant:order:quick-create'/)
|
|
|
|
|
assert.match(sql, /5305000, 0, 'MERCHANT', 'MENU',[\s\S]*?'merchant:finance:statement'/)
|
|
|
|
|
assert.match(sql, /r\.id=4000100[\s\S]*?客户全局管理员 Finance 模板授权不完整/)
|
2026-08-14 09:46:50 +08:00
|
|
|
})
|
|
|
|
|
|
2026-08-13 09:49:55 +08:00
|
|
|
test('统一初始化 SQL 提供人工验收所需公共仓储主数据', () => {
|
|
|
|
|
for (const marker of [
|
|
|
|
|
"'TP-001'", "'TP-002'", "'A1 普通库位'", "'A0 共享溢出位'",
|
2026-08-15 11:36:53 +08:00
|
|
|
"'CAR-001'", "'CARRIER'", "'SHIPPER'", "'EXP-TEST'", "'IT 测试快递'",
|
2026-08-13 09:49:55 +08:00
|
|
|
]) assert.ok(sql.includes(marker), `missing initialization marker: ${marker}`)
|
|
|
|
|
assert.match(sql, /'EXP-TEST', 'IT 测试快递'[\s\S]*?'ACTIVE'/)
|
2026-08-26 13:58:33 +08:00
|
|
|
assert.match(sql, /193000000000400003,0,193000000000200003,'RECEIVING','海外收货',1,'16:00:00',[\s\S]*?'洛杉矶仓海外接柜入仓收货能力'/)
|
|
|
|
|
assert.match(sql, /193000000000400004,0,193000000000200004,'RECEIVING','海外收货',1,'16:00:00',[\s\S]*?'纽约仓海外接柜入仓收货能力'/)
|
|
|
|
|
assert.match(sql, /固定平台作业仓收货能力写入校验失败/)
|
2026-08-14 18:50:07 +08:00
|
|
|
|
|
|
|
|
const palletSeed = sql.match(/INSERT INTO wh_pallet \([\s\S]*?remark = VALUES\(remark\);/)?.[0]
|
|
|
|
|
assert.ok(palletSeed, 'missing pallet initialization seed')
|
|
|
|
|
assert.doesNotMatch(palletSeed, /\bstatus\b|'AVAILABLE'/)
|
2026-08-13 09:49:55 +08:00
|
|
|
})
|
2026-08-20 13:34:29 +08:00
|
|
|
|
2026-08-26 21:17:14 +08:00
|
|
|
test('国内统一初始化支持真实空库且不越界创建海外业务库', () => {
|
|
|
|
|
assert.doesNotMatch(sql, /CREATE DATABASE IF NOT EXISTS baiyue_lastleg|USE baiyue_lastleg;/)
|
|
|
|
|
assert.match(sql, /IF EXISTS \(SELECT 1 FROM information_schema\.tables WHERE table_schema=DATABASE\(\)[\s\S]*?table_name='fl_ocean_booking'\) THEN[\s\S]*?UPDATE fl_container_booking container_booking[\s\S]*?JOIN fl_ocean_booking ocean_booking/)
|
|
|
|
|
assert.ok(
|
|
|
|
|
sql.indexOf("table_name='fl_ocean_booking') THEN") < sql.indexOf('CREATE TABLE IF NOT EXISTS fl_ocean_booking ('),
|
|
|
|
|
'empty-database backfill must be guarded before the ocean-booking baseline is created',
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
2026-08-24 20:14:40 +08:00
|
|
|
test('三期权限迁移删除退役权限并只保留 canonical 权限', () => {
|
2026-08-20 13:34:29 +08:00
|
|
|
for (const source of [sql, oceanBookingSchema]) {
|
2026-08-24 20:14:40 +08:00
|
|
|
assert.match(source, /DELETE FROM iam_permission WHERE id IN \(5102141,5102143,5102144,5102145\)/)
|
|
|
|
|
assert.match(source, /5102147[\s\S]*?platform:firstleg:ocean-booking:edit/)
|
|
|
|
|
assert.match(source, /5102148[\s\S]*?platform:firstleg:ocean-booking:delete/)
|
|
|
|
|
assert.doesNotMatch(source, /platform:firstleg:ocean-booking:(view|cancel|change)/)
|
2026-08-20 14:43:07 +08:00
|
|
|
assert.ok(
|
2026-08-24 20:14:40 +08:00
|
|
|
source.indexOf('DELETE FROM iam_permission WHERE id IN (5102141,5102143,5102144,5102145)') <
|
2026-08-20 14:43:07 +08:00
|
|
|
source.indexOf('(5102147,0,\'PLATFORM\',\'BUTTON\',5102140,\'platform:firstleg:ocean-booking:edit\''),
|
|
|
|
|
'legacy ocean-booking edit permission must be removed before assigning the code to 5102147',
|
|
|
|
|
)
|
2026-08-20 13:34:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const source of [sql, truckAppointmentSchema]) {
|
2026-08-24 20:14:40 +08:00
|
|
|
assert.match(source, /DELETE FROM iam_permission WHERE id=5102151/)
|
|
|
|
|
assert.doesNotMatch(source, /platform:firstleg:truck-appointment:view[',]/)
|
2026-08-20 13:34:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert.match(sql, /status='ACTIVE' AND deleted=0[\s\S]*?id BETWEEN 5102140 AND 5102149/)
|
|
|
|
|
assert.match(sql, /status='ACTIVE' AND deleted=0[\s\S]*?id BETWEEN 5102150 AND 5102154/)
|
|
|
|
|
})
|