70 lines
2.9 KiB
JavaScript
70 lines
2.9 KiB
JavaScript
// 测试用 PG 隔离工具。
|
|
//
|
|
// 🔴 背景:本仓**已删除 SQLite 后端**(用户指令:「删除sqlite 都用 pg数据库」)⇒
|
|
// 需要 DB 的用例**只有** PG 一条通路,连接串从 `DSHS_TEST_DB_URL` 取。
|
|
// ⛔ 缺连接串时**显式跳过并打印原因**(`announceSkip`)—— ⛔ 不许静默通过。
|
|
//
|
|
// ⚠️ 用法约定:`DSHS_TEST_DB_URL` 必须指向**专用测试库**(本模块的 `resetPg` 会
|
|
// TRUNCATE 该库 `public` 下的全部表)。⛔ 不要指向生产 / 有业务数据的库。
|
|
import pg from 'pg'
|
|
|
|
/** 测试连接串(未设 = `undefined`)。 */
|
|
export const PG_URL = process.env.DSHS_TEST_DB_URL
|
|
|
|
/** 缺连接串时的**统一跳过原因**(写进 `{ skip }` 并打印出来)。 */
|
|
export const PG_SKIP_REASON =
|
|
'DSHS_TEST_DB_URL 未设置 ⇒ 本仓已无 SQLite 后端(PG 是唯一通路),本套用例显式跳过'
|
|
|
|
/** 连接串是否可用。 */
|
|
export function pgAvailable() {
|
|
return PG_URL !== undefined && PG_URL !== ''
|
|
}
|
|
|
|
/**
|
|
* 打印跳过原因 —— 每个测试文件在缺连接串时**必须**调一次。
|
|
* @param {string} file - 测试文件名(照搬,便于在输出里定位)。
|
|
*/
|
|
export function announceSkip(file) {
|
|
console.log(`# SKIP ${file} — ${PG_SKIP_REASON}`)
|
|
}
|
|
|
|
/**
|
|
* 跑一次迁移(幂等),保证后面 `resetPg` 要 TRUNCATE 的表都已存在。
|
|
* ⚠️ 必须在任何 `resetPg()` **之前**调(空库上第一次 TRUNCATE 会报表不存在)。
|
|
* @param {string} url - 连接串(缺省取 `DSHS_TEST_DB_URL`)。
|
|
*/
|
|
export async function ensureMigrated(url = PG_URL) {
|
|
const { openPgAdapter } = await import('../lib/db/pg.js')
|
|
const db = await openPgAdapter(url, 100000)
|
|
await db.close()
|
|
}
|
|
|
|
/**
|
|
* 清空**专用测试库** `public` 下除 `schema_migrations` 之外的全部表。
|
|
*
|
|
* 为什么需要:SQLite 时代每条用例一个 `:memory:` 库(天然隔离);换到 PG 后
|
|
* 同一文件里的用例**共用一个库** ⇒ 不清理就会互相污染(例如每个用例都建
|
|
* `alice` ⇒ 第二个用例直接撞唯一约束)。
|
|
* @param {string} url - 连接串(缺省取 `DSHS_TEST_DB_URL`)。
|
|
*/
|
|
export async function resetPg(url = PG_URL) {
|
|
const client = new pg.Client({ connectionString: url })
|
|
await client.connect()
|
|
try {
|
|
const { rows } = await client.query(
|
|
"SELECT tablename FROM pg_tables WHERE schemaname = 'public' AND tablename <> 'schema_migrations'",
|
|
)
|
|
if (rows.length > 0) {
|
|
const list = rows.map((r) => `"${r.tablename}"`).join(', ')
|
|
await client.query(`TRUNCATE ${list} RESTART IDENTITY CASCADE`)
|
|
}
|
|
} finally {
|
|
await client.end()
|
|
}
|
|
}
|
|
|
|
/** 报告尾巴:给"跳过"一个可判的读数(调一次,让退出输出里也有证据)。 */
|
|
export function reportSkipSummary(file) {
|
|
console.log(`# ${file}: 0 条真实用例执行(仅一条显式 skip)—— 原因见上`)
|
|
}
|