// 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) })