@ -4,6 +4,9 @@
// the scripts computed it as `DATABASE_URL ?? ''` (no production fallback), so on the
// production box — where DATABASE_URL is unset — each script silently opened a brand-new
// empty SQLite file instead of the real Postgres.
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import { describe , it , expect } from 'vitest'
import { pgTargetDescription , requireDbUrlForEnv , resolveDbUrl } from '../src/db-pg'
@ -26,16 +29,58 @@ describe('resolveDbUrl (D19 engine selection, the one shared resolver)', () => {
} )
} )
describe ( 'requireDbUrlForEnv (deploy-script guard — hard-fail instead of a throwaway SQLite write )', ( ) = > {
it ( 'behaves exactly like resolveDbUrl when a Postgres target is available ', ( ) = > {
describe ( 'requireDbUrlForEnv (deploy-script guard — OUTCOME-based, not NODE_ENV-based )', ( ) = > {
it ( 'behaves exactly like resolveDbUrl when a Postgres target is available (no SQLite file check runs at all) ', ( ) = > {
expect ( requireDbUrlForEnv ( { DATABASE_URL : 'postgres://x/y' } ) ) . toBe ( 'postgres://x/y' )
expect ( requireDbUrlForEnv ( { NODE_ENV : 'production' } , HARDCODED ) ) . toBe ( HARDCODED )
expect ( requireDbUrlForEnv ( { } ) ) . toBe ( '' ) // dev with no env: SQLite is fine, no hard-fail
} )
it ( 'hard-fails instead of silently returning SQLite when NODE_ENV=production resolves nothing' , ( ) = > {
// Simulates the hardcoded URL having been accidentally emptied out.
expect ( ( ) = > requireDbUrlForEnv ( { NODE_ENV : 'production' } , '' ) ) . toThrow ( /refusing to run/i )
it ( 'resolves to SQLite without hard-failing when the target file already exists (mocked fs check)' , ( ) = > {
expect ( requireDbUrlForEnv ( { } , undefined , ( ) = > true ) ) . toBe ( '' )
// Even NODE_ENV=production is fine as long as the SQLite file it would land on already exists.
expect ( requireDbUrlForEnv ( { NODE_ENV : 'production' } , '' , ( ) = > true ) ) . toBe ( '' )
} )
it ( ':memory: never hard-fails — the guard never even calls the file-existence check' , ( ) = > {
expect (
requireDbUrlForEnv ( { HQ_DATA_DIR : ':memory:' } , undefined , ( ) = > {
throw new Error ( 'must not be called for :memory:' )
} ) ,
) . toBe ( '' )
} )
it ( 'refuses when resolution selects SQLite and the target does not exist — regardless of NODE_ENV' , ( ) = > {
// This is the original prod bug: an operator shell has no NODE_ENV set at all, so the old
// NODE_ENV==='production' gate never fired. The guard must fire on the OUTCOME instead.
expect ( ( ) = > requireDbUrlForEnv ( { } , undefined , ( ) = > false ) ) . toThrow ( /refusing to run/i )
expect ( ( ) = > requireDbUrlForEnv ( { } , undefined , ( ) = > false ) ) . toThrow ( /CREATE a new SQLite database/ )
// Also still refuses in the NODE_ENV=production case (hardcoded emptied out + no existing file).
expect ( ( ) = > requireDbUrlForEnv ( { NODE_ENV : 'production' } , '' , ( ) = > false ) ) . toThrow ( /refusing to run/i )
} )
} )
describe ( 'requireDbUrlForEnv — real filesystem (the actual script entry path, no mocks)' , ( ) = > {
it ( 'refuses a non-existent SQLite path' , ( ) = > {
const dir = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , 'hq-db-resolve-' ) )
try {
expect ( ( ) = > requireDbUrlForEnv ( { HQ_DATA_DIR : dir } ) ) . toThrow ( /refusing to run/i )
} finally {
fs . rmSync ( dir , { recursive : true , force : true } )
}
} )
it ( 'accepts an existing SQLite path' , ( ) = > {
const dir = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , 'hq-db-resolve-' ) )
try {
fs . writeFileSync ( path . join ( dir , 'hq.db' ) , '' )
expect ( requireDbUrlForEnv ( { HQ_DATA_DIR : dir } ) ) . toBe ( '' )
} finally {
fs . rmSync ( dir , { recursive : true , force : true } )
}
} )
it ( ':memory: still works with no filesystem check' , ( ) = > {
expect ( requireDbUrlForEnv ( { HQ_DATA_DIR : ':memory:' } ) ) . toBe ( '' )
} )
} )