scripts-setup-test-db-template.mjs
3.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#!/usr/bin/env node
// scripts/setup-test-db.mjs — DROP + CREATE 空测试库。
// 由 coding.mjs 的 test-gate 调用;schema 由 Flyway 在 Spring Boot 启动时重放。
// 只允许本地 host(或 TEST_DB_ALLOWED_HOSTS 白名单内的 host)+ 测试库名(含 test/_dev/_local/_ci)。
import { spawnSync } from 'node:child_process'
import { existsSync, readFileSync } from 'node:fs'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url))
const ENV_FILE = join(SCRIPT_DIR, '..', '.env.local')
function parseEnv(text) {
const env = {}
for (const rawLine of text.split(/\r?\n/)) {
const line = rawLine.trim()
if (line === '' || line.startsWith('#')) continue
const eq = line.indexOf('=')
if (eq === -1) continue
const key = line.slice(0, eq).trim()
if (!key) continue
let value = line.slice(eq + 1).trim()
if (
value.length >= 2 &&
((value.startsWith("'") && value.endsWith("'")) ||
(value.startsWith('"') && value.endsWith('"')))
) {
value = value.slice(1, -1)
}
env[key] = value
}
return env
}
if (!existsSync(ENV_FILE)) {
console.error(`[setup-test-db] .env.local 不存在(${ENV_FILE})`)
process.exit(1)
}
const env = parseEnv(readFileSync(ENV_FILE, 'utf8'))
const DB_HOST = env.DB_HOST ?? ''
const DB_PORT = env.DB_PORT ?? '3306'
const DB_USER = env.DB_USER ?? ''
const DB_PASSWORD = env.DB_PASSWORD ?? ''
const DB_SCHEMA = env.DB_SCHEMA ?? ''
// 防护 1:默认只允许本地 host(localhost / 127.0.0.1 / ::1)。
// 额外允许的远程 host 在 .env.local 的 TEST_DB_ALLOWED_HOSTS 中(空格或逗号分隔)。
const extraHosts = (env.TEST_DB_ALLOWED_HOSTS ?? '')
.split(/[\s,]+/)
.filter(Boolean)
const allowedHosts = ['localhost', '127.0.0.1', '::1', ...extraHosts]
if (!allowedHosts.includes(DB_HOST)) {
console.error(`[setup-test-db] 拒绝在非白名单 host (${DB_HOST}) 上执行 DROP DATABASE`)
console.error(` 当前白名单:${allowedHosts.join(' ')}`)
console.error(' 加入 host:在 .env.local 追加 TEST_DB_ALLOWED_HOSTS="<host1> <host2>"')
process.exit(1)
}
// 防护 2:schema 名需像测试/开发库(含 test / _dev / _local / _ci),否则拒绝。
const schemaLooksLikeTest =
/test/.test(DB_SCHEMA) || /_dev$/.test(DB_SCHEMA) || /_local$/.test(DB_SCHEMA) || /_ci$/.test(DB_SCHEMA)
if (!schemaLooksLikeTest) {
console.error(
`[setup-test-db] schema '${DB_SCHEMA}' 不像测试库(期望命名含 test / _dev / _local / _ci)`
)
process.exit(1)
}
console.log(`[setup-test-db] 即将 DROP + CREATE \`${DB_SCHEMA}\` on ${DB_HOST}:${DB_PORT}`)
if (!['localhost', '127.0.0.1', '::1'].includes(DB_HOST)) {
console.log(
'[setup-test-db] 目标是 **远程** host(已在 TEST_DB_ALLOWED_HOSTS 白名单中,每次 test.mjs 都会 DROP)'
)
console.log(`[setup-test-db] 当前白名单: ${allowedHosts.join(' ')}`)
console.log(
'[setup-test-db] 若不希望每次自动 DROP,从 .env.local 的 TEST_DB_ALLOWED_HOSTS 删掉此 host'
)
}
const sql =
`DROP DATABASE IF EXISTS \`${DB_SCHEMA}\`; ` +
`CREATE DATABASE \`${DB_SCHEMA}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;`
const mysqlArgs = [
`-h${DB_HOST}`,
`-P${DB_PORT}`,
`-u${DB_USER}`,
`-p${DB_PASSWORD}`,
'-e',
sql,
]
const res = spawnSync('mysql', mysqlArgs, { stdio: 'inherit' })
if (res.error) {
console.error(`[setup-test-db] FATAL: 无法执行 mysql(请确认其在 PATH 中): ${res.error.message}`)
process.exit(1)
}
if (res.status !== 0) {
console.error(`[setup-test-db] FAIL: mysql exit=${res.status}`)
process.exit(res.status === null ? 1 : res.status)
}
console.log('[setup-test-db] done — schema will be applied by Flyway when Spring Boot starts')