228d481211
Java: - LLM 180s 读超时不再被全局 call-timeout 静默截断成 90s(长思考请求被掐断→重试→付费网关二次计费) - 代理 HttpClient 缓存改有界 LRU(jikip 每次提取新 IP,无界缓存持续泄漏 selector 线程与连接池) - 12 个 service 的 Redis 任务锁移出 @Transactional(自旋最坏 10s 白占 DB 连接,池仅 30),远端对象删/传改 afterCommit - 结果文件 Job 闸门拒绝时不再回退内联执行(改重新入队,避免把背压转嫁给 MQ 消费线程) - imagevideo 每秒扫描加列投影、过期清理加 LIMIT;权限页整表查询改列投影(不再拉回密码哈希) - 哈希改 HexFormat;补 5 处"不能改"的技术依据注释(批量插入会丢回填主键、流式丢模板与图片等) 前端:4 个工具页轮询改轻量端点(带 fallback);PriceTrack 快照节流写盘;候选店铺表分页;页面隐藏时停表 客户端:HTTP 连接池按出口复用(Session 仍每请求新建,保持无跨请求状态);品牌检测 WIPO 逐请求握手; 代理配置按 mtime 缓存;串行任务改专属池;异常降级为标签页重连;紫鸟启动改端口轮询;模板编译缓存; 日志上报连接与落盘收口;Flask 版本 API 改按请求复用连接
228 lines
7.6 KiB
TypeScript
228 lines
7.6 KiB
TypeScript
import { test } from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import { useTaskProgressLoop } from '../src/shared/composables/useTaskProgressLoop.ts'
|
|
import {
|
|
TASK_POLL_VISIBLE_INTERVAL_MS,
|
|
TASK_POLL_HIDDEN_INTERVAL_MS,
|
|
configureTaskPolling,
|
|
resetTaskPollingConfig,
|
|
} from '../src/shared/task-progress-config.ts'
|
|
|
|
const tick = (ms = 0) => new Promise<void>((r) => globalThis.setTimeout(r, ms))
|
|
|
|
interface FakeDoc {
|
|
setVisibility: (state: string) => void
|
|
dispatch: (type: string) => void
|
|
listenerCount: (type: string) => number
|
|
listeners: (type: string) => Array<(e?: unknown) => void>
|
|
}
|
|
|
|
function installFakeDocument(initial: string): FakeDoc {
|
|
let state = initial
|
|
const listeners = new Map<string, Array<(e?: unknown) => void>>()
|
|
;(globalThis as Record<string, unknown>).document = {
|
|
get visibilityState() { return state },
|
|
get hidden() { return state !== 'visible' },
|
|
addEventListener: (type: string, fn: (e?: unknown) => void) => {
|
|
const arr = listeners.get(type) ?? []
|
|
arr.push(fn)
|
|
listeners.set(type, arr)
|
|
},
|
|
removeEventListener: (type: string, fn: (e?: unknown) => void) => {
|
|
const arr = listeners.get(type) ?? []
|
|
listeners.set(type, arr.filter((f) => f !== fn))
|
|
},
|
|
}
|
|
return {
|
|
setVisibility: (s) => { state = s },
|
|
dispatch: (type) => { for (const fn of listeners.get(type) ?? []) fn() },
|
|
listenerCount: (type) => (listeners.get(type) ?? []).length,
|
|
listeners: (type) => listeners.get(type) ?? [],
|
|
}
|
|
}
|
|
|
|
function cleanupDocument() {
|
|
delete (globalThis as Record<string, unknown>).document
|
|
resetTaskPollingConfig()
|
|
}
|
|
|
|
interface TimerSinkEntry {
|
|
fn: () => void
|
|
ms: number
|
|
}
|
|
|
|
let scopeCounter = 0
|
|
|
|
interface VisibilityHarness {
|
|
loop: ReturnType<typeof useTaskProgressLoop<{ taskId?: number; status?: string }>>
|
|
doc: FakeDoc
|
|
sink: TimerSinkEntry[]
|
|
/** 被 clearTimeout 取消过的定时器 id(用于断言隐藏时确实停了表) */
|
|
cleared: unknown[]
|
|
fetchCount: () => number
|
|
}
|
|
|
|
async function makeVisibilityLoop(
|
|
visibility: string,
|
|
config?: { enabled?: boolean; delay?: number },
|
|
): Promise<VisibilityHarness> {
|
|
if (config) {
|
|
const partial: Parameters<typeof configureTaskPolling>[0] = {}
|
|
if (config.enabled !== undefined) partial.foregroundRefreshEnabled = config.enabled
|
|
if (config.delay !== undefined) partial.foregroundRefreshDelayMs = config.delay
|
|
configureTaskPolling(partial)
|
|
}
|
|
const doc = installFakeDocument(visibility)
|
|
const sink: TimerSinkEntry[] = []
|
|
const cleared: unknown[] = []
|
|
const storage = new Map<string, string>()
|
|
let fetchCount = 0
|
|
;(globalThis as Record<string, unknown>).window = {
|
|
localStorage: {
|
|
getItem: (k: string) => storage.get(k) ?? null,
|
|
setItem: (k: string, v: string) => { storage.set(k, v) },
|
|
removeItem: (k: string) => { storage.delete(k) },
|
|
},
|
|
setTimeout: (fn: () => void, ms: number) => {
|
|
sink.push({ fn, ms })
|
|
return globalThis.setTimeout(fn, ms)
|
|
},
|
|
clearTimeout: (id: unknown) => {
|
|
cleared.push(id)
|
|
globalThis.clearTimeout(id as number)
|
|
},
|
|
setInterval: (fn: () => void, ms: number) => globalThis.setInterval(fn, ms),
|
|
clearInterval: (id: unknown) => globalThis.clearInterval(id as number),
|
|
}
|
|
const loop = useTaskProgressLoop<{ taskId?: number; status?: string }>({
|
|
scope: `vis-${scopeCounter++}`,
|
|
fetchProgress: async () => {
|
|
fetchCount += 1
|
|
return { items: [] }
|
|
},
|
|
extractTaskId: (d) => d?.taskId,
|
|
extractStatus: (d) => d?.status,
|
|
})
|
|
loop.reset([1])
|
|
return { loop, doc, sink, cleared, fetchCount: () => fetchCount }
|
|
}
|
|
|
|
test('test_hidden_long_interval', async () => {
|
|
const h = await makeVisibilityLoop('hidden')
|
|
await tick(10)
|
|
const last = h.sink.at(-1)
|
|
assert.equal(last?.ms, TASK_POLL_HIDDEN_INTERVAL_MS, '隐藏时按隐藏间隔排定下一轮')
|
|
h.loop.dispose()
|
|
cleanupDocument()
|
|
})
|
|
|
|
test('test_visible_short_interval', async () => {
|
|
const h = await makeVisibilityLoop('visible')
|
|
await tick(10)
|
|
const last = h.sink.at(-1)
|
|
assert.equal(last?.ms, TASK_POLL_VISIBLE_INTERVAL_MS, '可见时按可见间隔排定下一轮')
|
|
h.loop.dispose()
|
|
cleanupDocument()
|
|
})
|
|
|
|
test('test_foreground_refresh_triggered', async () => {
|
|
const h = await makeVisibilityLoop('hidden')
|
|
await tick(10)
|
|
assert.equal(h.fetchCount(), 1, '初始一轮已完成')
|
|
h.doc.setVisibility('visible')
|
|
h.doc.dispatch('visibilitychange')
|
|
await tick(10)
|
|
assert.equal(h.fetchCount(), 2, '切回可见立即刷新一轮')
|
|
h.loop.dispose()
|
|
cleanupDocument()
|
|
})
|
|
|
|
test('test_foreground_refresh_disabled', async () => {
|
|
const h = await makeVisibilityLoop('hidden', { enabled: false })
|
|
await tick(10)
|
|
h.doc.setVisibility('visible')
|
|
h.doc.dispatch('visibilitychange')
|
|
await tick(10)
|
|
assert.equal(h.fetchCount(), 1, '开关关闭时切回可见不刷新')
|
|
h.loop.dispose()
|
|
cleanupDocument()
|
|
})
|
|
|
|
test('test_foreground_refresh_delayed', async () => {
|
|
const h = await makeVisibilityLoop('hidden', { delay: 300 })
|
|
await tick(10)
|
|
h.doc.setVisibility('visible')
|
|
h.doc.dispatch('visibilitychange')
|
|
assert.ok(h.sink.some((t) => t.ms === 300), '按配置延迟排定刷新定时器')
|
|
await tick(100)
|
|
assert.equal(h.fetchCount(), 1, '延迟期内不刷新')
|
|
await tick(300)
|
|
assert.equal(h.fetchCount(), 2, '延迟到点后刷新一轮')
|
|
h.loop.dispose()
|
|
cleanupDocument()
|
|
})
|
|
|
|
test('test_hidden_polling_continues', async () => {
|
|
const h = await makeVisibilityLoop('hidden')
|
|
await tick(10)
|
|
assert.ok(h.sink.some((t) => t.ms === TASK_POLL_HIDDEN_INTERVAL_MS), '隐藏间隔定时器已排定')
|
|
const before = h.fetchCount()
|
|
await h.loop.refreshOnce()
|
|
assert.equal(h.fetchCount(), before + 1, '隐藏期间手动轮询仍工作')
|
|
assert.deepEqual(h.loop.taskIds.value, [1], '降频不改任务集合')
|
|
h.loop.dispose()
|
|
cleanupDocument()
|
|
})
|
|
|
|
test('test_visibility_event_bound_unbound', async () => {
|
|
const h = await makeVisibilityLoop('hidden')
|
|
assert.equal(h.doc.listenerCount('visibilitychange'), 1, '创建时绑定 visibilitychange')
|
|
const handler = h.doc.listeners('visibilitychange')[0]
|
|
h.loop.dispose()
|
|
assert.equal(h.doc.listenerCount('visibilitychange'), 0, 'dispose 后解绑')
|
|
assert.equal(typeof handler, 'function')
|
|
cleanupDocument()
|
|
})
|
|
|
|
test('test_hidden_no_immediate_refresh', async () => {
|
|
const h = await makeVisibilityLoop('hidden')
|
|
await tick(10)
|
|
h.doc.dispatch('visibilitychange')
|
|
await tick(10)
|
|
assert.equal(h.fetchCount(), 1, '隐藏状态下的事件不触发即时刷新')
|
|
h.loop.dispose()
|
|
cleanupDocument()
|
|
})
|
|
|
|
test('test_hidden_stops_polling', async () => {
|
|
const h = await makeVisibilityLoop('visible')
|
|
await tick(10)
|
|
const scheduledBefore = h.sink.length
|
|
const before = h.fetchCount()
|
|
h.doc.setVisibility('hidden')
|
|
h.doc.dispatch('visibilitychange')
|
|
await tick(10)
|
|
assert.equal(h.fetchCount(), before, '隐藏后不再发起请求')
|
|
assert.equal(h.sink.length, scheduledBefore, '隐藏后不再排定新的轮询定时器')
|
|
assert.ok(h.cleared.length > 0, '隐藏时清掉了已排定的轮询定时器')
|
|
h.loop.dispose()
|
|
cleanupDocument()
|
|
})
|
|
|
|
test('test_visible_restarts_polling', async () => {
|
|
const h = await makeVisibilityLoop('visible')
|
|
await tick(10)
|
|
h.doc.setVisibility('hidden')
|
|
h.doc.dispatch('visibilitychange')
|
|
await tick(10)
|
|
const before = h.fetchCount()
|
|
h.doc.setVisibility('visible')
|
|
h.doc.dispatch('visibilitychange')
|
|
await tick(10)
|
|
assert.equal(h.fetchCount(), before + 1, '恢复可见立即补拉一轮')
|
|
assert.equal(h.sink.at(-1)?.ms, TASK_POLL_VISIBLE_INTERVAL_MS, '恢复可见后按可见间隔重启轮询')
|
|
assert.deepEqual(h.loop.taskIds.value, [1], '停表期间任务集合不变')
|
|
h.loop.dispose()
|
|
cleanupDocument()
|
|
})
|