This commit is contained in:
2026-09-29 18:47:37 +08:00
parent 2c60a29e0f
commit c2927ab9a6
291 changed files with 4008 additions and 6298 deletions
+47 -198
View File
@@ -1,204 +1,53 @@
import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
import { readFileSync, existsSync, readdirSync } from 'node:fs'
import test from 'node:test'
const read = p => readFileSync(new URL('../' + p, import.meta.url), 'utf8').replaceAll('\r\n','\n')
const exists = p => existsSync(new URL('../' + p, import.meta.url))
const read = async (path) => (await readFile(new URL(`../${path}`, import.meta.url), 'utf8'))
.replaceAll('\r\n', '\n')
test('local, development and test Nacos publishing remain environment-isolated', async () => {
const publisher = await read('scripts/publish-nacos-config.ps1')
const local = [
await read('config/nacos/local/gateway-overseas.yml'),
await read('config/nacos/local/lastleg-service.yml'),
await read('config/nacos/local/warehouse-overseas-service.yml'),
].join('\n')
const remote = [
await read('config/nacos/overseas/gateway-overseas.yml'),
await read('config/nacos/overseas/lastleg-service.yml'),
await read('config/nacos/overseas/warehouse-overseas-service.yml'),
].join('\n')
assert.match(publisher, /ValidateSet\('local', 'dev', 'development', 'test', 'production'\)/)
assert.match(publisher, /必须显式指定非 public 的 Nacos namespace/)
assert.match(local, /127\.0\.0\.1:9100/)
assert.match(local, /127\.0\.0\.1:9303/)
assert.doesNotMatch(remote, /127\.0\.0\.1|password:\s*123456/)
assert.match(remote, /http:\/\/lastleg-service:9303/)
assert.match(remote, /\$\{LASTLEG_DB_URL\}/)
assert.match(remote, /\$\{WAREHOUSE_DB_URL\}/)
test('TEST has one complete Compose, namespace, session and no retired deployment entry', () => {
const compose=read('deploy/docker-compose.yml')
for(const service of ['gateway','auth','iam','firstleg','finance','order','warehouse','file','print','integration-service','lastleg-service','warehouse-overseas-service','audit-service']) assert.match(compose,new RegExp('^ '+service+':','m'))
assert.doesNotMatch(compose,/event-bridge|gateway-overseas|OVERSEAS_REDIS|OVERSEAS_NACOS|OVERSEAS_JWT|JWT_SECRET|test-cn|test-overseas/)
assert.match(compose,/SPRING_PROFILES_ACTIVE: test/)
assert.match(compose,/NACOS_NAMESPACE: \$\{NACOS_NAMESPACE:\?actual TEST namespace ID required\}/)
assert.match(compose,/REDIS_DATABASE: \$\{REDIS_DATABASE:-0\}/)
assert.match(compose,/SPRING_DATA_REDIS_USERNAME: \$\{REDIS_USERNAME:-default\}/)
assert.match(compose,/JWT_JWKS_URI: http:\/\/auth:9100\/api\/auth\/\.well-known\/jwks.json/)
assert.equal([...compose.matchAll(/^ ports:/gm)].length,1)
assert.equal([...compose.matchAll(/^ JWT_RSA_JWK_SET:/gm)].length,1)
assert.equal([...compose.matchAll(/^ EVENT_OPERATIONS_TOKEN:/gm)].length,3)
assert.match(compose,/LASTLEG_SERVICE_URL: http:\/\/lastleg-service:9303/)
assert.match(compose,/FIRSTLEG_SERVICE_URL: http:\/\/firstleg:9202/)
assert.match(compose,/FINANCE_SERVICE_URL: http:\/\/finance:9203/)
assert.match(compose,/FILE_UPLOADS_HOST_DIR/)
assert.match(compose,/ROCKETMQ_NAME_SERVER/)
for(const p of ['deploy/docker-compose.overseas.yml','deploy/scripts/deploy-blue-green-service.sh','deploy/scripts/deploy-development-service.sh']) assert.equal(exists(p),false,p)
})
test('Jenkins pipelines recognize all overseas deployable modules', async () => {
const controller = await read('Jenkinsfile')
const service = await read('Jenkinsfile.service')
for (const module of ['gateway-overseas', 'lastleg-service', 'warehouse-overseas-service']) {
assert.match(controller, new RegExp(`'${module}'`))
assert.match(service, new RegExp(`'${module}'`))
}
assert.match(controller, /'gateway-overseas': 'SERVICE_GATEWAY_OVERSEAS'/)
assert.match(controller, /'lastleg-service': 'SERVICE_LASTLEG'/)
assert.match(controller, /'warehouse-overseas-service': 'SERVICE_WAREHOUSE_OVERSEAS'/)
assert.match(service, /gateway-overseas\/target\/gateway-overseas-1\.0\.0-SNAPSHOT\.jar/)
assert.match(service, /lastleg-service\/target\/lastleg-service-1\.0\.0-SNAPSHOT\.jar/)
assert.match(service, /warehouse-overseas-service\/target\/warehouse-overseas-service-1\.0\.0-SNAPSHOT\.jar/)
assert.doesNotMatch(controller, /overseas-security-service|SERVICE_OVERSEAS_SECURITY/)
assert.doesNotMatch(service, /overseas-security-service/)
assert.match(controller, /EVENT_BRIDGE_REGION/)
assert.match(service, /EVENT_BRIDGE_REGION_OVERRIDE=\$\{bridgeRegion\}/)
assert.match(controller, /choices: \['BOTH', 'CN', 'OVERSEAS'\]/)
assert.match(service, /choices: \['BOTH', 'CN', 'OVERSEAS', 'SELECT'\]/)
assert.doesNotMatch(controller, /事件桥 BOTH 仅支持开发或测试环境/)
assert.match(service, /部署生产事件桥实例:/)
test('every retained application imports TEST configuration with explicit namespace and no SQL auto init',()=>{
const services=[...read('deploy/service-map.yml').matchAll(/^ ([a-z][a-z-]+):/gm)].map(m=>m[1])
for(const service of services){
const app=read(`${service}/src/main/resources/application.yml`)
assert.match(app,/nacos:baiyue-common.yml\?group=\$\{NACOS_GROUP:BAIYUE\}/)
assert.match(app,/\$\{NACOS_NAMESPACE\}/)
assert.doesNotMatch(app,/optional:nacos|NACOS_NAMESPACE:public|mode: always|EVENT_BRIDGE|JWT_SECRET|REDIS_PORT:6380/)
}
const common=read('config/nacos/test/baiyue-common.yml')
assert.match(common,/jwt-mode: jwks/)
assert.match(common,/session-mode: redis/)
assert.match(common,/session-validation-uri: \$\{AUTH_SESSION_VALIDATION_URL:http:\/\/auth:9100\/internal\/auth\/accounts\/sessions\/validate\}/)
assert.match(read('deploy/scripts/deploy-built-service.sh'),/Missing HEALTHCHECK; deployment failed/)
})
test('service map freezes unique internal ports and moves event bridge away from 9300', async () => {
const map = await read('deploy/service-map.yml')
assert.match(map, /^ gateway-overseas:[\s\S]*?port: 9300$/m)
assert.match(map, /^ lastleg-service:[\s\S]*?port: 9303$/m)
assert.match(map, /^ warehouse-overseas-service:[\s\S]*?port: 9305$/m)
assert.match(map, /^ event-bridge-service:[\s\S]*?port: 9312$/m)
assert.match(map, /^ gateway-overseas:[\s\S]*?regions: \[OVERSEAS\]$/m)
assert.match(map, /^ lastleg-service:[\s\S]*?regions: \[OVERSEAS\]$/m)
assert.match(map, /^ warehouse-overseas-service:[\s\S]*?regions: \[OVERSEAS\]$/m)
assert.doesNotMatch(map, /overseas-security-service|9307/)
assert.match(map, /^ event-bridge-service:[\s\S]*?regions: \[CN, OVERSEAS\]$/m)
})
test('overseas compose is isolated from CN defaults and disables implicit schema execution', async () => {
const compose = await read('deploy/docker-compose.overseas.yml')
assert.match(compose, /^name: firstleg-\$\{DEPLOY_ENV:-test\}-overseas$/m)
assert.match(compose, /NACOS_USERNAME: \$\{OVERSEAS_NACOS_USERNAME\}/)
assert.match(compose, /NACOS_PASSWORD: \$\{OVERSEAS_NACOS_PASSWORD\}/)
assert.match(compose, /NACOS_NAMESPACE: \$\{OVERSEAS_NACOS_NAMESPACE:-prod-overseas\}/)
assert.match(compose, /REGION_CODE: OVERSEAS/)
assert.match(compose, /SPRING_SQL_INIT_MODE: never/)
assert.match(compose, /^ gateway-overseas:\s*$/m)
assert.match(compose, /^ lastleg-service:\s*$/m)
assert.match(compose, /^ warehouse-overseas-service:\s*$/m)
assert.match(compose, /^ event-bridge-service:\s*$/m)
assert.match(compose, /EVENT_BRIDGE_OVERSEAS_PUBLISHED_PORT:-\$\{EVENT_BRIDGE_OVERSEAS_PORT:-9313\}}:9312/)
assert.match(compose, /INTERNAL_SERVICE_TOKEN: \$\{INTERNAL_SERVICE_TOKEN\}/)
assert.match(compose, /LASTLEG_DB_URL: \$\{LASTLEG_DB_URL\}/)
assert.match(compose, /EVENT_BRIDGE_DB_URL: \$\{EVENT_BRIDGE_OVERSEAS_DB_URL\}/)
assert.match(compose, /EVENT_BRIDGE_DB_USERNAME: \$\{EVENT_BRIDGE_OVERSEAS_DB_USERNAME\}/)
assert.match(compose, /EVENT_BRIDGE_DB_PASSWORD: \$\{EVENT_BRIDGE_OVERSEAS_DB_PASSWORD\}/)
assert.match(compose, /LASTLEG_SECURITY_SERVICE_URL: \$\{LASTLEG_SECURITY_SERVICE_URL:-http:\/\/lastleg-service:9303\}/)
assert.match(compose, /LASTLEG_SECURITY_EVENT_CONSUMER_URL: \$\{LASTLEG_SECURITY_EVENT_CONSUMER_URL:-http:\/\/lastleg-service:9303\/internal\/lastleg\/security\/v1\/events\}/)
assert.doesNotMatch(compose, /overseas-security-service|9307|OVERSEAS_SECURITY_DB|OVERSEAS_SECURITY_SERVICE_URL|OVERSEAS_SECURITY_EVENT_CONSUMER_URL/)
assert.match(compose, /CN_EVENT_BRIDGE_URL: \$\{CN_EVENT_BRIDGE_TARGET_URL:-http:\/\/event-bridge-cn:9312\}/)
assert.match(compose, /FIRSTLEG_EVENT_BRIDGE_URL: \$\{CN_EVENT_BRIDGE_TARGET_URL:-http:\/\/event-bridge-cn:9312\}/)
assert.match(compose, /FILE_SERVICE_URL: \$\{FILE_SERVICE_URL\}/)
assert.doesNotMatch(compose, /DOMESTIC_FILE_SERVICE_URL/)
assert.doesNotMatch(compose, /CN_EVENT_BRIDGE_URL: \$\{CN_EVENT_BRIDGE_URL:/)
assert.match(compose, /aliases: \[event-bridge-overseas\]/)
})
test('CN and overseas event bridges have explicit opposite-region consumers', async () => {
const cnCompose = await read('deploy/docker-compose.yml')
const overseasCompose = await read('deploy/docker-compose.overseas.yml')
const cn = await read('config/nacos/cn/event-bridge-service.yml')
const overseas = await read('config/nacos/overseas/event-bridge-service.yml')
const firstleg = await read('config/nacos/cn/firstleg-service.yml')
const iam = await read('config/nacos/cn/iam-service.yml')
const iamApplication = await read('iam/src/main/resources/application.yml')
const iamClient = await read('iam/src/main/java/com/baiyue/iam/client/SecurityEventBridgeClient.java')
const lastleg = await read('config/nacos/overseas/lastleg-service.yml')
const gatewayOverseas = await read('config/nacos/overseas/gateway-overseas.yml')
const scripts = `${await read('deploy/scripts/deploy-built-service.sh')}\n${await read('deploy/scripts/deploy-services.sh')}`
assert.match(cn, /EVENT_CONSUMER_URL:http:\/\/firstleg:9202\/internal\/firstleg\/v1\/container-events/)
assert.match(overseas, /EVENT_CONSUMER_URL:http:\/\/lastleg-service:9303\/internal\/lastleg\/events/)
assert.match(cnCompose, /EVENT_CONSUMER_URL: \$\{FIRSTLEG_CONTAINER_EVENT_CONSUMER_URL:-http:\/\/firstleg:9202\/internal\/firstleg\/v1\/container-events\}/)
assert.match(cnCompose, /aliases: \[event-bridge-cn\]/)
assert.match(overseasCompose, /EVENT_CONSUMER_URL: \$\{EVENT_CONSUMER_URL:-http:\/\/lastleg-service:9303\/internal\/lastleg\/events\}/)
assert.match(firstleg, /LASTLEG_EVENT_BRIDGE_URL/)
assert.match(iamClient, /url = "\$\{baiyue\.security-event\.event-bridge-url:\}"/)
assert.match(iam, /SECURITY_EVENT_BRIDGE_URL:http:\/\/event-bridge-overseas:9312/)
assert.match(iamApplication, /SECURITY_EVENT_BRIDGE_URL:http:\/\/event-bridge-overseas:9312/)
assert.match(cnCompose, /SECURITY_EVENT_BRIDGE_URL: \$\{SECURITY_EVENT_BRIDGE_URL:-http:\/\/event-bridge-overseas:9312\}/)
assert.match(overseasCompose, /aliases: \[event-bridge-overseas\]/)
assert.match(firstleg, /LASTLEG_OUTBOX_RELAY_LEASE_SECONDS/)
assert.match(lastleg, /CN_EVENT_BRIDGE_URL:\$\{FIRSTLEG_EVENT_BRIDGE_URL\}/)
assert.match(lastleg, /WAREHOUSE_OVERSEAS_SERVICE_URL:http:\/\/warehouse-overseas-service:9305/)
assert.match(lastleg, /domestic-file:\s*\n\s*url: \$\{FILE_SERVICE_URL\}/)
assert.doesNotMatch(lastleg, /DOMESTIC_FILE_SERVICE_URL/)
assert.match(gatewayOverseas, /LASTLEG_SECURITY_SERVICE_URL:http:\/\/lastleg-service:9303/)
assert.match(overseas, /LASTLEG_SECURITY_EVENT_CONSUMER_URL:http:\/\/lastleg-service:9303\/internal\/lastleg\/security\/v1\/events/)
assert.doesNotMatch(`${gatewayOverseas}\n${overseas}`, /overseas-security-service|9307|OVERSEAS_SECURITY_/)
assert.match(scripts, /FIRSTLEG_EVENT_BRIDGE_URL/)
assert.match(scripts, /LASTLEG_EVENT_BRIDGE_URL/)
assert.match(scripts, /FILE_SERVICE_URL/)
assert.doesNotMatch(scripts, /DOMESTIC_FILE_SERVICE_URL/)
assert.match(scripts, /docker-compose\.overseas\.yml/)
assert.match(scripts, /REGION_CODE=CN 或 REGION_CODE=OVERSEAS/)
assert.match(scripts, /env_file_value REGION_CODE/)
assert.match(scripts, /EVENT_BRIDGE_REGION_OVERRIDE/)
assert.match(scripts, /FIRSTLEG_CONTAINER_EVENT_CONSUMER_URL/)
assert.match(scripts, /COMPOSE_FILE="deploy\/docker-compose\.overseas\.yml"/)
assert.match(scripts, /PROJECT_NAME="firstleg-\$DEPLOY_ENV-overseas"/)
assert.match(scripts, /运行容器镜像与请求镜像不一致/)
assert.match(scripts, /REMOTE_ENV_NAME="\.env\.\$PROJECT_NAME"/)
assert.match(scripts, /REMOTE_COMPOSE_NAME="docker-compose\.\$PROJECT_NAME\.yml"/)
for (const key of [
'NACOS_SERVER_ADDR', 'NACOS_USERNAME', 'NACOS_PASSWORD', 'OVERSEAS_NACOS_NAMESPACE', 'INTERNAL_SERVICE_TOKEN',
'JWT_ISSUER', 'OVERSEAS_JWT_AUDIENCE', 'JWT_JWK_SET_URI'
]) assert.match(scripts, new RegExp(key))
})
test('domestic Auth receives the overseas JWT signing configuration', async () => {
const compose = await read('deploy/docker-compose.yml')
for (const key of [
'OVERSEAS_JWT_ENABLED', 'OVERSEAS_JWT_ISSUER', 'OVERSEAS_JWT_AUDIENCE',
'OVERSEAS_JWT_RSA_JWK_SET', 'OVERSEAS_JWT_CURRENT_KID', 'OVERSEAS_ACCESS_TOKEN_TTL'
]) assert.match(compose, new RegExp(`${key}: \\$\\{${key}`))
})
test('domestic event bridge initialization defines customer columns without legacy migration', async () => {
const baseline = await read('event-bridge-service/src/main/resources/db/event-bridge.sql')
const incremental = await read('deploy/sql/头程数据库统一增量-MySQL8.sql')
assert.equal((baseline.match(/customer_id VARCHAR\(128\) NOT NULL/g) || []).length, 2)
for (const table of ['bridge_inbox', 'bridge_outbox']) {
const definition = incremental.match(new RegExp(`CREATE TABLE ${table} \\(([\\s\\S]*?)\\) ENGINE`))?.[1]
assert.ok(definition, `${table} must be defined by the empty-database baseline`)
assert.match(definition, /customer_id varchar\(128\)[^\n]*NOT NULL/i)
assert.doesNotMatch(definition, /\btenant_id\b/)
}
assert.doesNotMatch(incremental, /RENAME COLUMN tenant_id TO customer_id/)
})
test('both overseas PDF consumers explicitly connect to the existing CN Print service', async () => {
const compose = await read('deploy/docker-compose.overseas.yml')
for (const service of ['lastleg-service', 'warehouse-overseas-service']) {
const block = compose.match(new RegExp(`^ ${service}:\\n([\\s\\S]*?)(?=^ [a-z][\\w-]*:|^networks:)`, 'm'))?.[1]
assert.ok(block, `${service} must have a deployment definition`)
assert.match(block, /PRINT_SERVICE_URL: \$\{PRINT_SERVICE_URL:-http:\/\/print:9207\}/,
`${service} cannot discover the CN Print service from its overseas Nacos namespace`)
const config = await read(`${service}/src/main/resources/application.yml`)
assert.match(config, /print:\s*\n\s+url: \$\{PRINT_SERVICE_URL:\}/)
}
})
test('domestic initialization defines the overseas hold version and comment in the table', async () => {
const incremental = await read('deploy/sql/头程数据库统一增量-MySQL8.sql')
const definition = incremental.match(/CREATE TABLE fl_inbound_order \(([\s\S]*?)\) ENGINE/)?.[1]
assert.ok(definition)
assert.match(definition, /last_overseas_hold_version bigint unsigned DEFAULT NULL COMMENT '[^']+'/i)
assert.doesNotMatch(incremental, /CALL (?:add_inbound_overseas_hold_version|queue_column_comment)\(/)
})
test('overseas unified SQL initializes the isolated Event Bridge database before services start', async () => {
const overseas = await read('deploy/sql/海外区域统一增量-MySQL8.sql')
assert.match(overseas, /CREATE DATABASE IF NOT EXISTS baiyue_event_bridge_overseas/)
assert.match(overseas, /USE baiyue_event_bridge_overseas;/)
for (const table of ['bridge_outbox', 'bridge_inbox', 'bridge_delivery_attempt']) {
assert.match(overseas, new RegExp(`CREATE TABLE ${table} \\(`))
}
for (const index of [
'uk_bridge_outbox_idempotency', 'idx_bridge_outbox_schedule',
'uk_bridge_inbox_idempotency', 'idx_bridge_inbox_consume',
'uk_bridge_attempt', 'idx_bridge_attempt_event'
]) assert.match(overseas, new RegExp(index))
assert.doesNotMatch(overseas, /CREATE TABLE IF NOT EXISTS/)
assert.match(overseas, /initialization_status/)
assert.ok(overseas.indexOf('USE baiyue_event_bridge_overseas;') < overseas.indexOf('USE baiyue_lastleg;'),
'overseas Event Bridge schema must be initialized before Lastleg tables')
test('Jenkins TEST publication and Nacos import have no dual-region or production execution',()=>{
for(const p of ['Jenkinsfile','Jenkinsfile.service']){
const s=read(p)
assert.doesNotMatch(s,/EVENT_BRIDGE|gateway-overseas|event-bridge-service|return 'production'|return 'development'|firstleg-production-env|java-dev/)
assert.doesNotMatch(s,/maven.test.skip/)
}
assert.match(read('Jenkinsfile.service'),/node\('java-test'\)/)
assert.match(read('Jenkinsfile.service'),/ALLOW_SAME_CONTRACT_ROLLBACK', defaultValue: false/)
const publisher=read('scripts/publish-nacos-config.ps1')
assert.match(publisher,/ValidateSet\('test'\)/)
assert.match(publisher,/config\/nacos\/test/)
assert.match(publisher,/\[switch\]\$DryRun/)
assert.match(publisher,/accessToken/)
})