144 lines
5.3 KiB
TypeScript
144 lines
5.3 KiB
TypeScript
import fs from 'fs'
|
|
import os from 'os'
|
|
import path from 'path'
|
|
import { spawnSync } from 'child_process'
|
|
|
|
describe('scripts/docker-entrypoint.sh', () => {
|
|
const entrypoint = fs.readFileSync(
|
|
path.join(process.cwd(), 'scripts/docker-entrypoint.sh'),
|
|
'utf8'
|
|
)
|
|
const dockerfile = fs.readFileSync(path.join(process.cwd(), 'Dockerfile'), 'utf8')
|
|
|
|
it('生产镜像复制启动脚本后必须转成 Linux 换行', () => {
|
|
expect(dockerfile).toContain("sed -i 's/\\r$//' ./docker-entrypoint.sh")
|
|
expect(dockerfile).toContain('chmod +x ./docker-entrypoint.sh')
|
|
})
|
|
|
|
it('数据库迁移失败时必须停止容器启动', () => {
|
|
expect(entrypoint).toContain('require_real_env "NEXTAUTH_SECRET"')
|
|
expect(entrypoint).toContain('require_real_env "APP_PUBLIC_URL"')
|
|
expect(entrypoint).toContain('require_real_env "LICENSE_PUBLIC_KEY"')
|
|
expect(entrypoint).toContain('require_real_env "LOCAL_ENCRYPTION_KEY"')
|
|
expect(entrypoint).toContain('require_real_env "S3_PUBLIC_DOMAIN"')
|
|
expect(entrypoint).toContain('postgresql://username:password@host:5432/database')
|
|
expect(entrypoint).toContain('npx prisma migrate deploy')
|
|
expect(entrypoint).toContain('数据库迁移失败')
|
|
expect(entrypoint).toMatch(/数据库迁移失败[\s\S]*exit 1/)
|
|
expect(entrypoint).not.toContain('数据库迁移失败,尝试继续启动')
|
|
})
|
|
|
|
it('启动脚本不能内置固定公网 IP 地址', () => {
|
|
expect(entrypoint).not.toMatch(/\b\d{1,3}(?:\.\d{1,3}){3}\b/)
|
|
})
|
|
|
|
it('生产占位环境变量必须在连接数据库前被拦截', () => {
|
|
if (process.platform !== 'linux') {
|
|
return
|
|
}
|
|
|
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'flnxi-entrypoint-env-'))
|
|
const binDir = path.join(tmpDir, 'bin')
|
|
fs.mkdirSync(binDir)
|
|
|
|
fs.writeFileSync(
|
|
path.join(binDir, 'npx'),
|
|
['#!/bin/sh', 'echo npx-should-not-run', 'exit 99', ''].join('\n'),
|
|
{ mode: 0o755 }
|
|
)
|
|
|
|
const scriptPath = path.join(process.cwd(), 'scripts/docker-entrypoint.sh')
|
|
const result = spawnSync('sh', [scriptPath], {
|
|
cwd: tmpDir,
|
|
env: {
|
|
...process.env,
|
|
PATH: `${binDir}${path.delimiter}${process.env.PATH || ''}`,
|
|
DATABASE_URL: 'postgresql://postgres:strong-password@postgres:5432/flnxi',
|
|
APP_PUBLIC_URL: 'http://192.168.1.20:4320',
|
|
NEXTAUTH_URL: 'http://192.168.1.20:4320',
|
|
NEXTAUTH_SECRET: 'replace-with-strong-secret',
|
|
LICENSE_SERVER_URL: 'https://license.example.test',
|
|
LICENSE_PROJECT_CODE: 'flnxi-sucai',
|
|
LICENSE_REQUEST_SIGNING_SECRET: 'a'.repeat(32),
|
|
LICENSE_PUBLIC_KEY: '-----BEGIN PUBLIC KEY-----\\nabc\\n-----END PUBLIC KEY-----',
|
|
LOCAL_ENCRYPTION_KEY: 'b'.repeat(32),
|
|
S3_ENDPOINT: 'http://minio:9000',
|
|
S3_BUCKET: 'flnxi-materials',
|
|
S3_ACCESS_KEY_ID: 'minioadmin',
|
|
S3_SECRET_ACCESS_KEY: 'minio-secret',
|
|
S3_PUBLIC_DOMAIN: 'http://192.168.1.20:9000/flnxi-materials',
|
|
},
|
|
encoding: 'utf8',
|
|
})
|
|
|
|
expect(result.status).toBe(1)
|
|
expect(`${result.stdout}\n${result.stderr}`).toContain('NEXTAUTH_SECRET 未配置为真实值')
|
|
expect(`${result.stdout}\n${result.stderr}`).not.toContain('npx-should-not-run')
|
|
})
|
|
|
|
it('迁移命令真实失败时不会继续启动 server.js', () => {
|
|
if (process.platform !== 'linux') {
|
|
return
|
|
}
|
|
|
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'flnxi-entrypoint-'))
|
|
const binDir = path.join(tmpDir, 'bin')
|
|
fs.mkdirSync(binDir)
|
|
|
|
fs.writeFileSync(
|
|
path.join(binDir, 'npx'),
|
|
[
|
|
'#!/bin/sh',
|
|
'if [ "$1 $2 $3" = "prisma db execute" ]; then',
|
|
' exit 0',
|
|
'fi',
|
|
'if [ "$1 $2 $3" = "prisma migrate deploy" ]; then',
|
|
' exit 42',
|
|
'fi',
|
|
'exit 1',
|
|
'',
|
|
].join('\n'),
|
|
{ mode: 0o755 }
|
|
)
|
|
|
|
fs.writeFileSync(
|
|
path.join(binDir, 'node'),
|
|
[
|
|
'#!/bin/sh',
|
|
`echo started > "${path.join(tmpDir, 'server-started').replace(/\\/g, '/')}"`,
|
|
'exit 0',
|
|
'',
|
|
].join('\n'),
|
|
{ mode: 0o755 }
|
|
)
|
|
|
|
const scriptPath = path.join(process.cwd(), 'scripts/docker-entrypoint.sh')
|
|
const result = spawnSync('sh', [scriptPath], {
|
|
cwd: tmpDir,
|
|
env: {
|
|
...process.env,
|
|
PATH: `${binDir}${path.delimiter}${process.env.PATH || ''}`,
|
|
DATABASE_URL: 'postgresql://postgres:strong-password@postgres:5432/flnxi',
|
|
APP_PUBLIC_URL: 'http://192.168.1.20:4320',
|
|
NEXTAUTH_URL: 'http://192.168.1.20:4320',
|
|
NEXTAUTH_SECRET: 'strong-nextauth-secret',
|
|
LICENSE_SERVER_URL: 'https://license.example.test',
|
|
LICENSE_PROJECT_CODE: 'flnxi-sucai',
|
|
LICENSE_REQUEST_SIGNING_SECRET: 'a'.repeat(32),
|
|
LICENSE_PUBLIC_KEY: '-----BEGIN PUBLIC KEY-----\\nabc\\n-----END PUBLIC KEY-----',
|
|
LOCAL_ENCRYPTION_KEY: 'b'.repeat(32),
|
|
S3_ENDPOINT: 'http://minio:9000',
|
|
S3_BUCKET: 'flnxi-materials',
|
|
S3_ACCESS_KEY_ID: 'minioadmin',
|
|
S3_SECRET_ACCESS_KEY: 'minio-secret',
|
|
S3_PUBLIC_DOMAIN: 'http://192.168.1.20:9000/flnxi-materials',
|
|
},
|
|
encoding: 'utf8',
|
|
})
|
|
|
|
expect(result.status).toBe(1)
|
|
expect(`${result.stdout}\n${result.stderr}`).toContain('数据库迁移失败')
|
|
expect(fs.existsSync(path.join(tmpDir, 'server-started'))).toBe(false)
|
|
})
|
|
})
|