148 lines
5.2 KiB
TypeScript
148 lines
5.2 KiB
TypeScript
import { mkdtempSync, rmSync, writeFileSync } from 'fs'
|
|
import os from 'os'
|
|
import path from 'path'
|
|
import {
|
|
classifyPublicTrafficPurpose,
|
|
getPublicTrafficPresetRange,
|
|
getPublicTrafficStats,
|
|
normalizeTrafficRange,
|
|
parsePublicTrafficLogLine,
|
|
} from '@/lib/public-traffic-stats'
|
|
|
|
describe('public traffic stats', () => {
|
|
test('parses standard nginx access log response bytes', () => {
|
|
const line =
|
|
'58.216.227.194 - - [04/Jul/2026:10:35:05 +0800] "POST /api/dingtalk/login HTTP/2.0" 200 349 "https://sc.jiansun.vip/dingtalk-callback" "Mozilla/5.0"'
|
|
|
|
const entry = parsePublicTrafficLogLine(line, 'sc.jiansun.vip')
|
|
|
|
expect(entry).toEqual(
|
|
expect.objectContaining({
|
|
domain: 'sc.jiansun.vip',
|
|
remoteAddress: '58.216.227.194',
|
|
method: 'POST',
|
|
path: '/api/dingtalk/login',
|
|
status: 200,
|
|
responseBytes: 349,
|
|
requestBytes: null,
|
|
}),
|
|
)
|
|
expect(entry?.timestamp.toISOString()).toBe('2026-07-04T02:35:05.000Z')
|
|
})
|
|
|
|
test('parses extended request_length when present', () => {
|
|
const line =
|
|
'58.216.227.194 - - [04/Jul/2026:10:42:52 +0800] "GET /api/materials/download?id=abc HTTP/1.1" 200 1024 "-" "curl" bytes_sent=1200 request_length=512'
|
|
|
|
const entry = parsePublicTrafficLogLine(line, 'sc.jiansun.vip')
|
|
|
|
expect(entry).toEqual(
|
|
expect.objectContaining({
|
|
method: 'GET',
|
|
path: '/api/materials/download',
|
|
responseBytes: 1200,
|
|
requestBytes: 512,
|
|
}),
|
|
)
|
|
})
|
|
|
|
test('normalizes default range to whole local days', () => {
|
|
const range = normalizeTrafficRange('2026-07-01', '2026-07-04')
|
|
|
|
expect(range.start.toISOString()).toBe('2026-06-30T16:00:00.000Z')
|
|
expect(range.end.toISOString()).toBe('2026-07-04T15:59:59.999Z')
|
|
})
|
|
|
|
test('builds public traffic presets using China business dates', () => {
|
|
const now = new Date('2026-07-08T05:00:00.000Z')
|
|
const today = getPublicTrafficPresetRange('today', now)
|
|
const last7 = getPublicTrafficPresetRange('last7', now)
|
|
const last30 = getPublicTrafficPresetRange('last30', now)
|
|
|
|
expect(today.start.toISOString()).toBe('2026-07-07T16:00:00.000Z')
|
|
expect(today.end.toISOString()).toBe('2026-07-08T15:59:59.999Z')
|
|
expect(last7.start.toISOString()).toBe('2026-07-01T16:00:00.000Z')
|
|
expect(last7.end.toISOString()).toBe('2026-07-08T15:59:59.999Z')
|
|
expect(last30.start.toISOString()).toBe('2026-06-08T16:00:00.000Z')
|
|
expect(last30.end.toISOString()).toBe('2026-07-08T15:59:59.999Z')
|
|
})
|
|
|
|
test('groups daily traffic by China business date', async () => {
|
|
const previousPaths = process.env.PUBLIC_TRAFFIC_LOG_PATHS
|
|
const tempDir = mkdtempSync(path.join(os.tmpdir(), 'public-traffic-'))
|
|
const logPath = path.join(tempDir, 'sc.jiansun.vip.access.log')
|
|
|
|
try {
|
|
writeFileSync(
|
|
logPath,
|
|
'58.216.227.194 - - [04/Jul/2026:01:35:05 +0800] "GET /assets/a.png HTTP/2.0" 200 2048 "-" "Mozilla/5.0"\n',
|
|
)
|
|
process.env.PUBLIC_TRAFFIC_LOG_PATHS = logPath
|
|
|
|
const stats = await getPublicTrafficStats(normalizeTrafficRange('2026-07-04', '2026-07-04'))
|
|
|
|
expect(stats.byDay).toEqual([
|
|
expect.objectContaining({
|
|
date: '2026-07-04',
|
|
responseBytes: 2048,
|
|
totalRequests: 1,
|
|
}),
|
|
])
|
|
expect(stats.byPurpose).toEqual([
|
|
expect.objectContaining({
|
|
key: 'static-asset',
|
|
label: '图片直链访问',
|
|
category: '资源文件',
|
|
}),
|
|
])
|
|
} finally {
|
|
if (previousPaths === undefined) {
|
|
delete process.env.PUBLIC_TRAFFIC_LOG_PATHS
|
|
} else {
|
|
process.env.PUBLIC_TRAFFIC_LOG_PATHS = previousPaths
|
|
}
|
|
rmSync(tempDir, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
test('classifies technical paths into human-readable business purposes', () => {
|
|
expect(classifyPublicTrafficPurpose('sc.jiansun.vip', '/api/materials/download', 'GET')).toEqual(
|
|
expect.objectContaining({
|
|
key: 'material-download',
|
|
label: '素材下载/打包下载',
|
|
}),
|
|
)
|
|
|
|
expect(classifyPublicTrafficPurpose('cp.jiansun.vip', '/api/intake/non-integrated', 'GET')).toEqual(
|
|
expect.objectContaining({
|
|
key: 'product-intake-non-integrated',
|
|
label: '产品设计平台未集成素材列表',
|
|
}),
|
|
)
|
|
|
|
expect(
|
|
classifyPublicTrafficPurpose(
|
|
's3.jiansun.vip',
|
|
'/gongxiangpan/uploads/direct/d685fa51-d8eb-42ea-b471-4a91269a7a90/1783137399966-p0j9wv.jpg',
|
|
'GET',
|
|
),
|
|
).toEqual(
|
|
expect.objectContaining({
|
|
key: 'direct-upload-asset',
|
|
label: '图片直链访问',
|
|
category: '直传资源',
|
|
detail: '直传上传目录下的图片、视频、压缩包等资源直链汇总,具体文件请看“流量最高路径”。',
|
|
}),
|
|
)
|
|
|
|
expect(classifyPublicTrafficPurpose('s3.jiansun.vip', '/gongxiangpan/uploads/1775120618610-g75pxl.png', 'GET')).toEqual(
|
|
expect.objectContaining({
|
|
key: 'legacy-upload-asset',
|
|
label: '图片直链访问',
|
|
category: '历史上传资源',
|
|
detail: '历史上传目录下的图片、视频、压缩包等资源直链汇总,具体文件请看“流量最高路径”。',
|
|
}),
|
|
)
|
|
})
|
|
})
|