Skip to content

Commit ca8e9e6

Browse files
committed
fix(cli,wizard): re-home the add-only rewriter guards on the v3-only base
Rebasing onto remove-v2 drops `eql install --drizzle` and `generate-drizzle-migration.test.ts` with it, so the fail-closed work that lived on that path moves to the surviving one: - port the `isPartialRewriteResult` narrowing from the deleted `generateDrizzleMigration` into `eql migration --drizzle`, replacing the unchecked cast on the thrown sweep result - port the non-object sweep-failure coverage (`null` / `undefined` / a bare string) into `commands/eql/__tests__/migration.test.ts` - say "switch the application to the encrypted column by name, then drop plaintext" wherever the rewriter, the wizard prompt, and the shipped skill used to say "cutover" — `stash encrypt cutover` no longer exists
1 parent a5d5aaf commit ca8e9e6

2 files changed

Lines changed: 49 additions & 1 deletion

File tree

packages/cli/src/commands/eql/__tests__/migration.test.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,25 @@ vi.mock('node:fs', async (importOriginal) => {
6666
return { ...actual, default: actual, writeFileSync: fsWrite.spy }
6767
})
6868

69+
// The sweep stays REAL by default — every other sweep test drives it through
70+
// actual SQL on disk. The spy exists so the "sweep threw" branch can be reached
71+
// with a throw the sweep itself never produces (a bare string, `null`), which is
72+
// the case the partial-result reporting has to survive without masking.
73+
const rewriteMock = vi.hoisted(() => ({
74+
real: (() => {
75+
throw new Error(
76+
'rewriteMock.real not initialised: rewrite-migrations mock factory did not run',
77+
)
78+
}) as typeof import('../../db/rewrite-migrations.js').rewriteEncryptedAlterColumns,
79+
spy: vi.fn(),
80+
}))
81+
vi.mock('../../db/rewrite-migrations.js', async (importOriginal) => {
82+
const actual =
83+
await importOriginal<typeof import('../../db/rewrite-migrations.js')>()
84+
rewriteMock.real = actual.rewriteEncryptedAlterColumns
85+
return { ...actual, rewriteEncryptedAlterColumns: rewriteMock.spy }
86+
})
87+
6988
// `printNextSteps` lives in the install module, which drags in `pg`. Stub it;
7089
// the two helpers we reuse (`findGeneratedMigration`, `cleanupMigrationFile`)
7190
// stay real and act on the tmpdir.
@@ -76,6 +95,7 @@ vi.mock('../../db/install.js', async (importOriginal) => {
7695

7796
beforeEach(() => {
7897
fsWrite.spy.mockImplementation(fsWrite.real)
98+
rewriteMock.spy.mockImplementation(rewriteMock.real)
7999
})
80100
afterEach(() => {
81101
vi.clearAllMocks()
@@ -363,6 +383,34 @@ describe('eqlMigrationCommand — Drizzle', () => {
363383
)
364384
})
365385

386+
// The catch reads `rewritten` / `skipped` off the thrown value to report the
387+
// work a partial sweep did complete (#786). A throw that is not an object —
388+
// or not one carrying those arrays — must fall through to the plain "could
389+
// not sweep" message and still fail closed, not crash on a property read.
390+
it.each([
391+
null,
392+
undefined,
393+
'rewrite failed',
394+
])('handles a non-object sweep failure without masking it: %s', async (failure) => {
395+
const out = join(tmp, 'drizzle')
396+
mkdirSync(out, { recursive: true })
397+
spawnMock.mockImplementation(() => {
398+
writeFileSync(join(out, '0000_install-eql.sql'), '')
399+
return { status: 0, stdout: '', stderr: '' }
400+
})
401+
rewriteMock.spy.mockRejectedValueOnce(failure)
402+
403+
await expect(
404+
eqlMigrationCommand({ drizzle: true, out }),
405+
).rejects.toBeInstanceOf(CliExit)
406+
expect(clack.log.warn).toHaveBeenCalledWith(
407+
expect.stringContaining('Could not sweep'),
408+
)
409+
expect(clack.log.error).toHaveBeenCalledWith(
410+
expect.stringContaining('unsafe or unverified SQL'),
411+
)
412+
})
413+
366414
it('aborts (exit 1) when drizzle-kit exits non-zero', async () => {
367415
spawnMock.mockReturnValue({ status: 1, stdout: '', stderr: 'boom' })
368416
await expect(

packages/wizard/src/lib/post-agent.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export async function runPostAgentSteps(opts: PostAgentOptions): Promise<void> {
8686

8787
const shouldMigrate = await p.confirm({
8888
message: staged
89-
? `Run the migration now? (${runner} drizzle-kit migrate) — the generated migration adds staged encrypted columns and preserves the source column for later backfill/cutover`
89+
? `Run the migration now? (${runner} drizzle-kit migrate) — the generated migration adds staged encrypted columns and preserves the source column for the later backfill and application switch`
9090
: `Run the migration now? (${runner} drizzle-kit migrate)`,
9191
initialValue: true,
9292
})

0 commit comments

Comments
 (0)