gongxiangp2/lib/__tests__/multipart-route-coverage.test.ts
chenpei 7326e273e1 fix: harden media upload and release safety
Protect upload cleanup and atomic completion paths, add durable media worker leases and brand asset validation, and introduce verified migration/bootstrap/deployment gates.
2026-08-12 18:04:08 +08:00

76 lines
3.3 KiB
TypeScript

/** @jest-environment node */
import { readFile } from 'fs/promises'
import path from 'path'
const guardedRoutes = [
'app/api/upload/s3-original/route.ts',
'app/api/upload/s3/route.ts',
'app/api/upload/preview/route.ts',
'app/api/materials/[id]/skus/upload/route.ts',
'app/api/user/avatar/route.ts',
'app/api/materials/[id]/route.ts',
'app/api/materials/update/route.ts',
'app/api/system-settings/upload-logo/route.ts',
] as const
const preBoundedLegacyRoutes = [
'app/api/upload/psd/route.ts',
'app/api/materials/[id]/white-background/route.ts',
'app/api/external/standard-model-white-background/by-barcode/route.ts',
'app/api/external/standard-model-white-background/by-spu/route.ts',
] as const
describe('multipart routes are bounded before parsing', () => {
test.each(guardedRoutes)('%s checks headers and acquires a slot before formData', async (routePath) => {
const source = await readFile(path.join(process.cwd(), routePath), 'utf8')
const headerGuard = source.indexOf('assertBoundedMultipartHeaders(')
const acquireSlot = source.indexOf('boundedMultipartRequestLimiter.acquire()')
const parseBody = source.indexOf('request.formData()')
const parsedFiles = source.indexOf('assertMultipartFiles(', parseBody)
expect(headerGuard).toBeGreaterThan(0)
expect(acquireSlot).toBeGreaterThan(headerGuard)
expect(parseBody).toBeGreaterThan(acquireSlot)
expect(parsedFiles).toBeGreaterThan(parseBody)
expect(source).toContain('releaseRequestSlot?.()')
})
test.each(preBoundedLegacyRoutes)('%s acquires a slot after its existing size check', async (routePath) => {
const source = await readFile(path.join(process.cwd(), routePath), 'utf8')
const headerGuard = source.indexOf("headers.get('content-length')")
const acquireSlot = source.indexOf('boundedMultipartRequestLimiter.acquire()')
const parseBody = source.indexOf('request.formData()')
expect(headerGuard).toBeGreaterThan(0)
expect(acquireSlot).toBeGreaterThan(headerGuard)
expect(parseBody).toBeGreaterThan(acquireSlot)
expect(source).toContain('releaseRequestSlot?.()')
})
test('stable proxy uses a small fallback and explicit upload route ceilings', async () => {
const source = (await readFile(
path.join(process.cwd(), 'scripts/git-incremental-deploy.ps1'),
'utf8',
)).replace(/\r\n/g, '\n')
expect(source).toContain('client_max_body_size 16m;')
expect(source).not.toContain('client_max_body_size 1000m;')
const requiredFragments = [
'location = /api/upload/preview {\n client_max_body_size 53m;',
'location ~ "^/api/upload/s3(?:-original)?\\$" {\n client_max_body_size 103m;',
'location = /api/materials/update {\n client_max_body_size 11m;',
'location ~ "^/api/(?:user/avatar|system-settings/upload-logo)\\$" {\n client_max_body_size 7m;',
'/skus/upload)\\$" {\n client_max_body_size 131m;',
'/white-background|api/external/standard-model-white-background/(?:by-spu|by-barcode))\\$" {\n client_max_body_size 106m;',
]
for (const fragment of requiredFragments) {
expect(source).toContain(fragment)
}
const boundedLocations = source.match(/client_max_body_size (?:7|11|53|103|106|131)m;[\s\S]{0,180}?proxy_request_buffering off;/g) || []
expect(boundedLocations.length).toBeGreaterThanOrEqual(7)
})
})