bootstrap-source-flyway.test.mjs 2.57 KB
// lib/bootstrap-source-flyway.test.mjs — 纯函数单测(checksum / 迁移名解析)。
// 完整 bootstrap 需真实 mysql2 + DB,不在离线范围;此处只锁定与 Flyway 对齐的关键不变量。
import { test } from 'node:test'
import assert from 'node:assert/strict'
import { flywayChecksum, parseMigrationName } from './bootstrap-source-flyway.mjs'

test('flywayChecksum: deterministic and returns a signed int32', () => {
  const a = flywayChecksum('CREATE TABLE t (id INT);\n')
  const b = flywayChecksum('CREATE TABLE t (id INT);\n')
  assert.equal(a, b)
  assert.equal(Number.isInteger(a), true)
  assert.ok(a >= -2147483648 && a <= 2147483647, 'signed int32 range')
})

// Flyway 按行读取(剥行终止符)——CRLF 与 LF 的同一内容 checksum 必须相同(跨平台 checkout 稳定)。
test('flywayChecksum: CRLF and LF of the same content match (line-ending normalized)', () => {
  const lf = 'line1\nline2\nline3\n'
  const crlf = 'line1\r\nline2\r\nline3\r\n'
  assert.equal(flywayChecksum(lf), flywayChecksum(crlf))
})

// 尾部有无换行不改变行集合末尾(BufferedReader 不返回"最后的空行")——单个尾换行与无尾换行一致。
test('flywayChecksum: a single trailing newline does not change the checksum', () => {
  assert.equal(flywayChecksum('a\nb'), flywayChecksum('a\nb\n'))
})

// 内容不同 → checksum 不同(基本区分性)。
test('flywayChecksum: different content yields different checksum', () => {
  assert.notEqual(flywayChecksum('SELECT 1;'), flywayChecksum('SELECT 2;'))
})

// 首行 UTF-8 BOM 被剥离(与 Flyway 一致)——带 BOM 与不带 BOM 的同一内容一致。
test('flywayChecksum: leading UTF-8 BOM is stripped', () => {
  assert.equal(flywayChecksum('CREATE TABLE t (id INT);\n'), flywayChecksum('CREATE TABLE t (id INT);\n'))
})

test('parseMigrationName: V1__initial_schema.sql → version 1, description "initial schema"', () => {
  assert.deepEqual(parseMigrationName('V1__initial_schema.sql'), { version: '1', description: 'initial schema' })
})

test('parseMigrationName: dotted/underscored versions normalize to dots', () => {
  assert.deepEqual(parseMigrationName('V2_1__add_index.sql'), { version: '2.1', description: 'add index' })
  assert.deepEqual(parseMigrationName('V10.3__foo.sql'), { version: '10.3', description: 'foo' })
})

test('parseMigrationName: non-migration filenames return null', () => {
  assert.equal(parseMigrationName('seed.sql'), null)
  assert.equal(parseMigrationName('V1_no_double_underscore.sql'), null)
  assert.equal(parseMigrationName('R__repeatable.sql'), null)
})