68 lines
2.9 KiB
JavaScript
68 lines
2.9 KiB
JavaScript
import { existsSync, mkdtempSync, readdirSync, realpathSync, rmSync } from 'node:fs'
|
||
import { tmpdir } from 'node:os'
|
||
import { basename, dirname, join, resolve, sep } from 'node:path'
|
||
import { spawnSync } from 'node:child_process'
|
||
import { fileURLToPath } from 'node:url'
|
||
|
||
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..')
|
||
const testPrefix = 'yyd-notification-center-test-'
|
||
const testRoot = mkdtempSync(join(tmpdir(), testPrefix))
|
||
const databasePath = join(testRoot, 'notifications.db')
|
||
// Prisma 的 SQLite file URL 必须使用正斜杠;Windows 原生反斜杠会让 schema-engine 无细节退出。
|
||
const databaseUrl = `file:${databasePath.split(sep).join('/')}`
|
||
const childEnv = { ...process.env, DATABASE_URL: databaseUrl }
|
||
// Prisma 5.22 的 Windows schema-engine 在部分主机未初始化 Rust 日志时会无细节提前退出。
|
||
// 只在 Windows 测试子进程中启用;Linux/Docker 不受影响,也不会记录数据库内容或密钥。
|
||
if (process.platform === 'win32') childEnv.RUST_LOG = 'trace'
|
||
|
||
const runNode = (args) => {
|
||
const result = spawnSync(process.execPath, args, {
|
||
cwd: repoRoot,
|
||
env: childEnv,
|
||
stdio: 'inherit',
|
||
})
|
||
if (result.error) throw result.error
|
||
return result.status ?? 1
|
||
}
|
||
|
||
const removeTestRoot = () => {
|
||
const resolvedTemp = realpathSync(tmpdir())
|
||
const resolvedTestRoot = realpathSync(testRoot)
|
||
const isTempChild = resolvedTestRoot.startsWith(`${resolvedTemp}${sep}`)
|
||
if (!isTempChild || !basename(resolvedTestRoot).startsWith(testPrefix)) {
|
||
throw new Error(`拒绝清理非测试临时目录: ${resolvedTestRoot}`)
|
||
}
|
||
rmSync(resolvedTestRoot, { recursive: true, force: true })
|
||
if (existsSync(resolvedTestRoot)) throw new Error('测试临时目录清理失败')
|
||
}
|
||
|
||
let exitCode = 0
|
||
try {
|
||
const prismaCli = resolve(repoRoot, 'node_modules', 'prisma', 'build', 'index.js')
|
||
// cwd 已固定为 repoRoot;避免 Windows schema-engine 处理含空格的绝对 schema 路径失败。
|
||
const schema = 'prisma/schema.prisma'
|
||
exitCode = runNode([prismaCli, 'migrate', 'deploy', '--schema', schema])
|
||
if (exitCode === 0) {
|
||
const tsxCli = resolve(repoRoot, 'node_modules', 'tsx', 'dist', 'cli.mjs')
|
||
const testsDirectory = resolve(repoRoot, 'src', '__tests__')
|
||
const testFiles = readdirSync(testsDirectory)
|
||
.filter((name) => name.endsWith('.test.ts'))
|
||
.sort()
|
||
.map((name) => resolve(testsDirectory, name))
|
||
if (testFiles.length === 0) throw new Error('未找到测试文件')
|
||
exitCode = runNode([tsxCli, '--test', '--test-concurrency=1', ...testFiles])
|
||
}
|
||
} catch (error) {
|
||
console.error('[test-runner]', error instanceof Error ? error.message : String(error))
|
||
exitCode = 1
|
||
} finally {
|
||
try {
|
||
removeTestRoot()
|
||
} catch (error) {
|
||
console.error('[test-runner-cleanup]', error instanceof Error ? error.message : String(error))
|
||
exitCode = 1
|
||
}
|
||
}
|
||
|
||
process.exitCode = exitCode
|