revert fix(pay): 余额充值选中预设档位不再因空 custom_price 被 REST 拒绝
选中任一所预设充值档位点「立即支付」,必然弹「无效参数:custom_price」。这是
WP REST 的参数校验层拦截,请求根本没进业务回调。
原因是 WP REST 的 default 只在「键完全缺失」时生效,键存在但值为空串时 default
不参与,空串会直接送进 pattern 校验:
'custom_price' => ['default' => '0', 'pattern' => '^(0|[1-9]\d*)(?:\.\d{1,2})?$']
而前端提交时无条件带上该字段(custom_price: custom),选预设档位时 custom 恒为
'',于是必然校验失败。另一个更隐蔽的变体:后台若关掉「自定义充值金额」,模板里
根本不渲染该输入框,customEl 为 null,custom 仍是 '',同样报错——也就是说这个
缺陷在只放开预设档位的配置下 100% 复现。
同文件的积分兑换路径(/pay/balance/points-exchange)早已写成
custom: validCustom ? custom : '0',余额充值这条只是漏了同样的处理。
- BalanceServiceRest:custom_price 的 pattern 改为整体可选分组
^(?:(?:0|[1-9]\d*)(?:\.\d{1,2})?)?$,显式放行空串;rest_charge_create 把空串
归一化为 '0',与参数声明的 default 语义对齐,避免 AssetAmount::balance() 拿到
空串后抛 balance_decimal_invalid
- credits.js:非自定义金额时显式送 '0',与积分兑换路径写法一致
- 契约测试补 4 条断言:模式可选、允许空串、回调归一化、前端送 '0'
两层都改是有意的:JS 会被浏览器与 CDN 缓存,旧缓存仍会发空串,后端放行兜住;
后端放行则保证任何客户端都不再被这个字段卡住。放宽仅针对空串,金额格式校验
不放松——007、abc、-1、1e3、1.234 与带首尾空格的值仍一律拒绝。
验证:新 pattern 逐例实测,''、0、1、9.99、9999.99 放行,007、abc、-1、1e3、
1.234、带空格全部拒绝;变异验证把 pattern 改回旧写法后该用例立刻转红,证明回归
测试确实拦得住此缺陷;node --test 全量 370 项 / 370 通过;PHPUnit 44 项 / 106
断言通过。断言未直接比对含反斜杠的整串——JS 字面量与 PHP 单引号串的反斜杠层数
不同会静默比对失败,故拆为不含反斜杠的片段。
315 lines
18 KiB
JavaScript
315 lines
18 KiB
JavaScript
const test = require('node:test')
|
|
const assert = require('node:assert/strict')
|
|
const fs = require('node:fs')
|
|
const path = require('node:path')
|
|
|
|
const root = path.resolve(__dirname, '..', '..')
|
|
|
|
function sourceFiles(dir) {
|
|
const out = []
|
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
if (entry.name === 'build' || entry.name === 'vendor' || entry.name === 'node_modules') continue
|
|
const full = path.join(dir, entry.name)
|
|
if (entry.isDirectory()) out.push(...sourceFiles(full))
|
|
else if (/\.(php|js|vue|cjs)$/.test(entry.name)) out.push(full)
|
|
}
|
|
return out
|
|
}
|
|
|
|
test('old asset implementations and compatibility aliases are absent', () => {
|
|
assert.equal(fs.existsSync(path.join(root, 'src', 'Credits', 'Credits.php')), false)
|
|
assert.equal(fs.existsSync(path.join(root, 'src', 'Credits', 'Balance.php')), false)
|
|
assert.equal(fs.existsSync(path.join(root, 'src', 'Payment', 'Rebate.php')), false)
|
|
assert.equal(fs.existsSync(path.join(root, 'src', 'Admin', 'Native', 'PayRebate.php')), false)
|
|
assert.equal(fs.existsSync(path.join(root, 'assets', 'admin', 'src', 'pages', 'payment', 'PaymentRebate.vue')), false)
|
|
assert.equal(fs.existsSync(path.join(root, 'assets', 'admin', 'src', 'pages', 'payment', 'components', 'RebateDetailDrawer.vue')), false)
|
|
|
|
const files = [
|
|
...sourceFiles(path.join(root, 'src')),
|
|
...sourceFiles(path.join(root, 'inc')),
|
|
...sourceFiles(path.join(root, 'assets', 'js')),
|
|
...sourceFiles(path.join(root, 'templates')),
|
|
]
|
|
const forbidden = [
|
|
/aksrt_credits(?:\b|_)/,
|
|
/['"]aksrt_balance['"]/,
|
|
/\bcredit_log\b/,
|
|
/\bbalance_log\b/,
|
|
/aksrt_credits_(?:added|deducted)/,
|
|
/aksrt_balance_(?:added|deducted)/,
|
|
/credits_(?:added|deducted)/,
|
|
// points_pass_exchange_* 现为正式设置键(卡密兑换积分功能,对齐参考主题命名),
|
|
// 由 card-pass-modal-redeem.test.cjs 覆盖,不再作为遗留别名禁用。
|
|
/legacy_task_points/,
|
|
/transfer_refund|recovery_failed/,
|
|
/\bPayRebate\b/,
|
|
/\bPaymentRebate\b/,
|
|
/\bRebateDetailDrawer\b/,
|
|
/\brebate_log\b/,
|
|
/\/payment\/rebate(?:\b|\/)/,
|
|
/\bpay_rebate\b/,
|
|
/aksrtpay_rebate_page/,
|
|
]
|
|
const violations = []
|
|
for (const file of files) {
|
|
const text = fs.readFileSync(file, 'utf8')
|
|
for (const pattern of forbidden) {
|
|
if (pattern.test(text)) violations.push(`${path.relative(root, file)}: ${pattern}`)
|
|
}
|
|
}
|
|
assert.deepEqual(violations, [])
|
|
})
|
|
|
|
test('asset tables are InnoDB and operation keys are unique', () => {
|
|
const schema = fs.readFileSync(path.join(root, 'src', 'Core', 'DBMigratorPart2.php'), 'utf8')
|
|
for (const table of ['asset_account', 'asset_transaction', 'asset_entry']) {
|
|
assert.match(schema, new RegExp(`CREATE TABLE \\{\\$prefix\\}aksrt_${table} \\([\\s\\S]*?\\) ENGINE=InnoDB`))
|
|
}
|
|
assert.match(schema, /UNIQUE KEY uniq_operation_key \(operation_key\)/)
|
|
assert.match(schema, /UNIQUE KEY uniq_transaction_line \(transaction_id, line_no\)/)
|
|
})
|
|
|
|
test('user asset submissions send idempotency keys', () => {
|
|
const request = fs.readFileSync(path.join(root, 'assets', 'js', 'core', 'request.js'), 'utf8')
|
|
assert.match(request, /createIdempotencyKey/)
|
|
for (const file of ['credits.js', 'reward.js', 'vip-purchase-coordinator.js', 'cms-download.js']) {
|
|
const source = fs.readFileSync(path.join(root, 'assets', 'js', file), 'utf8')
|
|
assert.match(source, /Idempotency-Key/, `${file} must send Idempotency-Key`)
|
|
}
|
|
})
|
|
|
|
test('profile asset records show order references and only the changed amount', () => {
|
|
for (const file of ['credits-transactions.php', 'credits-wallet-ledger.php']) {
|
|
const source = fs.readFileSync(path.join(root, 'inc', 'helpers', 'user-center-renderers', file), 'utf8')
|
|
assert.match(source, /\$record_note\s*=\s*\(string\) \$row\['reason'\] === 'points_exchange'[\s\S]*?\(string\) \$row\['ref_key'\]/)
|
|
assert.doesNotMatch(source, /uc-transactions__balance|交易 #%d|\$row\['fee_minor'\]|\$row\['ref_id'\]/)
|
|
}
|
|
})
|
|
|
|
test('asset record tabs and pagination stay in the DOM without URL state', () => {
|
|
const profile = fs.readFileSync(path.join(root, 'inc', 'helpers', 'user-center-renderers', 'profile-credits.php'), 'utf8')
|
|
const points = fs.readFileSync(path.join(root, 'inc', 'helpers', 'user-center-renderers', 'credits-transactions.php'), 'utf8')
|
|
const wallet = fs.readFileSync(path.join(root, 'inc', 'helpers', 'user-center-renderers', 'credits-wallet-ledger.php'), 'utf8')
|
|
const credits = fs.readFileSync(path.join(root, 'assets', 'js', 'credits.js'), 'utf8')
|
|
const center = fs.readFileSync(path.join(root, 'src', 'User', 'Center.php'), 'utf8')
|
|
|
|
assert.match(profile, /data-aksrt-asset-tab="balance"/)
|
|
assert.match(profile, /data-aksrt-asset-tab="points"/)
|
|
assert.match(points, /aksrt_render_asset_pagination\([^;]+, 'points'\)/)
|
|
assert.match(wallet, /aksrt_render_asset_pagination\([^;]+, 'balance'\)/)
|
|
assert.doesNotMatch(profile + points + wallet, /\$_GET\[['"](?:wallet_page|credits_page)['"]\]/)
|
|
assert.match(credits, /data-aksrt-asset-page/)
|
|
assert.match(credits, /user-center\/panel\/credits\?asset=/)
|
|
assert.match(center, /'asset'\s*=>\s*\[/)
|
|
assert.match(center, /\$context\['asset_request'\]/)
|
|
})
|
|
test('core asset paths never use floating-point money helpers', () => {
|
|
const files = [
|
|
'src/Ledger/LedgerCommand.php',
|
|
'src/Ledger/LedgerService.php',
|
|
'src/Credits/BalanceService.php',
|
|
'src/Credits/BalanceServiceCharge.php',
|
|
'src/Credits/BalanceServiceRest.php',
|
|
'src/Payment/BalanceGateway.php',
|
|
'src/Payment/ChargeCard.php',
|
|
'src/Payment/CommissionLedger.php',
|
|
'src/Payment/Revenue.php',
|
|
'src/Payment/Withdrawal.php',
|
|
'src/Admin/UserController.php',
|
|
'src/Content/PayRest.php',
|
|
'src/Content/VipServicePurchase.php',
|
|
]
|
|
const forbidden = [/\(float\)/, /floatval\s*\(/, /round\s*\(/, /number_format\s*\(/]
|
|
const violations = []
|
|
for (const relative of files) {
|
|
const text = fs.readFileSync(path.join(root, relative), 'utf8')
|
|
for (const pattern of forbidden) {
|
|
if (pattern.test(text)) violations.push(`${relative}: ${pattern}`)
|
|
}
|
|
}
|
|
assert.deepEqual(violations, [])
|
|
})
|
|
|
|
test('points orders bypass cash commissions and purchase dead paths stay removed', () => {
|
|
const commission = fs.readFileSync(path.join(root, 'src', 'Payment', 'CommissionLedger.php'), 'utf8')
|
|
const paidStart = commission.indexOf('public function record_paid_order')
|
|
const paidEnd = commission.indexOf('public function settle_due', paidStart)
|
|
const paidMethod = commission.slice(paidStart, paidEnd)
|
|
const pointsGuard = paidMethod.indexOf('AssetType::POINTS->value')
|
|
const affiliateGuard = paidMethod.indexOf('Settings::affiliate_enabled()')
|
|
|
|
assert.ok(paidStart >= 0 && paidEnd > paidStart)
|
|
assert.ok(pointsGuard >= 0 && affiliateGuard > pointsGuard)
|
|
assert.match(paidMethod, /\$currency\s*=\s*sanitize_key/)
|
|
|
|
const pay = fs.readFileSync(path.join(root, 'inc', 'helpers', 'aksrt-pay.php'), 'utf8')
|
|
const payTypesStart = pay.indexOf('function aksrt_get_pay_types')
|
|
const cashMethodsStart = pay.indexOf('function aksrt_list_cash_payment_methods', payTypesStart)
|
|
const payTypesMethod = pay.slice(payTypesStart, cashMethodsStart)
|
|
assert.ok(payTypesStart >= 0 && cashMethodsStart > payTypesStart)
|
|
assert.equal((payTypesMethod.match(/(?:^|\n)\s*return\b/g) || []).length, 1)
|
|
|
|
const cashier = fs.readFileSync(path.join(root, 'assets', 'js', 'cms-download.js'), 'utf8')
|
|
assert.match(cashier, /\[data-action="aksrt-need-purchase"\], \[data-aksrt-pay-content\]/)
|
|
assert.doesNotMatch(cashier, /const cashierLink\s*=\s*e\.target\.closest\('\.cashier-link'\)/)
|
|
})
|
|
|
|
test('content preview is read-only while purchase requires an operation key', () => {
|
|
const source = fs.readFileSync(path.join(root, 'src', 'Content', 'PayRest.php'), 'utf8')
|
|
const previewStart = source.indexOf('public function rest_preview')
|
|
const purchaseStart = source.indexOf('public function rest_purchase')
|
|
const purchaseEnd = source.indexOf('public function rest_order_status', purchaseStart)
|
|
assert.ok(previewStart >= 0 && purchaseStart > previewStart && purchaseEnd > purchaseStart)
|
|
assert.doesNotMatch(source.slice(previewStart, purchaseStart), /OperationKey::from_request/)
|
|
assert.match(source.slice(purchaseStart, purchaseEnd), /OperationKey::from_request\(\$request, 'content-purchase'/)
|
|
})
|
|
|
|
test('native financial actions and REST adjustments carry UUID operation keys', () => {
|
|
const income = fs.readFileSync(path.join(root, 'src', 'Admin', 'Native', 'PayIncome.php'), 'utf8')
|
|
const withdrawals = fs.readFileSync(path.join(root, 'src', 'Admin', 'Native', 'PayWithdrawals.php'), 'utf8')
|
|
const points = fs.readFileSync(path.join(root, 'src', 'User', 'CreditsAdmin.php'), 'utf8')
|
|
const balance = fs.readFileSync(path.join(root, 'src', 'User', 'BalanceAdmin.php'), 'utf8')
|
|
const restUsers = fs.readFileSync(path.join(root, 'src', 'Admin', 'UserController.php'), 'utf8')
|
|
for (const source of [income, withdrawals, points, balance]) {
|
|
assert.match(source, /wp_generate_uuid4\(\)/)
|
|
assert.match(source, /wp_is_uuid\(/)
|
|
}
|
|
assert.match(points, /PHP_INT_MAX/)
|
|
assert.match(points, /preg_match\('\/\^-\?/)
|
|
assert.match(points, /strcmp\(\$unsigned_delta, \$max_integer\)/)
|
|
assert.match(points, /current_user_can\('edit_user', \$user_id\)/)
|
|
assert.match(balance, /current_user_can\('edit_user', \$user_id\)/)
|
|
assert.match(restUsers, /OperationKey::from_request\(\$request, 'admin-adjustment'/)
|
|
assert.match(restUsers, /current_user_can\('edit_user', \$user_id\)/)
|
|
})
|
|
|
|
test('charge cards have no legacy input or metadata aliases', () => {
|
|
const source = fs.readFileSync(path.join(root, 'src', 'Payment', 'ChargeCard.php'), 'utf8')
|
|
assert.doesNotMatch(source, /\$params\[['"]code['"]\]/)
|
|
assert.doesNotMatch(source, /\$data\[['"]code['"]\]/)
|
|
assert.doesNotMatch(source, /\$raw\[['"]vip_days['"]\]/)
|
|
assert.doesNotMatch(source, /\$raw\[['"]credits['"]\]/)
|
|
assert.match(source, /\$params\['password'\]/)
|
|
assert.match(source, /\$raw\['days'\]/)
|
|
assert.match(source, /\$raw\['points'\]/)
|
|
})
|
|
|
|
test('VIP card redemption is one locked transaction with bounded retry and replay checks', () => {
|
|
const source = fs.readFileSync(path.join(root, 'src', 'Payment', 'ChargeCard.php'), 'utf8')
|
|
const start = source.indexOf('private function redeem_vip_card')
|
|
const end = source.indexOf('private function vip_card_replay_result', start)
|
|
const method = source.slice(start, end)
|
|
assert.ok(start >= 0 && end > start)
|
|
assert.match(method, /for \(\$attempt = 1; \$attempt <= 3; \$attempt\+\+\)/)
|
|
assert.match(method, /START TRANSACTION/)
|
|
assert.match(method, /FOR UPDATE/)
|
|
assert.match(method, /request_key = %s FOR UPDATE/)
|
|
assert.match(method, /COMMIT/)
|
|
assert.match(method, /ROLLBACK/)
|
|
assert.match(method, /\[1205, 1213\]/)
|
|
assert.match(method, /vip_card_replay_result/)
|
|
})
|
|
|
|
test('admin ledger keeps cash minor values as decimal strings', () => {
|
|
const controller = fs.readFileSync(path.join(root, 'src', 'Admin', 'UserController.php'), 'utf8')
|
|
const page = fs.readFileSync(path.join(root, 'assets', 'admin', 'src', 'pages', 'CmsCredits.vue'), 'utf8')
|
|
assert.match(controller, /asset_minor_for_response/)
|
|
assert.match(controller, /decimal_from_minor/)
|
|
assert.doesNotMatch(page, /\/\s*100(?:\D|$)/)
|
|
})
|
|
|
|
test('asset purchases share the order center and refresh every wallet surface', () => {
|
|
const domain = fs.readFileSync(path.join(root, 'src', 'Core', 'DomainModel.php'), 'utf8')
|
|
const order = fs.readFileSync(path.join(root, 'src', 'Payment', 'Order.php'), 'utf8')
|
|
const query = fs.readFileSync(path.join(root, 'src', 'Payment', 'OrderCenterQuery.php'), 'utf8')
|
|
const userOrders = fs.readFileSync(path.join(root, 'src', 'Payment', 'UserOrders.php'), 'utf8')
|
|
const repository = fs.readFileSync(path.join(root, 'src', 'Payment', 'OrderRepositoryCore.php'), 'utf8')
|
|
const adminRepository = fs.readFileSync(path.join(root, 'src', 'Payment', 'OrderRepositoryAdmin.php'), 'utf8')
|
|
const payOrders = fs.readFileSync(path.join(root, 'src', 'Admin', 'Native', 'PayOrders.php'), 'utf8')
|
|
const paymentRuntime = fs.readFileSync(path.join(root, 'src', 'Settings', 'PaymentRuntime.php'), 'utf8')
|
|
const paymentSchema = fs.readFileSync(path.join(root, 'assets', 'admin', 'src', 'schema', 'sections', 'payment', 'knowledge.js'), 'utf8')
|
|
const balanceRest = fs.readFileSync(path.join(root, 'src', 'Credits', 'BalanceServiceRest.php'), 'utf8')
|
|
const chargeCard = fs.readFileSync(path.join(root, 'src', 'Payment', 'ChargeCard.php'), 'utf8')
|
|
const creditsJs = fs.readFileSync(path.join(root, 'assets', 'js', 'credits.js'), 'utf8')
|
|
const sidebar = fs.readFileSync(path.join(root, 'inc', 'helpers', 'user-center-renderers', 'sidebar-commerce.php'), 'utf8')
|
|
const header = fs.readFileSync(path.join(root, 'inc', 'helpers', 'aksrt-header-userbox.php'), 'utf8')
|
|
|
|
assert.match(domain, /ORDER_PRODUCT_POINTS\s*=\s*'points'/)
|
|
assert.match(domain, /self::ORDER_PRODUCT_POINTS\s*=>\s*__\('积分购买'/)
|
|
const statuses = ['PENDING', 'PAID', 'REFUNDING', 'REFUNDED', 'CLOSED']
|
|
assert.deepEqual(
|
|
[...domain.matchAll(/public const ORDER_STATUS_([A-Z_]+)\s*=\s*'([^']+)'/g)].map((match) => [match[1], match[2]]),
|
|
statuses.map((status) => [status, status.toLowerCase()]),
|
|
)
|
|
assert.deepEqual(
|
|
[...order.matchAll(/public const STATUS_([A-Z_]+)\s*=\s*\\AKSRT\\Core\\DomainModel::ORDER_STATUS_([A-Z_]+);/g)].map((match) => [match[1], match[2]]),
|
|
statuses.map((status) => [status, status]),
|
|
)
|
|
for (const status of statuses) {
|
|
assert.match(domain, new RegExp(`ORDER_STATUS_${status}\\s*=\\s*'${status.toLowerCase()}'`))
|
|
assert.match(order, new RegExp(`STATUS_${status}\\s*=\\s*\\\\AKSRT\\\\Core\\\\DomainModel::ORDER_STATUS_${status}`))
|
|
assert.match(query, new RegExp(`STATUS_${status}\\s*=\\s*'${status.toLowerCase()}'`))
|
|
}
|
|
for (const status of ['CREATED', 'DELIVERED', 'COMPLETED', 'PENDING_REVIEW', 'CANCELLED', 'EXPIRED']) {
|
|
assert.doesNotMatch(domain, new RegExp(`ORDER_STATUS_${status}`))
|
|
assert.doesNotMatch(order, new RegExp(`STATUS_${status}`))
|
|
}
|
|
const transitionSource = domain.slice(domain.indexOf('public static function order_transitions'), domain.indexOf('public static function normalize_order_status'))
|
|
const transitions = Object.fromEntries(
|
|
[...transitionSource.matchAll(/self::ORDER_STATUS_([A-Z_]+)\s*=>\s*\[([\s\S]*?)\],/g)].map((match) => [
|
|
match[1],
|
|
[...match[2].matchAll(/self::ORDER_STATUS_([A-Z_]+)/g)].map((target) => target[1]),
|
|
]),
|
|
)
|
|
assert.deepEqual(transitions, {
|
|
PENDING: ['PAID', 'CLOSED'],
|
|
PAID: ['REFUNDING'],
|
|
REFUNDING: ['REFUNDED', 'PAID'],
|
|
REFUNDED: [],
|
|
CLOSED: [],
|
|
})
|
|
assert.match(userOrders, /return isset\(DomainModel::order_status_labels\(\)\[\$status\]\) \? \$status : ''/)
|
|
assert.doesNotMatch(userOrders, /can_open|entitlement_label|resolve_entitlement_url|post_permalink_cache/)
|
|
assert.match(repository, /SUM\(CASE WHEN status = %s THEN 1 ELSE 0 END\) AS paid/)
|
|
assert.doesNotMatch(repository, /\$completed_statuses|\$completed_placeholders/)
|
|
assert.match(adminRepository, /\$valid_statuses = array_keys\(\\AKSRT\\Core\\DomainModel::order_status_labels\(\)\)/)
|
|
assert.doesNotMatch(payOrders, /<select name="new_status">/)
|
|
assert.doesNotMatch(`${order}\n${repository}\n${userOrders}\n${payOrders}`, /['"]force['"]\s*=>\s*true/)
|
|
assert.match(paymentRuntime, /\$status_keys = \['pending', 'paid', 'refunding', 'refunded', 'closed'\]/)
|
|
for (const status of ['pending', 'paid', 'refunding', 'refunded', 'closed']) {
|
|
assert.match(paymentSchema, new RegExp(`payment_order_status_${status}`))
|
|
}
|
|
assert.doesNotMatch(paymentSchema, /payment_order_status_(?:completed|cancelled|processing)/)
|
|
|
|
assert.match(balanceRest, /register_rest_route\('aksrt\/v1', '\/pay\/assets'/)
|
|
assert.match(balanceRest, /'product_type'\s*=>\s*\\AKSRT\\Core\\DomainModel::ORDER_PRODUCT_POINTS/)
|
|
assert.match(balanceRest, /\$existing_order\s*=\s*\$repo->get_by_request_key\(\$operation_key\)/)
|
|
assert.match(balanceRest, /\$order_number\s*=\s*\$repo->allocate_order_number\(\)/)
|
|
assert.doesNotMatch(balanceRest, /['"]PX['"]\s*\.|hash\('sha256',\s*\$operation_key\)/)
|
|
assert.match(balanceRest, /\['type'\s*=>\s*'order',\s*'key'\s*=>\s*\$order_number\]/)
|
|
assert.match(balanceRest, /'order_number'\s*=>\s*\$order_number/)
|
|
assert.match(chargeCard, /self::TYPE_CREDITS[\s\S]*?ORDER_PRODUCT_POINTS/)
|
|
|
|
assert.match(creditsJs, /function refreshAssetBalances\(/)
|
|
assert.match(creditsJs, /querySelectorAll\('\[data-aksrt-wallet-value\]'/)
|
|
assert.match(creditsJs, /querySelectorAll\('\[data-aksrt-points-value\]'/)
|
|
assert.match(sidebar, /data-aksrt-wallet-value/)
|
|
assert.match(sidebar, /data-aksrt-points-value/)
|
|
assert.match(header, /data-aksrt-wallet-value/)
|
|
assert.match(header, /data-aksrt-points-value/)
|
|
})
|
|
|
|
test('recharge completion always credits the ledger and expiry SQL is strict-mode safe', () => {
|
|
const repository = fs.readFileSync(path.join(root, 'src', 'Payment', 'OrderRepositoryCore.php'), 'utf8')
|
|
const charge = fs.readFileSync(path.join(root, 'src', 'Credits', 'BalanceServiceCharge.php'), 'utf8')
|
|
const gateway = fs.readFileSync(path.join(root, 'src', 'Payment', 'Gateway.php'), 'utf8')
|
|
|
|
assert.match(repository, /\$new_status === \\AKSRT\\Core\\DomainModel::ORDER_STATUS_PAID/)
|
|
assert.match(charge, /'status'\s*=>\s*\\AKSRT\\Payment\\Order::STATUS_PAID/)
|
|
assert.match(charge, /\\AKSRT\\Payment\\Order::STATUS_PENDING,\s*\\AKSRT\\Payment\\Order::STATUS_PAID/)
|
|
assert.doesNotMatch(charge, /Order::STATUS_(?:CREATED|COMPLETED)/)
|
|
assert.match(gateway, /ORDER_PRODUCT_RECHARGE[\s\S]*?update_status\(\$order_number, Order::STATUS_PAID/)
|
|
assert.doesNotMatch(repository, /expires_at\s*<>\s*''/)
|
|
assert.doesNotMatch(repository, /NULLIF\(cancelled_at,\s*''\)/)
|
|
})
|