diff --git a/src/drivers/SqliteDriver.ts b/src/drivers/SqliteDriver.ts new file mode 100644 index 0000000..fc852ed --- /dev/null +++ b/src/drivers/SqliteDriver.ts @@ -0,0 +1,1222 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +/** + * @athenna/database + * + * (c) João Lenon + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { + Exec, + Is, + Json, + Options, + type PaginatedResponse +} from '@athenna/common' + +import type { Knex } from 'knex' +import { Driver } from '#src/drivers/Driver' +import { DriverFactory } from '#src/factories/DriverFactory' +import type { ConnectionOptions, Direction } from '#src/types' +import { Transaction } from '#src/database/transactions/Transaction' +import { MigrationSource } from '#src/database/migrations/MigrationSource' +import { WrongMethodException } from '#src/exceptions/WrongMethodException' +import { PROTECTED_QUERY_METHODS } from '#src/constants/ProtectedQueryMethods' +import { NotConnectedDatabaseException } from '#src/exceptions/NotConnectedDatabaseException' + +export class SqliteDriver extends Driver { + /** + * Connect to database. + */ + public connect(options: ConnectionOptions = {}): void { + options = Options.create(options, { + force: false, + saveOnFactory: true, + connect: true + }) + + if (!options.connect) { + return + } + + if (this.isConnected && !options.force) { + return + } + + this.client = DriverFactory.createConnection( + this.connection, + Json.pick(options, ['saveOnFactory']) + ) + + this.isConnected = true + this.isSavedOnFactory = options.saveOnFactory + + this.qb = this.query() + } + + /** + * Close the connection with database in this instance. + */ + public async close(): Promise { + if (!this.isConnected) { + return + } + + if (this.isSavedOnFactory) { + await DriverFactory.closeConnection(this.connection) + } else { + await this.client.destroy() + } + + this.qb = null + this.tableName = null + this.client = null + this.isConnected = false + } + + /** + * Creates a new instance of query builder. + */ + public query(): Knex.QueryBuilder { + if (!this.isConnected) { + throw new NotConnectedDatabaseException() + } + + const query = this.useSetQB + ? this.qb.table(this.tableName) + : this.client.queryBuilder().table(this.tableName) + + const handler = { + get: (target: Knex.QueryBuilder, propertyKey: string) => { + if (PROTECTED_QUERY_METHODS.includes(propertyKey)) { + this.qb = this.query() + } + + return target[propertyKey] + } + } + + return new Proxy(query, handler) + } + + /** + * Create a new transaction. + */ + public async startTransaction(): Promise< + Transaction + > { + const trx = await this.client.transaction() + + return new Transaction(this.clone().setClient(trx)) + } + + /** + * Commit the transaction. + */ + public async commitTransaction(): Promise { + const client = this.client as Knex.Transaction + + await client.commit() + + this.tableName = null + this.client = null + this.isConnected = false + } + + /** + * Rollback the transaction. + */ + public async rollbackTransaction(): Promise { + const client = this.client as Knex.Transaction + + await client.rollback() + + this.tableName = null + this.client = null + this.isConnected = false + } + + /** + * Run database migrations. + */ + public async runMigrations(): Promise { + await this.client.migrate.latest({ + migrationSource: new MigrationSource(this.connection) + }) + } + + /** + * Revert database migrations. + */ + public async revertMigrations(): Promise { + await this.client.migrate.rollback({ + migrationSource: new MigrationSource(this.connection) + }) + } + + /** + * List all databases available. + */ + public async getDatabases(): Promise { + const databases = await this.raw('PRAGMA database_list') + + return databases.map(database => database.name) + } + + /** + * Get the current database name. + */ + public async getCurrentDatabase(): Promise { + return this.client.client.database() + } + + /** + * Verify if database exists. + */ + public async hasDatabase(database: string): Promise { + const databases = await this.getDatabases() + + return databases.includes(database) + } + + /** + * Create a new database. + */ + public async createDatabase(database: string): Promise { + /** + * Catching the error to simulate IF NOT EXISTS + */ + try { + await this.raw('CREATE DATABASE ??', database) + } catch (err) {} + } + + /** + * Drop some database. + */ + public async dropDatabase(database: string): Promise { + /** + * Catching the error to simulate IF EXISTS + */ + try { + await this.raw('DROP DATABASE ??', database) + } catch (err) {} + } + + /** + * List all tables available. + */ + public async getTables(): Promise { + const tables = await this.raw( + "SELECT name FROM sqlite_schema WHERE type = 'table' AND name NOT LIKE 'sqlite_%'", + await this.getCurrentDatabase() + ) + + return tables.map(table => table.name) + } + + /** + * Verify if table exists. + */ + public async hasTable(table: string): Promise { + return this.client.schema.hasTable(table) + } + + /** + * Create a new table in database. + */ + public async createTable( + table: string, + closure: (builder: Knex.TableBuilder) => void | Promise + ): Promise { + await this.client.schema.createTable(table, closure) + } + + /** + * Drop a table in database. + */ + public async dropTable(table: string): Promise { + await this.client.schema.dropTableIfExists(table) + } + + /** + * Remove all data inside some database table + * and restart the identity of the table. + */ + public async truncate(table: string): Promise { + await this.raw('DELETE FROM ??', table) + } + + /** + * Make a raw query in database. + */ + public raw(sql: string, bindings?: any): T { + return this.client.raw(sql, bindings) as any + } + + /** + * Calculate the average of a given column. + */ + public async avg(column: string): Promise { + const [{ avg }] = await this.qb.avg({ avg: column }) + + return avg + } + + /** + * Calculate the average of a given column using distinct. + */ + public async avgDistinct(column: string): Promise { + const [{ avg }] = await this.qb.avgDistinct({ avg: column }) + + return avg + } + + /** + * Get the max number of a given column. + */ + public async max(column: string): Promise { + const [{ max }] = await this.qb.max({ max: column }) + + return max + } + + /** + * Get the min number of a given column. + */ + public async min(column: string): Promise { + const [{ min }] = await this.qb.min({ min: column }) + + return min + } + + /** + * Sum all numbers of a given column. + */ + public async sum(column: string): Promise { + const [{ sum }] = await this.qb.sum({ sum: column }) + + return sum + } + + /** + * Sum all numbers of a given column in distinct mode. + */ + public async sumDistinct(column: string): Promise { + const [{ sum }] = await this.qb.sumDistinct({ sum: column }) + + return sum + } + + /** + * Increment a value of a given column. + */ + public async increment(column: string): Promise { + await this.qb.increment(column) + } + + /** + * Decrement a value of a given column. + */ + public async decrement(column: string): Promise { + await this.qb.decrement(column) + } + + /** + * Calculate the average of a given column using distinct. + */ + public async count(column: string = '*'): Promise { + const [{ count }] = await this.qb.count({ count: column }) + + return `${count}` + } + + /** + * Calculate the average of a given column using distinct. + */ + public async countDistinct(column: string): Promise { + const [{ count }] = await this.qb.countDistinct({ count: column }) + + return `${count}` + } + + /** + * Find a value in database. + */ + public async find(): Promise { + return this.qb.first() + } + + /** + * Find many values in database. + */ + public async findMany(): Promise { + const data = await this.qb + + this.qb = this.query() + + return data + } + + /** + * Find many values in database and return as paginated response. + */ + public async paginate( + page = 0, + limit = 10, + resourceUrl = '/' + ): Promise> { + const [{ count }] = await this.qb + .clone() + .clearOrder() + .clearSelect() + .count({ count: '*' }) + + const data = await this.offset(page).limit(limit).findMany() + + return Exec.pagination(data, parseInt(count), { page, limit, resourceUrl }) + } + + /** + * Create a value in database. + */ + public async create( + data: Partial = {}, + primaryKey: string = 'id' + ): Promise { + if (Is.Array(data)) { + throw new WrongMethodException('create', 'createMany') + } + + const created = await this.createMany([data], primaryKey) + + return created[0] + } + + /** + * Create many values in database. + */ + public async createMany( + data: Partial[] = [], + _primaryKey: string = 'id' + ): Promise { + if (!Is.Array(data)) { + throw new WrongMethodException('createMany', 'create') + } + + return this.qb.insert(data, '*') + } + + /** + * Create data or update if already exists. + */ + public async createOrUpdate( + data: Partial = {}, + primaryKey: string = 'id' + ): Promise { + const query = this.qb.clone() + const hasValue = await query.first() + + if (hasValue) { + await this.qb.where(primaryKey, hasValue[primaryKey]).update(data) + + return this.where(primaryKey, hasValue[primaryKey]).find() + } + + return this.create(data, primaryKey) + } + + /** + * Update a value in database. + */ + public async update(data: Partial): Promise { + await this.qb.clone().update(data) + + const result = await this.findMany() + + if (result.length === 1) { + return result[0] + } + + return result + } + + /** + * Delete one value in database. + */ + public async delete(): Promise { + await this.qb.delete() + } + + /** + * Set the table that this query will be executed. + */ + public table(table: string): this { + if (!this.isConnected) { + throw new NotConnectedDatabaseException() + } + + this.tableName = table + this.qb = this.query() + + return this + } + + /** + * Log in console the actual query built. + */ + public dump(): this { + console.log(this.qb.toSQL().toNative()) + + return this + } + + /** + * Set the columns that should be selected on query. + */ + public select(...columns: string[]): this { + this.qb.select(...columns) + + return this + } + + /** + * Set the columns that should be selected on query raw. + */ + public selectRaw(sql: string, bindings?: any): this { + return this.select(this.raw(sql, bindings) as any) + } + + /** + * Set the table that should be used on query. + * Different from `table()` method, this method + * doesn't change the driver table. + */ + public from(table: string): this { + this.qb.from(table) + + return this + } + + /** + * Set the table that should be used on query raw. + * Different from `table()` method, this method + * doesn't change the driver table. + */ + public fromRaw(sql: string, bindings?: any): this { + return this.from(this.raw(sql, bindings) as any) + } + + /** + * Set a join statement in your query. + */ + public join(table: any, column1?: any, operation?: any, column2?: any): this { + return this.joinByType('join', table, column1, operation, column2) + } + + /** + * Set a left join statement in your query. + */ + public leftJoin( + table: any, + column1?: any, + operation?: any, + column2?: any + ): this { + return this.joinByType('leftJoin', table, column1, operation, column2) + } + + /** + * Set a right join statement in your query. + */ + public rightJoin( + table: any, + column1?: any, + operation?: any, + column2?: any + ): this { + return this.joinByType('rightJoin', table, column1, operation, column2) + } + + /** + * Set a cross join statement in your query. + */ + public crossJoin( + table: any, + column1?: any, + operation?: any, + column2?: any + ): this { + return this.joinByType('crossJoin', table, column1, operation, column2) + } + + /** + * Set a full outer join statement in your query. + */ + public fullOuterJoin( + table: any, + column1?: any, + operation?: any, + column2?: any + ): this { + return this.joinByType('fullOuterJoin', table, column1, operation, column2) + } + + /** + * Set a left outer join statement in your query. + */ + public leftOuterJoin( + table: any, + column1?: any, + operation?: any, + column2?: any + ): this { + return this.joinByType('leftOuterJoin', table, column1, operation, column2) + } + + /** + * Set a right outer join statement in your query. + */ + public rightOuterJoin( + table: any, + column1?: any, + operation?: any, + column2?: any + ): this { + return this.joinByType('rightOuterJoin', table, column1, operation, column2) + } + + /** + * Set a join raw statement in your query. + */ + public joinRaw(sql: string, bindings?: any): this { + this.qb.joinRaw(sql, bindings) + + return this + } + + /** + * Set a group by statement in your query. + */ + public groupBy(...columns: string[]): this { + this.qb.groupBy(...columns) + + return this + } + + /** + * Set a group by raw statement in your query. + */ + public groupByRaw(sql: string, bindings?: any): this { + this.qb.groupByRaw(sql, bindings) + + return this + } + + /** + * Set a having statement in your query. + */ + public having(column: any, operation?: any, value?: any): this { + if (operation === undefined) { + this.qb.having(column) + + return this + } + + if (value === undefined) { + this.qb.having(column, '=', operation) + + return this + } + + this.qb.having(column, operation, value) + + return this + } + + /** + * Set a having raw statement in your query. + */ + public havingRaw(sql: string, bindings?: any): this { + this.qb.havingRaw(sql, bindings) + + return this + } + + /** + * Set a having exists statement in your query. + */ + public havingExists(closure: (query: SqliteDriver) => void): this { + const driver = this.clone() as SqliteDriver + + // @ts-ignore + this.qb.havingExists(function () { + closure(driver.setQueryBuilder(this, { useSetQB: true })) + }) + + return this + } + + /** + * Set a having not exists statement in your query. + */ + public havingNotExists(closure: (query: SqliteDriver) => void): this { + const driver = this.clone() as SqliteDriver + + // @ts-ignore + this.qb.havingNotExists(function () { + closure(driver.setQueryBuilder(this, { useSetQB: true })) + }) + + return this + } + + /** + * Set a having in statement in your query. + */ + public havingIn(column: string, values: any[]): this { + this.qb.havingIn(column, values) + + return this + } + + /** + * Set a having not in statement in your query. + */ + public havingNotIn(column: string, values: any[]): this { + this.qb.havingNotIn(column, values) + + return this + } + + /** + * Set a having between statement in your query. + */ + public havingBetween(column: string, values: [any, any]): this { + this.qb.havingBetween(column, values) + + return this + } + + /** + * Set a having not between statement in your query. + */ + public havingNotBetween(column: string, values: [any, any]): this { + this.qb.havingNotBetween(column, values) + + return this + } + + /** + * Set a having null statement in your query. + */ + public havingNull(column: string): this { + this.qb.havingNull(column) + + return this + } + + /** + * Set a having not null statement in your query. + */ + public havingNotNull(column: string): this { + this.qb.havingNotNull(column) + + return this + } + + /** + * Set an or having statement in your query. + */ + public orHaving(column: any, operation?: any, value?: any): this { + if (operation === undefined) { + this.qb.orHaving(column) + + return this + } + + if (value === undefined) { + this.qb.orHaving(column, '=', operation) + + return this + } + + this.qb.orHaving(column, operation, value) + + return this + } + + /** + * Set an or having raw statement in your query. + */ + public orHavingRaw(sql: string, bindings?: any): this { + this.qb.orHavingRaw(sql, bindings) + + return this + } + + /** + * Set an or having exists statement in your query. + */ + public orHavingExists(closure: (query: SqliteDriver) => void): this { + const driver = this.clone() as SqliteDriver + + // @ts-ignore + this.qb.orHavingExists(function () { + closure(driver.setQueryBuilder(this, { useSetQB: true })) + }) + + return this + } + + /** + * Set an or having not exists statement in your query. + */ + public orHavingNotExists(closure: (query: SqliteDriver) => void): this { + const driver = this.clone() as SqliteDriver + + // @ts-ignore + this.qb.orHavingNotExists(function () { + closure(driver.setQueryBuilder(this, { useSetQB: true })) + }) + + return this + } + + /** + * Set an or having in statement in your query. + */ + public orHavingIn(column: string, values: any[]): this { + // @ts-ignore + this.qb.orHavingIn(column, values) + + return this + } + + /** + * Set an or having not in statement in your query. + */ + public orHavingNotIn(column: string, values: any[]): this { + this.qb.orHavingNotIn(column, values) + + return this + } + + /** + * Set an or having between statement in your query. + */ + public orHavingBetween(column: string, values: [any, any]): this { + this.qb.orHavingBetween(column, values) + + return this + } + + /** + * Set an or having not between statement in your query. + */ + public orHavingNotBetween(column: string, values: [any, any]): this { + this.qb.orHavingNotBetween(column, values) + + return this + } + + /** + * Set an or having null statement in your query. + */ + public orHavingNull(column: string): this { + // @ts-ignore + this.qb.orHavingNull(column) + + return this + } + + /** + * Set an or having not null statement in your query. + */ + public orHavingNotNull(column: string): this { + // @ts-ignore + this.qb.orHavingNotNull(column) + + return this + } + + /** + * Set a where statement in your query. + */ + public where(statement: any, operation?: any, value?: any): this { + if (Is.Function(statement)) { + const driver = this.clone() + + this.qb.where(function () { + statement(driver.setQueryBuilder(this, { useSetQB: true })) + }) + + return this + } + + if (operation === undefined) { + this.qb.where(statement) + + return this + } + + if (value === undefined) { + this.qb.where(statement, operation) + + return this + } + + this.qb.where(statement, operation, value) + + return this + } + + /** + * Set a where not statement in your query. + */ + public whereNot(statement: any, value?: any): this { + if (Is.Function(statement)) { + const driver = this.clone() + + this.qb.whereNot(function () { + statement(driver.setQueryBuilder(this, { useSetQB: true })) + }) + + return this + } + + if (value === undefined) { + this.qb.whereNot(statement) + + return this + } + + this.qb.whereNot(statement, value) + + return this + } + + /** + * Set a where raw statement in your query. + */ + public whereRaw(sql: string, bindings?: any): this { + this.qb.whereRaw(sql, bindings) + + return this + } + + /** + * Set a where exists statement in your query. + */ + public whereExists(closure: (query: SqliteDriver) => void): this { + const driver = this.clone() as SqliteDriver + + this.qb.whereExists(function () { + closure(driver.setQueryBuilder(this, { useSetQB: true })) + }) + + return this + } + + /** + * Set a where not exists statement in your query. + */ + public whereNotExists(closure: (query: SqliteDriver) => void): this { + const driver = this.clone() as SqliteDriver + + this.qb.whereNotExists(function () { + closure(driver.setQueryBuilder(this, { useSetQB: true })) + }) + + return this + } + + /** + * Set a where like statement in your query. + */ + public whereLike(column: string, value: any): this { + this.qb.whereLike(column, value) + + return this + } + + /** + * Set a where ILike statement in your query. + */ + public whereILike(column: string, value: any): this { + this.qb.whereLike(column, value) + + return this + } + + /** + * Set a where in statement in your query. + */ + public whereIn(column: string, values: any[]): this { + this.qb.whereIn(column, values) + + return this + } + + /** + * Set a where not in statement in your query. + */ + public whereNotIn(column: string, values: any[]): this { + this.qb.whereNotIn(column, values) + + return this + } + + /** + * Set a where between statement in your query. + */ + public whereBetween(column: string, values: [any, any]): this { + this.qb.whereBetween(column, values) + + return this + } + + /** + * Set a where not between statement in your query. + */ + public whereNotBetween(column: string, values: [any, any]): this { + this.qb.whereNotBetween(column, values) + + return this + } + + /** + * Set a where null statement in your query. + */ + public whereNull(column: string): this { + this.qb.whereNull(column) + + return this + } + + /** + * Set a where not null statement in your query. + */ + public whereNotNull(column: string): this { + this.qb.whereNotNull(column) + + return this + } + + /** + * Set a or where statement in your query. + */ + public orWhere(statement: any, operation?: any, value?: any): this { + if (Is.Function(statement)) { + const driver = this.clone() + + this.qb.orWhere(function () { + statement(driver.setQueryBuilder(this, { useSetQB: true })) + }) + + return this + } + + if (operation === undefined) { + this.qb.orWhere(statement) + + return this + } + + if (value === undefined) { + this.qb.orWhere(statement, operation) + + return this + } + + this.qb.orWhere(statement, operation, value) + + return this + } + + /** + * Set an or where not statement in your query. + */ + public orWhereNot(statement: any, value?: any): this { + if (Is.Function(statement)) { + const driver = this.clone() + + this.qb.orWhereNot(function () { + statement(driver.setQueryBuilder(this, { useSetQB: true })) + }) + + return this + } + + if (value === undefined) { + this.qb.orWhereNot(statement) + + return this + } + + this.qb.orWhereNot(statement, value) + + return this + } + + /** + * Set a or where raw statement in your query. + */ + public orWhereRaw(sql: string, bindings?: any): this { + this.qb.orWhereRaw(sql, bindings) + + return this + } + + /** + * Set an or where exists statement in your query. + */ + public orWhereExists(closure: (query: SqliteDriver) => void): this { + const driver = this.clone() as SqliteDriver + + this.qb.orWhereExists(function () { + closure(driver.setQueryBuilder(this, { useSetQB: true })) + }) + + return this + } + + /** + * Set an or where not exists statement in your query. + */ + public orWhereNotExists(closure: (query: SqliteDriver) => void): this { + const driver = this.clone() as SqliteDriver + + this.qb.orWhereNotExists(function () { + closure(driver.setQueryBuilder(this, { useSetQB: true })) + }) + + return this + } + + /** + * Set an or where like statement in your query. + */ + public orWhereLike(column: any, value: any): this { + this.qb.orWhereLike(column, value) + + return this + } + + /** + * Set an or where ILike statement in your query. + */ + public orWhereILike(column: any, value: any): this { + this.qb.orWhereLike(column, value) + + return this + } + + /** + * Set an or where in statement in your query. + */ + public orWhereIn(column: string, values: any[]): this { + this.qb.orWhereIn(column, values) + + return this + } + + /** + * Set an or where not in statement in your query. + */ + public orWhereNotIn(column: string, values: any[]): this { + this.qb.orWhereNotIn(column, values) + + return this + } + + /** + * Set an or where between statement in your query. + */ + public orWhereBetween(column: string, values: [any, any]): this { + this.qb.orWhereBetween(column, values) + + return this + } + + /** + * Set an or where not between statement in your query. + */ + public orWhereNotBetween(column: string, values: [any, any]): this { + this.qb.orWhereNotBetween(column, values) + + return this + } + + /** + * Set an or where null statement in your query. + */ + public orWhereNull(column: string): this { + this.qb.orWhereNull(column) + + return this + } + + /** + * Set an or where not null statement in your query. + */ + public orWhereNotNull(column: string): this { + this.qb.orWhereNotNull(column) + + return this + } + + /** + * Set an order by statement in your query. + */ + public orderBy(column: string, direction: Direction = 'ASC'): this { + this.qb.orderBy(column, direction.toUpperCase()) + + return this + } + + /** + * Set an order by raw statement in your query. + */ + public orderByRaw(sql: string, bindings?: any): this { + this.qb.orderByRaw(sql, bindings) + + return this + } + + /** + * Order the results easily by the latest date. By default, the result will + * be ordered by the table's "createdAt" column. + */ + public latest(column: string = 'createdAt'): this { + return this.orderBy(column, 'DESC') + } + + /** + * Order the results easily by the oldest date. By default, the result will + * be ordered by the table's "createdAt" column. + */ + public oldest(column: string = 'createdAt'): this { + return this.orderBy(column, 'ASC') + } + + /** + * Set the skip number in your query. + */ + public offset(number: number): this { + this.qb.offset(number) + + return this + } + + /** + * Set the limit number in your query. + */ + public limit(number: number): this { + this.qb.limit(number) + + return this + } +} diff --git a/src/factories/ConnectionFactory.ts b/src/factories/ConnectionFactory.ts index 5ac67c1..3c9a4b6 100644 --- a/src/factories/ConnectionFactory.ts +++ b/src/factories/ConnectionFactory.ts @@ -24,6 +24,7 @@ export class ConnectionFactory { switch (driver) { case 'fake': case 'mysql': + case 'sqlite': case 'postgres': await client.destroy() break @@ -52,6 +53,13 @@ export class ConnectionFactory { return this.mongoose(con) } + /** + * Create the connection with a sqlite database. + */ + public static sqlite(con: string): Knex { + return this.knex(con, 'better-sqlite3') + } + /** * Create the connection with a postgres database. */ diff --git a/src/factories/DriverFactory.ts b/src/factories/DriverFactory.ts index 9c49c6e..669d83d 100644 --- a/src/factories/DriverFactory.ts +++ b/src/factories/DriverFactory.ts @@ -14,6 +14,7 @@ import { Exec, Options } from '@athenna/common' import type { Driver } from '#src/drivers/Driver' import type { DriverKey } from '#src/types/DriverKey' import { MySqlDriver } from '#src/drivers/MySqlDriver' +import { SqliteDriver } from '#src/drivers/SqliteDriver' import { PostgresDriver } from '#src/drivers/PostgresDriver' import type { CreateConOptions } from '#src/types/CreateConOptions' import { ConnectionFactory } from '#src/factories/ConnectionFactory' @@ -30,6 +31,10 @@ export class DriverFactory { Driver: MySqlDriver, client: null }) + .set('sqlite', { + Driver: SqliteDriver, + client: null + }) .set('postgres', { Driver: PostgresDriver, client: null diff --git a/tests/fixtures/config/database.ts b/tests/fixtures/config/database.ts index 0494a59..381808e 100644 --- a/tests/fixtures/config/database.ts +++ b/tests/fixtures/config/database.ts @@ -38,6 +38,13 @@ export default { }, debug: false }, + 'sqlite-memory': { + driver: 'sqlite', + connection: { + filename: ':memory:' + }, + debug: false + }, 'postgres-docker': { driver: 'postgres', connection: { diff --git a/tests/fixtures/migrations/2022_10_08_000007_create_students_table.ts b/tests/fixtures/migrations/2022_10_08_0000010_create_students_table.ts similarity index 100% rename from tests/fixtures/migrations/2022_10_08_000007_create_students_table.ts rename to tests/fixtures/migrations/2022_10_08_0000010_create_students_table.ts diff --git a/tests/fixtures/migrations/2022_10_08_000008_create_courses_table.ts b/tests/fixtures/migrations/2022_10_08_0000011_create_courses_table.ts similarity index 100% rename from tests/fixtures/migrations/2022_10_08_000008_create_courses_table.ts rename to tests/fixtures/migrations/2022_10_08_0000011_create_courses_table.ts diff --git a/tests/fixtures/migrations/2022_10_08_000009_create_students_courses_table.ts b/tests/fixtures/migrations/2022_10_08_0000011_create_students_courses_table.ts similarity index 100% rename from tests/fixtures/migrations/2022_10_08_000009_create_students_courses_table.ts rename to tests/fixtures/migrations/2022_10_08_0000011_create_students_courses_table.ts diff --git a/tests/fixtures/migrations/2022_10_08_000005_create_countries_table.ts b/tests/fixtures/migrations/2022_10_08_0000012_create_countries_table.ts similarity index 100% rename from tests/fixtures/migrations/2022_10_08_000005_create_countries_table.ts rename to tests/fixtures/migrations/2022_10_08_0000012_create_countries_table.ts diff --git a/tests/fixtures/migrations/2022_10_08_000006_create_capitals_table.ts b/tests/fixtures/migrations/2022_10_08_0000013_create_capitals_table.ts similarity index 100% rename from tests/fixtures/migrations/2022_10_08_000006_create_capitals_table.ts rename to tests/fixtures/migrations/2022_10_08_0000013_create_capitals_table.ts diff --git a/tests/fixtures/migrations/2022_10_08_000006_create_users_sqlite_table.ts b/tests/fixtures/migrations/2022_10_08_000006_create_users_sqlite_table.ts new file mode 100644 index 0000000..65c8462 --- /dev/null +++ b/tests/fixtures/migrations/2022_10_08_000006_create_users_sqlite_table.ts @@ -0,0 +1,23 @@ +import { DatabaseImpl, Migration } from '#src' + +export class UserMigration extends Migration { + public static connection() { + return 'sqlite-memory' + } + + public tableName = 'users' + + public async up(db: DatabaseImpl) { + return db.createTable(this.tableName, table => { + table.increments('id') + table.string('name') + table.string('email').unique() + table.timestamps(true, true, true) + table.dateTime('deletedAt').nullable().defaultTo(null) + }) + } + + public async down(db: DatabaseImpl) { + return db.dropTable(this.tableName) + } +} diff --git a/tests/fixtures/migrations/2022_10_08_000007_create_products_sqlite_table.ts b/tests/fixtures/migrations/2022_10_08_000007_create_products_sqlite_table.ts new file mode 100644 index 0000000..c68f06c --- /dev/null +++ b/tests/fixtures/migrations/2022_10_08_000007_create_products_sqlite_table.ts @@ -0,0 +1,25 @@ +import { DatabaseImpl, Migration } from '#src' + +export class ProductMigration extends Migration { + public static connection() { + return 'sqlite-memory' + } + + public tableName = 'products' + + public async up(db: DatabaseImpl) { + return db.createTable(this.tableName, table => { + table.increments('id') + table.string('name', 255) + table.integer('price').defaultTo(0) + table.integer('userId').unsigned().index().references('id').inTable('users') + + table.timestamps(true, true, true) + table.dateTime('deletedAt').nullable().defaultTo(null) + }) + } + + public async down(db: DatabaseImpl) { + return db.dropTable(this.tableName) + } +} diff --git a/tests/fixtures/migrations/2022_10_08_000008_create_products_details_sqlite_table.ts b/tests/fixtures/migrations/2022_10_08_000008_create_products_details_sqlite_table.ts new file mode 100644 index 0000000..ed30dc2 --- /dev/null +++ b/tests/fixtures/migrations/2022_10_08_000008_create_products_details_sqlite_table.ts @@ -0,0 +1,24 @@ +import { DatabaseImpl, Migration } from '#src' + +export class ProductDetailsMigration extends Migration { + public static connection() { + return 'sqlite-memory' + } + + public tableName = 'product_details' + + public async up(db: DatabaseImpl) { + return db.createTable(this.tableName, table => { + table.increments('id') + table.string('content', 255) + table.integer('productId').unsigned().index().references('id').inTable('products') + + table.timestamps(true, true, true) + table.dateTime('deletedAt').nullable().defaultTo(null) + }) + } + + public async down(db: DatabaseImpl) { + return db.dropTable(this.tableName) + } +} diff --git a/tests/unit/database/migrations/MigrationSourceTest.ts b/tests/unit/database/migrations/MigrationSourceTest.ts index b50f459..ca9e247 100644 --- a/tests/unit/database/migrations/MigrationSourceTest.ts +++ b/tests/unit/database/migrations/MigrationSourceTest.ts @@ -28,8 +28,8 @@ export default class MigrationSourceTest { const migrations = await new MigrationSource('fake').getMigrations() - assert.deepEqual(migrations[0].name, '2022_10_08_000005_create_countries_table.ts') - assert.deepEqual(migrations[1].name, '2022_10_08_000006_create_capitals_table.ts') + assert.deepEqual(migrations[0].name, '2022_10_08_0000012_create_countries_table.ts') + assert.deepEqual(migrations[1].name, '2022_10_08_0000013_create_capitals_table.ts') } @Test() @@ -50,7 +50,7 @@ export default class MigrationSourceTest { const migrations = await migrationSource.getMigrations() const name = migrationSource.getMigrationName(migrations[0]) - assert.deepEqual(name, '2022_10_08_000005_create_countries_table.ts') + assert.deepEqual(name, '2022_10_08_0000012_create_countries_table.ts') } @Test() diff --git a/tests/unit/drivers/SqliteDriverTest.ts b/tests/unit/drivers/SqliteDriverTest.ts new file mode 100644 index 0000000..7132cdb --- /dev/null +++ b/tests/unit/drivers/SqliteDriverTest.ts @@ -0,0 +1,2856 @@ +/** + * @athenna/database + * + * (c) João Lenon + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { Config } from '@athenna/config' +import { Collection, Exec, Path } from '@athenna/common' +import { DriverFactory } from '#src/factories/DriverFactory' +import { SqliteDriver } from '#src/drivers/SqliteDriver' +import { WrongMethodException } from '#src/exceptions/WrongMethodException' +import { NotFoundDataException } from '#src/exceptions/NotFoundDataException' +import { Test, Mock, AfterEach, BeforeEach, type Context, Cleanup, Skip } from '@athenna/test' +import { NotConnectedDatabaseException } from '#src/exceptions/NotConnectedDatabaseException' + +export default class SqliteDriverTest { + public driver = new SqliteDriver('sqlite-memory') + + @BeforeEach() + public async beforeEach() { + await Config.loadAll(Path.fixtures('config')) + this.driver.connect() + await this.driver.dropDatabase('trx') + await this.driver.dropTable('trx') + await this.driver.dropTable('rents') + await this.driver.dropTable('students_courses') + await this.driver.dropTable('students') + await this.driver.dropTable('courses') + await this.driver.dropTable('product_details') + await this.driver.dropTable('products') + await this.driver.dropTable('users') + await this.driver.dropTable('orders') + await this.driver.dropTable('migrations') + await this.driver.dropTable('migrations_lock') + + await this.driver.createTable('orders', builder => { + builder.string('id').primary() + }) + + await this.driver.createTable('products', builder => { + builder.string('id').primary() + builder.integer('quantity').defaultTo(0) + }) + + await this.driver.createTable('users', builder => { + builder.string('id').primary() + builder.string('name') + builder.timestamp('created_at').defaultTo(this.driver.getClient().fn.now()) + }) + + await this.driver.createTable('rents', builder => { + builder.string('id').primary() + builder.string('user_id').references('id').inTable('users') + }) + } + + @AfterEach() + public async afterEach() { + Mock.restoreAll() + + await this.driver.close() + + Config.clear() + } + + @Test() + public async shouldBeAbleToCloneTheDriverInstance({ assert }: Context) { + const result = this.driver.clone() + + assert.notDeepEqual(result, this.driver) + assert.instanceOf(result, SqliteDriver) + } + + @Test() + public async shouldBeAbleToGetTheClientOfTheDriver({ assert }: Context) { + const result = this.driver.getClient() + + assert.isDefined(result.select) + assert.isDefined(result.where) + } + + @Test() + public async shouldBeAbleToGetTheQueryBuilderOfTheDriver({ assert }: Context) { + const result = this.driver.getQueryBuilder() + + assert.isDefined(result.select) + assert.isDefined(result.where) + } + + @Test() + public async shouldBeAbleToSetDifferentQueryBuilderToDriver({ assert }: Context) { + const query: any = {} + + this.driver.setQueryBuilder(query) + + assert.deepEqual(this.driver.getQueryBuilder(), query) + } + + @Test() + @Cleanup(() => DriverFactory.closeConnection('postgres-docker')) + public async shouldBeAbleToConnectToDatabaseUsingSqliteDriver({ assert }: Context) { + const driver = new SqliteDriver('postgres-docker') + + assert.isFalse(driver.isConnected) + + driver.connect() + + assert.isTrue(driver.isConnected) + } + + @Test() + public async shouldBeAbleToConnectToDatabaseWithoutSavingConnectionInFactory({ assert }: Context) { + await DriverFactory.closeAllConnections() + + const driver = new SqliteDriver('postgres-docker') + + assert.isFalse(driver.isConnected) + + driver.connect({ saveOnFactory: false }) + + assert.isTrue(driver.isConnected) + assert.isFalse(DriverFactory.availableDrivers({ onlyConnected: true }).includes('postgres')) + } + + @Test() + public async shouldBeAbleToCallConnectMethodButWithoutConnectingToDatabase({ assert }: Context) { + await DriverFactory.closeAllConnections() + + const driver = new SqliteDriver('postgres-docker') + + assert.isFalse(driver.isConnected) + + driver.connect({ connect: false }) + + assert.isFalse(driver.isConnected) + } + + @Test() + public async shouldNotReconnectToDatabaseIfIsAlreadyConnected({ assert }: Context) { + Mock.spy(DriverFactory, 'createConnection') + + await DriverFactory.closeAllConnections() + + const driver = new SqliteDriver('postgres-docker') + + assert.isFalse(driver.isConnected) + + driver.connect() + assert.isTrue(driver.isConnected) + + driver.connect() + + assert.calledOnce(DriverFactory.createConnection) + } + + @Test() + @Cleanup(() => DriverFactory.closeAllConnections()) + public async shouldReconnectToDatabaseEvenIfIsAlreadyConnectedWhenForceIsSet({ assert }: Context) { + Mock.spy(DriverFactory, 'createConnection') + + await DriverFactory.closeAllConnections() + + const driver = new SqliteDriver('postgres-docker') + + assert.isFalse(driver.isConnected) + + driver.connect() + assert.isTrue(driver.isConnected) + + driver.connect({ force: true }) + + assert.calledTimes(DriverFactory.createConnection, 2) + } + + @Test() + @Cleanup(() => DriverFactory.closeAllConnections()) + public async shouldBeAbleToCloseTheConnectionWithDriver({ assert }: Context) { + Mock.spy(DriverFactory, 'closeConnection') + + await DriverFactory.closeAllConnections() + + const driver = new SqliteDriver('postgres-docker') + + assert.isFalse(driver.isConnected) + + driver.connect() + driver.close() + + assert.calledOnce(DriverFactory.closeConnection) + } + + @Test() + public async shouldNotTryToCloseConnectionWithDriverIfConnectionIsClosed({ assert }: Context) { + Mock.spy(DriverFactory, 'closeConnection') + + await DriverFactory.closeAllConnections() + + const driver = new SqliteDriver('postgres-docker') + + assert.isFalse(driver.isConnected) + + driver.close() + assert.notCalled(DriverFactory.closeConnection) + } + + @Test() + public async shouldBeAbleToCloseConnectionsThatAreNotSavedInTheDriverFactory({ assert }: Context) { + await DriverFactory.closeAllConnections() + + const driver = new SqliteDriver('postgres-docker') + + assert.isFalse(driver.isConnected) + + driver.connect({ saveOnFactory: false }) + Mock.spy(driver.getClient(), 'destroy') + + driver.close() + + assert.calledOnce(driver.getClient().destroy) + } + + @Test() + public async shouldBeAbleToCreateQueryUsingDriverQueryBuilder({ assert }: Context) { + const query = this.driver.query() + + assert.isDefined(query) + assert.isDefined(query.select) + assert.isDefined(query.where) + } + + @Test() + public async shouldThrowNotConnectedDatabaseExceptionIfTryingToCreateQueryWithConnectionClosed({ assert }: Context) { + await this.driver.close() + + assert.throws(() => this.driver.query(), NotConnectedDatabaseException) + } + + @Test() + public async shouldBeAbleToCreateAndRollbackDatabaseTransactionsFromDriver({ assert }: Context) { + const trx = await this.driver.startTransaction() + const query = trx.table('users') + const data = await query.create({ id: '1', name: 'Lenon' }) + + assert.deepEqual(data, await query.where('id', '1').find()) + + await trx.rollbackTransaction() + + assert.isUndefined(await this.driver.table('users').where('id', '1').find()) + } + + @Test() + public async shouldBeAbleToCreateAndCommitDatabaseTransactionsFromDriver({ assert }: Context) { + const trx = await this.driver.startTransaction() + const query = trx.table('users') + const data = await query.create({ id: '1', name: 'Lenon' }) + + assert.deepEqual(data, await query.where('id', '1').find()) + + await trx.commitTransaction() + + assert.isDefined(await this.driver.table('users').where('id', '1').find()) + } + + @Test() + public async shouldBeAbleToRunMigrationsUsingDriver({ assert }: Context) { + await this.driver.dropTable('rents') + await this.driver.dropTable('products') + await this.driver.dropTable('users') + + Mock.when(Path, 'migrations').return(Path.fixtures('migrations')) + + await this.driver.runMigrations() + + assert.isTrue(await this.driver.hasTable('users')) + } + + @Test() + public async shouldBeAbleToRollbackMigrationsUsingDriver({ assert }: Context) { + await this.driver.dropTable('rents') + await this.driver.dropTable('products') + await this.driver.dropTable('users') + + Mock.when(Path, 'migrations').return(Path.fixtures('migrations')) + + await this.driver.runMigrations() + + assert.isTrue(await this.driver.hasTable('users')) + + await this.driver.revertMigrations() + + assert.isFalse(await this.driver.hasTable('users')) + } + + @Test() + public async shouldBeAbleToGetTheDatabasesOfDriver({ assert }: Context) { + const databases = await this.driver.getDatabases() + + assert.deepEqual(databases, ['main']) + } + + @Test() + public async shouldBeAbleToGetTheCurrentDatabaseNameThatIsBeingUsed({ assert }: Context) { + const database = await this.driver.getCurrentDatabase() + + assert.isUndefined(database) + } + + @Test() + public async shouldBeAbleToValidateThatDatabaseExists({ assert }: Context) { + const exists = await this.driver.hasDatabase('main') + + assert.isTrue(exists) + } + + @Test() + public async shouldBeAbleToValidateThatDatabaseDoesNotExist({ assert }: Context) { + const exists = await this.driver.hasDatabase('not-found') + + assert.isFalse(exists) + } + + @Test() + @Skip('Not working') + public async shouldBeAbleToCreateDatabaseUsingDriver({ assert }: Context) { + await this.driver.createDatabase('trx') + + assert.isTrue(await this.driver.hasDatabase('trx')) + } + + @Test() + @Skip('Not working') + public async shouldNotThrowErrorsWhenCreatingADatabaseThatAlreadyExists({ assert }: Context) { + await this.driver.createDatabase('trx') + + await assert.doesNotRejects(() => this.driver.createDatabase('trx')) + assert.isTrue(await this.driver.hasDatabase('trx')) + } + + @Test() + public async shouldBeAbleToDropDatabaseUsingDriver({ assert }: Context) { + await this.driver.createDatabase('trx') + await this.driver.dropDatabase('trx') + + assert.isFalse(await this.driver.hasDatabase('trx')) + } + + @Test() + public async shouldNotThrowErrorsWhenDroppingADatabaseThatDoesNotExists({ assert }: Context) { + await this.driver.dropDatabase('trx') + + await assert.doesNotRejects(() => this.driver.dropDatabase('trx')) + assert.isFalse(await this.driver.hasDatabase('trx')) + } + + @Test() + public async shouldBeAbleToGetAllTheTablesFromDatabase({ assert }: Context) { + const tables = await this.driver.getTables() + + assert.isTrue(tables.includes('orders')) + assert.isTrue(tables.includes('products')) + assert.isTrue(tables.includes('users')) + } + + @Test() + public async shouldBeAbleToGetAllTablesEvenRecentlyCreatedFromDatabase({ assert }: Context) { + await this.driver.createTable('migrations', builder => { + builder.string('id').primary() + }) + + const tables = await this.driver.getTables() + + assert.isTrue(tables.includes('migrations')) + assert.isTrue(tables.includes('products')) + assert.isTrue(tables.includes('orders')) + assert.isTrue(tables.includes('users')) + } + + @Test() + public async shouldBeAbleToValidateThatATableExists({ assert }: Context) { + await this.driver.createTable('migrations', builder => { + builder.string('id').primary() + }) + + const exists = await this.driver.hasTable('migrations') + + assert.isTrue(exists) + } + + @Test() + public async shouldBeAbleToValidateThatATableDoesNotExists({ assert }: Context) { + const exists = await this.driver.hasTable('migrations') + + assert.isFalse(exists) + } + + @Test() + public async shouldBeAbleToCreateTablesUsingDriver({ assert }: Context) { + const exists = await this.driver.hasTable('orders') + + assert.isTrue(exists) + } + + @Test() + public async shouldBeAbleToDropTablesUsingDriver({ assert }: Context) { + assert.isTrue(await this.driver.hasTable('orders')) + + await this.driver.dropTable('orders') + + assert.isFalse(await this.driver.hasTable('orders')) + } + + @Test() + public async shouldBeAbleToTruncateTheTableLeavingItClean({ assert }: Context) { + const data = [{ id: '1' }, { id: '2' }, { id: '3' }] + + await this.driver.table('orders').createMany(data) + const orders = await this.driver.table('orders').findMany() + + assert.deepEqual(orders, data) + + await this.driver.truncate('orders') + + assert.deepEqual(await this.driver.table('orders').findMany(), []) + } + + @Test() + public async shouldBeAbleToExecuteRawSQLQueriesWithDriver({ assert }: Context) { + const data = [{ id: '1' }, { id: '2' }, { id: '3' }] + + await this.driver.table('orders').createMany(data) + const result = await this.driver.raw('SELECT * FROM orders') + + assert.deepEqual(data, result) + } + + @Test() + public async shouldBeAbleToGetTheAvgOfAGivenColumnWhenTableGotValues({ assert }: Context) { + await this.driver.table('products').createMany([ + { id: '1', quantity: 10 }, + { id: '2', quantity: 10 } + ]) + + const result = await this.driver.table('products').avg('quantity') + + assert.equal(result, 10) + } + + @Test() + public async shouldReturnNullWhenAvgCantFindAnyValue({ assert }: Context) { + const result = await this.driver.table('products').avg('quantity') + + assert.isNull(result) + } + + @Test() + public async shouldBeAbleToGetTheAvgDistinctOfAGivenColumnWhenTableGotValues({ assert }: Context) { + await this.driver.table('products').createMany([ + { id: '1', quantity: 10 }, + { id: '2', quantity: 10 } + ]) + + const result = await this.driver.table('products').avgDistinct('quantity') + + assert.equal(result, 10) + } + + @Test() + public async shouldReturnNullWhenAvgDistinctCantFindAnyValue({ assert }: Context) { + const result = await this.driver.table('products').avgDistinct('quantity') + + assert.isNull(result) + } + + @Test() + public async shouldBeAbleToGetTheMaxNumberOfAGivenColumnWhenTableGotValues({ assert }: Context) { + await this.driver.table('products').createMany([ + { id: '1', quantity: 10 }, + { id: '2', quantity: 20 } + ]) + + const result = await this.driver.table('products').max('quantity') + + assert.equal(result, 20) + } + + @Test() + public async shouldReturnNullWhenMaxCantFindAnyValue({ assert }: Context) { + const result = await this.driver.table('products').max('quantity') + + assert.isNull(result) + } + + @Test() + public async shouldBeAbleToGetTheMinNumberOfAGivenColumnWhenTableGotValues({ assert }: Context) { + await this.driver.table('products').createMany([ + { id: '1', quantity: 10 }, + { id: '2', quantity: 20 } + ]) + + const result = await this.driver.table('products').min('quantity') + + assert.equal(result, 10) + } + + @Test() + public async shouldReturnNullWhenMinCantFindAnyValue({ assert }: Context) { + const result = await this.driver.table('products').min('quantity') + + assert.isNull(result) + } + + @Test() + public async shouldBeAbleToSumTheNumberOfAGivenColumnWhenTableGotValues({ assert }: Context) { + await this.driver.table('products').createMany([ + { id: '1', quantity: 10 }, + { id: '2', quantity: 10 } + ]) + + const result = await this.driver.table('products').sum('quantity') + + assert.equal(result, 20) + } + + @Test() + public async shouldReturnNullWhenSumCantFindAnyValue({ assert }: Context) { + const result = await this.driver.table('products').sum('quantity') + + assert.isNull(result) + } + + @Test() + public async shouldBeAbleToGetTheSumDistinctOfAGivenColumnWhenTableGotValues({ assert }: Context) { + await this.driver.table('products').createMany([ + { id: '1', quantity: 10 }, + { id: '2', quantity: 10 } + ]) + + const result = await this.driver.table('products').sumDistinct('quantity') + + assert.equal(result, 10) + } + + @Test() + public async shouldReturnNullWhenSumDistinctCantFindAnyValue({ assert }: Context) { + const result = await this.driver.table('products').sumDistinct('quantity') + + assert.isNull(result) + } + + @Test() + public async shouldBeAbleToIncrementTheNumberOfAGivenColumnWhenTableGotValues({ assert }: Context) { + await this.driver.table('products').createMany([ + { id: '1', quantity: 10 }, + { id: '2', quantity: 10 } + ]) + + await this.driver.table('products').increment('quantity') + const avg = await this.driver.table('products').avg('quantity') + + assert.equal(avg, 11) + } + + @Test() + public async shouldBeAbleToDecrementTheNumberOfAGivenColumnWhenTableGotValues({ assert }: Context) { + await this.driver.table('products').createMany([ + { id: '1', quantity: 10 }, + { id: '2', quantity: 10 } + ]) + + await this.driver.table('products').decrement('quantity') + const avg = await this.driver.table('products').avg('quantity') + + assert.equal(avg, 9) + } + + @Test() + public async shouldBeAbleToCountRecords({ assert }: Context) { + await this.driver.table('products').createMany([ + { id: '1', quantity: 10 }, + { id: '2', quantity: 10 } + ]) + + const result = await this.driver.table('products').count() + + assert.equal(result, 2) + } + + @Test() + public async shouldBeAbleToCountColumnsValues({ assert }: Context) { + await this.driver.table('products').createMany([ + { id: '1', quantity: 10 }, + { id: '2', quantity: 10 } + ]) + + const result = await this.driver.table('products').count('quantity') + + assert.equal(result, '2') + } + + @Test() + public async shouldBeAbleToCountDistinctColumnsValues({ assert }: Context) { + await this.driver.table('products').createMany([ + { id: '1', quantity: 10 }, + { id: '2', quantity: 10 } + ]) + + const result = await this.driver.table('products').countDistinct('quantity') + + assert.equal(result, '1') + } + + @Test() + public async shouldBeAbleToFindDataUsingFindOrFail({ assert }: Context) { + const data = { id: '1', name: 'John Doe' } + await this.driver.table('users').create(data) + + const result = await this.driver.table('users').findOrFail() + + assert.containsSubset(result, data) + } + + @Test() + public async shouldThrowNotFoundDataExceptionWhenFindOrFailFail({ assert }: Context) { + await assert.rejects(() => this.driver.table('users').findOrFail(), NotFoundDataException) + } + + @Test() + public async shouldBeAbleToUseFindOrMethodToFindData({ assert }: Context) { + const data = { id: '1', name: 'John Doe' } + await this.driver.table('users').create(data) + + const result = await this.driver.table('users').findOr(() => { + return { id: '1', name: 'Marie Curie' } + }) + + assert.containsSubset(result, data) + } + + @Test() + public async shouldBeAbleToReturnDataFromCallbackWhenFindOrFail({ assert }: Context) { + const result = await this.driver.table('users').findOr(() => { + return { id: '1', name: 'Marie Curie' } + }) + + assert.deepEqual(result, { id: '1', name: 'Marie Curie' }) + } + + @Test() + public async shouldBeAbleToExecuteSomeClosureWhenCriteriaIsTrue({ assert }: Context) { + const data = { id: '1', name: 'Marie Curie' } + await this.driver.table('users').create(data) + + const result = await this.driver + .when(true, query => { + query.select('name') + }) + .find() + + assert.deepEqual(result, { name: 'Marie Curie' }) + } + + @Test() + public async shouldNotExecuteSomeClosureWhenCriteriaIsFalse({ assert }: Context) { + const data = { id: '1', name: 'Marie Curie' } + await this.driver.table('users').create(data) + + const result = await this.driver + .when(false, query => { + query.select('*') + }) + .find() + + assert.containsSubset(result, { id: '1', name: 'Marie Curie' }) + } + + @Test() + public async shouldBeAbleToFindDataUsingDriver({ assert }: Context) { + const data = { id: '1', name: 'Charles Babbage' } + await this.driver.table('users').create(data) + + const result = await this.driver.table('users').find() + + assert.containsSubset(result, data) + } + + @Test() + public async shouldReturnUndefinedWhenFindMethodCantFindNothing({ assert }: Context) { + const result = await this.driver.table('users').find() + + assert.isUndefined(result) + } + + @Test() + public async shouldBeAbleToFindManyDataUsingDriver({ assert }: Context) { + const data = [{ id: '1', name: 'Charles Babbage' }] + await this.driver.table('users').createMany(data) + + const result = await this.driver.table('users').findMany() + + assert.containsSubset(result, data) + } + + @Test() + public async shouldReturnEmptyArrayWhenFindManyMethodCantFindNothing({ assert }: Context) { + const result = await this.driver.table('users').findMany() + + assert.isEmpty(result) + } + + @Test() + public async shouldBeAbleToFindManyDataAndReturnAsCollectionUsingDriver({ assert }: Context) { + const data = [{ id: '1', name: 'Alan Turing' }] + await this.driver.table('users').createMany(data) + + const result = await this.driver.table('users').findMany() + + assert.containsSubset(result, data) + } + + @Test() + public async shouldReturnEmptyCollectionWhenCollectionMethodCantFindNothing({ assert }: Context) { + const result = await this.driver.table('users').collection() + + assert.instanceOf(result, Collection) + } + + @Test() + public async shouldBeAbleToFindManyDataAndReturnPaginatedUsingDriver({ assert }: Context) { + const data = [{ id: '1', name: 'Alan Turing' }] + await this.driver.table('users').createMany(data) + + const result = await this.driver.table('users').paginate() + + assert.containsSubset(result.data, data) + assert.deepEqual(result.meta, { + currentPage: 0, + itemCount: 1, + itemsPerPage: 10, + totalItems: 1, + totalPages: 1 + }) + assert.deepEqual(result.links, { + first: '/?limit=10', + last: '/?page=1&limit=10', + next: '/?page=1&limit=10', + previous: '/?page=0&limit=10' + }) + } + + @Test() + public async shouldBeAbleToSetDifferentUrlToFindManyDataAndReturnPaginatedUsingDriver({ assert }: Context) { + const data = [{ id: '1', name: 'Alan Turing' }] + await this.driver.table('users').createMany(data) + + const result = await this.driver.table('users').paginate(0, 10, '/users') + + assert.containsSubset(result.data, data) + assert.deepEqual(result.meta, { + currentPage: 0, + itemCount: 1, + itemsPerPage: 10, + totalItems: 1, + totalPages: 1 + }) + assert.deepEqual(result.links, { + first: '/users?limit=10', + last: '/users?page=1&limit=10', + next: '/users?page=1&limit=10', + previous: '/users?page=0&limit=10' + }) + } + + @Test() + public async shouldReturnEmptyDataWhenPaginateMethodCantFindNothing({ assert }: Context) { + const result = await this.driver.table('users').paginate() + + assert.isEmpty(result.data) + } + + @Test() + public async shouldBeAbleToCreateDataUsingDriver({ assert }: Context) { + const data = { id: '1', name: 'Robert Kiyosaki' } + + const result = await this.driver.table('users').create(data) + + assert.containsSubset(result, data) + } + + @Test() + public async shouldThrowWrongMethodExceptionWhenCallingCreateWithObject({ assert }: Context) { + await assert.rejects( + () => this.driver.table('users').create([{ id: '1', name: 'Robert Kiyosaki' }] as any), + WrongMethodException + ) + } + + @Test() + public async shouldBeAbleToCreateManyDataUsingDriver({ assert }: Context) { + const data = [ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ] + + const result = await this.driver.table('users').createMany(data) + + assert.containsSubset(result, data) + } + + @Test() + public async shouldThrowWrongMethodExceptionWhenCallingCreateManyWithObject({ assert }: Context) { + await assert.rejects( + () => this.driver.table('users').createMany({ id: '1', name: 'Robert Kiyosaki' } as any), + WrongMethodException + ) + } + + @Test() + public async shouldBeAbleToCreateDataUsingCreateOrUpdateMethod({ assert }: Context) { + const data = { id: '1', name: 'Robert Kiyosaki' } + + const result = await this.driver.table('users').createOrUpdate(data) + + assert.containsSubset(result, data) + } + + @Test() + public async shouldBeAbleToUpdateDataUsingCreateOrUpdateMethod({ assert }: Context) { + const data = { id: '1', name: 'Robert Kiyosaki' } + + await this.driver.table('users').create(data) + const result = await this.driver.table('users').createOrUpdate({ ...data, name: 'Robert Kiyosaki Millennials' }) + + assert.containsSubset(result, { ...data, name: 'Robert Kiyosaki Millennials' }) + } + + @Test() + public async shouldBeAbleToUpdateSingleDataAndReturnSingleObject({ assert }: Context) { + const data = { id: '1', name: 'Robert Kiyosaki' } + + await this.driver.table('users').create(data) + const result = await this.driver.table('users').update({ ...data, name: 'Robert Kiyosaki Millennials' }) + + assert.containsSubset(result, { ...data, name: 'Robert Kiyosaki Millennials' }) + } + + @Test() + public async shouldBeAbleToUpdateMultipleDataAndReturnAnArrayOfObjects({ assert }: Context) { + const data = [ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ] + + await this.driver.table('users').createMany(data) + const result = await this.driver + .table('users') + .whereIn('id', ['1', '2']) + .update({ name: 'Robert Kiyosaki Millennials' }) + + assert.containsSubset(result, [ + { id: '1', name: 'Robert Kiyosaki Millennials' }, + { id: '2', name: 'Robert Kiyosaki Millennials' } + ]) + } + + @Test() + public async shouldBeAbleToDeleteDataUsingDeleteMethod({ assert }: Context) { + const data = { id: '1', name: 'Robert Kiyosaki' } + + await this.driver.table('users').create(data) + await this.driver.table('users').where('id', '1').delete() + + assert.isUndefined(await this.driver.table('users').where('id', '1').find()) + } + + @Test() + public async shouldBeAbleToChangeInWhichTableTheDriverWillPerformTheOperations({ assert }: Context) { + const data = { id: '1', name: 'Robert Kiyosaki' } + + await this.driver.table('users').create(data) + + assert.deepEqual(await this.driver.table('users').count(), '1') + } + + @Test() + public async shouldThrowNotConnectedDatabaseExceptionWhenTryingToChangeTable({ assert }: Context) { + await this.driver.close() + + await assert.rejects(() => this.driver.table('users'), NotConnectedDatabaseException) + } + + @Test() + public async shouldBeAbleToDumpTheSQLQuery({ assert }: Context) { + Mock.when(console, 'log').return(undefined) + + this.driver.table('users').select('*').dump() + + assert.calledWith(console.log, { bindings: [], sql: 'select * from `users`' }) + } + + @Test() + public async shouldAllowSelectingSpecificColumnsFromTable({ assert }: Context) { + await this.driver.table('users').create({ id: '1', name: 'Alan Turing' }) + + const data = await this.driver.table('users').select('name').where('id', '1').findMany() + + assert.deepEqual(data, [{ name: 'Alan Turing' }]) + } + + @Test() + public async shouldAllowSelectingAllColumnsFromTable({ assert }: Context) { + await this.driver.table('users').create({ id: '1', name: 'Alan Turing' }) + + const data = await this.driver.table('users').select('*').where('id', '1').findMany() + + assert.containsSubset(data, [{ id: '1', name: 'Alan Turing' }]) + } + + @Test() + public async shouldAllowRawSqlSelectionForSpecializedQueries({ assert }: Context) { + await this.driver.table('users').create({ id: '1', name: 'Alan Turing' }) + + const data = await this.driver.table('users').selectRaw('COUNT(*) as user_count').find() + + assert.deepEqual(data, { user_count: 1 }) + } + + @Test() + public async shouldAllowSelectingAllColumnsFromTableUsingFrom({ assert }: Context) { + await this.driver.table('users').create({ id: '1', name: 'Alan Turing' }) + + const data = await this.driver.select('*').from('users').where('id', '1').findMany() + + assert.containsSubset(data, [{ id: '1', name: 'Alan Turing' }]) + } + + @Test() + public async shouldAllowRawSqlSelectionForSpecializedQueriesUsingFromRaw({ assert }: Context) { + await this.driver.table('users').create({ id: '1', name: 'Alan Turing' }) + + const data = await this.driver.selectRaw('COUNT(*) as user_count').fromRaw('users').find() + + assert.deepEqual(data, { user_count: 1 }) + } + + @Test() + public async shouldBeAbleToJoinAnotherTableBasedOnSpecifiedColumns({ assert }: Context) { + await this.driver.table('users').createMany([{ id: '1', name: 'Robert Kiyosaki' }]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' } + ]) + + const data = await this.driver + .table('users') + .select('users.id as user_id') + .select('users.name as user_name') + .select('rents.id as rent_id') + .join('rents', 'users.id', 'rents.user_id') + .findMany() + + assert.deepEqual(data, [ + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '1' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '2' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '3' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '4' } + ]) + } + + @Test() + public async shouldBeAbleToJoinAnotherTableBasedOnSpecifiedColumnsAndOperation({ assert }: Context) { + await this.driver.table('users').createMany([{ id: '1', name: 'Robert Kiyosaki' }]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' } + ]) + + const data = await this.driver + .table('users') + .select('users.id as user_id') + .select('users.name as user_name') + .select('rents.id as rent_id') + .join('rents', 'users.id', '=', 'rents.user_id') + .findMany() + + assert.deepEqual(data, [ + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '1' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '2' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '3' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '4' } + ]) + } + + @Test() + public async shouldBeAbleToJoinAnotherTableBasedOnSpecifiedFunction({ assert }: Context) { + await this.driver.table('users').createMany([{ id: '1', name: 'Robert Kiyosaki' }]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' } + ]) + + const data = await this.driver + .table('users') + .select('users.id as user_id') + .select('users.name as user_name') + .select('rents.id as rent_id') + .join('rents', function () { + this.on('users.id', '=', 'rents.user_id') + }) + .findMany() + + assert.deepEqual(data, [ + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '1' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '2' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '3' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '4' } + ]) + } + + @Test() + public async shouldBeAbleToLeftJoinAnotherTableBasedOnSpecifiedColumnsAndOperation({ assert }: Context) { + await this.driver.table('users').createMany([{ id: '1', name: 'Robert Kiyosaki' }]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' } + ]) + + const data = await this.driver + .table('users') + .select('users.id as user_id') + .select('users.name as user_name') + .select('rents.id as rent_id') + .leftJoin('rents', 'users.id', '=', 'rents.user_id') + .findMany() + + assert.deepEqual(data, [ + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '1' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '2' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '3' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '4' } + ]) + } + + @Test() + public async shouldBeAbleToRightJoinAnotherTableBasedOnSpecifiedColumnsAndOperation({ assert }: Context) { + await this.driver.table('users').createMany([{ id: '1', name: 'Robert Kiyosaki' }]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' } + ]) + + const data = await this.driver + .table('users') + .select('users.id as user_id') + .select('users.name as user_name') + .select('rents.id as rent_id') + .rightJoin('rents', 'users.id', '=', 'rents.user_id') + .findMany() + + assert.deepEqual(data, [ + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '1' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '2' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '3' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '4' } + ]) + } + + @Test() + public async shouldBeAbleToCrossJoinAnotherTableBasedOnSpecifiedColumnsAndOperation({ assert }: Context) { + await this.driver.table('users').createMany([{ id: '1', name: 'Robert Kiyosaki' }]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' } + ]) + + const data = await this.driver + .table('users') + .select('users.id as user_id') + .select('users.name as user_name') + .select('rents.id as rent_id') + .crossJoin('rents') + .findMany() + + assert.deepEqual(data, [ + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '1' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '2' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '3' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '4' } + ]) + } + + @Test() + public async shouldBeAbleToFullOuterJoinAnotherTableBasedOnSpecifiedColumnsAndOperation({ assert }: Context) { + await this.driver.table('users').createMany([{ id: '1', name: 'Robert Kiyosaki' }]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' } + ]) + + const data = await this.driver + .table('users') + .select('users.id as user_id') + .select('users.name as user_name') + .select('rents.id as rent_id') + .fullOuterJoin('rents', 'users.id', '=', 'rents.user_id') + .findMany() + + assert.deepEqual(data, [ + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '1' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '2' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '3' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '4' } + ]) + } + + @Test() + public async shouldBeAbleToLeftOuterJoinAnotherTableBasedOnSpecifiedColumnsAndOperation({ assert }: Context) { + await this.driver.table('users').createMany([{ id: '1', name: 'Robert Kiyosaki' }]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' } + ]) + + const data = await this.driver + .table('users') + .select('users.id as user_id') + .select('users.name as user_name') + .select('rents.id as rent_id') + .leftOuterJoin('rents', 'users.id', '=', 'rents.user_id') + .findMany() + + assert.deepEqual(data, [ + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '1' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '2' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '3' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '4' } + ]) + } + + @Test() + public async shouldBeAbleToRightOuterJoinAnotherTableBasedOnSpecifiedColumnsAndOperation({ assert }: Context) { + await this.driver.table('users').createMany([{ id: '1', name: 'Robert Kiyosaki' }]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' } + ]) + + const data = await this.driver + .table('users') + .select('users.id as user_id') + .select('users.name as user_name') + .select('rents.id as rent_id') + .rightOuterJoin('rents', 'users.id', '=', 'rents.user_id') + .findMany() + + assert.deepEqual(data, [ + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '1' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '2' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '3' }, + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '4' } + ]) + } + + @Test() + public async shouldApplyJoinRawForGivenTableAndConditions({ assert }: Context) { + await this.driver.table('users').createMany([{ id: '1', name: 'Robert Kiyosaki' }]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' } + ]) + + const data = await this.driver + .table('users') + .select('users.id as user_id') + .select('users.name as user_name') + .select('rents.id as rent_id') + .joinRaw('NATURAL FULL JOIN rents') + .findMany() + + assert.deepEqual(data, [ + { user_id: '1', user_name: 'Robert Kiyosaki', rent_id: '1' }, + { user_id: null, user_name: null, rent_id: '2' }, + { user_id: null, user_name: null, rent_id: '3' }, + { user_id: null, user_name: null, rent_id: '4' } + ]) + } + + @Test() + public async shouldBeAbleToGroupBySpecifiedColumnsUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver.table('rents').select('user_id').groupBy('user_id').orderBy('user_id').findMany() + + assert.deepEqual(data, [{ user_id: '1' }, { user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToGroupByRawSpecifiedColumnsUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver.table('rents').select('user_id').groupByRaw('user_id ORDER BY user_id').findMany() + + assert.deepEqual(data, [{ user_id: '1' }, { user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToAddAHavingClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .select('user_id') + .groupBy('user_id') + .having('user_id', '<=', '2') + .orderBy('user_id') + .findMany() + + assert.deepEqual(data, [{ user_id: '1' }, { user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToAddAHavingClauseWithDefaultEqualOpToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .select('user_id') + .groupBy('user_id') + .having('user_id', '2') + .orderBy('user_id') + .findMany() + + assert.deepEqual(data, [{ user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToAddAHavingClauseAsRawToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .select('user_id') + .groupBy('user_id') + .having(this.driver.raw("user_id <= '2'")) + .orderBy('user_id') + .findMany() + + assert.deepEqual(data, [{ user_id: '1' }, { user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToAddAHavingRawClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .select('user_id') + .groupBy('user_id') + .havingRaw("user_id <= '2' ORDER BY user_id") + .findMany() + + assert.deepEqual(data, [{ user_id: '1' }, { user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToAddAHavingExistsClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .groupBy('id', 'user_id') + .havingExists(query => { + query.select(query.raw('1')).from('users').whereRaw('users.id = rents.user_id') + }) + .orderBy('id') + .findMany() + + assert.deepEqual(data, [ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + } + + @Test() + public async shouldBeAbleToAddAHavingNotExistsClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('users') + .select('id', 'name') + .groupBy('id', 'name') + .havingNotExists(query => { + query.select(query.raw('1')).from('rents').whereRaw('users.id = rents.user_id') + }) + .findMany() + + assert.deepEqual(data, [{ id: '3', name: 'Alan Turing' }]) + } + + @Test() + public async shouldBeAbleToAddAHavingInClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('users') + .select('id', 'name') + .groupBy('id', 'name') + .havingIn('name', ['Alan Turing']) + .findMany() + + assert.deepEqual(data, [{ id: '3', name: 'Alan Turing' }]) + } + + @Test() + public async shouldBeAbleToAddAHavingNotInClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver.table('users').groupBy('id', 'name').havingNotIn('id', ['1', '2']).findMany() + + assert.containsSubset(data, [{ id: '3', name: 'Alan Turing' }]) + } + + @Test() + public async shouldBeAbleToAddAHavingBetweenClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver.table('users').groupBy('id', 'name').havingBetween('id', ['1', '3']).findMany() + + assert.containsSubset(data, [ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + } + + @Test() + public async shouldBeAbleToAddAHavingNotBetweenClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver.table('users').groupBy('id', 'name').havingNotBetween('id', ['1', '3']).findMany() + + assert.isEmpty(data) + } + + @Test() + public async shouldBeAbleToAddAHavingNullClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' }, + { id: '4', name: null } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('users') + .select('id', 'name') + .groupBy('id', 'name') + .havingNull('name') + .findMany() + + assert.deepEqual(data, [{ id: '4', name: null }]) + } + + @Test() + public async shouldBeAbleToAddAHavingNotNullClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' }, + { id: '4', name: null } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('users') + .select('id', 'name') + .groupBy('id', 'name') + .havingNotNull('name') + .orderBy('id') + .findMany() + + assert.deepEqual(data, [ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + } + + @Test() + public async shouldBeAbleToAddAOrHavingClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .select('user_id') + .groupBy('user_id') + .orHaving('user_id', '<=', '2') + .orderBy('user_id') + .findMany() + + assert.deepEqual(data, [{ user_id: '1' }, { user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToAddAOrHavingClauseWithDefaultEqualOpToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .select('user_id') + .groupBy('user_id') + .orHaving('user_id', '2') + .orderBy('user_id') + .findMany() + + assert.deepEqual(data, [{ user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToAddAOrHavingClauseAsRawToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .select('user_id') + .groupBy('user_id') + .orHaving(this.driver.raw("user_id <= '2'")) + .orderBy('user_id') + .findMany() + + assert.deepEqual(data, [{ user_id: '1' }, { user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToAddAOrHavingRawClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .select('user_id') + .groupBy('user_id') + .orHavingRaw("user_id <= '2' ORDER BY user_id") + .findMany() + + assert.deepEqual(data, [{ user_id: '1' }, { user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToAddAOrHavingExistsClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .groupBy('id', 'user_id') + .orHavingExists(query => { + query.select(query.raw('1')).from('users').whereRaw('users.id = rents.user_id') + }) + .orderBy('id') + .findMany() + + assert.deepEqual(data, [ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + } + + @Test() + public async shouldBeAbleToAddAOrHavingNotExistsClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('users') + .select('id', 'name') + .groupBy('id', 'name') + .orHavingNotExists(query => { + query.select(query.raw('1')).from('rents').whereRaw('users.id = rents.user_id') + }) + .findMany() + + assert.deepEqual(data, [{ id: '3', name: 'Alan Turing' }]) + } + + @Test() + public async shouldBeAbleToAddAOrHavingInClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('users') + .select('id', 'name') + .groupBy('id', 'name') + .orHavingIn('name', ['Alan Turing']) + .findMany() + + assert.deepEqual(data, [{ id: '3', name: 'Alan Turing' }]) + } + + @Test() + public async shouldBeAbleToAddAOrHavingNotInClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver.table('users').groupBy('id', 'name').orHavingNotIn('id', ['1', '2']).findMany() + + assert.containsSubset(data, [{ id: '3', name: 'Alan Turing' }]) + } + + @Test() + public async shouldBeAbleToAddAOrHavingBetweenClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver.table('users').groupBy('id', 'name').orHavingBetween('id', ['1', '3']).findMany() + + assert.containsSubset(data, [ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + } + + @Test() + public async shouldBeAbleToAddAOrHavingNotBetweenClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver.table('users').groupBy('id', 'name').orHavingNotBetween('id', ['1', '3']).findMany() + + assert.isEmpty(data) + } + + @Test() + public async shouldBeAbleToAddAOrHavingNullClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' }, + { id: '4', name: null } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('users') + .select('id', 'name') + .groupBy('id', 'name') + .orHavingNull('name') + .findMany() + + assert.deepEqual(data, [{ id: '4', name: null }]) + } + + @Test() + public async shouldBeAbleToAddAOrHavingNotNullClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' }, + { id: '4', name: null } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('users') + .select('id', 'name') + .groupBy('id', 'name') + .orHavingNotNull('name') + .orderBy('id') + .findMany() + + assert.deepEqual(data, [ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + } + + @Test() + public async shouldBeAbleToAddAWhereClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .select('user_id') + .where('user_id', '=', '2') + .orderBy('user_id') + .findMany() + + assert.deepEqual(data, [{ user_id: '2' }, { user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToAddAWhereClauseWithDefaultEqualOpToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver.table('rents').select('user_id').where('user_id', '2').orderBy('user_id').findMany() + + assert.deepEqual(data, [{ user_id: '2' }, { user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToAddAWhereClauseAsRawToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .select('user_id') + .where(this.driver.raw("user_id = '2'")) + .orderBy('user_id') + .findMany() + + assert.deepEqual(data, [{ user_id: '2' }, { user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToAddAWhereClauseAsClosureToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .select('user_id') + .where(query => { + query.whereIn('user_id', ['2']) + }) + .orderBy('user_id') + .findMany() + + assert.deepEqual(data, [{ user_id: '2' }, { user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToAddAWhereRawClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver.table('rents').select('user_id').whereRaw("user_id = '2'").findMany() + + assert.deepEqual(data, [{ user_id: '2' }, { user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToAddAWhereNotClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .select('user_id') + .whereNot('user_id', '1') + .orderBy('user_id') + .findMany() + + assert.deepEqual(data, [{ user_id: '2' }, { user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToAddAWhereNotClauseAsRawToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .select('user_id') + .whereNot(this.driver.raw("user_id = '1'")) + .orderBy('user_id') + .findMany() + + assert.deepEqual(data, [{ user_id: '2' }, { user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToAddAWhereNotClauseAsFunctionToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .select('user_id') + .whereNot(query => { + query.whereIn('user_id', ['1']) + }) + .orderBy('user_id') + .findMany() + + assert.deepEqual(data, [{ user_id: '2' }, { user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToAddAWhereLikeClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + + const data = await this.driver + .table('users') + .select('id', 'name') + .whereLike('name', '%Warren Buffet%') + .orderBy('id') + .findMany() + + assert.deepEqual(data, [{ id: '2', name: 'Warren Buffet' }]) + } + + @Test() + public async shouldBeAbleToAddAWhereILikeClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + + const data = await this.driver + .table('users') + .select('id', 'name') + .whereILike('name', '%Warren Buffet%') + .orderBy('id') + .findMany() + + assert.deepEqual(data, [{ id: '2', name: 'Warren Buffet' }]) + } + + @Test() + public async shouldBeAbleToAddAWhereExistsClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .whereExists(query => { + query.select(query.raw('1')).from('users').whereRaw('users.id = rents.user_id') + }) + .findMany() + + assert.deepEqual(data, [ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + } + + @Test() + public async shouldBeAbleToAddAWhereNotExistsClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('users') + .select('id', 'name') + .whereNotExists(query => { + query.select(query.raw('1')).from('rents').whereRaw('users.id = rents.user_id') + }) + .findMany() + + assert.deepEqual(data, [{ id: '3', name: 'Alan Turing' }]) + } + + @Test() + public async shouldBeAbleToAddAWhereInClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver.table('users').select('id', 'name').whereIn('name', ['Alan Turing']).findMany() + + assert.deepEqual(data, [{ id: '3', name: 'Alan Turing' }]) + } + + @Test() + public async shouldBeAbleToAddAWhereNotInClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver.table('users').whereNotIn('id', ['1', '2']).findMany() + + assert.containsSubset(data, [{ id: '3', name: 'Alan Turing' }]) + } + + @Test() + public async shouldBeAbleToAddAWhereBetweenClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver.table('users').whereBetween('id', ['1', '3']).findMany() + + assert.containsSubset(data, [ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + } + + @Test() + public async shouldBeAbleToAddAWhereNotBetweenClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver.table('users').whereNotBetween('id', ['1', '3']).findMany() + + assert.isEmpty(data) + } + + @Test() + public async shouldBeAbleToAddAWhereNullClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' }, + { id: '4', name: null } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver.table('users').select('id', 'name').whereNull('name').findMany() + + assert.deepEqual(data, [{ id: '4', name: null }]) + } + + @Test() + public async shouldBeAbleToAddAWhereNotNullClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' }, + { id: '4', name: null } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver.table('users').select('id', 'name').whereNotNull('name').orderBy('id').findMany() + + assert.deepEqual(data, [ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + } + + @Test() + public async shouldBeAbleToAddAOrWhereClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .select('user_id') + .orWhere('user_id', '=', '2') + .orderBy('user_id') + .findMany() + + assert.deepEqual(data, [{ user_id: '2' }, { user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToAddAOrWhereClauseWithDefaultEqualOpToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .select('user_id') + .orWhere('user_id', '2') + .orderBy('user_id') + .findMany() + + assert.deepEqual(data, [{ user_id: '2' }, { user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToAddAOrWhereClauseAsRawToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .select('user_id') + .orWhere(this.driver.raw("user_id = '2'")) + .orderBy('user_id') + .findMany() + + assert.deepEqual(data, [{ user_id: '2' }, { user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToAddAOrWhereClauseAsClosureToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .select('user_id') + .orWhere(query => { + query.whereIn('user_id', ['2']) + }) + .orderBy('user_id') + .findMany() + + assert.deepEqual(data, [{ user_id: '2' }, { user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToAddAOrWhereRawClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .select('user_id') + .orWhereRaw("user_id = '2' ORDER BY user_id") + .findMany() + + assert.deepEqual(data, [{ user_id: '2' }, { user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToAddAOrWhereNotClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .select('user_id') + .orWhereNot('user_id', '1') + .orderBy('user_id') + .findMany() + + assert.deepEqual(data, [{ user_id: '2' }, { user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToAddAOrWhereNotClauseAsRawToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .select('user_id') + .orWhereNot(this.driver.raw("user_id = '1'")) + .orderBy('user_id') + .findMany() + + assert.deepEqual(data, [{ user_id: '2' }, { user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToAddAOrWhereNotClauseAsFunctionToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .select('user_id') + .orWhereNot(query => { + query.whereIn('user_id', ['1']) + }) + .orderBy('user_id') + .findMany() + + assert.deepEqual(data, [{ user_id: '2' }, { user_id: '2' }]) + } + + @Test() + public async shouldBeAbleToAddAOrWhereLikeClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + + const data = await this.driver + .table('users') + .select('id', 'name') + .orWhereLike('name', '%Warren Buffet%') + .orderBy('id') + .findMany() + + assert.deepEqual(data, [{ id: '2', name: 'Warren Buffet' }]) + } + + @Test() + public async shouldBeAbleToAddAOrWhereILikeClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + + const data = await this.driver + .table('users') + .select('id', 'name') + .orWhereILike('name', '%Warren Buffet%') + .orderBy('id') + .findMany() + + assert.deepEqual(data, [{ id: '2', name: 'Warren Buffet' }]) + } + + @Test() + public async shouldBeAbleToAddAOrWhereExistsClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('rents') + .orWhereExists(query => { + query.select(query.raw('1')).from('users').whereRaw('users.id = rents.user_id') + }) + .findMany() + + assert.deepEqual(data, [ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + } + + @Test() + public async shouldBeAbleToAddAOrWhereNotExistsClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver + .table('users') + .select('id', 'name') + .orWhereNotExists(query => { + query.select(query.raw('1')).from('rents').whereRaw('users.id = rents.user_id') + }) + .findMany() + + assert.deepEqual(data, [{ id: '3', name: 'Alan Turing' }]) + } + + @Test() + public async shouldBeAbleToAddAOrWhereInClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver.table('users').select('id', 'name').orWhereIn('name', ['Alan Turing']).findMany() + + assert.deepEqual(data, [{ id: '3', name: 'Alan Turing' }]) + } + + @Test() + public async shouldBeAbleToAddAOrWhereNotInClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver.table('users').orWhereNotIn('id', ['1', '2']).findMany() + + assert.containsSubset(data, [{ id: '3', name: 'Alan Turing' }]) + } + + @Test() + public async shouldBeAbleToAddAOrWhereBetweenClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver.table('users').orWhereBetween('id', ['1', '3']).findMany() + + assert.containsSubset(data, [ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + } + + @Test() + public async shouldBeAbleToAddAOrWhereNotBetweenClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver.table('users').orWhereNotBetween('id', ['1', '3']).findMany() + + assert.isEmpty(data) + } + + @Test() + public async shouldBeAbleToAddAOrWhereNullClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' }, + { id: '4', name: null } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver.table('users').select('id', 'name').orWhereNull('name').findMany() + + assert.deepEqual(data, [{ id: '4', name: null }]) + } + + @Test() + public async shouldBeAbleToAddAOrWhereNotNullClauseToTheQueryUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' }, + { id: '4', name: null } + ]) + await this.driver.table('rents').createMany([ + { id: '1', user_id: '1' }, + { id: '2', user_id: '1' }, + { id: '3', user_id: '1' }, + { id: '4', user_id: '1' }, + { id: '5', user_id: '2' }, + { id: '6', user_id: '2' } + ]) + + const data = await this.driver.table('users').select('id', 'name').orWhereNotNull('name').orderBy('id').findMany() + + assert.deepEqual(data, [ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + } + + @Test() + public async shouldOrderBySpecifiedColumnInASCUpperCaseDirectionUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + + const data = await this.driver.select('name').orderBy('name', 'ASC').findMany() + + assert.deepEqual(data, [{ name: 'Alan Turing' }, { name: 'Robert Kiyosaki' }, { name: 'Warren Buffet' }]) + } + + @Test() + public async shouldOrderBySpecifiedColumnInASCLowerCaseDirectionUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + + const data = await this.driver.select('name').orderBy('name', 'asc').findMany() + + assert.deepEqual(data, [{ name: 'Alan Turing' }, { name: 'Robert Kiyosaki' }, { name: 'Warren Buffet' }]) + } + + @Test() + public async shouldOrderBySpecifiedColumnInDESCUpperCaseDirectionUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + + const data = await this.driver.select('name').orderBy('name', 'DESC').findMany() + + assert.deepEqual(data, [{ name: 'Warren Buffet' }, { name: 'Robert Kiyosaki' }, { name: 'Alan Turing' }]) + } + + @Test() + public async shouldOrderBySpecifiedColumnInDESCLowerCaseDirectionUsingDriver({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + + const data = await this.driver.select('name').orderBy('name', 'desc').findMany() + + assert.deepEqual(data, [{ name: 'Warren Buffet' }, { name: 'Robert Kiyosaki' }, { name: 'Alan Turing' }]) + } + + @Test() + public async shouldOrderBySpecifiedColumnInGivenDirectionUsingRawSQL({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' }, + { id: '4', name: null } + ]) + + const data = await this.driver.table('users').select('name').orderByRaw('name DESC NULLS LAST').findMany() + + assert.deepEqual(data, [ + { name: 'Warren Buffet' }, + { name: 'Robert Kiyosaki' }, + { name: 'Alan Turing' }, + { name: null } + ]) + } + + @Test() + public async shouldBeAbleToAutomaticallyOrderTheDataByDatesUsingLatest({ assert }: Context) { + await this.driver.table('users').create({ id: '1', name: 'Robert Kiyosaki' }) + await Exec.sleep(2000) + const latest = await this.driver.table('users').create({ id: '3', name: 'Alan Turing' }) + + const data = await this.driver.table('users').latest('created_at').find() + + assert.deepEqual(latest, data) + } + + @Test() + public async shouldBeAbleToAutomaticallyOrderTheDataByDatesUsingOldest({ assert }: Context) { + const oldest = await this.driver.table('users').create({ id: '1', name: 'Robert Kiyosaki' }) + await this.driver.table('users').create({ id: '3', name: 'Alan Turing' }) + + const data = await this.driver.table('users').oldest('created_at').find() + + assert.deepEqual(oldest, data) + } + + @Test() + public async shouldOffsetTheResultsByGivenValue({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + + const data = await this.driver.table('users').select('name').offset(1).findMany() + + assert.deepEqual(data, [{ name: 'Warren Buffet' }, { name: 'Alan Turing' }]) + } + + @Test() + public async shouldLimitTheResultsByGivenValue({ assert }: Context) { + await this.driver.table('users').createMany([ + { id: '1', name: 'Robert Kiyosaki' }, + { id: '2', name: 'Warren Buffet' }, + { id: '3', name: 'Alan Turing' } + ]) + + const data = await this.driver.table('users').select('name').limit(1).findMany() + + assert.deepEqual(data, [{ name: 'Robert Kiyosaki' }]) + } +} diff --git a/tests/unit/factories/ConnectionFactoryTest.ts b/tests/unit/factories/ConnectionFactoryTest.ts index 2ee8280..10f2d2c 100644 --- a/tests/unit/factories/ConnectionFactoryTest.ts +++ b/tests/unit/factories/ConnectionFactoryTest.ts @@ -81,6 +81,29 @@ export default class ConnectionFactoryTest { }) } + @Test() + public async shouldBeAbleToCreateTheConnectionToSqliteDirectly({ assert }: Context) { + const knexFake = Mock.fake() + + Mock.when(ConnectionFactory, 'getKnex').return({ default: knexFake }) + + ConnectionFactory.sqlite('sqlite') + + assert.calledWith(knexFake, { + client: 'better-sqlite3', + debug: false, + migrations: { + tableName: 'migrations' + }, + pool: { + acquireTimeoutMillis: 60000, + max: 20, + min: 2 + }, + useNullAsDefault: false + }) + } + @Test() public async shouldBeAbleToCreateTheConnectionToPostgresDirectly({ assert }: Context) { const knexFake = Mock.fake() diff --git a/tests/unit/factories/DriverFactoryTest.ts b/tests/unit/factories/DriverFactoryTest.ts index daffe54..b84c871 100644 --- a/tests/unit/factories/DriverFactoryTest.ts +++ b/tests/unit/factories/DriverFactoryTest.ts @@ -40,7 +40,7 @@ export default class DriverFactoryTest { public shouldBeAbleToListAllAvailableDrivers({ assert }: Context) { const drivers = DriverFactory.availableDrivers() - assert.deepEqual(drivers, ['mysql', 'postgres', 'fake']) + assert.deepEqual(drivers, ['mysql', 'sqlite', 'postgres', 'fake']) } @Test()