Skip to content

Commit

Permalink
initial tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
igalklebanov committed Jan 3, 2024
1 parent 83f5e6f commit 702c4c0
Show file tree
Hide file tree
Showing 2 changed files with 369 additions and 0 deletions.
172 changes: 172 additions & 0 deletions tests/nodejs/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import {expect} from 'chai'
import {DeleteResult, InsertResult, UpdateResult, sql} from 'kysely'
import {jsonArrayFrom as jsonArrayFromMssql} from 'kysely/helpers/mssql'
import {jsonArrayFrom as jsonArrayFromMySQL} from 'kysely/helpers/mysql'
import {jsonArrayFrom as jsonArrayFromPostgres} from 'kysely/helpers/postgres'
import {jsonArrayFrom as jsonArrayFromSQLite} from 'kysely/helpers/sqlite'
import {omit} from 'lodash'
import {SUPPORTED_DIALECTS} from '../../src/supported-dialects.js'
import {PersonEntity} from './entity/Person.js'
import {DEFAULT_DATA_SET, PerDialect, TestContext, initTest, seedDatabase} from './test-setup.js'

for (const dialect of SUPPORTED_DIALECTS) {
describe(`KyselyTypeORMDialect: ${dialect}`, () => {
let ctx: TestContext

const jsonArrayFrom = {
'better-sqlite3': jsonArrayFromSQLite,
mssql: jsonArrayFromMssql,
mysql: jsonArrayFromMySQL,
postgres: jsonArrayFromPostgres,
sqlite: jsonArrayFromSQLite,
}[dialect] as typeof jsonArrayFromMySQL

beforeEach(async function () {
ctx = await initTest(this, dialect)
await seedDatabase(ctx)
})

afterEach(async () => {
await ctx.typeORMDataSource.dropDatabase()
})

after(async () => {
await ctx.kysely.destroy()
})

it('should be able to perform select queries', async () => {
const ormPeople = await PersonEntity.find({
select: {
firstName: true,
gender: true,
lastName: true,
maritalStatus: true,
middleName: true,
pets: {
name: true,
species: true,
toys: true,
},
},
relations: ['pets', 'pets.toys'],
order: {id: 1},
})

expect(ormPeople).to.have.lengthOf(DEFAULT_DATA_SET.length)

const normalizedOrmPeople = ormPeople.map((ormPerson) =>
omit({...ormPerson, pets: ormPerson.pets.map((pet) => omit(pet, ['id', 'owner', 'ownerId']))}, ['id']),
)

expect(normalizedOrmPeople).to.deep.equal(DEFAULT_DATA_SET)

const queryBuilderPeople = await ctx.kysely
.selectFrom('person')
.select((eb) => [
'firstName',
'gender',
'lastName',
'maritalStatus',
'middleName',
jsonArrayFrom(
eb
.selectFrom('pet')
.whereRef('pet.ownerId', '=', 'person.id')
.select(['pet.name', 'pet.species', sql`'[]'`.as('toys')]),
).as('pets'),
])
.execute()

expect(queryBuilderPeople).to.deep.equal(normalizedOrmPeople)
})

it('should be able to perform insert queries', async () => {
const result = await ctx.kysely.insertInto('person').values({gender: 'female'}).executeTakeFirstOrThrow()

expect(result).to.deep.equal(
(
{
'better-sqlite3': {insertId: BigInt(DEFAULT_DATA_SET.length + 1), numInsertedOrUpdatedRows: BigInt(1)},
mssql: {insertId: undefined, numInsertedOrUpdatedRows: BigInt(1)},
mysql: {insertId: BigInt(DEFAULT_DATA_SET.length + 1), numInsertedOrUpdatedRows: BigInt(1)},
postgres: {insertId: undefined, numInsertedOrUpdatedRows: BigInt(1)},
sqlite: {insertId: undefined, numInsertedOrUpdatedRows: undefined},
} satisfies PerDialect<{[K in keyof InsertResult]: InsertResult[K]}>
)[dialect],
)
})

if (dialect === 'postgres' || dialect === 'sqlite') {
it('should be able to perform insert queries with returning', async () => {
const result = await ctx.kysely
.insertInto('person')
.values({gender: 'female'})
.returning('id')
.executeTakeFirst()

expect(result).to.deep.equal({id: DEFAULT_DATA_SET.length + 1})
})
}

it('should be able to perform update queries', async () => {
const result = await ctx.kysely
.updateTable('person')
.set({maritalStatus: 'widowed'})
.where('id', '=', 1)
.executeTakeFirstOrThrow()

expect(result).to.deep.equal(
(
{
'better-sqlite3': {numChangedRows: undefined, numUpdatedRows: BigInt(1)},
mssql: {numChangedRows: undefined, numUpdatedRows: BigInt(1)},
mysql: {numChangedRows: BigInt(1), numUpdatedRows: BigInt(1)},
postgres: {numChangedRows: undefined, numUpdatedRows: BigInt(1)},
sqlite: {numChangedRows: undefined, numUpdatedRows: BigInt(0)},
} satisfies PerDialect<{[K in keyof UpdateResult]: UpdateResult[K]}>
)[dialect],
)
})

if (dialect === 'postgres' || dialect === 'sqlite') {
it('should be able to perform update queries with returning', async () => {
const result = await ctx.kysely
.updateTable('person')
.set({maritalStatus: 'widowed'})
.where('id', '=', 1)
.returning(['gender'])
.executeTakeFirstOrThrow()

expect(result).to.deep.equal({gender: DEFAULT_DATA_SET[0].gender})
})
}

it('should be able to perform delete queries', async () => {
const result = await ctx.kysely.deleteFrom('person').where('id', '=', 1).executeTakeFirstOrThrow()

expect(result).to.deep.equal(
(
{
'better-sqlite3': {numDeletedRows: BigInt(1)},
mssql: {numDeletedRows: BigInt(1)},
mysql: {numDeletedRows: BigInt(1)},
postgres: {numDeletedRows: BigInt(1)},
sqlite: {numDeletedRows: BigInt(0)},
} satisfies PerDialect<{[K in keyof DeleteResult]: DeleteResult[K]}>
)[dialect],
)
})

if (dialect === 'postgres' || dialect === 'sqlite') {
it('should be able to perform delete queries with returning', async () => {
const result = await ctx.kysely
.deleteFrom('person')
.where('id', '=', 1)
.returning('gender')
.executeTakeFirstOrThrow()

expect(result).to.deep.equal({gender: DEFAULT_DATA_SET[0].gender})
})
}
})
}
197 changes: 197 additions & 0 deletions tests/nodejs/test-setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import {
CamelCasePlugin,
Kysely,
MssqlAdapter,
MssqlIntrospector,
MssqlQueryCompiler,
MysqlAdapter,
MysqlIntrospector,
MysqlQueryCompiler,
ParseJSONResultsPlugin,
PostgresAdapter,
PostgresIntrospector,
PostgresQueryCompiler,
SqliteAdapter,
SqliteIntrospector,
SqliteQueryCompiler,
type KyselyPlugin,
} from 'kysely'
import 'reflect-metadata'
import {DataSource, type DataSourceOptions, type DeepPartial} from 'typeorm'
import {SnakeNamingStrategy} from 'typeorm-naming-strategies'
import {KyselyTypeORMDialect, type KyselifyEntity, type KyselySubDialect, type KyselyTypeORMDialectConfig} from '../..'
import type {SupportedDialect} from '../../src/supported-dialects.js'
import {PersonEntity} from './entity/Person'
import {PetEntity} from './entity/Pet'
import {ToyEntity} from './entity/Toy'

export type Person = KyselifyEntity<PersonEntity>
export type Pet = KyselifyEntity<PetEntity>
export type Toy = KyselifyEntity<ToyEntity>

export interface Database {
person: Person
pet: Pet
toy: Toy
}

export interface TestContext {
kysely: Kysely<Database>
typeORMDataSource: DataSource
}

export type PerDialect<T> = Record<SupportedDialect, T>

const TEST_INIT_TIMEOUT = 5 * 60 * 1_000

export const PLUGINS: KyselyPlugin[] = [new ParseJSONResultsPlugin(), new CamelCasePlugin()]

const POOL_SIZE = 10

const sqliteSubDialect = {
createAdapter: () => new SqliteAdapter(),
createIntrospector: (db) => new SqliteIntrospector(db),
createQueryCompiler: () => new SqliteQueryCompiler(),
} satisfies KyselySubDialect

const BASE_DATA_SOURCE_OPTIONS = {
entities: [PersonEntity, PetEntity, ToyEntity],
logging: false,
// logging: 'all',
namingStrategy: new SnakeNamingStrategy(),
} satisfies Omit<DataSourceOptions, 'type'>

export const CONFIGS: PerDialect<
Omit<KyselyTypeORMDialectConfig, 'typeORMDataSource'> & {
typeORMDataSourceOptions: DataSourceOptions
}
> = {
'better-sqlite3': {
kyselySubDialect: sqliteSubDialect,
typeORMDataSourceOptions: {
...BASE_DATA_SOURCE_OPTIONS,
database: ':memory:',
type: 'better-sqlite3',
},
},
mssql: {
kyselySubDialect: {
createAdapter: () => new MssqlAdapter(),
createIntrospector: (db) => new MssqlIntrospector(db),
createQueryCompiler: () => new MssqlQueryCompiler(),
},
typeORMDataSourceOptions: {
...BASE_DATA_SOURCE_OPTIONS,
database: 'kysely_test',
host: 'localhost',
password: 'KyselyTest0',
pool: {min: 0, max: POOL_SIZE},
port: 21433,
type: 'mssql',
username: 'sa',
options: {
trustServerCertificate: true,
useUTC: true,
},
},
},
mysql: {
kyselySubDialect: {
createAdapter: () => new MysqlAdapter(),
createIntrospector: (db) => new MysqlIntrospector(db),
createQueryCompiler: () => new MysqlQueryCompiler(),
},
typeORMDataSourceOptions: {
...BASE_DATA_SOURCE_OPTIONS,
bigNumberStrings: true,
database: 'kysely_test',
host: 'localhost',
password: 'kysely',
poolSize: POOL_SIZE,
port: 3308,
supportBigNumbers: true,
type: 'mysql',
username: 'kysely',
},
},
postgres: {
kyselySubDialect: {
createAdapter: () => new PostgresAdapter(),
createIntrospector: (db) => new PostgresIntrospector(db),
createQueryCompiler: () => new PostgresQueryCompiler(),
},
typeORMDataSourceOptions: {
...BASE_DATA_SOURCE_OPTIONS,
database: 'kysely_test',
host: 'localhost',
poolSize: POOL_SIZE,
port: 5434,
type: 'postgres',
username: 'kysely',
useUTC: true,
},
},
sqlite: {
kyselySubDialect: sqliteSubDialect,
typeORMDataSourceOptions: {
...BASE_DATA_SOURCE_OPTIONS,
database: ':memory:',
type: 'sqlite',
},
},
}

export async function initTest(ctx: Mocha.Context, dialect: SupportedDialect): Promise<TestContext> {
const config = CONFIGS[dialect]

const typeORMDataSource = new DataSource(config.typeORMDataSourceOptions)

ctx.timeout(TEST_INIT_TIMEOUT)
await typeORMDataSource.initialize()

await typeORMDataSource.synchronize(true)

const kysely = new Kysely<Database>({
dialect: new KyselyTypeORMDialect({
kyselySubDialect: config.kyselySubDialect,
shouldDestroyDataSource: true,
typeORMDataSource,
}),
plugins: PLUGINS,
})

return {kysely, typeORMDataSource}
}

export const DEFAULT_DATA_SET = [
{
firstName: 'Jennifer',
middleName: null,
lastName: 'Aniston',
gender: 'female',
pets: [{name: 'Catto', species: 'cat', toys: []}],
maritalStatus: 'divorced',
},
{
firstName: 'Arnold',
middleName: null,
lastName: 'Schwarzenegger',
gender: 'male',
pets: [{name: 'Doggo', species: 'dog', toys: []}],
maritalStatus: 'divorced',
},
{
firstName: 'Sylvester',
middleName: 'Rocky',
lastName: 'Stallone',
gender: 'male',
pets: [{name: 'Hammo', species: 'hamster', toys: []}],
maritalStatus: 'married',
},
] as const satisfies DeepPartial<PersonEntity>[]

export async function seedDatabase(_ctx: TestContext): Promise<void> {
for (const datum of DEFAULT_DATA_SET) {
await PersonEntity.create(datum).save()
}
}

0 comments on commit 702c4c0

Please sign in to comment.