Commit 37c9d660265dcb4368a0b65f3c3aa97540231303

Authored by zichun
1 parent 62d2fb9e

apply-ddl: drop dead env aliases + collapse dbEnvFromConfig two-hop

- resolveDbConfig only ever received the 5 DB_* keys dbEnvFromConfig emits;
  the MYSQL_*/DB_PASS/DB_NAME fallbacks had no producer (dead speculative
  generality, a dotenv-era leftover). Remove them.
- collapse the config → DB_* env-shape → connection two-hop: resolveDbConfig
  now reads config.database.{host,port,user,password,schema} directly, and
  dbEnvFromConfig is removed.
- CLI contract (node apply-ddl.mjs <configPath> <ddlPath>), the only caller
  via db-init, is unchanged.
- rewrite tests to the parsed-config shape; drop the alias-only tests. 53 pass.
lib/apply-ddl.mjs
1 1 import { parseYamlConfig } from './yaml-config.mjs'
2 2  
3 3 /**
4   - * Flatten config-vars.yaml's `database:` section into the DB_* env-shape that
5   - * resolveDbConfig consumes. Pure; tolerates a missing section.
6   - *
7   - * @param {Record<string, any>} config parsed config-vars.yaml
8   - * @returns {Record<string, string|undefined>}
9   - */
10   -export function dbEnvFromConfig(config) {
11   - const db = (config && config.database) || {}
12   - return {
13   - DB_HOST: db.host,
14   - DB_PORT: db.port != null ? String(db.port) : undefined,
15   - DB_USER: db.user,
16   - DB_PASSWORD: db.password,
17   - DB_SCHEMA: db.schema,
18   - }
19   -}
20   -
21   -/**
22 4 * Apply a DDL file to a MySQL database using mysql2/promise.
23 5 * DB credentials are read from config-vars.yaml's `database:` section.
24 6 *
... ... @@ -28,9 +10,9 @@ export function dbEnvFromConfig(config) {
28 10 export async function applyDDL({ configPath, ddlPath }) {
29 11 const { readFileSync } = await import('node:fs')
30 12  
31   - const env = dbEnvFromConfig(parseYamlConfig(readFileSync(configPath, 'utf8')))
  13 + const config = parseYamlConfig(readFileSync(configPath, 'utf8'))
32 14 const ddl = readFileSync(ddlPath, 'utf8')
33   - const { host, port, user, password, database } = resolveDbConfig(env, configPath)
  15 + const { host, port, user, password, database } = resolveDbConfig(config, configPath)
34 16  
35 17 let mysql
36 18 try {
... ... @@ -55,21 +37,23 @@ export async function applyDDL({ configPath, ddlPath }) {
55 37 }
56 38  
57 39 /**
58   - * Resolve mysql2 connection settings from a parsed env object. Pure (no I/O),
59   - * so it is unit-testable without mysql2 installed.
  40 + * Resolve mysql2 connection settings from a parsed config-vars.yaml object,
  41 + * reading its `database:` section directly. Pure (no I/O), so it is
  42 + * unit-testable without mysql2 installed.
60 43 *
61 44 * Throws if no schema resolves — V1 has no USE/CREATE DATABASE.
62 45 *
63   - * @param {Record<string,string>} env
  46 + * @param {Record<string, any>} config parsed config-vars.yaml
64 47 * @param {string} [cfgPath] only used to make the error message actionable
65 48 * @returns {{host:string, port:number, user:string, password:string, database:string}}
66 49 */
67   -export function resolveDbConfig(env, cfgPath = 'config-vars.yaml') {
68   - const host = env.DB_HOST || env.MYSQL_HOST || '127.0.0.1'
69   - const port = Number(env.DB_PORT || env.MYSQL_PORT || 3306)
70   - const user = env.DB_USER || env.MYSQL_USER || 'root'
71   - const password = env.DB_PASS || env.DB_PASSWORD || env.MYSQL_PASSWORD || ''
72   - const database = env.DB_SCHEMA || env.DB_NAME || env.MYSQL_DATABASE || undefined
  50 +export function resolveDbConfig(config, cfgPath = 'config-vars.yaml') {
  51 + const db = (config && config.database) || {}
  52 + const host = db.host || '127.0.0.1'
  53 + const port = Number(db.port || 3306)
  54 + const user = db.user || 'root'
  55 + const password = db.password || ''
  56 + const database = db.schema || undefined
73 57 if (!database) {
74 58 throw new Error(`apply-ddl: 缺数据库名 — 请在 ${cfgPath} 的 database.schema 填写`)
75 59 }
... ...
lib/apply-ddl.test.mjs
1 1 import { test } from 'node:test'
2 2 import assert from 'node:assert/strict'
3   -import { dbEnvFromConfig, resolveDbConfig } from './apply-ddl.mjs'
  3 +import { resolveDbConfig } from './apply-ddl.mjs'
4 4  
5   -// ── dbEnvFromConfig(config-vars.yaml database: → DB_* env-shape adapter)──
6   -test('dbEnvFromConfig maps the database section to the DB_* shape', () => {
7   - const env = dbEnvFromConfig({
8   - database: { host: 'db.local', port: 3307, user: 'u', password: 'p@ss', schema: 'erp_test' },
  5 +// ── resolveDbConfig(直接读 config-vars.yaml 解析后的 database: 段)─────────
  6 +test('resolveDbConfig maps the database section to mysql2 settings', () => {
  7 + const c = resolveDbConfig({
  8 + database: { host: 'db.local', port: '3307', user: 'u', password: 'p@ss', schema: 'erp_test' },
9 9 })
10   - assert.equal(env.DB_HOST, 'db.local')
11   - assert.equal(env.DB_PORT, '3307') // coerced to string for resolveDbConfig
12   - assert.equal(env.DB_USER, 'u')
13   - assert.equal(env.DB_PASSWORD, 'p@ss')
14   - assert.equal(env.DB_SCHEMA, 'erp_test')
15   -})
16   -
17   -test('dbEnvFromConfig tolerates a missing/empty config', () => {
18   - assert.equal(dbEnvFromConfig({}).DB_HOST, undefined)
19   - assert.equal(dbEnvFromConfig(null).DB_SCHEMA, undefined)
20   -})
21   -
22   -test('dbEnvFromConfig → resolveDbConfig round-trips a filled database section', () => {
23   - const c = resolveDbConfig(dbEnvFromConfig({ database: { host: 'localhost', port: 3306, schema: 'erp_dev' } }))
24   - assert.equal(c.host, 'localhost')
25   - assert.equal(c.port, 3306)
26   - assert.equal(c.database, 'erp_dev')
27   -})
28   -
29   -// ── resolveDbConfig(M1:DB_SCHEMA 是插件契约的 schema 键)─────────
30   -test('resolveDbConfig maps DB_SCHEMA to database (plugin canonical key)', () => {
31   - const c = resolveDbConfig({ DB_SCHEMA: 'erp_test', DB_USER: 'u', DB_PASS: 'p', DB_HOST: 'db.local', DB_PORT: '3307' })
32   - assert.equal(c.database, 'erp_test')
33 10 assert.equal(c.host, 'db.local')
34 11 assert.equal(c.port, 3307)
35 12 assert.equal(c.user, 'u')
36   - assert.equal(c.password, 'p')
37   -})
38   -
39   -test('resolveDbConfig honors DB_NAME / MYSQL_DATABASE aliases', () => {
40   - assert.equal(resolveDbConfig({ DB_NAME: 'a' }).database, 'a')
41   - assert.equal(resolveDbConfig({ MYSQL_DATABASE: 'b' }).database, 'b')
42   - // DB_SCHEMA wins over aliases
43   - assert.equal(resolveDbConfig({ DB_SCHEMA: 's', DB_NAME: 'a' }).database, 's')
  13 + assert.equal(c.password, 'p@ss')
  14 + assert.equal(c.database, 'erp_test')
44 15 })
45 16  
46   -test('resolveDbConfig fails closed when no schema key is present (M1)', () => {
47   - assert.throws(() => resolveDbConfig({ DB_USER: 'root' }, 'config-vars.yaml'), /database\.schema/)
  17 +test('resolveDbConfig fails closed when database.schema is missing (M1)', () => {
  18 + assert.throws(() => resolveDbConfig({ database: { user: 'root' } }, 'config-vars.yaml'), /database\.schema/)
  19 + assert.throws(() => resolveDbConfig({}), /database\.schema/)
48 20 })
49 21  
50 22 test('resolveDbConfig applies sane defaults for host/port/user/password', () => {
51   - const c = resolveDbConfig({ DB_SCHEMA: 's' })
  23 + const c = resolveDbConfig({ database: { schema: 's' } })
52 24 assert.equal(c.host, '127.0.0.1')
53 25 assert.equal(c.port, 3306)
54 26 assert.equal(c.user, 'root')
... ... @@ -56,6 +28,6 @@ test(&#39;resolveDbConfig applies sane defaults for host/port/user/password&#39;, () =&gt;
56 28 })
57 29  
58 30 test('resolveDbConfig rejects invalid ports', () => {
59   - assert.throws(() => resolveDbConfig({ DB_SCHEMA: 'erp_test', DB_PORT: 'abc' }), /database\.port/)
60   - assert.throws(() => resolveDbConfig({ DB_SCHEMA: 'erp_test', DB_PORT: '70000' }), /database\.port/)
  31 + assert.throws(() => resolveDbConfig({ database: { schema: 'erp_test', port: 'abc' } }), /database\.port/)
  32 + assert.throws(() => resolveDbConfig({ database: { schema: 'erp_test', port: '70000' } }), /database\.port/)
61 33 })
... ...