-
-
Notifications
You must be signed in to change notification settings - Fork 195
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add
db:truncate
command with TestUtils helper (#889)
* fix: truncate a table with reserved name was throwing with PG Like "user" that is a reserved word in postgres * feat: add `db:truncate` command + TestUtils helper * test: fix failing tests * chore: fix container ports * fix: cleanup table created in another test
- Loading branch information
1 parent
6afea22
commit 215631e
Showing
11 changed files
with
285 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
/* | ||
* @adonisjs/lucid | ||
* | ||
* (c) Harminder Virk <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
import { BaseCommand, flags } from '@adonisjs/core/build/standalone' | ||
import { QueryClientContract } from '@ioc:Adonis/Lucid/Database' | ||
|
||
export default class DbTruncate extends BaseCommand { | ||
public static commandName = 'db:truncate' | ||
public static description = 'Truncate all tables in database' | ||
public static settings = { | ||
loadApp: true, | ||
} | ||
|
||
/** | ||
* Choose a custom pre-defined connection. Otherwise, we use the | ||
* default connection | ||
*/ | ||
@flags.string({ description: 'Define a custom database connection', alias: 'c' }) | ||
public connection: string | ||
|
||
/** | ||
* Force command execution in production | ||
*/ | ||
@flags.boolean({ description: 'Explicitly force command to run in production' }) | ||
public force: boolean | ||
|
||
/** | ||
* Not a valid connection | ||
*/ | ||
private printNotAValidConnection(connection: string) { | ||
this.logger.error( | ||
`"${connection}" is not a valid connection name. Double check "config/database" file` | ||
) | ||
} | ||
|
||
/** | ||
* Prompts to take consent when truncating the database in production | ||
*/ | ||
private async takeProductionConstent(): Promise<boolean> { | ||
/** | ||
* Do not prompt when CLI is not interactive | ||
*/ | ||
if (!this.isInteractive) { | ||
return false | ||
} | ||
|
||
const question = 'You are in production environment. Want to continue truncating the database?' | ||
try { | ||
return await this.prompt.confirm(question) | ||
} catch (error) { | ||
return false | ||
} | ||
} | ||
|
||
/** | ||
* Truncate all tables except adonis migrations table | ||
*/ | ||
private async performTruncate(client: QueryClientContract) { | ||
let tables = await client.getAllTables(['public']) | ||
tables = tables.filter((table) => !['adonis_schema', 'adonis_schema_versions'].includes(table)) | ||
|
||
await Promise.all(tables.map((table) => client.truncate(table, true))) | ||
this.logger.success('Truncated tables successfully') | ||
} | ||
|
||
/** | ||
* Run as a subcommand. Never close database connections or exit | ||
* process inside this method | ||
*/ | ||
private async runAsSubCommand() { | ||
const db = this.application.container.use('Adonis/Lucid/Database') | ||
this.connection = this.connection || db.primaryConnectionName | ||
const connection = db.connection(this.connection || db.primaryConnectionName) | ||
|
||
/** | ||
* Continue with clearing the database when not in production | ||
* or force flag is passed | ||
*/ | ||
let continueTruncate = !this.application.inProduction || this.force | ||
if (!continueTruncate) { | ||
continueTruncate = await this.takeProductionConstent() | ||
} | ||
|
||
/** | ||
* Do not continue when in prod and the prompt was cancelled | ||
*/ | ||
if (!continueTruncate) { | ||
return | ||
} | ||
|
||
/** | ||
* Invalid database connection | ||
*/ | ||
if (!db.manager.has(this.connection)) { | ||
this.printNotAValidConnection(this.connection) | ||
this.exitCode = 1 | ||
return | ||
} | ||
|
||
await this.performTruncate(connection) | ||
} | ||
|
||
/** | ||
* Branching out, so that if required we can implement | ||
* "runAsMain" separately from "runAsSubCommand". | ||
* | ||
* For now, they both are the same | ||
*/ | ||
private async runAsMain() { | ||
await this.runAsSubCommand() | ||
} | ||
|
||
/** | ||
* Handle command | ||
*/ | ||
public async run(): Promise<void> { | ||
if (this.isMain) { | ||
await this.runAsMain() | ||
} else { | ||
await this.runAsSubCommand() | ||
} | ||
} | ||
|
||
/** | ||
* Lifecycle method invoked by ace after the "run" | ||
* method. | ||
*/ | ||
public async completed() { | ||
if (this.isMain) { | ||
await this.application.container.use('Adonis/Lucid/Database').manager.closeAll(true) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/* | ||
* @adonisjs/lucid | ||
* | ||
* (c) Harminder Virk <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
import type Ace from '@ioc:Adonis/Core/Ace' | ||
|
||
/** | ||
* Migrator class to be used for testing. | ||
*/ | ||
export class TestsTruncator { | ||
constructor(private ace: typeof Ace, private connectionName?: string) {} | ||
|
||
private async runCommand(commandName: string, args: string[] = []) { | ||
if (this.connectionName) { | ||
args.push(`--connection=${this.connectionName}`) | ||
} | ||
|
||
const command = await this.ace.exec(commandName, args) | ||
if (command.exitCode) { | ||
if (command.error) { | ||
throw command.error | ||
} else { | ||
throw new Error(`"${commandName}" failed`) | ||
} | ||
} | ||
} | ||
|
||
public async run() { | ||
await this.runCommand('migration:run', ['--compact-output']) | ||
return () => this.runCommand('db:truncate') | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/* | ||
* @adonisjs/lucid | ||
* | ||
* (c) Harminder Virk <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
import { test } from '@japa/runner' | ||
import { Kernel } from '@adonisjs/core/build/standalone' | ||
import { ApplicationContract } from '@ioc:Adonis/Core/Application' | ||
import { fs, setup, cleanup, getDb, setupApplication } from '../../test-helpers' | ||
import DbTruncate from '../../commands/DbTruncate' | ||
|
||
let app: ApplicationContract | ||
let db: ReturnType<typeof getDb> | ||
|
||
test.group('db:truncate', (group) => { | ||
group.each.setup(async () => { | ||
app = await setupApplication() | ||
return () => fs.cleanup() | ||
}) | ||
|
||
group.each.setup(async () => { | ||
db = getDb(app) | ||
app.container.bind('Adonis/Lucid/Database', () => db) | ||
await setup() | ||
|
||
return async () => { | ||
await cleanup() | ||
await cleanup(['adonis_schema', 'adonis_schema_versions']) | ||
await db.manager.closeAll() | ||
} | ||
}) | ||
|
||
test('should truncate all tables', async ({ assert }) => { | ||
const kernel = new Kernel(app).mockConsoleOutput() | ||
kernel.register([DbTruncate]) | ||
|
||
await db.table('users').insert({ username: 'bonjour' }) | ||
await db.table('users').insert({ username: 'bonjour2' }) | ||
await db.table('friends').insert({ username: 'bonjour' }) | ||
|
||
await kernel.exec('db:truncate', []) | ||
|
||
const usersCount = await db.from('users').count('*', 'total') | ||
const friendsCount = await db.from('friends').count('*', 'total') | ||
|
||
assert.equal(usersCount[0]['total'], 0) | ||
assert.equal(friendsCount[0]['total'], 0) | ||
}) | ||
|
||
test('should not truncate adonis migrations tables', async ({ assert }) => { | ||
const kernel = new Kernel(app).mockConsoleOutput() | ||
kernel.register([DbTruncate]) | ||
|
||
await db.connection().schema.createTable('adonis_schema', (table) => { | ||
table.increments('id') | ||
table.string('name') | ||
}) | ||
|
||
await db.connection().schema.createTable('adonis_schema_versions', (table) => { | ||
table.increments('id') | ||
table.string('name') | ||
}) | ||
|
||
await db.table('adonis_schema').insert({ name: 'bonjour' }) | ||
await db.table('adonis_schema_versions').insert({ name: 'bonjour' }) | ||
|
||
await kernel.exec('db:truncate', []) | ||
|
||
const adonisSchemaCount = await db.from('adonis_schema').count('*', 'total') | ||
const adonisSchemaVersionsCount = await db.from('adonis_schema_versions').count('*', 'total') | ||
|
||
assert.equal(adonisSchemaCount[0]['total'], 1) | ||
assert.equal(adonisSchemaVersionsCount[0]['total'], 1) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters