gongxiangp2/scripts/git-incremental-deploy.test.mjs
2026-09-09 13:29:33 +08:00

92 lines
3.9 KiB
JavaScript

import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import { spawnSync } from 'node:child_process'
import test from 'node:test'
import {
extractRemoteTemplate,
parseArguments,
renderRemoteTemplate,
} from './git-incremental-deploy-macos.mjs'
test('generated remote deployment payload is valid bash', () => {
const source = readFileSync(new URL('./git-incremental-deploy.ps1', import.meta.url), 'utf8')
const payload = extractRemoteTemplate(source).replace(/__[A-Z0-9_]+__/g, "''")
const result = spawnSync('bash', ['-n'], {
input: payload,
encoding: 'utf8',
})
assert.equal(result.status, 0, result.stderr || result.stdout)
})
test('Mac entry renders every canonical remote deployment token', () => {
const source = readFileSync(new URL('./git-incremental-deploy.ps1', import.meta.url), 'utf8')
const template = extractRemoteTemplate(source)
const tokens = [...new Set(template.match(/__[A-Z0-9_]+__/g) ?? [])]
const values = Object.fromEntries(tokens.map((token, index) => [token, token === '__COMMIT__'
? 'a'.repeat(40)
: `value ${index} with quote ' safely escaped`]))
const payload = renderRemoteTemplate(template, values)
assert.doesNotMatch(payload, /__[A-Z0-9_]+__/)
const result = spawnSync('bash', ['-n'], { input: payload, encoding: 'utf8' })
assert.equal(result.status, 0, result.stderr || result.stdout)
})
test('Mac entry requires an exact commit outside DryRun', () => {
assert.throws(() => parseArguments([]), /正式部署必须/)
assert.throws(() => parseArguments(['--commit', 'abc']), /40 位小写/)
assert.equal(parseArguments(['--dry-run']).dryRun, true)
assert.equal(parseArguments(['--commit', 'b'.repeat(40)]).commit, 'b'.repeat(40))
})
test('deployment SSH hops keep long builds and cutovers alive', () => {
const macSource = readFileSync(new URL('./git-incremental-deploy-macos.mjs', import.meta.url), 'utf8')
const powerShellSource = readFileSync(new URL('./git-incremental-deploy.ps1', import.meta.url), 'utf8')
const payload = extractRemoteTemplate(powerShellSource)
for (const source of [macSource, payload]) {
assert.match(source, /ServerAliveInterval=3/)
assert.match(source, /ServerAliveCountMax=10/)
assert.match(source, /TCPKeepAlive=yes/)
}
})
test('migration deployment gate recognizes the latest approved schema migration', () => {
const source = readFileSync(new URL('./git-incremental-deploy.ps1', import.meta.url), 'utf8')
const gateStart = source.indexOf('const expectedMigrationNames = new Set([')
const gateEnd = source.indexOf(']);', gateStart)
assert.ok(gateStart >= 0 && gateEnd > gateStart, 'migration gate list is missing')
const gate = source.slice(gateStart, gateEnd)
assert.match(gate, /20260831_add_private_task_detail/)
assert.match(gate, /20260908_add_brand_knowledge/)
})
test('brand knowledge upload stays bounded in the route and stable proxy', () => {
const deploySource = readFileSync(new URL('./git-incremental-deploy.ps1', import.meta.url), 'utf8')
.replace(/\r\n/g, '\n')
const routeSource = readFileSync(
new URL('../app/api/department-dashboard/brand-knowledge/route.ts', import.meta.url),
'utf8',
)
assert.match(
deploySource,
/location = \/api\/department-dashboard\/brand-knowledge \{\n\s+client_max_body_size 58m;\n\s+proxy_request_buffering off;/,
)
const headerGuard = routeSource.indexOf('assertBoundedMultipartHeaders(')
const acquireSlot = routeSource.indexOf('boundedMultipartRequestLimiter.acquire()')
const parseBody = routeSource.indexOf('request.formData()')
const parsedFiles = routeSource.indexOf('assertMultipartFiles(', parseBody)
assert.ok(headerGuard > 0)
assert.ok(acquireSlot > headerGuard)
assert.ok(parseBody > acquireSlot)
assert.ok(parsedFiles > parseBody)
assert.match(routeSource, /const MAX_FILE_SIZE = 50 \* 1024 \* 1024/)
assert.match(routeSource, /const MAX_REQUEST_SIZE = 58 \* 1024 \* 1024/)
assert.match(routeSource, /releaseRequestSlot\?\.\(\)/)
})