78 lines
3.9 KiB
JavaScript
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/)
|
||
|
|
})
|