This commit is contained in:
277
scripts/tests/pollingConfigBrowserFixtures.mjs
Normal file
277
scripts/tests/pollingConfigBrowserFixtures.mjs
Normal file
@@ -0,0 +1,277 @@
|
||||
import { mockApiPlugin as gatewayFixtures } from './gatewayBrowserFixtures.mjs'
|
||||
|
||||
const time = '2026-10-26T10:00:00+08:00'
|
||||
const configs = [1, 2, 3].map((id) => ({
|
||||
id,
|
||||
config_name: `模拟配置 ${id}`,
|
||||
priority: id,
|
||||
description: '本地模拟,不代表真实配置',
|
||||
status: id === 3 ? 0 : 1,
|
||||
carrier_id: 8,
|
||||
carrier_name: '模拟运营商账户',
|
||||
card_category: 'normal',
|
||||
card_condition: 'activated',
|
||||
asset_status: 2,
|
||||
package_state: 'active',
|
||||
realname_check_interval: null,
|
||||
carddata_check_interval: 300,
|
||||
package_check_interval: 600,
|
||||
card_status_check_interval: 0,
|
||||
pre_reset_window: 3600,
|
||||
pre_reset_carddata_interval: 60,
|
||||
created_at: time,
|
||||
updated_at: time
|
||||
}))
|
||||
const tasks = new Map()
|
||||
const page = (items, url) => {
|
||||
const current = Number(url.searchParams.get('page') || 1)
|
||||
const size = Number(url.searchParams.get('page_size') || 20)
|
||||
return {
|
||||
items: items.slice((current - 1) * size, current * size),
|
||||
page: current,
|
||||
size,
|
||||
page_size: size,
|
||||
total: items.length,
|
||||
total_pages: Math.ceil(items.length / size)
|
||||
}
|
||||
}
|
||||
|
||||
// 本地测试专用:所有 /api 都被当前或 gateway fixture 消费,无真实后端代理。
|
||||
export const mockApiPlugin = {
|
||||
name: 'polling-config-local-mock-api',
|
||||
configureServer(server) {
|
||||
server.middlewares.use(async (req, res, next) => {
|
||||
const url = new URL(req.url, 'http://127.0.0.1')
|
||||
const path = url.pathname
|
||||
const auth = ['/api/auth/me', '/api/auth/login', '/api/auth/logout'].includes(path)
|
||||
if (
|
||||
!auth &&
|
||||
!path.startsWith('/api/admin/polling-configs') &&
|
||||
!['/api/admin/polling-stats/pre-reset', '/api/admin/polling-stats/tiers'].includes(path)
|
||||
)
|
||||
return next()
|
||||
let body = ''
|
||||
for await (const chunk of req) body += chunk
|
||||
const input = body ? JSON.parse(body) : {}
|
||||
const viewer = req.headers.authorization?.includes('local-polling-viewer')
|
||||
const denied = req.headers.authorization?.includes('local-polling-denied')
|
||||
const admin = !viewer && !denied
|
||||
let data = {}
|
||||
let code = 0
|
||||
let msg = 'local mock only'
|
||||
if (auth) {
|
||||
const menus = [
|
||||
{ url: '/polling-management/config', name: '轮询配置' },
|
||||
{
|
||||
url: '/polling-management/config-stats',
|
||||
name: '轮询配置统计',
|
||||
perm_code: 'polling_config_stats:page'
|
||||
}
|
||||
]
|
||||
data = {
|
||||
access_token: viewer
|
||||
? 'local-polling-viewer'
|
||||
: denied
|
||||
? 'local-polling-denied'
|
||||
: 'local-polling-admin',
|
||||
refresh_token: '',
|
||||
expires_in: 3600,
|
||||
user: {
|
||||
id: admin ? 1 : 2,
|
||||
user_type: admin ? 1 : 2,
|
||||
username: admin ? '本地模拟管理员' : '本地普通账号',
|
||||
roles: [admin ? 'R_SUPER' : 'R_ADMIN']
|
||||
},
|
||||
permissions: denied ? [] : ['polling_config_stats:page'],
|
||||
buttons: denied
|
||||
? []
|
||||
: [
|
||||
'polling_config:match_preview',
|
||||
'polling_config:recalculate_progress',
|
||||
'polling_config_stats:pre_reset',
|
||||
'polling_config_stats:tiers'
|
||||
],
|
||||
menus: [{ url: '/polling-management', name: '轮询管理', children: menus }]
|
||||
}
|
||||
} else if (path === '/api/admin/polling-configs/match-preview') {
|
||||
const iccid = url.searchParams.get('iccid')
|
||||
if (!iccid || (req.method === 'POST' && input.iccid !== iccid)) {
|
||||
code = 1001
|
||||
msg = 'Query 和 Body 的 ICCID 必须一致'
|
||||
} else
|
||||
data = {
|
||||
card: {
|
||||
iccid,
|
||||
card_id: 42,
|
||||
carrier_id: 8,
|
||||
enable_polling: true,
|
||||
asset_status: 2,
|
||||
card_category: 'normal',
|
||||
card_condition: 'activated',
|
||||
package_state: 'active',
|
||||
package_state_name: '有生效套餐'
|
||||
},
|
||||
matched:
|
||||
req.method === 'POST' && input.overrides?.length
|
||||
? input.overrides
|
||||
.filter((item) => !item.disabled)
|
||||
.map((item) => ({
|
||||
id: item.id,
|
||||
config_name: item.config_name,
|
||||
priority: item.priority,
|
||||
draft: true
|
||||
}))
|
||||
: [{ id: 1, config_name: configs[0].config_name, priority: 1, draft: false }],
|
||||
intervals: [
|
||||
{
|
||||
config_id: 1,
|
||||
config_name: configs[0].config_name,
|
||||
inherited: false,
|
||||
interval: 300,
|
||||
mode: 'interval',
|
||||
task_type: 'polling:carddata',
|
||||
task_type_name: '流量'
|
||||
},
|
||||
{
|
||||
config_id: 1,
|
||||
config_name: configs[0].config_name,
|
||||
inherited: true,
|
||||
interval: 0,
|
||||
mode: 'disabled',
|
||||
task_type: 'polling:card_status',
|
||||
task_type_name: '卡状态'
|
||||
},
|
||||
{
|
||||
config_id: 0,
|
||||
config_name: '',
|
||||
inherited: false,
|
||||
interval: null,
|
||||
mode: 'unmatched',
|
||||
task_type: 'polling:realname',
|
||||
task_type_name: '实名'
|
||||
}
|
||||
]
|
||||
}
|
||||
} else if (path === '/api/admin/polling-configs/priorities') {
|
||||
if (!admin) {
|
||||
code = 403
|
||||
msg = '仅管理员可操作'
|
||||
} else {
|
||||
input.ordered_ids.forEach((id, index) => {
|
||||
const row = configs.find((item) => item.id === id)
|
||||
if (row) row.priority = index + 1
|
||||
})
|
||||
data = null
|
||||
}
|
||||
} else if (path === '/api/admin/polling-configs/recalculate-due' && req.method === 'POST') {
|
||||
if (!admin) {
|
||||
code = 403
|
||||
msg = '仅管理员可操作'
|
||||
} else {
|
||||
const id = `local-task-${tasks.size + 1}`
|
||||
data = {
|
||||
task_id: id,
|
||||
status: 'pending',
|
||||
carrier_id: input.carrier_id == null ? '' : String(input.carrier_id),
|
||||
total: 100,
|
||||
processed: 0,
|
||||
advanced: 0,
|
||||
error: '',
|
||||
created_at: time,
|
||||
started_at: '',
|
||||
finished_at: ''
|
||||
}
|
||||
tasks.set(id, { data, polls: 0 })
|
||||
}
|
||||
} else if (path.startsWith('/api/admin/polling-configs/recalculate-due/')) {
|
||||
const id = decodeURIComponent(
|
||||
path.slice('/api/admin/polling-configs/recalculate-due/'.length)
|
||||
)
|
||||
if (!tasks.has(id))
|
||||
tasks.set(id, {
|
||||
data: {
|
||||
task_id: id,
|
||||
carrier_id: '8',
|
||||
total: 100,
|
||||
processed: 0,
|
||||
advanced: 0,
|
||||
error: '',
|
||||
created_at: time,
|
||||
started_at: '',
|
||||
finished_at: ''
|
||||
},
|
||||
polls: 0
|
||||
})
|
||||
const entry = tasks.get(id)
|
||||
entry.polls++
|
||||
data = {
|
||||
...entry.data,
|
||||
status: entry.polls > 2 ? 'completed' : 'running',
|
||||
processed: Math.min(100, entry.polls * 40),
|
||||
advanced: entry.polls * 10,
|
||||
started_at: time,
|
||||
finished_at: entry.polls > 2 ? time : ''
|
||||
}
|
||||
} else if (path === '/api/admin/polling-stats/pre-reset') {
|
||||
data = {
|
||||
carriers: [
|
||||
{
|
||||
carrier_id: 8,
|
||||
carrier_name: '模拟运营商账户',
|
||||
previous: {
|
||||
active_package_cards: 100,
|
||||
checked_in_window: 80,
|
||||
not_checked_in_window: 20,
|
||||
reset_at: time,
|
||||
last_offset_buckets: { le_5: 0, le_15: 20, le_30: 15, le_60: 25, gt_60: 10 }
|
||||
},
|
||||
upcoming: null
|
||||
}
|
||||
]
|
||||
}
|
||||
} else if (path === '/api/admin/polling-stats/tiers') {
|
||||
data = {
|
||||
by_config: [
|
||||
{ config_id: 1, config_name: configs[0].config_name, cards: 95 },
|
||||
{ config_id: 0, config_name: '', cards: 5 }
|
||||
],
|
||||
by_package_state: { active: 80, depleted: 10, pending: 5, none: 3, unknown: 2 },
|
||||
computed_at: time,
|
||||
total_cards: 100,
|
||||
unmatched_in_stock: 2,
|
||||
unmatched_sold_no_package: 3
|
||||
}
|
||||
} else if (path === '/api/admin/polling-configs' && req.method === 'GET') {
|
||||
const query = url.searchParams.get('config_name') || ''
|
||||
data = page(
|
||||
configs.filter((item) => item.config_name.includes(query)),
|
||||
url
|
||||
)
|
||||
} else if (path === '/api/admin/polling-configs' && req.method === 'POST') {
|
||||
data = {
|
||||
...input,
|
||||
id: configs.length + 1,
|
||||
status: 1,
|
||||
created_at: time,
|
||||
updated_at: time,
|
||||
warnings: ['本地模拟:保存提示']
|
||||
}
|
||||
configs.push(data)
|
||||
} else if (/^\/api\/admin\/polling-configs\/\d+$/.test(path) && req.method === 'PUT') {
|
||||
const row = configs.find((item) => item.id === Number(path.split('/').at(-1)))
|
||||
Object.assign(row, input)
|
||||
for (const key of input.clear_fields || []) row[key] = null
|
||||
delete row.clear_fields
|
||||
data = { ...row, warnings: ['本地模拟:保存提示'] }
|
||||
} else if (/^\/api\/admin\/polling-configs\/\d+$/.test(path) && req.method === 'GET')
|
||||
data = configs.find((item) => item.id === Number(path.split('/').at(-1)))
|
||||
else {
|
||||
code = 400
|
||||
msg = '此本地验收接口未定义,已拦截写请求'
|
||||
}
|
||||
res.setHeader('Content-Type', 'application/json; charset=utf-8')
|
||||
res.end(JSON.stringify({ code, msg, data }))
|
||||
})
|
||||
gatewayFixtures.configureServer(server)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user