fix: some
All checks were successful
构建并部署前端到测试环境 / deploy (push) Successful in 55s

This commit is contained in:
luo
2026-10-13 11:25:03 +08:00
parent 52268859aa
commit b2a76adc9a
67 changed files with 1982 additions and 200 deletions

View File

@@ -0,0 +1,279 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
import { resolve } from 'node:path'
import { createRequire } from 'node:module'
const require = createRequire(import.meta.url)
const esbuild = createRequire(require.resolve('vite'))('esbuild')
async function loadModule(entry, stubService = false) {
const result = await esbuild.build({
entryPoints: [entry],
bundle: true,
write: false,
format: 'cjs',
platform: 'node',
alias: { '@': resolve('src') },
plugins: stubService
? [
{
name: 'stub-base-service',
setup(build) {
build.onResolve({ filter: /BaseService$/ }, () => ({
path: 'base-service',
namespace: 'stub'
}))
build.onLoad({ filter: /.*/, namespace: 'stub' }, () => ({
contents: `
export class BaseService {
static calls = [];
static put(url, data, config) { this.calls.push({url, data, config}); return Promise.resolve({code:0, data:{}}); }
static update(url, data) { return this.put(url, data); }
static post(url, data, config) { this.calls.push({url, data, config}); return Promise.resolve({code:0, data:{}}); }
}`
}))
}
}
]
: []
})
const module = { exports: {} }
new Function('require', 'module', 'exports', result.outputFiles[0].text)(
require,
module,
module.exports
)
return module.exports
}
test('new login password accepts only 8-12 ASCII letters/digits with all three classes', async () => {
const { getNewLoginPasswordError, createNewLoginPasswordRule } = await loadModule(
'src/utils/validation/loginPassword.ts'
)
for (const password of ['Abc12345', 'Abc123456789'])
assert.equal(getNewLoginPasswordError(password), undefined)
for (const password of [
'Abc1234',
'Abc123456789X',
'abcd1234',
'ABCD1234',
'AbcDefGh',
'Abc1234!',
'Abc1234_',
'Abc1234 ',
' Abc12345',
'Abc1234\n',
'Abc1234中',
'Abc123😀',
'Abc12345'
]) {
assert.ok(getNewLoginPasswordError(password), `must reject ${JSON.stringify(password)}`)
}
const rule = createNewLoginPasswordRule()
for (const value of ['', undefined, null])
rule.validator({}, value, (error) => assert.equal(error, undefined))
rule.validator({}, 'Abc1234!', (error) => assert.ok(error instanceof Error))
})
test('account password requests use the right field and omit unchanged optional password', async () => {
const { AccountService } = await loadModule('src/api/modules/account.ts', true)
AccountService.calls = []
await AccountService.updateAccountPassword(12, 'Abc12345')
assert.deepEqual(AccountService.calls[0].data, { new_password: 'Abc12345' })
await AccountService.updateAccount(12, { username: 'operator', password: '' })
assert.deepEqual(AccountService.calls[1].data, { username: 'operator' })
await assert.rejects(AccountService.updateAccount(12, { password: 'Abc1234!' }))
await assert.rejects(AccountService.updateAccountPassword(12, 'abcd1234'))
assert.equal(AccountService.calls.length, 2)
})
test('writeoff terminal failure blocks recovery but allows resubmission; unknown result stays recoverable', async () => {
const module = await loadModule(
'src/views/finance/employee-collection/employeeCollectionDisplay.ts'
)
assert.equal(module.canRecoverApplicationApproval({ status: 4, recoverable: true }), false)
assert.equal(module.canResubmitApplication({ status: 4 }), true)
assert.equal(
module.canRecoverApplicationApproval({
status: 0,
approval_submission_status: 4,
recoverable: true
}),
true
)
assert.equal(module.canRecoverApplicationApproval({ status: 0, recoverable: false }), false)
assert.equal(module.getApplicationStatusTagType(4), 'danger')
for (const [value, label] of [
[0, '待提交'],
[1, '提交中'],
[2, '已提交'],
[3, '提交失败'],
[4, '结果未知']
]) {
assert.equal(module.getApplicationSubmissionLabel({ approval_submission_status: value }), label)
}
})
test('refund submission failure uses resubmit, not recovery, without changing status 6 anomaly handling', async () => {
const module = await loadModule('src/config/constants/refund.ts')
assert.equal(module.canRecoverRefundApproval({ status: 7, approval_recoverable: true }), false)
assert.equal(module.canResubmitRefund({ status: 7 }), true)
assert.equal(module.canResubmitRefund({ status: 3 }), true)
assert.equal(
module.canRecoverRefundApproval({
status: 1,
submission_status: 4,
approval_recoverable: true
}),
true
)
assert.equal(module.canResubmitRefund({ status: 6, anomaly_flag: 1 }), false)
assert.equal(module.canResubmitRefund({ status: 6, anomaly_flag: 0 }), true)
assert.equal(module.RefundStatusNameMap[7], '审批提交失败')
})
test('admin agent registration does not recover terminal failures and provides status fallback', async () => {
const module = await loadModule('src/types/api/agentDistributionRegistration.ts')
assert.equal(module.canRecoverAgentRegistration({ status: 3, recoverable: true }), false)
assert.equal(module.canRecoverAgentRegistration({ status: 0, recoverable: true }), true)
assert.equal(module.getAgentRegistrationStatusName({ status: 3 }), '审批提交失败')
})
test('offline recharge failure keeps read actions and starts a new order only with existing role/permission', async () => {
const { buildAgentRechargeActions } = await loadModule(
'src/views/finance/agent-recharge/agentRechargeActions.ts'
)
const row = { id: 8, status: 7, payment_method: 'offline', payment_voucher_key: ['voucher'] }
let restarted = false
const options = {
hasAuth: () => true,
canCreateOffline: true,
onRestart: () => {
restarted = true
},
onViewPaymentVoucher() {},
onConfirmPayment() {},
onReject() {},
onTriggerApproval() {}
}
const actions = buildAgentRechargeActions(row, options)
assert.deepEqual(
actions.map((action) => action.label),
['支付凭证', '重新发起']
)
actions[1].handler()
assert.equal(restarted, true)
assert.deepEqual(
buildAgentRechargeActions(row, { ...options, canCreateOffline: false }).map(
(action) => action.label
),
['支付凭证']
)
assert.deepEqual(buildAgentRechargeActions(row, { ...options, hasAuth: () => false }), [])
})
test('withdrawal failure is independently typed from commission clawback and supports resubmit', async () => {
const module = await loadModule('src/config/constants/commission.ts')
assert.equal(module.WithdrawalStatusMap[5].label, '审批提交失败')
assert.equal(module.canResubmitWithdrawal(5), true)
assert.equal(module.canResubmitWithdrawal(3), true)
assert.equal(module.canResubmitWithdrawal(1), false)
assert.equal(module.getCommissionStatusLabel(5), '回溯')
assert.equal(
module.WITHDRAWAL_QUALIFICATION_STATUS_OPTIONS.find((item) => item.value === 4).label,
'审批提交失败'
)
})
test('order payment record status is independent from order status and channels preserve unknown codes', async () => {
const module = await loadModule('src/utils/business/orderPayment.ts')
assert.equal(module.getOrderPaymentRecordStatusName(0), '待支付')
assert.equal(module.getOrderPaymentRecordStatusName(1), '已支付')
assert.equal(module.getOrderPaymentRecordStatusName(2), '已失败')
assert.equal(module.getOrderPaymentRecordStatusName(3), '已退款')
assert.equal(module.getOrderPaymentRecordTagType(2), 'danger')
assert.equal(module.getOrderPaymentChannelName('wechat'), '微信直连 v3')
assert.equal(module.getOrderPaymentChannelName('wechat_v2'), '微信直连 v2')
assert.equal(module.getOrderPaymentChannelName('fuiou'), '富友')
assert.equal(module.getOrderPaymentChannelName(''), '-')
assert.equal(module.getOrderPaymentChannelName('future-channel'), 'future-channel')
})
test('exchange phone conflicts support HTTP error and business response without inventing a missing number', async () => {
const module = await loadModule('src/utils/business/exchangeError.ts')
const failure = { code: 1202, data: { phone: '13800000000' } }
assert.match(module.getExchangePhoneConflictMessage(failure), /13800000000/)
assert.equal(
module.getExchangePhoneConflictMessage({ response: { data: failure } }),
module.getExchangePhoneConflictMessage(failure)
)
assert.match(module.getExchangePhoneConflictMessage({ code: 1202 }), /请先到/)
assert.doesNotMatch(module.getExchangePhoneConflictMessage({ code: 1202 }), /undefined|null/)
assert.equal(module.getExchangePhoneConflictMessage({ code: 1206 }), undefined)
assert.equal(
module.getExchangeBusinessErrorMessage({ response: { data: { message: '迁移失败' } } }),
'迁移失败'
)
const { ExchangeService } = await loadModule('src/api/modules/exchange.ts', true)
ExchangeService.calls = []
const config = { requestOptions: { errorMessageMode: 'none' } }
await ExchangeService.completeExchange(9, config)
assert.deepEqual(ExchangeService.calls[0], {
url: '/api/admin/exchanges/9/complete',
data: {},
config
})
})
test('password validation errors accept backend message as well as msg', async () => {
const { normalizeApiError } = await loadModule('src/utils/business/apiError.ts')
for (const field of ['msg', 'message']) {
assert.equal(
normalizeApiError({
isAxiosError: true,
response: { status: 400, data: { [field]: '新密码不合法' } }
}).message,
'新密码不合法'
)
}
})
test('traffic snapshots preserve zero, missing values and MB precision', async () => {
const { formatTrafficAlertMb } = await loadModule('src/utils/business/trafficAlert.ts')
assert.equal(formatTrafficAlertMb(0), '0.00')
assert.equal(formatTrafficAlertMb(1234.567), '1234.57')
for (const value of [undefined, null, NaN]) assert.equal(formatTrafficAlertMb(value), '-')
})
test('CSV download has the new exact four-column header and preserves phone identifiers as text', async () => {
const { SHOP_BUSINESS_OWNER_CSV_TEMPLATE } = await loadModule(
'src/utils/business/shopBusinessOwnerImport.ts'
)
assert.equal(SHOP_BUSINESS_OWNER_CSV_TEMPLATE.charCodeAt(0), 0xfeff)
const [header, instruction] = SHOP_BUSINESS_OWNER_CSV_TEMPLATE.slice(1).trimEnd().split('\r\n')
assert.deepEqual(header.split(','), ['店铺编码', '操作类型', '平台登录账号', '备注'])
assert.equal(instruction.split(',').length, 4)
assert.match(instruction, /手机号/)
assert.match(instruction, /清空留空/)
assert.doesNotMatch(SHOP_BUSINESS_OWNER_CSV_TEMPLATE, /业务员登录账号/)
})
test('affected view contracts preserve raw payment history, package quota and device template', async () => {
const order = await readFile('src/views/order-management/order-list/detail.vue', 'utf8')
assert.match(order, /detailData\.payments \|\| \[\]/)
assert.doesNotMatch(order, /payments\.(filter|sort)/)
const rules = await readFile('src/views/package-management/traffic-alert-rules/index.vue', 'utf8')
assert.match(rules, /item\.real_data_mb/)
assert.doesNotMatch(rules, /row\.real_data_mb|prop: 'real_data_mb'/)
const devices = await readFile(
'src/views/asset-management/task-management/device-task/index.vue',
'utf8'
)
assert.ok(
/独立的 IMEI\s+列/.test(devices),
'import instructions must require the standalone IMEI column'
)
assert.match(devices, /@\/template\/设备导入模板\.xlsx/)
assert.match(devices, /failed_items/)
})