2026-08-06 11:49:51 +08:00
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import { dirname , resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
const currentDir = dirname ( fileURLToPath ( import . meta . url ))
2026-08-30 17:02:19 +08:00
const warehouseRoot = resolve ( currentDir , '..' )
2026-08-06 11:49:51 +08:00
const sqlPath = resolve ( warehouseRoot , 'src/main/resources/db/warehouse-public-master-data.sql' )
const acceptedPath = resolve ( warehouseRoot , 'src/main/resources/db/master-data/fba-public-addresses.accepted.csv' )
const rejectedPath = resolve ( warehouseRoot , 'src/main/resources/db/master-data/fba-public-addresses.rejected.csv' )
const fixedWarehousePath = resolve ( warehouseRoot , 'src/main/resources/db/master-data/platform-fixed-warehouses.csv' )
function parseCsv ( text ) {
const rows = []
let field = ''
let row = []
let quoted = false
for ( let index = 0 ; index < text . length ; index += 1 ) {
const char = text [ index ]
if ( quoted ) {
if ( char === '"' && text [ index + 1 ] === '"' ) {
field += '"'
index += 1
} else if ( char === '"' ) {
quoted = false
} else {
field += char
}
} else if ( char === '"' ) {
quoted = true
} else if ( char === ',' ) {
row . push ( field )
field = ''
} else if ( char === '\n' ) {
row . push ( field . replace ( /\r$/ , '' ))
rows . push ( row )
row = []
field = ''
} else {
field += char
}
}
if ( field . length > 0 || row . length > 0 ) {
row . push ( field )
rows . push ( row )
}
const [ headers , ... data ] = rows . filter (( item ) => item . some (( value ) => value !== '' ))
return data . map (( values ) => Object . fromEntries ( headers . map (( header , index ) => [ header , values [ index ] ?? '' ])))
}
const sql = readFileSync ( sqlPath , 'utf8' )
const accepted = parseCsv ( readFileSync ( acceptedPath , 'utf8' ))
const rejected = parseCsv ( readFileSync ( rejectedPath , 'utf8' ))
const fixedWarehouses = parseCsv ( readFileSync ( fixedWarehousePath , 'utf8' ))
assert . equal ( accepted . length , 252 )
assert . equal ( rejected . length , 54 )
assert . equal ( fixedWarehouses . length , 4 )
const codes = new Set ()
const warehouseIds = new Set ()
const addressIds = new Set ()
for ( const row of accepted ) {
assert . match ( row . warehouse_code , /^[A-Z0-9]{3,5}$/ )
assert . equal ( row . warehouse_code , row . warehouse_code . trim (). toUpperCase ())
assert . ok ( row . city . trim ())
assert . ok ( row . state_province . trim ())
assert . ok ( row . address_line1 . trim ())
assert . ok ( row . postal_code . trim ())
assert . ok ( row . timezone . trim ())
assert . equal ( row . country_code , 'US' )
assert . ok ( ! codes . has ( row . warehouse_code ), `duplicate code ${ row . warehouse_code } ` )
assert . ok ( ! warehouseIds . has ( row . warehouse_id_seed ), `duplicate warehouse id ${ row . warehouse_id_seed } ` )
assert . ok ( ! addressIds . has ( row . address_id_seed ), `duplicate address id ${ row . address_id_seed } ` )
codes . add ( row . warehouse_code )
warehouseIds . add ( row . warehouse_id_seed )
addressIds . add ( row . address_id_seed )
}
assert . ok ( rejected . some (( row ) => row . reject_reason . includes ( 'non_fba_business_node' )))
assert . equal ( rejected . filter (( row ) => row . reject_reason . includes ( 'timezone_pending_confirmation' )). length , 1 )
assert . deepEqual ( fixedWarehouses . map (( row ) => row . warehouse_code ), [
'CN-YIWU-01' ,
'CN-DONGGUAN-01' ,
'US-LAX-01' ,
'US-NYC-01' ,
])
assert . deepEqual ( fixedWarehouses . map (( row ) => row . timezone ), [
'UTC+8' ,
'UTC+8' ,
'UTC-8' ,
'UTC-5' ,
])
assert . ok ( fixedWarehouses . every (( row ) => row . address_line1 . includes ( '待维护' ) || row . address_line1 . includes ( 'pending maintenance' )))
assert . match ( sql , /CREATE TEMPORARY TABLE tmp_wh_fixed_platform_warehouse/ )
assert . match ( sql , /'CN-YIWU-01','义乌仓','DOMESTIC','CN','UTC\+8'/ )
assert . match ( sql , /'CN-DONGGUAN-01','东莞仓','DOMESTIC','CN','UTC\+8'/ )
assert . match ( sql , /'US-LAX-01','洛杉矶仓','OVERSEAS','US','UTC-8'/ )
assert . match ( sql , /'US-NYC-01','纽约仓','OVERSEAS','US','UTC-5'/ )
assert . match ( sql , /固定平台仓地址种子ID与既有数据冲突/ )
assert . match ( sql , /固定平台仓地址父子解析失败/ )
assert . match ( sql , /FROM tmp_wh_fixed_platform_warehouse source[\s\S]+ON DUPLICATE KEY UPDATE\s+tenant_id = VALUES\(tenant_id\);/ )
assert . match ( sql , /CREATE TEMPORARY TABLE tmp_wh_fba_public_master_data/ )
assert . match ( sql , /JOIN wh_warehouse warehouse\s+ON warehouse\.tenant_id = 0\s+AND warehouse\.warehouse_code = source\.warehouse_code/s)
assert . match ( sql , /ON DUPLICATE KEY UPDATE\s+warehouse_type = VALUES\(warehouse_type\),\s+country_code = VALUES\(country_code\),\s+timezone = VALUES\(timezone\),\s+updated_at = CURRENT_TIMESTAMP\(3\);/s)
assert . doesNotMatch ( sql , /status\s*=\s*VALUES\(status\)/ )
assert . doesNotMatch ( sql , /contact_name\s*=\s*VALUES\(contact_name\)/ )
assert . doesNotMatch ( sql , /contact_phone\s*=\s*VALUES\(contact_phone\)/ )
assert . doesNotMatch ( sql , /contact_email\s*=\s*VALUES\(contact_email\)/ )
assert . doesNotMatch ( sql , /remark\s*=\s*VALUES\(remark\)/ )
2026-08-26 13:58:33 +08:00
assert . match ( sql , /四个固定平台作业仓必须启用收货能力/ )
2026-08-24 21:00:22 +08:00
assert . match ( sql , /INSERT INTO wh_service_capability[\s\S]+?'RECEIVING'/i )
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 , /warehouse_id IN \(193000000000200001,193000000000200002,193000000000200003,193000000000200004\)[\s\S]*?<> 4 THEN[\s\S]*?固定平台作业仓收货能力写入校验失败/ )
2026-08-24 21:00:22 +08:00
const fbaBlock = sql . slice ( sql . indexOf ( 'CREATE TEMPORARY TABLE tmp_wh_fba_public_master_data' ))
assert . doesNotMatch ( fbaBlock , /INSERT INTO wh_service_capability/i )
2026-08-06 11:49:51 +08:00
assert . match ( sql , /FBA公共主数据地址种子ID与既有数据冲突/ )
assert . match ( sql , /FBA公共地址不得创建平台作业能力/ )