mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-10-25 17:54:34 +08:00
test(release): verify packaged sqlite runtime
This commit is contained in:
1 parent
6283281bd8
commit
a50c263232
2 files changed
+159
No files matched your search
@@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Verify that the packaged Windows Electron runtime can load the shipped
|
||||
* better-sqlite3 binding and execute a real SQLite query.
|
||||
*/
|
||||
|
||||
import { spawnSync } from 'node:child_process'
|
||||
import { existsSync, statSync } from 'node:fs'
|
||||
import { createRequire } from 'node:module'
|
||||
import { join, resolve } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const currentFile = fileURLToPath(import.meta.url)
|
||||
const repoRoot = resolve(import.meta.dirname, '..')
|
||||
const defaultDistDir = join(repoRoot, 'apps', 'desktop', 'dist')
|
||||
const desktopRequire = createRequire(join(repoRoot, 'apps', 'desktop', 'package.json'))
|
||||
|
||||
export function getPackagedSqlitePaths(distDir) {
|
||||
const appDir = join(distDir, 'win-unpacked')
|
||||
return {
|
||||
executablePath: join(appDir, 'ChatLab.exe'),
|
||||
bindingPath: join(appDir, 'resources', 'native', 'better_sqlite3.node'),
|
||||
}
|
||||
}
|
||||
|
||||
function assertNonEmptyFile(filePath, label) {
|
||||
if (!existsSync(filePath)) throw new Error(`${label} is missing: ${filePath}`)
|
||||
if (statSync(filePath).size === 0) throw new Error(`${label} is empty: ${filePath}`)
|
||||
}
|
||||
|
||||
function safeChildError(result) {
|
||||
return (result.stderr || result.stdout || result.error?.message || `child exited with status ${result.status}`).trim()
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用最终 ChatLab.exe 的 Electron ABI 加载最终 binding,并执行真实内存 SQLite 查询。
|
||||
* 该检查只读成品文件且只创建内存数据库,不会访问任何用户数据目录。
|
||||
*/
|
||||
export function verifyPackagedSqlite({
|
||||
distDir = defaultDistDir,
|
||||
betterSqliteEntry = desktopRequire.resolve('better-sqlite3'),
|
||||
spawn = spawnSync,
|
||||
} = {}) {
|
||||
const { executablePath, bindingPath } = getPackagedSqlitePaths(distDir)
|
||||
assertNonEmptyFile(executablePath, 'Packaged ChatLab executable')
|
||||
assertNonEmptyFile(bindingPath, 'Packaged better-sqlite3 binding')
|
||||
|
||||
const probe = `
|
||||
const Database = require(process.argv[1]);
|
||||
const db = new Database(':memory:', { nativeBinding: process.argv[2] });
|
||||
const row = db.prepare('SELECT 1 AS value').get();
|
||||
db.close();
|
||||
process.stdout.write(JSON.stringify({ abi: process.versions.modules, value: row.value }));
|
||||
`
|
||||
const result = spawn(executablePath, ['-e', probe, betterSqliteEntry, bindingPath], {
|
||||
encoding: 'utf8',
|
||||
env: { ...process.env, ELECTRON_RUN_AS_NODE: '1' },
|
||||
})
|
||||
|
||||
if (result.status !== 0) {
|
||||
throw new Error(`Packaged better-sqlite3 failed to load: ${safeChildError(result)}`)
|
||||
}
|
||||
|
||||
let parsed
|
||||
try {
|
||||
parsed = JSON.parse(result.stdout)
|
||||
} catch (error) {
|
||||
throw new Error(`Invalid packaged SQLite probe output: ${result.stdout}`, { cause: error })
|
||||
}
|
||||
if (parsed.value !== 1 || typeof parsed.abi !== 'string') {
|
||||
throw new Error(`Invalid packaged SQLite probe result: ${JSON.stringify(parsed)}`)
|
||||
}
|
||||
return { abi: parsed.abi, value: parsed.value }
|
||||
}
|
||||
|
||||
function main() {
|
||||
try {
|
||||
const result = verifyPackagedSqlite()
|
||||
console.log(`[check-packaged-sqlite] OK: Electron ABI ${result.abi}, SELECT 1 = ${result.value}`)
|
||||
} catch (error) {
|
||||
console.error(`[check-packaged-sqlite] FAILED: ${error instanceof Error ? error.message : String(error)}`)
|
||||
process.exitCode = 1
|
||||
}
|
||||
}
|
||||
|
||||
if (process.argv[1] && currentFile === resolve(process.argv[1])) main()
|
||||
@@ -0,0 +1,73 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import test from 'node:test'
|
||||
|
||||
import { verifyPackagedSqlite } from './check-packaged-sqlite.mjs'
|
||||
|
||||
const fixtures = []
|
||||
const BETTER_SQLITE_ENTRY = '/repo/node_modules/better-sqlite3/lib/index.js'
|
||||
|
||||
function makeFixture({ executable, binding }) {
|
||||
const distDir = mkdtempSync(join(tmpdir(), 'chatlab-packaged-sqlite-'))
|
||||
fixtures.push(distDir)
|
||||
|
||||
const appDir = join(distDir, 'win-unpacked')
|
||||
const resourcesDir = join(appDir, 'resources')
|
||||
mkdirSync(join(resourcesDir, 'native'), { recursive: true })
|
||||
if (executable) writeFileSync(join(appDir, 'ChatLab.exe'), 'executable')
|
||||
if (binding) writeFileSync(join(resourcesDir, 'native', 'better_sqlite3.node'), 'binding')
|
||||
return distDir
|
||||
}
|
||||
|
||||
test.afterEach(() => {
|
||||
for (const fixture of fixtures.splice(0)) rmSync(fixture, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
test('verifyPackagedSqlite rejects a missing packaged binding', () => {
|
||||
const distDir = makeFixture({ executable: true, binding: false })
|
||||
|
||||
assert.throws(
|
||||
() =>
|
||||
verifyPackagedSqlite({
|
||||
distDir,
|
||||
betterSqliteEntry: BETTER_SQLITE_ENTRY,
|
||||
spawn: () => assert.fail('spawn must not run when the binding is missing'),
|
||||
}),
|
||||
/binding.*missing/i
|
||||
)
|
||||
})
|
||||
|
||||
test('verifyPackagedSqlite reports Electron load failures', () => {
|
||||
const distDir = makeFixture({ executable: true, binding: true })
|
||||
|
||||
assert.throws(
|
||||
() =>
|
||||
verifyPackagedSqlite({
|
||||
distDir,
|
||||
betterSqliteEntry: BETTER_SQLITE_ENTRY,
|
||||
spawn: () => ({ status: 1, stdout: '', stderr: 'NODE_MODULE_VERSION 137 requires 133' }),
|
||||
}),
|
||||
/137 requires 133/
|
||||
)
|
||||
})
|
||||
|
||||
test('verifyPackagedSqlite accepts a real SQLite probe result', () => {
|
||||
const distDir = makeFixture({ executable: true, binding: true })
|
||||
let invocation
|
||||
|
||||
const result = verifyPackagedSqlite({
|
||||
distDir,
|
||||
betterSqliteEntry: BETTER_SQLITE_ENTRY,
|
||||
spawn: (executablePath, args, options) => {
|
||||
invocation = { executablePath, args, options }
|
||||
return { status: 0, stdout: '{"abi":"133","value":1}', stderr: '' }
|
||||
},
|
||||
})
|
||||
|
||||
assert.deepEqual(result, { abi: '133', value: 1 })
|
||||
assert.equal(invocation.executablePath, join(distDir, 'win-unpacked', 'ChatLab.exe'))
|
||||
assert.equal(invocation.args.at(-1), join(distDir, 'win-unpacked', 'resources', 'native', 'better_sqlite3.node'))
|
||||
assert.equal(invocation.options.env.ELECTRON_RUN_AS_NODE, '1')
|
||||
})
|
||||
Reference in new issue
Block a user