From 61faf94b3445d64ed617531278b9d95c03788f17 Mon Sep 17 00:00:00 2001 From: Mario564 Date: Wed, 2 Oct 2024 12:49:33 -0700 Subject: [PATCH] Add MySQL tests --- .../src/validate-schema/foreign-key.ts | 4 +- drizzle-kit/src/validate-schema/utils.ts | 4 +- drizzle-kit/src/validate-schema/validate.ts | 16 +- .../tests/validate-schema/mysql.test.ts | 496 ++++++++++++++++++ drizzle-kit/tests/validate-schema/pg.test.ts | 10 +- 5 files changed, 516 insertions(+), 14 deletions(-) create mode 100644 drizzle-kit/tests/validate-schema/mysql.test.ts diff --git a/drizzle-kit/src/validate-schema/foreign-key.ts b/drizzle-kit/src/validate-schema/foreign-key.ts index b0ae8eaaf..71654b726 100644 --- a/drizzle-kit/src/validate-schema/foreign-key.ts +++ b/drizzle-kit/src/validate-schema/foreign-key.ts @@ -21,8 +21,8 @@ export class ValidateForeignKey { mismatchingDataTypes(foreignKey: Table['foreignKeys'][number]) { const { columns, foreignColumns } = foreignKey.reference; - const types = `(${columns.map((c) => c.getSQLType()).join(', ')})`; - const foreignTypes = `(${foreignColumns.map((c) => c.getSQLType()).join(', ')})`; + const types = `(${columns.map((c) => c.sqlType).join(', ')})`; + const foreignTypes = `(${foreignColumns.map((c) => c.sqlType).join(', ')})`; if (types !== foreignTypes) { this.errors.push({ diff --git a/drizzle-kit/src/validate-schema/utils.ts b/drizzle-kit/src/validate-schema/utils.ts index 980f7682a..fc785a4e3 100644 --- a/drizzle-kit/src/validate-schema/utils.ts +++ b/drizzle-kit/src/validate-schema/utils.ts @@ -76,7 +76,7 @@ export type Table = { foreignKeys: { name: string; reference: { columns: { name: string; - getSQLType: () => string; + sqlType: string; table: { name: string; schema?: string; @@ -84,7 +84,7 @@ export type Table = { }[]; foreignColumns: { name: string; - getSQLType: () => string; + sqlType: string; table: { name: string; schema?: string; diff --git a/drizzle-kit/src/validate-schema/validate.ts b/drizzle-kit/src/validate-schema/validate.ts index 707cd3432..6cc65ac9a 100644 --- a/drizzle-kit/src/validate-schema/validate.ts +++ b/drizzle-kit/src/validate-schema/validate.ts @@ -121,7 +121,7 @@ export function validatePgSchema( const tableConfig = getPgTableConfig(column.table); return { name: getColumnCasing(column, casing), - getSQLType: column.getSQLType, + sqlType: column.getSQLType(), table: { name: tableConfig.name, schema: tableConfig.schema @@ -134,7 +134,7 @@ export function validatePgSchema( const tableConfig = getPgTableConfig(column.table); return { name: getColumnCasing(column, casing), - getSQLType: column.getSQLType, + sqlType: column.getSQLType(), table: { name: tableConfig.name, schema: tableConfig.schema @@ -328,7 +328,7 @@ export function validateMySqlSchema( const tableConfig = getMySqlTableConfig(column.table); return { name: getColumnCasing(column, casing), - getSQLType: column.getSQLType, + sqlType: column.getSQLType(), table: { name: tableConfig.name, schema: tableConfig.schema @@ -341,7 +341,7 @@ export function validateMySqlSchema( const tableConfig = getMySqlTableConfig(column.table); return { name: getColumnCasing(column, casing), - getSQLType: column.getSQLType, + sqlType: column.getSQLType(), table: { name: tableConfig.name, schema: tableConfig.schema @@ -406,7 +406,8 @@ export function validateMySqlSchema( }; })(); - const v = new ValidateDatabase().validateSchema(undefined); + const vDb = new ValidateDatabase(); + const v = vDb.validateSchema(undefined); v .constraintNameCollisions( @@ -441,6 +442,11 @@ export function validateMySqlSchema( .validatePrimaryKey(primaryKey.name) .columnsMixingTables(primaryKey); } + + return { + messages: vDb.errors, + codes: vDb.errorCodes + }; } export function validateSQLiteSchema( diff --git a/drizzle-kit/tests/validate-schema/mysql.test.ts b/drizzle-kit/tests/validate-schema/mysql.test.ts new file mode 100644 index 000000000..9e2bfcede --- /dev/null +++ b/drizzle-kit/tests/validate-schema/mysql.test.ts @@ -0,0 +1,496 @@ +import { expect, test } from 'vitest'; +import { validateMySqlSchema } from 'src/validate-schema/validate'; +import { foreignKey, index, int, mysqlTable, mysqlView, primaryKey, serial, text, unique } from 'drizzle-orm/mysql-core'; +import { prepareFromExports } from 'src/serializer/mysqlImports'; +import { SchemaValidationErrors as Err } from 'src/validate-schema/errors'; +import { sql } from 'drizzle-orm'; + +test('schema entity name collisions #1', () => { + const schema = { + table1: mysqlTable('test', { + id: serial().primaryKey() + }), + table2: mysqlTable('test', { + id: serial().primaryKey() + }), + }; + + const { tables, views } = prepareFromExports(schema); + + const { messages, codes } = validateMySqlSchema( + undefined, + tables, + views, + ); + + expect(messages).length(1); + expect(codes).contains(Err.SchemaEntityNameCollisions); +}); + +test('schema entity name collisions #2', () => { + const schema = { + table: mysqlTable('test', { + id: serial().primaryKey() + }), + view: mysqlView('test', { + id: serial().primaryKey() + }).as(sql``) + }; + + const { tables, views } = prepareFromExports(schema); + + const { messages, codes } = validateMySqlSchema( + undefined, + tables, + views, + ); + + expect(messages).length(1); + expect(codes).contains(Err.SchemaEntityNameCollisions); +}); + +test('schema constraint name collisions #1', () => { + const schema = { + table1: mysqlTable('test1', { + id: serial().primaryKey(), + name: text('name').notNull() + }, (table) => ({ + idx: index('name_idx').on(table.name), + })), + table2: mysqlTable('test2', { + id: serial().primaryKey(), + name: text('name').notNull() + }, (table) => ({ + idx: index('name_idx').on(table.name), + })), + }; + + const { tables, views } = prepareFromExports(schema); + + const { messages, codes } = validateMySqlSchema( + undefined, + tables, + views + ); + + expect(messages).length(1); + expect(codes).contains(Err.SchemaConstraintNameCollisions); +}); + +test('schema constraint name collisions #2', () => { + const schema = { + table1: mysqlTable('test1', { + id: serial().primaryKey(), + name: text('name').notNull() + }, (table) => ({ + idx: index('test1_name_idx').on(table.name), + })), + table2: mysqlTable('test2', { + id: serial().primaryKey(), + name: text('name').notNull() + }, (table) => ({ + unique: unique('test2_name_idx').on(table.name), + })), + }; + + const { tables, views } = prepareFromExports(schema); + + const { messages, codes } = validateMySqlSchema( + undefined, + tables, + views, + ); + + expect(messages).length(0); + expect(codes).not.contains(Err.SchemaConstraintNameCollisions); +}); + +test('table column name collisions #1', () => { + const schema = { + table: mysqlTable('test', { + id: serial().primaryKey(), + firstName: text('first_name').notNull(), + lastName: text('last_name').notNull() + }) + }; + + const { tables, views } = prepareFromExports(schema); + + const { messages, codes } = validateMySqlSchema( + undefined, + tables, + views, + ); + + expect(messages).length(0); + expect(codes).not.contains(Err.TableColumnNameCollisions); +}); + +test('table column name collisions #2', () => { + const schema = { + table: mysqlTable('test', { + id: serial().primaryKey(), + firstName: text('first_name').notNull(), + lastName: text('first_name').notNull() + }) + }; + + const { tables, views } = prepareFromExports(schema); + + const { messages, codes } = validateMySqlSchema( + undefined, + tables, + views, + ); + + expect(messages).length(1); + expect(codes).contains(Err.TableColumnNameCollisions); +}); + +test('table column name collisions #3', () => { + const schema = { + table: mysqlTable('test', { + id: serial().primaryKey(), + firstName: text('last_name').notNull(), + lastName: text().notNull() + }) + }; + + const { tables, views } = prepareFromExports(schema); + + const { messages, codes } = validateMySqlSchema( + undefined, + tables, + views, + ); + + expect(messages).length(0); + expect(codes).not.contains(Err.TableColumnNameCollisions); +}); + +test('table column name collisions #4', () => { + const schema = { + table: mysqlTable('test', { + id: serial().primaryKey(), + firstName: text('last_name').notNull(), + lastName: text().notNull() + }) + }; + + const { tables, views } = prepareFromExports(schema); + + const { messages, codes } = validateMySqlSchema( + 'snake_case', + tables, + views, + ); + + expect(messages).length(1); + expect(codes).contains(Err.TableColumnNameCollisions); +}); + +test('table column name collisions #5', () => { + const schema = { + table: mysqlTable('test', { + id: serial().primaryKey(), + first_name: text('lastName').notNull(), + last_name: text().notNull() + }) + }; + + const { tables, views } = prepareFromExports(schema); + + const { messages, codes } = validateMySqlSchema( + 'camelCase', + tables, + views, + ); + + expect(messages).length(1); + expect(codes).contains(Err.TableColumnNameCollisions); +}); + +test('foreign key mismatching column count #2', () => { + const table1 = mysqlTable('test1', { + id: serial().primaryKey(), + c1: int().notNull(), + c2: int().notNull() + }, (table) => ({ + fk: foreignKey({ + columns: [table.c1, table.c2], + foreignColumns: [table2.c1, table2.c2], + }) + })); + const table2 = mysqlTable('test2', { + c1: int().notNull(), + c2: int().notNull() + }, (table) => ({ + pk: primaryKey({ + columns: [table.c1, table.c2] + }) + })); + const schema = { + table1, + table2 + }; + + const { tables, views } = prepareFromExports(schema); + + const { messages, codes } = validateMySqlSchema( + undefined, + tables, + views, + ); + + expect(messages).length(0); + expect(codes).not.contains(Err.ForeignKeyMismatchingColumnCount); +}); + +test('foreign key mismatching data types #1', () => { + const table1 = mysqlTable('test1', { + id: serial().primaryKey(), + c1: int().notNull(), + c2: int().notNull() + }, (table) => ({ + fk: foreignKey({ + columns: [table.c1, table.c2], + foreignColumns: [table2.c1, table2.c2], + }) + })); + const table2 = mysqlTable('test2', { + c1: int().notNull(), + c2: text().notNull() + }, (table) => ({ + pk: primaryKey({ + columns: [table.c1, table.c2] + }) + })); + const schema = { + table1, + table2 + }; + + const { tables, views } = prepareFromExports(schema); + + const { messages, codes } = validateMySqlSchema( + undefined, + tables, + views, + ); + + expect(messages).length(1); + expect(codes).contains(Err.ForeignKeyMismatchingDataTypes); +}); + +test('foreign key mismatching data types #2', () => { + const table1 = mysqlTable('test1', { + id: serial().primaryKey(), + c1: int().notNull(), + c2: text().notNull() + }, (table) => ({ + fk: foreignKey({ + columns: [table.c1, table.c2], + foreignColumns: [table2.c1, table2.c2], + }) + })); + const table2 = mysqlTable('test2', { + c1: int().notNull(), + c2: text().notNull() + }, (table) => ({ + pk: primaryKey({ + columns: [table.c1, table.c2] + }) + })); + const schema = { + table1, + table2 + }; + + const { tables, views } = prepareFromExports(schema); + + const { messages, codes } = validateMySqlSchema( + undefined, + tables, + views, + ); + + expect(messages).length(0); + expect(codes).not.contains(Err.ForeignKeyMismatchingDataTypes); +}); + +test('foreign key columns mixing tables #1', () => { + const table1 = mysqlTable('test1', { + id: serial().primaryKey(), + c1: int().notNull(), + c2: text().notNull() + }, (table) => ({ + fk: foreignKey({ + columns: [table.c1, table.c2], + foreignColumns: [table2.c1, table2.c2], + }) + })); + const table2 = mysqlTable('test2', { + c1: int().notNull(), + c2: text().notNull() + }, (table) => ({ + pk: primaryKey({ + columns: [table.c1, table.c2] + }) + })); + const schema = { + table1, + table2 + }; + + const { tables, views } = prepareFromExports(schema); + + const { messages, codes } = validateMySqlSchema( + undefined, + tables, + views, + ); + + expect(messages).length(0); + expect(codes).not.contains(Err.ForeignKeyColumnsMixingTables); + expect(codes).not.contains(Err.ForeignKeyForeignColumnsMixingTables); +}); + +test('foreign key columns mixing tables #2', () => { + const table1 = mysqlTable('test1', { + id: serial().primaryKey(), + c1: int().notNull(), + c2: text().notNull() + }, (table) => ({ + fk: foreignKey({ + columns: [table2.c1, table.c2], + foreignColumns: [table2.c1, table2.c2], + }) + })); + const table2 = mysqlTable('test2', { + c1: int().notNull(), + c2: text().notNull() + }, (table) => ({ + pk: primaryKey({ + columns: [table.c1, table.c2] + }) + })); + const schema = { + table1, + table2 + }; + + const { tables, views } = prepareFromExports(schema); + + const { messages, codes } = validateMySqlSchema( + undefined, + tables, + views, + ); + + expect(messages).length(1); + expect(codes).contains(Err.ForeignKeyColumnsMixingTables); + expect(codes).not.contains(Err.ForeignKeyForeignColumnsMixingTables); +}); + +test('foreign key columns mixing tables #3', () => { + const table1 = mysqlTable('test1', { + id: serial().primaryKey(), + c1: int().notNull(), + c2: text().notNull() + }, (table) => ({ + fk: foreignKey({ + columns: [table.c1, table.c2], + foreignColumns: [table2.c1, table.c2], + }) + })); + const table2 = mysqlTable('test2', { + c1: int().notNull(), + c2: text().notNull() + }, (table) => ({ + pk: primaryKey({ + columns: [table.c1, table.c2] + }) + })); + const schema = { + table1, + table2 + }; + + const { tables, views } = prepareFromExports(schema); + + const { messages, codes } = validateMySqlSchema( + undefined, + tables, + views, + ); + + expect(messages).length(1); + expect(codes).not.contains(Err.ForeignKeyColumnsMixingTables); + expect(codes).contains(Err.ForeignKeyForeignColumnsMixingTables); +}); + +test('foreign key columns mixing tables #4', () => { + const table1 = mysqlTable('test1', { + id: serial().primaryKey(), + c1: int().notNull(), + c2: text().notNull() + }, (table) => ({ + fk: foreignKey({ + columns: [table.c1, table2.c2], + foreignColumns: [table2.c1, table.c2], + }) + })); + const table2 = mysqlTable('test2', { + c1: int().notNull(), + c2: text().notNull() + }, (table) => ({ + pk: primaryKey({ + columns: [table.c1, table.c2] + }) + })); + const schema = { + table1, + table2 + }; + + const { tables, views } = prepareFromExports(schema); + + const { messages, codes } = validateMySqlSchema( + undefined, + tables, + views, + ); + + expect(messages).length(2); + expect(codes).contains(Err.ForeignKeyColumnsMixingTables); + expect(codes).contains(Err.ForeignKeyForeignColumnsMixingTables); +}); + +test('primary key columns mixing tables #1', () => { + const table1 = mysqlTable('test1', { + c1: int().notNull(), + c2: text().notNull() + }, (table) => ({ + fk: primaryKey({ + columns: [table2.c1, table.c2] + }) + })); + const table2 = mysqlTable('test2', { + c1: int().notNull(), + }); + const schema = { + table1, + table2 + }; + + const { tables, views } = prepareFromExports(schema); + + const { messages, codes } = validateMySqlSchema( + undefined, + tables, + views, + ); + + expect(messages).length(1); + expect(codes).contains(Err.PrimaryKeyColumnsMixingTables); +}); \ No newline at end of file diff --git a/drizzle-kit/tests/validate-schema/pg.test.ts b/drizzle-kit/tests/validate-schema/pg.test.ts index 053ccfb6e..790e7f302 100644 --- a/drizzle-kit/tests/validate-schema/pg.test.ts +++ b/drizzle-kit/tests/validate-schema/pg.test.ts @@ -1,5 +1,5 @@ import { expect, test } from 'vitest'; -import { printValidationErrors, validatePgSchema } from 'src/validate-schema/validate'; +import { validatePgSchema } from 'src/validate-schema/validate'; import { foreignKey, index, integer, pgEnum, pgSchema, pgSequence, pgTable, primaryKey, serial, text, unique, vector } from 'drizzle-orm/pg-core'; import { prepareFromExports } from 'src/serializer/pgImports'; import { SchemaValidationErrors as Err } from 'src/validate-schema/errors'; @@ -168,13 +168,13 @@ test('schema constraint name collisions #2', () => { id: serial().primaryKey(), name: text('name').notNull() }, (table) => ({ - idx: index('name_idx').on(table.name), + idx: index('test1_name_idx').on(table.name), })), table2: pgTable('test2', { id: serial().primaryKey(), name: text('name').notNull() }, (table) => ({ - unique: unique('name_idx').on(table.name), + unique: unique('test2_name_idx').on(table.name), })), }; @@ -190,8 +190,8 @@ test('schema constraint name collisions #2', () => { sequences ); - expect(messages).length(1); - expect(codes).contains(Err.SchemaConstraintNameCollisions); + expect(messages).length(0); + expect(codes).not.contains(Err.SchemaConstraintNameCollisions); }); test('enum value collisions #1', () => {