Files
kra-new/web/deploy/release.test.mjs
T

55 lines
3.3 KiB
JavaScript

import test from 'node:test'
import assert from 'node:assert/strict'
import fs from 'node:fs'
import path from 'node:path'
import os from 'node:os'
import { execFileSync } from 'node:child_process'
import { frontendRoot, frontendImage, validTag, shellQuote, deploymentTarget, validateEntries, inspectPackage, bashExecutable } from './release.mjs'
import { installScript } from './deploy-test.mjs'
test('release and SSH inputs reject paths, options and shell injection', () => {
for (const value of ['', '../release', '-latest', 'a;touch x', 'a\nother']) assert.throws(() => validTag(value))
assert.equal(validTag('a846bd90-rc.1'), 'a846bd90-rc.1')
assert.equal(frontendImage('a846bd90-rc.1'), 'harbor.fuhua91.com/game/sw2:a846bd90-rc.1')
assert.throws(() => frontendImage('../other'))
for (const args of [{ host: '-oProxyCommand=id' }, { user: 'root;id' }, { directory: '/' }, { directory: '/www/../etc' }, { port: '65536' }]) assert.throws(() => deploymentTarget(args))
assert.equal(shellQuote("a'b$(id)"), "'a'\\''b$(id)'")
assert.equal(deploymentTarget().base, '/www/wwwroot/sw2.fuhua91.com')
})
test('runtime archives exclude backend source and filesystem links', () => {
const required = ['./Dockerfile', './nginx.conf', './deploy.sh', './release.json', './dist/index.html', './dist/version.txt']
validateEntries(required, required.map(() => '-'))
for (const extra of ['../outside', '/etc/passwd', './dist/../../outside', './backend/app', './internal/data.go', './dist\\..\\outside']) assert.throws(() => validateEntries([...required, extra], required.map(() => '-')))
for (const type of ['l', 'h', 'p', 'b']) assert.throws(() => validateEntries(required, ['-', type]))
})
test('package inspection verifies embedded version and reads the actual archive checksum', () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'sw-frontend-release-test-'))
try {
const stage = path.join(root, 'stage'); fs.mkdirSync(path.join(stage, 'dist'), { recursive: true })
for (const file of ['Dockerfile', 'nginx.conf', 'deploy.sh', 'dist/index.html']) fs.writeFileSync(path.join(stage, file), 'test\n')
fs.writeFileSync(path.join(stage, 'release.json'), JSON.stringify({ formatVersion: 1, version: 'test-1', revision: 'a'.repeat(40), tree: 'b'.repeat(40) }))
fs.writeFileSync(path.join(stage, 'dist/version.txt'), 'test-1\n')
const archive = path.join(root, 'release.tar.gz')
execFileSync('tar', ['-czf', archive, '-C', stage, '.'])
const result = inspectPackage(archive)
assert.equal(result.info.version, 'test-1')
assert.match(result.checksum, /^[a-f0-9]{64}$/)
fs.writeFileSync(path.join(stage, 'dist/version.txt'), 'wrong-version\n')
execFileSync('tar', ['-czf', archive, '-C', stage, '.'])
assert.throws(() => inspectPackage(archive), /版本标记不一致/)
} finally {
assert.equal(path.dirname(root), path.resolve(os.tmpdir()))
fs.rmSync(root, { recursive: true })
}
})
test('server shell syntax and deployment rollback preserve the backend', () => {
const bash = bashExecutable()
execFileSync(bash, ['-n', 'deploy/deploy.sh'], { cwd: frontendRoot })
execFileSync(bash, ['-n'], { input: installScript, cwd: frontendRoot })
const output = execFileSync(bash, ['deploy/deploy.test.sh'], { cwd: frontendRoot, encoding: 'utf8' })
assert.match(output, /all deployment scenarios passed/)
})