import assert from 'node:assert/strict'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { spawnSync } from 'node:child_process'; const repoRoot = process.cwd(); const scriptPath = path.join(repoRoot, 'scripts', 'production-readiness-check.js'); function runReadiness(envContent, options = {}) { const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tiku-readiness-')); const envFile = path.join(tempDir, '.env'); fs.writeFileSync(envFile, envContent, 'utf8'); const args = [scriptPath, '--env-file', envFile, '--skip-db', '--json']; if (options.providerRows) { const fixtureFile = path.join(tempDir, 'provider-fixture.json'); fs.writeFileSync(fixtureFile, JSON.stringify({ rows: options.providerRows }, null, 2), 'utf8'); args.push('--provider-config-fixture', fixtureFile); } if (options.tenantRows) { const fixtureFile = path.join(tempDir, 'tenant-fixture.json'); fs.writeFileSync(fixtureFile, JSON.stringify({ rows: options.tenantRows }, null, 2), 'utf8'); args.push('--tenant-config-fixture', fixtureFile); } if (Object.prototype.hasOwnProperty.call(options, 'environmentSafetyRow')) { const fixtureFile = path.join(tempDir, 'environment-safety-fixture.json'); fs.writeFileSync( fixtureFile, JSON.stringify({ row: options.environmentSafetyRow }, null, 2), 'utf8', ); args.push('--environment-safety-fixture', fixtureFile); } if (Object.prototype.hasOwnProperty.call(options, 'migrationHistoryRow')) { const fixtureFile = path.join(tempDir, 'migration-history-fixture.json'); fs.writeFileSync( fixtureFile, JSON.stringify({ row: options.migrationHistoryRow }, null, 2), 'utf8', ); args.push('--migration-history-fixture', fixtureFile); } const result = spawnSync(process.execPath, args, { cwd: repoRoot, encoding: 'utf8', env: { PATH: process.env.PATH || '', Path: process.env.Path || '', SystemRoot: process.env.SystemRoot || '', ComSpec: process.env.ComSpec || '', TEMP: process.env.TEMP || os.tmpdir(), TMP: process.env.TMP || os.tmpdir(), }, }); const payload = JSON.parse(result.stdout || '{}'); fs.rmSync(tempDir, { recursive: true, force: true }); return { ...result, payload }; } function productionEnv(overrides = '') { return ` NODE_ENV=production ${overrides} DATABASE_URL=postgresql://tiku_api:prod_password@db.prod.internal:5432/tiku DB_EXPECTED_RUNTIME_ROLE=tiku_api CORS_ORIGIN=https://platform-admin.gongxue100.com CORS_TENANT_DOMAINS_ENABLED=true CORS_TENANT_DOMAIN_CACHE_TTL_MS=60000 CORS_TENANT_DOMAIN_NEGATIVE_CACHE_TTL_MS=10000 CORS_TENANT_DOMAIN_CACHE_MAX_ENTRIES=10000 AUTH_SMS_PROVIDER=aliyun-pnvs AUTH_CODE_PEPPER=s3cure-prod-code-pepper-2026-06-29-abcdef AUTH_SESSION_SECRET=s3cure-prod-session-secret-2026-06-29-ghijkl AUTH_JWT_JWKS_URL=https://auth.gongxue100.com/auth/v1/.well-known/jwks.json AUTH_JWT_ISSUER=https://auth.gongxue100.com/auth/v1 ALLOW_LEGACY_AUTH_HEADERS=false ALLOW_PLATFORM_ADMIN_KEY=false PLATFORM_ADMIN_API_KEY=s3cure-platform-admin-key-2026-06-29-mnopqr STORAGE_DEFAULT_PROVIDER=aliyun_oss STORAGE_DEFAULT_BUCKET=tiku-assets STORAGE_REQUIRE_TENANT_PREFIX=true ALIYUN_OSS_REGION=cn-hangzhou ALIYUN_OSS_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com ALIYUN_OSS_ACCESS_KEY_ID=LTAI_READINESS_TEST_ONLY ALIYUN_OSS_ACCESS_KEY_SECRET=aliyun-readiness-secret-placeholder WORKER_ASSET_SECURITY_SCANNER=metadata_rules,http WORKER_ASSET_SECURITY_SCAN_HTTP_ENDPOINT=https://scanner.gongxue100.com/api/scan WORKER_ASSET_SECURITY_SCAN_HTTP_TOKEN=s3cure-asset-scanner-token-2026-06-29-stuvwx WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN=false `; } const unsafe = runReadiness(` NODE_ENV=development DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:54322/postgres AUTH_SMS_PROVIDER=mock `); assert.notEqual(unsafe.status, 0, 'unsafe production readiness should fail'); assert.ok(unsafe.payload.summary?.blocker > 0, 'unsafe readiness should report blockers'); assert.ok( unsafe.payload.checks?.some(item => item.id === 'env.node_env' && item.status === 'blocker'), 'unsafe readiness should block non-production NODE_ENV', ); assert.ok( unsafe.payload.checks?.some(item => item.id === 'env.auth_sms_provider' && item.status === 'blocker'), 'unsafe readiness should block mock SMS provider', ); const unsupportedSmsProvider = runReadiness(` NODE_ENV=production DATABASE_URL=postgresql://prod_user:prod_password@db.prod.internal:5432/tiku CORS_ORIGIN=https://student.gongxue100.com CORS_TENANT_DOMAINS_ENABLED=true CORS_TENANT_DOMAIN_CACHE_TTL_MS=60000 CORS_TENANT_DOMAIN_NEGATIVE_CACHE_TTL_MS=10000 CORS_TENANT_DOMAIN_CACHE_MAX_ENTRIES=10000 AUTH_SMS_PROVIDER=aliyun-production AUTH_CODE_PEPPER=s3cure-prod-code-pepper-2026-06-29-abcdef AUTH_SESSION_SECRET=s3cure-prod-session-secret-2026-06-29-ghijkl AUTH_JWT_JWKS_URL=https://auth.gongxue100.com/auth/v1/.well-known/jwks.json AUTH_JWT_ISSUER=https://auth.gongxue100.com/auth/v1 ALLOW_LEGACY_AUTH_HEADERS=false ALLOW_PLATFORM_ADMIN_KEY=false PLATFORM_ADMIN_API_KEY=s3cure-platform-admin-key-2026-06-29-mnopqr STORAGE_DEFAULT_PROVIDER=aliyun_oss STORAGE_DEFAULT_BUCKET=tiku-assets STORAGE_REQUIRE_TENANT_PREFIX=true ALIYUN_OSS_REGION=cn-hangzhou ALIYUN_OSS_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com ALIYUN_OSS_ACCESS_KEY_ID=LTAI_READINESS_TEST_ONLY ALIYUN_OSS_ACCESS_KEY_SECRET=aliyun-readiness-secret-placeholder WORKER_ASSET_SECURITY_SCANNER=metadata_rules,http WORKER_ASSET_SECURITY_SCAN_HTTP_ENDPOINT=https://scanner.gongxue100.com/api/scan WORKER_ASSET_SECURITY_SCAN_HTTP_TOKEN=s3cure-asset-scanner-token-2026-06-29-stuvwx WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN=false `); assert.notEqual(unsupportedSmsProvider.status, 0, 'unsupported production SMS provider readiness should fail'); assert.ok( unsupportedSmsProvider.payload.checks?.some(item => item.id === 'env.auth_sms_provider' && item.status === 'blocker'), 'readiness should block unsupported AUTH_SMS_PROVIDER values', ); const traditionalAliyunSmsProvider = runReadiness(` NODE_ENV=production DATABASE_URL=postgresql://prod_user:prod_password@db.prod.internal:5432/tiku CORS_ORIGIN=https://student.gongxue100.com CORS_TENANT_DOMAINS_ENABLED=true CORS_TENANT_DOMAIN_CACHE_TTL_MS=60000 CORS_TENANT_DOMAIN_NEGATIVE_CACHE_TTL_MS=10000 CORS_TENANT_DOMAIN_CACHE_MAX_ENTRIES=10000 AUTH_SMS_PROVIDER=aliyun AUTH_CODE_PEPPER=s3cure-prod-code-pepper-2026-06-29-abcdef AUTH_SESSION_SECRET=s3cure-prod-session-secret-2026-06-29-ghijkl AUTH_JWT_JWKS_URL=https://auth.gongxue100.com/auth/v1/.well-known/jwks.json AUTH_JWT_ISSUER=https://auth.gongxue100.com/auth/v1 ALLOW_LEGACY_AUTH_HEADERS=false ALLOW_PLATFORM_ADMIN_KEY=false PLATFORM_ADMIN_API_KEY=s3cure-platform-admin-key-2026-06-29-mnopqr STORAGE_DEFAULT_PROVIDER=aliyun_oss STORAGE_DEFAULT_BUCKET=tiku-assets STORAGE_REQUIRE_TENANT_PREFIX=true ALIYUN_OSS_REGION=cn-hangzhou ALIYUN_OSS_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com ALIYUN_OSS_ACCESS_KEY_ID=LTAI_READINESS_TEST_ONLY ALIYUN_OSS_ACCESS_KEY_SECRET=aliyun-readiness-secret-placeholder WORKER_ASSET_SECURITY_SCANNER=metadata_rules,http WORKER_ASSET_SECURITY_SCAN_HTTP_ENDPOINT=https://scanner.gongxue100.com/api/scan WORKER_ASSET_SECURITY_SCAN_HTTP_TOKEN=s3cure-asset-scanner-token-2026-06-29-stuvwx WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN=false `); assert.notEqual(traditionalAliyunSmsProvider.status, 0, 'traditional Aliyun SMS provider readiness should fail in production'); assert.ok( traditionalAliyunSmsProvider.payload.checks?.some(item => item.id === 'env.auth_sms_provider' && item.status === 'blocker'), 'readiness should require PNVS instead of traditional Aliyun SMS', ); const strongSecretA = 's3cure-prod-code-pepper-2026-06-29-abcdef'; const strongSecretB = 's3cure-prod-session-secret-2026-06-29-ghijkl'; const strongSecretC = 's3cure-platform-admin-key-2026-06-29-mnopqr'; const safe = runReadiness(` NODE_ENV=production DATABASE_URL=postgresql://prod_user:prod_password@db.prod.internal:5432/tiku CORS_ORIGIN=https://student.gongxue100.com,https://tenant-admin.gongxue100.com,https://platform-admin.gongxue100.com CORS_TENANT_DOMAINS_ENABLED=true CORS_TENANT_DOMAIN_CACHE_TTL_MS=60000 CORS_TENANT_DOMAIN_NEGATIVE_CACHE_TTL_MS=10000 CORS_TENANT_DOMAIN_CACHE_MAX_ENTRIES=10000 AUTH_SMS_PROVIDER=aliyun-pnvs AUTH_CODE_PEPPER=${strongSecretA} AUTH_SESSION_SECRET=${strongSecretB} AUTH_JWT_JWKS_URL=https://auth.gongxue100.com/auth/v1/.well-known/jwks.json AUTH_JWT_ISSUER=https://auth.gongxue100.com/auth/v1 ALLOW_LEGACY_AUTH_HEADERS=false ALLOW_PLATFORM_ADMIN_KEY=false PLATFORM_ADMIN_API_KEY=${strongSecretC} MAX_JSON_BODY_BYTES=1048576 MAX_IMPORT_JSON_BODY_BYTES=10485760 STORAGE_DEFAULT_PROVIDER=aliyun_oss STORAGE_DEFAULT_BUCKET=tiku-assets STORAGE_REQUIRE_TENANT_PREFIX=true STORAGE_ALLOWED_MIME_TYPES=application/pdf,image/png,image/jpeg,video/mp4,text/plain,text/csv ALIYUN_OSS_REGION=cn-hangzhou ALIYUN_OSS_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com ALIYUN_OSS_ACCESS_KEY_ID=LTAI_READINESS_TEST_ONLY ALIYUN_OSS_ACCESS_KEY_SECRET=aliyun-readiness-secret-placeholder WORKER_ASSET_SECURITY_SCANNER=metadata_rules,http WORKER_ASSET_SECURITY_SCAN_HTTP_ENDPOINT=https://scanner.gongxue100.com/api/scan WORKER_ASSET_SECURITY_SCAN_HTTP_TOKEN=s3cure-asset-scanner-token-2026-06-29-stuvwx WORKER_ASSET_SECURITY_SCAN_HTTP_TIMEOUT_MS=10000 WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN=false WORKER_CRM_ALLOW_INSECURE_LOCALHOST=false WORKER_PLATFORM_AUDIT_NOTIFICATION_ALLOW_INSECURE_LOCALHOST=false WORKER_PLATFORM_DUNNING_NOTIFICATION_ALLOW_INSECURE_LOCALHOST=false WORKER_CRM_BATCH_SIZE=20 WORKER_CRM_POLL_INTERVAL_MS=10000 WORKER_COMMERCE_BATCH_SIZE=20 WORKER_COMMERCE_POLL_INTERVAL_MS=30000 WORKER_PROVIDER_BILL_POLL_INTERVAL_MS=60000 WORKER_PLATFORM_DUNNING_NOTIFICATION_POLL_INTERVAL_MS=30000 WORKER_PLATFORM_AUDIT_NOTIFICATION_POLL_INTERVAL_MS=30000 WORKER_ASSET_BATCH_SIZE=50 WORKER_ASSET_POLL_INTERVAL_MS=30000 WORKER_IMPORT_BATCH_SIZE=5 WORKER_IMPORT_POLL_INTERVAL_MS=10000 WORKER_IMPORT_LEASE_SECONDS=120 WORKER_IMPORT_HEARTBEAT_INTERVAL_MS=30000 WORKER_PUBLIC_BANK_SYNC_BATCH_SIZE=5 WORKER_PUBLIC_BANK_SYNC_POLL_INTERVAL_MS=60000 WORKER_EXPORT_POLL_INTERVAL_MS=10000 `); assert.equal(safe.status, 0, `safe readiness should pass without blockers: ${safe.stdout} ${safe.stderr}`); assert.equal(safe.payload.summary?.blocker, 0, 'safe readiness should have no blockers'); assert.ok( safe.payload.checks?.some(item => item.id === 'db.skipped' && item.status === 'warn'), 'env-only readiness should explicitly warn that DB checks are skipped', ); const migrationFiles = fs.readdirSync(path.join(repoRoot, 'supabase', 'migrations')) .filter(file => file.endsWith('.sql')) .sort(); const latestMigrationVersion = /^(\d+)_/.exec(migrationFiles.at(-1) || '')?.[1]; assert.ok(latestMigrationVersion, 'repository must have a latest numeric Supabase migration'); const currentMigrationHistory = runReadiness(productionEnv(), { migrationHistoryRow: { latestVersion: latestMigrationVersion, appliedCount: migrationFiles.length, distinctVersionCount: migrationFiles.length, expectedVersionApplied: true, }, }); assert.equal(currentMigrationHistory.status, 0, 'current migration history fixture should pass'); assert.ok( currentMigrationHistory.payload.checks?.some(item => ( item.id === 'db.migrations.current' && item.status === 'pass' )), 'readiness should pass migration history that includes the repository latest version', ); const staleMigrationHistory = runReadiness(productionEnv(), { migrationHistoryRow: { latestVersion: '202607120018', appliedCount: migrationFiles.length - 1, distinctVersionCount: migrationFiles.length - 1, expectedVersionApplied: false, }, }); assert.notEqual(staleMigrationHistory.status, 0, 'stale migration history must fail readiness'); assert.ok( staleMigrationHistory.payload.checks?.some(item => ( item.id === 'db.migrations.current' && item.status === 'blocker' )), 'readiness should block a database missing the repository latest migration', ); const inconsistentMigrationHistory = runReadiness(productionEnv(), { migrationHistoryRow: { latestVersion: latestMigrationVersion, appliedCount: migrationFiles.length + 1, distinctVersionCount: migrationFiles.length, expectedVersionApplied: true, }, }); assert.notEqual(inconsistentMigrationHistory.status, 0, 'duplicate migration history must fail readiness'); const truncatedMigrationHistory = runReadiness(productionEnv(), { migrationHistoryRow: { latestVersion: latestMigrationVersion, appliedCount: migrationFiles.length - 1, distinctVersionCount: migrationFiles.length - 1, expectedVersionApplied: true, }, }); assert.notEqual( truncatedMigrationHistory.status, 0, 'migration history shorter than the repository migration set must fail readiness', ); const dynamicTenantCorsDisabled = runReadiness(productionEnv('CORS_TENANT_DOMAINS_ENABLED=false')); assert.notEqual(dynamicTenantCorsDisabled.status, 0, 'disabled tenant-domain CORS must fail production readiness'); assert.ok( dynamicTenantCorsDisabled.payload.checks?.some(item => ( item.id === 'env.cors_tenant_domains_enabled' && item.status === 'blocker' )), 'readiness must require dynamic tenant-domain CORS in production', ); for (const [override, checkId] of [ ['WORKER_IMPORT_LEASE_SECONDS=9', 'env.worker_import_lease_seconds'], [ 'WORKER_IMPORT_LEASE_SECONDS=60\nWORKER_IMPORT_HEARTBEAT_INTERVAL_MS=30000', 'env.worker_import_heartbeat_interval_ms', ], ]) { const result = runReadiness(productionEnv(override)); assert.notEqual(result.status, 0, `${checkId} should fail production readiness`); assert.ok( result.payload.checks?.some(item => item.id === checkId && item.status === 'blocker'), `${checkId} should be reported as a blocker`, ); } for (const [key, unsafeValue] of [ ['CORS_TENANT_DOMAIN_CACHE_TTL_MS', '999'], ['CORS_TENANT_DOMAIN_NEGATIVE_CACHE_TTL_MS', '300001'], ['CORS_TENANT_DOMAIN_CACHE_MAX_ENTRIES', '99'], ]) { const result = runReadiness(productionEnv(`${key}=${unsafeValue}`)); assert.notEqual(result.status, 0, `${key} outside the production range must fail readiness`); assert.ok( result.payload.checks?.some(item => ( item.id === `env.${key.toLowerCase()}` && item.status === 'blocker' )), `readiness must block unsafe ${key}`, ); } const missingExpectedRole = runReadiness(productionEnv('DB_EXPECTED_RUNTIME_ROLE=')); assert.equal( missingExpectedRole.status, 0, 'env-only readiness should defer the authoritative runtime-role identity check to --check-db', ); assert.ok( missingExpectedRole.payload.checks?.some(item => ( item.id === 'env.db_expected_runtime_role' && item.status === 'warn' )), 'env-only readiness should warn when DB_EXPECTED_RUNTIME_ROLE is absent', ); const mismatchedExpectedRole = runReadiness(productionEnv('DB_EXPECTED_RUNTIME_ROLE=tiku_worker')); assert.notEqual(mismatchedExpectedRole.status, 0, 'DATABASE_URL role mismatch must fail readiness'); assert.ok( mismatchedExpectedRole.payload.checks?.some(item => ( item.id === 'env.database_runtime_role' && item.status === 'blocker' )), 'readiness must block a DATABASE_URL username that differs from DB_EXPECTED_RUNTIME_ROLE', ); const invalidWorkerPollInterval = runReadiness(` NODE_ENV=production DATABASE_URL=postgresql://prod_user:prod_password@db.prod.internal:5432/tiku CORS_ORIGIN=https://student.gongxue100.com,https://tenant-admin.gongxue100.com,https://platform-admin.gongxue100.com CORS_TENANT_DOMAINS_ENABLED=true CORS_TENANT_DOMAIN_CACHE_TTL_MS=60000 CORS_TENANT_DOMAIN_NEGATIVE_CACHE_TTL_MS=10000 CORS_TENANT_DOMAIN_CACHE_MAX_ENTRIES=10000 AUTH_SMS_PROVIDER=aliyun-pnvs AUTH_CODE_PEPPER=${strongSecretA} AUTH_SESSION_SECRET=${strongSecretB} AUTH_JWT_JWKS_URL=https://auth.gongxue100.com/auth/v1/.well-known/jwks.json AUTH_JWT_ISSUER=https://auth.gongxue100.com/auth/v1 ALLOW_LEGACY_AUTH_HEADERS=false ALLOW_PLATFORM_ADMIN_KEY=false PLATFORM_ADMIN_API_KEY=${strongSecretC} STORAGE_DEFAULT_PROVIDER=aliyun_oss STORAGE_DEFAULT_BUCKET=tiku-assets STORAGE_REQUIRE_TENANT_PREFIX=true ALIYUN_OSS_REGION=cn-hangzhou ALIYUN_OSS_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com ALIYUN_OSS_ACCESS_KEY_ID=LTAI_READINESS_TEST_ONLY ALIYUN_OSS_ACCESS_KEY_SECRET=aliyun-readiness-secret-placeholder WORKER_ASSET_SECURITY_SCANNER=metadata_rules,http WORKER_ASSET_SECURITY_SCAN_HTTP_ENDPOINT=https://scanner.gongxue100.com/api/scan WORKER_ASSET_SECURITY_SCAN_HTTP_TOKEN=s3cure-asset-scanner-token-2026-06-29-stuvwx WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN=false WORKER_CRM_POLL_INTERVAL_MS=0 `); assert.notEqual(invalidWorkerPollInterval.status, 0, 'invalid production worker poll interval should fail readiness'); assert.ok( invalidWorkerPollInterval.payload.checks?.some(item => ( item.id === 'env.worker_crm_poll_interval_ms' && item.status === 'blocker' )), 'readiness should block invalid worker poll intervals', ); const safeAliyunPnvs = runReadiness(` NODE_ENV=production DATABASE_URL=postgresql://prod_user:prod_password@db.prod.internal:5432/tiku CORS_ORIGIN=https://student.gongxue100.com,https://tenant-admin.gongxue100.com,https://platform-admin.gongxue100.com CORS_TENANT_DOMAINS_ENABLED=true CORS_TENANT_DOMAIN_CACHE_TTL_MS=60000 CORS_TENANT_DOMAIN_NEGATIVE_CACHE_TTL_MS=10000 CORS_TENANT_DOMAIN_CACHE_MAX_ENTRIES=10000 AUTH_SMS_PROVIDER=aliyun-pnvs AUTH_CODE_PEPPER=${strongSecretA} AUTH_SESSION_SECRET=${strongSecretB} AUTH_JWT_JWKS_URL=https://auth.gongxue100.com/auth/v1/.well-known/jwks.json AUTH_JWT_ISSUER=https://auth.gongxue100.com/auth/v1 ALLOW_LEGACY_AUTH_HEADERS=false ALLOW_PLATFORM_ADMIN_KEY=false PLATFORM_ADMIN_API_KEY=${strongSecretC} STORAGE_DEFAULT_PROVIDER=aliyun_oss STORAGE_DEFAULT_BUCKET=tiku-assets STORAGE_REQUIRE_TENANT_PREFIX=true ALIYUN_OSS_REGION=cn-hangzhou ALIYUN_OSS_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com ALIYUN_OSS_ACCESS_KEY_ID=LTAI_READINESS_TEST_ONLY ALIYUN_OSS_ACCESS_KEY_SECRET=aliyun-readiness-secret-placeholder WORKER_ASSET_SECURITY_SCANNER=metadata_rules,http WORKER_ASSET_SECURITY_SCAN_HTTP_ENDPOINT=https://scanner.gongxue100.com/api/scan WORKER_ASSET_SECURITY_SCAN_HTTP_TOKEN=s3cure-asset-scanner-token-2026-06-29-stuvwx WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN=false WORKER_CRM_BATCH_SIZE=20 WORKER_COMMERCE_BATCH_SIZE=20 WORKER_ASSET_BATCH_SIZE=50 WORKER_IMPORT_BATCH_SIZE=5 WORKER_PUBLIC_BANK_SYNC_BATCH_SIZE=5 `); assert.equal(safeAliyunPnvs.status, 0, `aliyun-pnvs readiness should pass without blockers: ${safeAliyunPnvs.stdout} ${safeAliyunPnvs.stderr}`); assert.equal(safeAliyunPnvs.payload.summary?.blocker, 0, 'aliyun-pnvs readiness should have no blockers'); const safeAliyunPnvsUnderscoreAlias = runReadiness(` NODE_ENV=production DATABASE_URL=postgresql://prod_user:prod_password@db.prod.internal:5432/tiku CORS_ORIGIN=https://student.gongxue100.com,https://tenant-admin.gongxue100.com,https://platform-admin.gongxue100.com CORS_TENANT_DOMAINS_ENABLED=true CORS_TENANT_DOMAIN_CACHE_TTL_MS=60000 CORS_TENANT_DOMAIN_NEGATIVE_CACHE_TTL_MS=10000 CORS_TENANT_DOMAIN_CACHE_MAX_ENTRIES=10000 AUTH_SMS_PROVIDER=aliyun_pnvs AUTH_CODE_PEPPER=${strongSecretA} AUTH_SESSION_SECRET=${strongSecretB} AUTH_JWT_JWKS_URL=https://auth.gongxue100.com/auth/v1/.well-known/jwks.json AUTH_JWT_ISSUER=https://auth.gongxue100.com/auth/v1 ALLOW_LEGACY_AUTH_HEADERS=false ALLOW_PLATFORM_ADMIN_KEY=false PLATFORM_ADMIN_API_KEY=${strongSecretC} STORAGE_DEFAULT_PROVIDER=aliyun_oss STORAGE_DEFAULT_BUCKET=tiku-assets STORAGE_REQUIRE_TENANT_PREFIX=true ALIYUN_OSS_REGION=cn-hangzhou ALIYUN_OSS_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com ALIYUN_OSS_ACCESS_KEY_ID=LTAI_READINESS_TEST_ONLY ALIYUN_OSS_ACCESS_KEY_SECRET=aliyun-readiness-secret-placeholder WORKER_ASSET_SECURITY_SCANNER=metadata_rules,http WORKER_ASSET_SECURITY_SCAN_HTTP_ENDPOINT=https://scanner.gongxue100.com/api/scan WORKER_ASSET_SECURITY_SCAN_HTTP_TOKEN=s3cure-asset-scanner-token-2026-06-29-stuvwx WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN=false `, { providerRows: [ { source: 'auth', tenantId: 'tenant-pnvs', provider: 'aliyun-pnvs', configPublic: { signName: '短信签名', templateCode: 'SMS_123456789', endpoint: 'https://dypnsapi.aliyuncs.com', templateParam: { code: '##code##', min: '5' }, }, }, ], }); assert.equal(safeAliyunPnvsUnderscoreAlias.status, 0, `aliyun_pnvs readiness should pass without blockers: ${safeAliyunPnvsUnderscoreAlias.stdout} ${safeAliyunPnvsUnderscoreAlias.stderr}`); assert.equal(safeAliyunPnvsUnderscoreAlias.payload.summary?.blocker, 0, 'aliyun_pnvs readiness should have no blockers'); assert.ok( safeAliyunPnvsUnderscoreAlias.payload.checks?.some(item => item.id === 'db.auth_sms_provider_configured' && item.status === 'pass'), 'aliyun_pnvs readiness should still validate matching PNVS provider rows', ); const pnvsTemplateParamWarning = runReadiness( ` NODE_ENV=production DATABASE_URL=postgresql://prod_user:prod_password@db.prod.internal:5432/tiku CORS_ORIGIN=https://student.gongxue100.com,https://tenant-admin.gongxue100.com,https://platform-admin.gongxue100.com CORS_TENANT_DOMAINS_ENABLED=true CORS_TENANT_DOMAIN_CACHE_TTL_MS=60000 CORS_TENANT_DOMAIN_NEGATIVE_CACHE_TTL_MS=10000 CORS_TENANT_DOMAIN_CACHE_MAX_ENTRIES=10000 AUTH_SMS_PROVIDER=aliyun-pnvs AUTH_CODE_PEPPER=${strongSecretA} AUTH_SESSION_SECRET=${strongSecretB} AUTH_JWT_JWKS_URL=https://auth.gongxue100.com/auth/v1/.well-known/jwks.json AUTH_JWT_ISSUER=https://auth.gongxue100.com/auth/v1 ALLOW_LEGACY_AUTH_HEADERS=false ALLOW_PLATFORM_ADMIN_KEY=false PLATFORM_ADMIN_API_KEY=${strongSecretC} STORAGE_DEFAULT_PROVIDER=aliyun_oss STORAGE_DEFAULT_BUCKET=tiku-assets STORAGE_REQUIRE_TENANT_PREFIX=true ALIYUN_OSS_REGION=cn-hangzhou ALIYUN_OSS_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com ALIYUN_OSS_ACCESS_KEY_ID=LTAI_READINESS_TEST_ONLY ALIYUN_OSS_ACCESS_KEY_SECRET=aliyun-readiness-secret-placeholder WORKER_ASSET_SECURITY_SCANNER=metadata_rules,http WORKER_ASSET_SECURITY_SCAN_HTTP_ENDPOINT=https://scanner.gongxue100.com/api/scan WORKER_ASSET_SECURITY_SCAN_HTTP_TOKEN=s3cure-asset-scanner-token-2026-06-29-stuvwx WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN=false `, { providerRows: [ { source: 'auth', tenantId: 'tenant-pnvs', provider: 'aliyun-pnvs', configPublic: { signName: '短信签名', templateCode: 'SMS_123456789', endpoint: 'https://dypnsapi.aliyuncs.com', templateParam: { min: '5' }, }, }, ], }, ); assert.equal(pnvsTemplateParamWarning.status, 0, 'PNVS templateParam warning should not block readiness'); assert.ok( pnvsTemplateParamWarning.payload.checks?.some(item => item.id === 'db.auth.aliyun-pnvs.template_param' && item.status === 'warn'), 'readiness should warn when PNVS templateParam lacks ##code##', ); assert.ok( pnvsTemplateParamWarning.payload.checks?.some(item => item.id === 'db.auth_sms_provider_configured' && item.status === 'pass'), 'readiness should pass when AUTH_SMS_PROVIDER has a matching PNVS provider row', ); const mismatchedSmsProviderFixture = runReadiness( ` NODE_ENV=production DATABASE_URL=postgresql://prod_user:prod_password@db.prod.internal:5432/tiku CORS_ORIGIN=https://student.gongxue100.com,https://tenant-admin.gongxue100.com,https://platform-admin.gongxue100.com CORS_TENANT_DOMAINS_ENABLED=true CORS_TENANT_DOMAIN_CACHE_TTL_MS=60000 CORS_TENANT_DOMAIN_NEGATIVE_CACHE_TTL_MS=10000 CORS_TENANT_DOMAIN_CACHE_MAX_ENTRIES=10000 AUTH_SMS_PROVIDER=aliyun-pnvs AUTH_CODE_PEPPER=${strongSecretA} AUTH_SESSION_SECRET=${strongSecretB} AUTH_JWT_JWKS_URL=https://auth.gongxue100.com/auth/v1/.well-known/jwks.json AUTH_JWT_ISSUER=https://auth.gongxue100.com/auth/v1 ALLOW_LEGACY_AUTH_HEADERS=false ALLOW_PLATFORM_ADMIN_KEY=false PLATFORM_ADMIN_API_KEY=${strongSecretC} STORAGE_DEFAULT_PROVIDER=aliyun_oss STORAGE_DEFAULT_BUCKET=tiku-assets STORAGE_REQUIRE_TENANT_PREFIX=true ALIYUN_OSS_REGION=cn-hangzhou ALIYUN_OSS_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com ALIYUN_OSS_ACCESS_KEY_ID=LTAI_READINESS_TEST_ONLY ALIYUN_OSS_ACCESS_KEY_SECRET=aliyun-readiness-secret-placeholder WORKER_ASSET_SECURITY_SCANNER=metadata_rules,http WORKER_ASSET_SECURITY_SCAN_HTTP_ENDPOINT=https://scanner.gongxue100.com/api/scan WORKER_ASSET_SECURITY_SCAN_HTTP_TOKEN=s3cure-asset-scanner-token-2026-06-29-stuvwx WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN=false `, { providerRows: [ { source: 'auth', tenantId: 'tenant-aliyun', provider: 'aliyun', configPublic: { signName: '短信签名', templateCode: 'SMS_123456789', endpoint: 'https://dysmsapi.aliyuncs.com', }, }, ], }, ); assert.notEqual(mismatchedSmsProviderFixture.status, 0, 'readiness should fail when AUTH_SMS_PROVIDER has no matching active provider row'); assert.ok( mismatchedSmsProviderFixture.payload.checks?.some(item => item.id === 'db.auth_sms_provider_configured' && item.status === 'blocker'), 'readiness should block env/provider mismatch', ); assert.ok( mismatchedSmsProviderFixture.payload.checks?.some(item => item.id === 'db.auth.aliyun.legacy_sms_provider' && item.status === 'blocker'), 'readiness should block active traditional Aliyun SMS auth provider rows', ); const legacyTencentSmsProviderFixture = runReadiness( ` NODE_ENV=production DATABASE_URL=postgresql://prod_user:prod_password@db.prod.internal:5432/tiku CORS_ORIGIN=https://student.gongxue100.com,https://tenant-admin.gongxue100.com,https://platform-admin.gongxue100.com CORS_TENANT_DOMAINS_ENABLED=true CORS_TENANT_DOMAIN_CACHE_TTL_MS=60000 CORS_TENANT_DOMAIN_NEGATIVE_CACHE_TTL_MS=10000 CORS_TENANT_DOMAIN_CACHE_MAX_ENTRIES=10000 AUTH_SMS_PROVIDER=aliyun-pnvs AUTH_CODE_PEPPER=${strongSecretA} AUTH_SESSION_SECRET=${strongSecretB} AUTH_JWT_JWKS_URL=https://auth.gongxue100.com/auth/v1/.well-known/jwks.json AUTH_JWT_ISSUER=https://auth.gongxue100.com/auth/v1 ALLOW_LEGACY_AUTH_HEADERS=false ALLOW_PLATFORM_ADMIN_KEY=false PLATFORM_ADMIN_API_KEY=${strongSecretC} STORAGE_DEFAULT_PROVIDER=aliyun_oss STORAGE_DEFAULT_BUCKET=tiku-assets STORAGE_REQUIRE_TENANT_PREFIX=true ALIYUN_OSS_REGION=cn-hangzhou ALIYUN_OSS_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com ALIYUN_OSS_ACCESS_KEY_ID=LTAI_READINESS_TEST_ONLY ALIYUN_OSS_ACCESS_KEY_SECRET=aliyun-readiness-secret-placeholder WORKER_ASSET_SECURITY_SCANNER=metadata_rules,http WORKER_ASSET_SECURITY_SCAN_HTTP_ENDPOINT=https://scanner.gongxue100.com/api/scan WORKER_ASSET_SECURITY_SCAN_HTTP_TOKEN=s3cure-asset-scanner-token-2026-06-29-stuvwx WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN=false `, { providerRows: [ { source: 'auth', tenantId: 'tenant-tencent', provider: 'tencent-sms', configPublic: { smsSdkAppId: '1400000000', signName: '短信签名', templateId: '123456', }, }, { source: 'auth', tenantId: 'tenant-pnvs', provider: 'aliyun-pnvs', configPublic: { signName: '短信签名', templateCode: 'SMS_123456789', endpoint: 'https://dypnsapi.aliyuncs.com', templateParam: { code: '##code##', min: '5' }, }, }, ], }, ); assert.notEqual(legacyTencentSmsProviderFixture.status, 0, 'readiness should fail when Tencent SMS auth provider rows remain active'); assert.ok( legacyTencentSmsProviderFixture.payload.checks?.some(item => item.id === 'db.auth.tencent-sms.legacy_sms_provider' && item.status === 'blocker'), 'readiness should block active Tencent SMS auth provider rows', ); const unsafeProviderFixture = runReadiness( ` NODE_ENV=production DATABASE_URL=postgresql://prod_user:prod_password@db.prod.internal:5432/tiku CORS_ORIGIN=https://student.gongxue100.com,https://tenant-admin.gongxue100.com,https://platform-admin.gongxue100.com CORS_TENANT_DOMAINS_ENABLED=true CORS_TENANT_DOMAIN_CACHE_TTL_MS=60000 CORS_TENANT_DOMAIN_NEGATIVE_CACHE_TTL_MS=10000 CORS_TENANT_DOMAIN_CACHE_MAX_ENTRIES=10000 AUTH_SMS_PROVIDER=aliyun-pnvs AUTH_CODE_PEPPER=${strongSecretA} AUTH_SESSION_SECRET=${strongSecretB} AUTH_JWT_JWKS_URL=https://auth.gongxue100.com/auth/v1/.well-known/jwks.json AUTH_JWT_ISSUER=https://auth.gongxue100.com/auth/v1 ALLOW_LEGACY_AUTH_HEADERS=false ALLOW_PLATFORM_ADMIN_KEY=false PLATFORM_ADMIN_API_KEY=${strongSecretC} STORAGE_DEFAULT_PROVIDER=aliyun_oss STORAGE_DEFAULT_BUCKET=tiku-assets STORAGE_REQUIRE_TENANT_PREFIX=true ALIYUN_OSS_REGION=cn-hangzhou ALIYUN_OSS_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com ALIYUN_OSS_ACCESS_KEY_ID=LTAI_READINESS_TEST_ONLY ALIYUN_OSS_ACCESS_KEY_SECRET=aliyun-readiness-secret-placeholder WORKER_ASSET_SECURITY_SCANNER=metadata_rules,http WORKER_ASSET_SECURITY_SCAN_HTTP_ENDPOINT=https://scanner.gongxue100.com/api/scan WORKER_ASSET_SECURITY_SCAN_HTTP_TOKEN=s3cure-asset-scanner-token-2026-06-29-stuvwx WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN=false `, { providerRows: [ { source: 'auth', tenantId: 'tenant-a', provider: 'qq-oauth', configPublic: { appId: '101000000', redirectUri: 'http://localhost:5173/auth/qq/callback', }, }, { source: 'payment', tenantId: 'tenant-a', provider: 'wechat_pay', configPublic: { appId: 'wx123', merchantId: '1900000001', merchantSerialNo: 'serial123', }, }, { source: 'payment', tenantId: 'tenant-b', provider: 'alipay', configPublic: { appId: '2021000000000000', notifyUrl: 'http://pay.example.com/notify', privateKey: 'should-not-be-public', }, }, ], }, ); assert.notEqual(unsafeProviderFixture.status, 0, 'unsafe provider fixture readiness should fail'); assert.ok( unsafeProviderFixture.payload.checks?.some(item => item.id === 'db.auth.qq-oauth.redirect_uri.unsafe' && item.status === 'blocker'), 'readiness should block unsafe QQ redirectUri', ); assert.ok( unsafeProviderFixture.payload.checks?.some(item => item.id === 'db.payment.wechat_pay.public_required' && item.status === 'blocker'), 'readiness should block missing WeChat Pay notifyUrl', ); assert.ok( unsafeProviderFixture.payload.checks?.some(item => item.id === 'db.payment.alipay.notify_url.unsafe' && item.status === 'blocker'), 'readiness should block unsafe Alipay notifyUrl', ); assert.ok( unsafeProviderFixture.payload.checks?.some(item => item.id === 'db.payment.alipay.public_secret' && item.status === 'blocker'), 'readiness should block secret-like public payment config keys', ); const missingJwksIssuer = runReadiness(` NODE_ENV=production DATABASE_URL=postgresql://prod_user:prod_password@db.prod.internal:5432/tiku CORS_ORIGIN=https://student.gongxue100.com CORS_TENANT_DOMAINS_ENABLED=true CORS_TENANT_DOMAIN_CACHE_TTL_MS=60000 CORS_TENANT_DOMAIN_NEGATIVE_CACHE_TTL_MS=10000 CORS_TENANT_DOMAIN_CACHE_MAX_ENTRIES=10000 AUTH_SMS_PROVIDER=aliyun-pnvs AUTH_CODE_PEPPER=${strongSecretA} AUTH_SESSION_SECRET=${strongSecretB} AUTH_JWT_JWKS_URL=https://auth.gongxue100.com/auth/v1/.well-known/jwks.json ALLOW_LEGACY_AUTH_HEADERS=false ALLOW_PLATFORM_ADMIN_KEY=false PLATFORM_ADMIN_API_KEY=${strongSecretC} STORAGE_DEFAULT_PROVIDER=aliyun_oss STORAGE_DEFAULT_BUCKET=tiku-assets STORAGE_REQUIRE_TENANT_PREFIX=true ALIYUN_OSS_REGION=cn-hangzhou ALIYUN_OSS_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com ALIYUN_OSS_ACCESS_KEY_ID=LTAI_READINESS_TEST_ONLY ALIYUN_OSS_ACCESS_KEY_SECRET=aliyun-readiness-secret-placeholder WORKER_ASSET_SECURITY_SCANNER=metadata_rules,http WORKER_ASSET_SECURITY_SCAN_HTTP_ENDPOINT=https://scanner.gongxue100.com/api/scan WORKER_ASSET_SECURITY_SCAN_HTTP_TOKEN=s3cure-asset-scanner-token-2026-06-29-stuvwx WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN=false WORKER_PLATFORM_AUDIT_NOTIFICATION_ALLOW_INSECURE_LOCALHOST=false WORKER_PLATFORM_DUNNING_NOTIFICATION_ALLOW_INSECURE_LOCALHOST=false `); assert.notEqual(missingJwksIssuer.status, 0, 'JWKS readiness without issuer should fail'); assert.ok( missingJwksIssuer.payload.checks?.some(item => item.id === 'env.auth_jwt_issuer' && item.status === 'blocker'), 'JWKS readiness should block missing AUTH_JWT_ISSUER', ); const unsafePlatformAuditNotificationLocalhost = runReadiness(` NODE_ENV=production DATABASE_URL=postgresql://prod_user:prod_password@db.prod.internal:5432/tiku CORS_ORIGIN=https://student.gongxue100.com CORS_TENANT_DOMAINS_ENABLED=true CORS_TENANT_DOMAIN_CACHE_TTL_MS=60000 CORS_TENANT_DOMAIN_NEGATIVE_CACHE_TTL_MS=10000 CORS_TENANT_DOMAIN_CACHE_MAX_ENTRIES=10000 AUTH_SMS_PROVIDER=aliyun-pnvs AUTH_CODE_PEPPER=${strongSecretA} AUTH_SESSION_SECRET=${strongSecretB} AUTH_JWT_JWKS_URL=https://auth.gongxue100.com/auth/v1/.well-known/jwks.json AUTH_JWT_ISSUER=https://auth.gongxue100.com/auth/v1 ALLOW_LEGACY_AUTH_HEADERS=false ALLOW_PLATFORM_ADMIN_KEY=false PLATFORM_ADMIN_API_KEY=${strongSecretC} STORAGE_DEFAULT_PROVIDER=aliyun_oss STORAGE_DEFAULT_BUCKET=tiku-assets STORAGE_REQUIRE_TENANT_PREFIX=true ALIYUN_OSS_REGION=cn-hangzhou ALIYUN_OSS_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com ALIYUN_OSS_ACCESS_KEY_ID=LTAI_READINESS_TEST_ONLY ALIYUN_OSS_ACCESS_KEY_SECRET=aliyun-readiness-secret-placeholder WORKER_ASSET_SECURITY_SCANNER=metadata_rules,http WORKER_ASSET_SECURITY_SCAN_HTTP_ENDPOINT=https://scanner.gongxue100.com/api/scan WORKER_ASSET_SECURITY_SCAN_HTTP_TOKEN=s3cure-asset-scanner-token-2026-06-29-stuvwx WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN=false WORKER_PLATFORM_AUDIT_NOTIFICATION_ALLOW_INSECURE_LOCALHOST=true `); assert.notEqual(unsafePlatformAuditNotificationLocalhost.status, 0, 'platform audit notification localhost readiness should fail'); assert.ok( unsafePlatformAuditNotificationLocalhost.payload.checks?.some(item => item.id === 'env.worker_platform_audit_notification_insecure_localhost' && item.status === 'blocker'), 'readiness should block platform audit notification localhost mode in production', ); const unsafePlatformDunningNotificationLocalhost = runReadiness(` NODE_ENV=production DATABASE_URL=postgresql://prod_user:prod_password@db.prod.internal:5432/tiku CORS_ORIGIN=https://student.gongxue100.com CORS_TENANT_DOMAINS_ENABLED=true CORS_TENANT_DOMAIN_CACHE_TTL_MS=60000 CORS_TENANT_DOMAIN_NEGATIVE_CACHE_TTL_MS=10000 CORS_TENANT_DOMAIN_CACHE_MAX_ENTRIES=10000 AUTH_SMS_PROVIDER=aliyun-pnvs AUTH_CODE_PEPPER=${strongSecretA} AUTH_SESSION_SECRET=${strongSecretB} AUTH_JWT_JWKS_URL=https://auth.gongxue100.com/auth/v1/.well-known/jwks.json AUTH_JWT_ISSUER=https://auth.gongxue100.com/auth/v1 ALLOW_LEGACY_AUTH_HEADERS=false ALLOW_PLATFORM_ADMIN_KEY=false PLATFORM_ADMIN_API_KEY=${strongSecretC} STORAGE_DEFAULT_PROVIDER=aliyun_oss STORAGE_DEFAULT_BUCKET=tiku-assets STORAGE_REQUIRE_TENANT_PREFIX=true ALIYUN_OSS_REGION=cn-hangzhou ALIYUN_OSS_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com ALIYUN_OSS_ACCESS_KEY_ID=LTAI_READINESS_TEST_ONLY ALIYUN_OSS_ACCESS_KEY_SECRET=aliyun-readiness-secret-placeholder WORKER_ASSET_SECURITY_SCANNER=metadata_rules,http WORKER_ASSET_SECURITY_SCAN_HTTP_ENDPOINT=https://scanner.gongxue100.com/api/scan WORKER_ASSET_SECURITY_SCAN_HTTP_TOKEN=s3cure-asset-scanner-token-2026-06-29-stuvwx WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN=false WORKER_PLATFORM_AUDIT_NOTIFICATION_ALLOW_INSECURE_LOCALHOST=false WORKER_PLATFORM_DUNNING_NOTIFICATION_ALLOW_INSECURE_LOCALHOST=true `); assert.notEqual(unsafePlatformDunningNotificationLocalhost.status, 0, 'platform dunning notification localhost readiness should fail'); assert.ok( unsafePlatformDunningNotificationLocalhost.payload.checks?.some(item => item.id === 'env.worker_platform_dunning_notification_insecure_localhost' && item.status === 'blocker'), 'readiness should block platform dunning notification localhost mode in production', ); const tenantConfigBaseEnv = ` NODE_ENV=production DATABASE_URL=postgresql://prod_user:prod_password@db.prod.internal:5432/tiku CORS_ORIGIN=https://student.gongxue100.com CORS_TENANT_DOMAINS_ENABLED=true CORS_TENANT_DOMAIN_CACHE_TTL_MS=60000 CORS_TENANT_DOMAIN_NEGATIVE_CACHE_TTL_MS=10000 CORS_TENANT_DOMAIN_CACHE_MAX_ENTRIES=10000 AUTH_SMS_PROVIDER=aliyun-pnvs AUTH_CODE_PEPPER=${strongSecretA} AUTH_SESSION_SECRET=${strongSecretB} AUTH_JWT_JWKS_URL=https://auth.gongxue100.com/auth/v1/.well-known/jwks.json AUTH_JWT_ISSUER=https://auth.gongxue100.com/auth/v1 ALLOW_LEGACY_AUTH_HEADERS=false ALLOW_PLATFORM_ADMIN_KEY=false PLATFORM_ADMIN_API_KEY=${strongSecretC} STORAGE_DEFAULT_PROVIDER=aliyun_oss STORAGE_DEFAULT_BUCKET=tiku-assets STORAGE_REQUIRE_TENANT_PREFIX=true ALIYUN_OSS_REGION=cn-hangzhou ALIYUN_OSS_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com ALIYUN_OSS_ACCESS_KEY_ID=LTAI_READINESS_TEST_ONLY ALIYUN_OSS_ACCESS_KEY_SECRET=aliyun-readiness-secret-placeholder WORKER_ASSET_SECURITY_SCANNER=metadata_rules,http WORKER_ASSET_SECURITY_SCAN_HTTP_ENDPOINT=https://scanner.gongxue100.com/api/scan WORKER_ASSET_SECURITY_SCAN_HTTP_TOKEN=s3cure-asset-scanner-token-2026-06-29-stuvwx WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN=false `; const unsafeTenantPublicUrl = runReadiness(tenantConfigBaseEnv, { tenantRows: [{ tenantId: 'tenant-local-url', slug: 'local-url', name: 'Local URL tenant', publicConfig: { frontend: { appUrl: 'http://127.0.0.1:5173' } }, publishedTheme: { primaryColor: '#2563eb' }, themeStatus: 'published', publishedAt: '2026-07-11T00:00:00.000Z', }], }); assert.notEqual(unsafeTenantPublicUrl.status, 0, 'active tenant localhost public URLs should fail readiness'); assert.ok( unsafeTenantPublicUrl.payload.checks?.some(item => item.id === 'db.tenant_public_urls' && item.status === 'blocker'), 'readiness should block active tenant public URLs that are not production HTTPS', ); const tenantWithoutPublishedTheme = runReadiness(tenantConfigBaseEnv, { tenantRows: [{ tenantId: 'tenant-default-theme', slug: 'default-theme', name: 'Default theme tenant', publicConfig: { appUrl: 'https://student.gongxue100.com' }, }], }); assert.equal(tenantWithoutPublishedTheme.status, 0, 'missing tenant theme should use platform defaults without blocking readiness'); assert.ok( tenantWithoutPublishedTheme.payload.checks?.some(item => item.id === 'db.tenant_theme_published' && item.status === 'warn'), 'readiness should warn when an active tenant has no published or branding fallback theme', ); const tenantWithBrandingFallback = runReadiness(tenantConfigBaseEnv, { tenantRows: [{ tenantId: 'tenant-branding-theme', slug: 'branding-theme', name: 'Branding theme tenant', publicConfig: { appUrl: 'https://student.gongxue100.com' }, brandingTheme: { primaryColor: '#0f766e' }, }], }); assert.equal(tenantWithBrandingFallback.status, 0, 'branding theme fallback should pass readiness'); assert.ok( tenantWithBrandingFallback.payload.checks?.some(item => item.id === 'db.tenant_theme_published' && item.status === 'pass'), 'readiness should accept a non-empty tenant branding fallback theme', ); for (const environmentSafetyRow of [ null, { environment: 'production', allowDestructiveTests: false }, { environment: 'staging', allow_destructive_tests: false }, ]) { const result = runReadiness(tenantConfigBaseEnv, { environmentSafetyRow }); assert.equal( result.status, 0, `production readiness should accept a missing or disabled production/staging marker: ${result.stdout} ${result.stderr}`, ); assert.ok( result.payload.checks?.some(item => ( item.id === 'db.environment.destructive_tests_disabled' && item.status === 'pass' )), 'production readiness should record the safe destructive-test marker state', ); } for (const environmentSafetyRow of [ { environment: 'local', allowDestructiveTests: false }, { environment: 'test', allowDestructiveTests: false }, { environment: 'ci', allowDestructiveTests: false }, { environment: 'production', allowDestructiveTests: true }, { environment: 'staging', allowDestructiveTests: true }, { environment: 'unknown', allowDestructiveTests: false }, { environment: 'production', allowDestructiveTests: 'false' }, ]) { const result = runReadiness(tenantConfigBaseEnv, { environmentSafetyRow }); assert.notEqual( result.status, 0, `production readiness must reject unsafe destructive-test marker ${JSON.stringify(environmentSafetyRow)}`, ); assert.ok( result.payload.checks?.some(item => ( item.id === 'db.environment.destructive_tests_disabled' && item.status === 'blocker' )), 'production readiness should block unsafe destructive-test marker state', ); } const readinessSource = fs.readFileSync(scriptPath, 'utf8'); assert.match( readinessSource, /to_regclass\('app_private\.environment_safety'\)/, 'database readiness must verify that the environment safety migration exists', ); assert.match( readinessSource, /from app_private\.environment_safety[\s\S]*where id = true/i, 'database readiness must read the authoritative destructive-test marker', ); for (const gateId of [ 'db.runtime_role.identity', 'db.runtime_role.attributes', 'db.runtime_role.schema_acl', 'db.runtime_role.table_acl', 'db.runtime_role.function_acl', 'db.runtime_role.ownership', 'db.runtime_role.ddl_denied', ]) { assert.ok(readinessSource.includes(gateId), `database readiness must include ${gateId}`); } assert.match( readinessSource, /current_user[\s\S]*session_user[\s\S]*DB_EXPECTED_RUNTIME_ROLE/i, 'database readiness must verify current_user and session_user against DB_EXPECTED_RUNTIME_ROLE', ); assert.match( readinessSource, /has_database_privilege[\s\S]*has_schema_privilege[\s\S]*db\.runtime_role\.ddl_denied/i, 'database readiness must verify effective persistent DDL privileges without writing to production', ); console.log('[PASS] production readiness check script');