Files
baiyue-firstleg/deploy/sql/scripts/db-migrations.test.mjs
2026-09-19 17:30:20 +08:00

78 lines
3.9 KiB
JavaScript

import assert from 'node:assert/strict'
import { mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises'
import os from 'node:os'
import path from 'node:path'
import test from 'node:test'
import {
buildPlan,
check,
createMigration,
discoverMigrations,
loadScopes,
runManifest,
} from './db-migrations.mjs'
async function fixture() {
const root = await mkdtemp(path.join(os.tmpdir(), 'db-migrations-'))
const sqlRoot = path.join(root, 'sql')
await mkdir(path.join(sqlRoot, 'migrations', 'cn', 'baiyue_auth'), { recursive: true })
await writeFile(path.join(sqlRoot, 'scopes.json'), JSON.stringify({
schemaVersion: 1,
regions: {
CN: {
baseline: 'baseline/cn.sql',
schemas: [
{ name: 'baiyue_auth', services: ['auth'], order: 10 },
{ name: 'baiyue_order', services: ['order'], order: 20 },
],
},
},
}), 'utf8')
return { root, sqlRoot, scopesPath: path.join(sqlRoot, 'scopes.json') }
}
test('new 自动分配 schema 内下一个版本,并且可重复执行检查', async (t) => {
const context = await fixture()
t.after(() => rm(context.root, { recursive: true, force: true }))
const scopes = await loadScopes(context.scopesPath)
const first = await createMigration({ sqlRoot: context.sqlRoot, scopes, region: 'CN', schemaName: 'baiyue_auth', description: 'Add login audit' })
assert.equal(first.fileName, 'V1__add_login_audit.sql')
const second = await createMigration({ sqlRoot: context.sqlRoot, scopes, region: 'CN', schemaName: 'baiyue_auth', description: 'add_session_index' })
assert.equal(second.fileName, 'V2__add_session_index.sql')
assert.match(await readFile(path.join(context.sqlRoot, first.path), 'utf8'), /Schema: baiyue_auth/)
assert.equal((await discoverMigrations(context.sqlRoot, 'CN', 'baiyue_auth')).length, 2)
})
test('plan 只包含选中服务,不把同提交其他 schema 的 SQL 带入制品', async (t) => {
const context = await fixture()
t.after(() => rm(context.root, { recursive: true, force: true }))
const scopes = await loadScopes(context.scopesPath)
const created = await createMigration({ sqlRoot: context.sqlRoot, scopes, region: 'CN', schemaName: 'baiyue_auth', description: 'add_login_audit' })
const plan = await buildPlan({ sqlRoot: context.sqlRoot, scopes, regions: ['CN'], services: ['auth'], commit: 'abc1234' })
assert.equal(plan.sourceCommit, 'abc1234')
assert.deepEqual(plan.regions[0].schemas.map((schema) => schema.schema), ['baiyue_auth'])
assert.equal(plan.regions[0].schemas[0].target, '1')
assert.equal(plan.regions[0].schemas[0].migrations[0].path, created.path)
})
test('check 拒绝跨 schema 引用和跨库控制语句', async (t) => {
const context = await fixture()
t.after(() => rm(context.root, { recursive: true, force: true }))
const scopes = await loadScopes(context.scopesPath)
await writeFile(path.join(context.sqlRoot, 'migrations', 'cn', 'baiyue_auth', 'V1__bad.sql'), 'ALTER TABLE baiyue_order.t ADD c INT;\nUSE baiyue_order;\n', 'utf8')
await assert.rejects(() => check({ sqlRoot: context.sqlRoot, scopes, regions: ['CN'] }), /其他 schema|跨库控制语句/)
})
test('run 默认只能 dry-run,不能无意连接数据库', async (t) => {
const context = await fixture()
t.after(() => rm(context.root, { recursive: true, force: true }))
const scopes = await loadScopes(context.scopesPath)
await createMigration({ sqlRoot: context.sqlRoot, scopes, region: 'CN', schemaName: 'baiyue_auth', description: 'add_login_audit' })
const plan = await buildPlan({ sqlRoot: context.sqlRoot, scopes, regions: ['CN'], services: ['auth'] })
const manifestPath = path.join(context.root, 'release-manifest.json')
await writeFile(manifestPath, JSON.stringify(plan), 'utf8')
const dryRun = await runManifest({ sqlRoot: context.sqlRoot, manifestPath, dryRun: true })
assert.equal(dryRun.mode, 'dry-run')
await assert.rejects(() => runManifest({ sqlRoot: context.sqlRoot, manifestPath }), /DB_MIGRATION_ALLOW/)
})