import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; const providerSource = fs.readFileSync(path.join(process.cwd(), 'apps/api/src/features/auth/providers.ts'), 'utf8'); const routeSource = fs.readFileSync(path.join(process.cwd(), 'apps/api/src/features/auth/routes.ts'), 'utf8'); const tenantAdminRouteSource = fs.readFileSync(path.join(process.cwd(), 'apps/api/src/features/tenant-admin/routes.ts'), 'utf8'); const deployEnvExample = fs.readFileSync(path.join(process.cwd(), 'scripts/deploy/env/api.env.example'), 'utf8'); const providerDoc = fs.readFileSync(path.join(process.cwd(), 'docs/refactor/auth-payment-provider-plan.md'), 'utf8'); const launchChecklist = fs.readFileSync(path.join(process.cwd(), 'docs/refactor/web-launch-acceptance-checklist.md'), 'utf8'); const readinessSource = fs.readFileSync(path.join(process.cwd(), 'scripts/production-readiness-check.js'), 'utf8'); const packageJson = fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8'); const tenantProviderConfigSource = fs.readFileSync(path.join(process.cwd(), 'apps/api/src/core/tenant-provider-config.ts'), 'utf8'); const platformDunningNotificationWorker = fs.readFileSync(path.join(process.cwd(), 'apps/worker/src/jobs/platform-dunning-notifications.ts'), 'utf8'); assert.match(providerSource, /class AliyunPnvsSmsProvider/, 'PNVS provider class should exist'); assert.match(providerSource, /SendSmsVerifyCode/, 'PNVS provider should call SendSmsVerifyCode'); assert.match(providerSource, /CheckSmsVerifyCode/, 'PNVS provider should call CheckSmsVerifyCode'); assert.match(providerSource, /VerifyResult.*PASS/s, 'PNVS verify should require provider PASS result'); assert.match(providerSource, /##code##/, 'PNVS provider should preserve Aliyun-generated code placeholder'); assert.match(routeSource, /aliyun-pnvs/, 'auth routes should recognize aliyun-pnvs aliases'); assert.match(routeSource, /replace\(\s*\/\[_\\s\]\/g,\s*'-'\s*\)/, 'auth routes should normalize underscore PNVS provider aliases before runtime selection'); assert.match(routeSource, /verification.*provider/s, 'auth routes should store provider verification mode'); assert.match(routeSource, /provider\.verify/, 'auth routes should delegate PNVS verification to provider'); assert.match(routeSource, /export async function bindPhoneRoute[\s\S]*activeSmsProvider[\s\S]*consumeSmsCode/, 'phone binding should reuse active SMS provider verification'); assert.match(routeSource, /purpose !== 'bind_phone'/, 'phone binding should require bind_phone purpose'); assert.match(tenantProviderConfigSource, /normalized === 'aliyun-pnvs'[\s\S]*return 'sms'/, 'PNVS secrets should default to sms scope'); assert.match(tenantAdminRouteSource, /normalized === 'aliyun-pnvs'[\s\S]*return 'sms'/, 'tenant admin PNVS secrets should default to sms scope'); assert.match(deployEnvExample, /AUTH_SMS_PROVIDER=aliyun-pnvs/, 'deploy env example should prefer aliyun-pnvs'); assert.match(readinessSource, /aliyunPnvs/, 'production readiness should validate aliyun-pnvs public config'); assert.match(providerDoc, /SendSmsVerifyCode/, 'provider doc should document PNVS send action'); assert.match(providerDoc, /CheckSmsVerifyCode/, 'provider doc should document PNVS verify action'); assert.match(providerDoc, /PNVS 只用于手机号登录和 `bind_phone`/, 'provider doc should constrain PNVS to verification-code auth flows'); assert.doesNotMatch(platformDunningNotificationWorker, /aliyun-pnvs|SendSmsVerifyCode|CheckSmsVerifyCode/, 'platform dunning notifications must not use PNVS verification APIs'); assert.match(packageJson, /diagnose:aliyun-pnvs/, 'package scripts should expose PNVS diagnostics'); assert.match(launchChecklist, /AUTH_SMS_PROVIDER=aliyun-pnvs\s*```/, 'launch checklist should require aliyun-pnvs for production SMS verification'); assert.match(launchChecklist, /diagnose:aliyun-pnvs/, 'launch checklist should run PNVS diagnostics before remote smoke'); console.log('[PASS] Aliyun PNVS provider contract');