Skip to content

Commit

Permalink
test: fix failing tests
Browse files Browse the repository at this point in the history
  • Loading branch information
thetutlage committed Dec 4, 2024
1 parent 761f823 commit c136782
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 104 deletions.
4 changes: 2 additions & 2 deletions providers/database_provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ declare module '@vinejs/vine' {
* Ensure the value is unique inside the database by table and column name.
* Optionally, you can define a filter to narrow down the query.
*/
unique(options: VineDbSearchOptions): this
unique(options: VineDbSearchOptions<ValueType>): this

/**
* Ensure the value is unique inside the database by self
Expand All @@ -59,7 +59,7 @@ declare module '@vinejs/vine' {
* Ensure the value exists inside the database by table and column name.
* Optionally, you can define a filter to narrow down the query.
*/
exists(options: VineDbSearchOptions): this
exists(options: VineDbSearchOptions<ValueType>): this

/**
* Ensure the value exists inside the database by self
Expand Down
181 changes: 95 additions & 86 deletions src/bindings/vinejs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,113 +25,122 @@ export const messages = {
* VineJS.
*/
export function defineValidationRules(db: Database) {
const uniqueRule = vine.createRule<VineDbSearchCallback | VineDbSearchOptions>(
async (value, callbackOrOptions, field) => {
if (!field.isValid) {
return
}
function uniqueRule<ValueType>() {
return vine.createRule<VineDbSearchCallback<ValueType> | VineDbSearchOptions<ValueType>>(
async (value, callbackOrOptions, field) => {
if (!field.isValid) {
return
}

/**
* Rely on the callback to execute the query and return value
* a boolean.
*
* True means value is unique
* False means value is not unique
*/
if (typeof callbackOrOptions === 'function') {
const isUnique = await callbackOrOptions(db, value as string, field)
if (!isUnique) {
field.report(messages['database.unique'], 'database.unique', field)
/**
* Rely on the callback to execute the query and return value
* a boolean.
*
* True means value is unique
* False means value is not unique
*/
if (typeof callbackOrOptions === 'function') {
const isUnique = await callbackOrOptions(db, value as ValueType, field)
if (!isUnique) {
field.report(messages['database.unique'], 'database.unique', field)
}
return
}
return
}

const { table, column, filter, connection, caseInsensitive } = callbackOrOptions
const query = db.connection(connection).from(table).select(column)
const { table, column, filter, connection, caseInsensitive } = callbackOrOptions
const query = db.connection(connection).from(table).select(column)

/**
* Apply where clause respecting the caseInsensitive flag.
*/
if (caseInsensitive) {
query.whereRaw(`lower(${column}) = ?`, [db.raw(`lower(?)`, [value])])
} else {
query.where(column, value as string)
}
/**
* Apply where clause respecting the caseInsensitive flag.
*/
if (caseInsensitive) {
query.whereRaw(`lower(${column}) = ?`, [db.raw(`lower(?)`, [value])])
} else {
query.where(column, value as string | number)
}

/**
* Apply user filter
*/
await filter?.(query, value as string, field)

/**
* Fetch the first row from the database
*/
const row = await query.first()
if (row) {
field.report(messages['database.unique'], 'database.unique', field)
}
}
)
/**
* Apply user filter
*/
await filter?.(query, value as ValueType, field)

const existsRule = vine.createRule<VineDbSearchCallback | VineDbSearchOptions>(
async (value, callbackOrOptions, field) => {
if (!field.isValid) {
return
/**
* Fetch the first row from the database
*/
const row = await query.first()
if (row) {
field.report(messages['database.unique'], 'database.unique', field)
}
}
)
}

function existsRule<ValueType>() {
return vine.createRule<VineDbSearchCallback<ValueType> | VineDbSearchOptions<ValueType>>(
async (value, callbackOrOptions, field) => {
if (!field.isValid) {
return
}

/**
* Rely on the callback to execute the query and return value
* a boolean.
*
* True means value exists
* False means value does not exist
*/
if (typeof callbackOrOptions === 'function') {
const exists = await callbackOrOptions(db, value as string, field)
if (!exists) {
field.report(messages['database.exists'], 'database.exists', field)
/**
* Rely on the callback to execute the query and return value
* a boolean.
*
* True means value exists
* False means value does not exist
*/
if (typeof callbackOrOptions === 'function') {
const exists = await callbackOrOptions(db, value as ValueType, field)
if (!exists) {
field.report(messages['database.exists'], 'database.exists', field)
}
return
}
return
}

const { table, column, filter, connection, caseInsensitive } = callbackOrOptions
const query = db.connection(connection).from(table).select(column)
const { table, column, filter, connection, caseInsensitive } = callbackOrOptions
const query = db.connection(connection).from(table).select(column)

/**
* Apply where clause respecting the caseInsensitive flag.
*/
if (caseInsensitive) {
query.whereRaw(`lower(${column}) = ?`, [db.raw(`lower(?)`, [value])])
} else {
query.where(column, value as string)
}
/**
* Apply where clause respecting the caseInsensitive flag.
*/
if (caseInsensitive) {
query.whereRaw(`lower(${column}) = ?`, [db.raw(`lower(?)`, [value])])
} else {
query.where(column, value as string | number)
}

/**
* Apply user filter
*/
await filter?.(query, value as string, field)

/**
* Fetch the first row from the database
*/
const row = await query.first()
if (!row) {
field.report(messages['database.exists'], 'database.exists', field)
/**
* Apply user filter
*/
await filter?.(query, value as ValueType, field)

/**
* Fetch the first row from the database
*/
const row = await query.first()
if (!row) {
field.report(messages['database.exists'], 'database.exists', field)
}
}
}
)
)
}

const uniqueRuleForString = uniqueRule<string>()
const uniqueRuleForNumber = uniqueRule<number>()
const existsRuleForString = existsRule<string>()
const existsRuleForNumber = existsRule<number>()

VineString.macro('unique', function (this: VineString, callbackOrOptions) {
return this.use(uniqueRule(callbackOrOptions))
return this.use(uniqueRuleForString(callbackOrOptions))
})
VineString.macro('exists', function (this: VineString, callbackOrOptions) {
return this.use(existsRule(callbackOrOptions))
return this.use(existsRuleForString(callbackOrOptions))
})

VineNumber.macro('unique', function (this: VineNumber, callbackOrOptions) {
return this.use(uniqueRule(callbackOrOptions))
return this.use(uniqueRuleForNumber(callbackOrOptions))
})
VineNumber.macro('exists', function (this: VineNumber, callbackOrOptions) {
return this.use(existsRule(callbackOrOptions))
return this.use(existsRuleForNumber(callbackOrOptions))
})
}
4 changes: 2 additions & 2 deletions src/types/vine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import type { DatabaseQueryBuilderContract } from './querybuilder.js'
/**
* Options for the unique and the exists validations
*/
export type VineDbSearchOptions = {
export type VineDbSearchOptions<ValueType> = {
/**
* Database table for the query
*/
Expand Down Expand Up @@ -49,7 +49,7 @@ export type VineDbSearchOptions = {
*/
filter?: (
db: DatabaseQueryBuilderContract,
value: string,
value: ValueType,
field: FieldContext
) => void | Promise<void>
}
Expand Down
51 changes: 37 additions & 14 deletions test/bindings/vinejs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import { defineValidationRules } from '../../src/bindings/vinejs.js'
import { getConfig, setup, cleanup, logger, createEmitter } from '../../test-helpers/index.js'

let db: Database
const dialectPerformsCaseSensitiveSearch = ['mysql', 'mysql_legacy', 'mssql'].includes(
process.env.DB!
)

test.group('VineJS | unique', (group) => {
group.setup(async () => {
Expand Down Expand Up @@ -102,13 +105,23 @@ test.group('VineJS | unique', (group) => {
email: '[email protected]',
})
} catch (error) {
assert.deepEqual(error.messages, [
{
field: 'email',
message: 'The email has already been taken',
rule: 'database.unique',
},
])
if (dialectPerformsCaseSensitiveSearch) {
assert.deepEqual(error.messages, [
{
field: 'email',
message: 'The email has already been taken',
rule: 'database.unique',
},
])
} else {
assert.deepEqual(error.messages, [
{
field: 'email',
message: 'The email has already been taken',
rule: 'database.unique',
},
])
}
}
})

Expand Down Expand Up @@ -338,13 +351,23 @@ test.group('VineJS | exists', (group) => {
email: '[email protected]',
})
} catch (error) {
assert.deepEqual(error.messages, [
{
field: 'username',
message: 'The selected username is invalid',
rule: 'database.exists',
},
])
if (dialectPerformsCaseSensitiveSearch) {
assert.deepEqual(error.messages, [
{
field: 'email',
message: 'The email has already been taken',
rule: 'database.unique',
},
])
} else {
assert.deepEqual(error.messages, [
{
field: 'username',
message: 'The selected username is invalid',
rule: 'database.exists',
},
])
}
}
})

Expand Down

0 comments on commit c136782

Please sign in to comment.