import assert from 'node:assert/strict' import { readFile } from 'node:fs/promises' import test from 'node:test' 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('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('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 SQL converges legacy tenant columns to the customer entity contract', 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) assert.equal((incremental.slice(0, incremental.indexOf('CREATE TABLE IF NOT EXISTS bridge_delivery_attempt')) .match(/customer_id VARCHAR\(128\) NOT NULL/g) || []).length, 2) assert.match(incremental, /ALTER TABLE bridge_inbox RENAME COLUMN tenant_id TO customer_id/) assert.match(incremental, /ALTER TABLE bridge_outbox RENAME COLUMN tenant_id TO customer_id/) assert.match(incremental, /bridge_inbox_customer_column = 0 AND @bridge_inbox_tenant_column = 1/) assert.match(incremental, /bridge_outbox_customer_column = 0 AND @bridge_outbox_tenant_column = 1/) }) 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 unified SQL creates the overseas hold version before queuing its comment', async () => { const incremental = await read('deploy/sql/头程数据库统一增量-MySQL8.sql') const addColumn = incremental.indexOf('CALL add_inbound_overseas_hold_version();') const queueComment = incremental.indexOf("CALL queue_column_comment('baiyue_firstleg', 'fl_inbound_order', 'last_overseas_hold_version'") assert.notEqual(addColumn, -1) assert.notEqual(queueComment, -1) assert.ok(addColumn < queueComment, 'existing databases must add last_overseas_hold_version before the strict comment queue reads it') }) 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 IF NOT EXISTS ${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.match(overseas, /COUNT\(DISTINCT CONCAT\(table_name, ':', index_name\)\)/) assert.match(overseas, /CALL assert_overseas_event_bridge_schema\(\);/) assert.ok(overseas.indexOf('USE baiyue_event_bridge_overseas;') < overseas.indexOf('USE baiyue_lastleg;'), 'overseas Event Bridge schema must be initialized before Lastleg tables') })