diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 12b95be..bd83200 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,9 +23,6 @@ jobs: - name: Install dependencies run: pnpm i - - name: Typecheck - run: npm run test:types - - name: Lint run: npm run lint diff --git a/package.json b/package.json index bfb9720..4449c04 100644 --- a/package.json +++ b/package.json @@ -1,26 +1,29 @@ { - "name": "drizzle-schema-checker", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "test": "pnpm run vitest" - }, - "keywords": [], - "author": "", - "license": "ISC", - "devDependencies": { - "@biomejs/biome": "^1.9.0", - "db0": "^0.1.4", - "tsup": "^8.2.4", - "vitest": "^2.1.1" - }, - "dependencies": { - "@libsql/client": "^0.11.0", - "better-sqlite3": "^11.3.0", - "consola": "^3.2.3", - "drizzle-orm": "^0.33.0", - "zod": "^3.23.8" - }, - "packageManager": "pnpm@9.10.0" + "name": "drizzle-schema-checker", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "pnpm run vitest", + "test:ci": "pnpm test -- --coverage --reporter=default --reporter=junit && pnpm test:bun", + "lint": "biome lint", + "format": "biome format --write" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "@biomejs/biome": "^1.9.0", + "db0": "^0.1.4", + "tsup": "^8.2.4", + "vitest": "^2.1.1" + }, + "dependencies": { + "@libsql/client": "^0.11.0", + "better-sqlite3": "^11.3.0", + "consola": "^3.2.3", + "drizzle-orm": "^0.33.0", + "zod": "^3.23.8" + }, + "packageManager": "pnpm@9.10.0" } diff --git a/src/index.ts b/src/index.ts index 1723be7..9d1cf23 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,96 +3,103 @@ import z from "zod"; import consola from "consola"; import { SqliteTableChecker } from "./lib/sqlite-table-checker"; -export type supportedConnectors = Extract; -const CONNECTOR_NAME = ["sqlite", "libsql", "bun-sqlite"] as const satisfies supportedConnectors[]; +export type supportedConnectors = Extract< + ConnectorName, + "sqlite" | "libsql" | "bun-sqlite" +>; +const CONNECTOR_NAME = [ + "sqlite", + "libsql", + "bun-sqlite", +] as const satisfies supportedConnectors[]; const DatabaseSchema = z.object({ - exec: z.function(), - prepare: z.function(), - sql: z.function(), + exec: z.function(), + prepare: z.function(), + sql: z.function(), }); const TableNamesSchema = z.object({ - users: z.string().min(1), - sessions: z.string().min(1), + users: z.string().min(1), + sessions: z.string().min(1), }); export interface tableNames { - users: string - sessions: string - oauthAccounts: string + users: string; + sessions: string; + oauthAccounts: string; } export function checkDatabaseValidity( - db: unknown, - tableNames: unknown, + db: unknown, + tableNames: unknown, ): Database { - if (!db) { - throw new Error("No database to check, please provide one"); - } - - if (!tableNames) { - throw new Error( - "No tableNames provided for SlipAuth: { users: string, sessions: string, oauthAccounts: string }", - ); - } - - const { success: tableNamesSuccess } = TableNamesSchema.safeParse(tableNames); - - if (!tableNamesSuccess) { - throw new Error( - "tableNames provided for SlipAuth are incorrect, { users: string, sessions: string }", - ); - } - - const { data: validatedDatabase, success: databaseValidity } - = DatabaseSchema.safeParse(db); - if (!databaseValidity) { - throw new Error( - "The provided database is not a valid db0 database, see https://github.com/unjs/db0", - ); - } - - return validatedDatabase as Database; + if (!db) { + throw new Error("No database to check, please provide one"); + } + + if (!tableNames) { + throw new Error( + "No tableNames provided for SlipAuth: { users: string, sessions: string, oauthAccounts: string }", + ); + } + + const { success: tableNamesSuccess } = TableNamesSchema.safeParse(tableNames); + + if (!tableNamesSuccess) { + throw new Error( + "tableNames provided for SlipAuth are incorrect, { users: string, sessions: string }", + ); + } + + const { data: validatedDatabase, success: databaseValidity } = + DatabaseSchema.safeParse(db); + if (!databaseValidity) { + throw new Error( + "The provided database is not a valid db0 database, see https://github.com/unjs/db0", + ); + } + + return validatedDatabase as Database; } export async function checkDbAndTables( - _database: Database, - connectorType: supportedConnectors, - tableNames: tableNames, + _database: Database, + connectorType: supportedConnectors, + tableNames: tableNames, ): Promise { - const database = checkDatabaseValidity(_database, tableNames); - - let tableChecker: SqliteTableChecker; - - switch (connectorType) { - case "sqlite": - tableChecker = new SqliteTableChecker(database, tableNames); - break; - case "libsql": - tableChecker = new SqliteTableChecker(database, tableNames); - break; - case "bun-sqlite": - tableChecker = new SqliteTableChecker(database, tableNames); - break; - default: - throw new Error( - `Invalid enum value. Expected ${CONNECTOR_NAME.map(name => `'${name}'`).join(" | ")}, received '${connectorType}'`, - ); - } - - const isUserTableOk = await tableChecker.checkUserTable(); - consola.success(`Table "${tableNames.users}" exists and has a valid schema`); - - const isSessionTableOk = await tableChecker.checkSessionTable(); - consola.success( - `Table "${tableNames.sessions}" exists and has a valid schema`, - ); - - const isOauthTableOk = await tableChecker.checkOauthAccountTable(); - consola.success( - `Table "${tableNames.oauthAccounts}" exists and has a valid schema`, - ); - - return isUserTableOk && isSessionTableOk && isOauthTableOk; + const database = checkDatabaseValidity(_database, tableNames); + + let tableChecker: SqliteTableChecker; + + switch (connectorType) { + case "sqlite": + tableChecker = new SqliteTableChecker(database, tableNames); + break; + case "libsql": + tableChecker = new SqliteTableChecker(database, tableNames); + break; + case "bun-sqlite": + tableChecker = new SqliteTableChecker(database, tableNames); + break; + default: + throw new Error( + `Invalid enum value. Expected ${CONNECTOR_NAME.map((name) => `'${name}'`).join(" | ")}, received '${connectorType}'`, + ); + } + + const isUserTableOk = await tableChecker.checkUserTable(); + consola.success(`Table "${tableNames.users}" exists and has a valid schema`); + + const isSessionTableOk = await tableChecker.checkSessionTable(); + consola.success( + `Table "${tableNames.sessions}" exists and has a valid schema`, + ); + + const isOauthTableOk = await tableChecker.checkOauthAccountTable(); + consola.success( + `Table "${tableNames.oauthAccounts}" exists and has a valid schema`, + ); + + return isUserTableOk && isSessionTableOk && isOauthTableOk; } diff --git a/src/lib/schema.ts b/src/lib/schema.ts index cbcc068..690b2f3 100644 --- a/src/lib/schema.ts +++ b/src/lib/schema.ts @@ -1,38 +1,57 @@ -import { sqliteTable, text, integer, primaryKey } from "drizzle-orm/sqlite-core"; +import { + sqliteTable, + text, + integer, + primaryKey, +} from "drizzle-orm/sqlite-core"; import type { tableNames } from ".."; import { sql } from "drizzle-orm"; const datesColumns = { - created_at: integer("created_at", { mode: "timestamp" }).notNull().default(sql`CURRENT_TIMESTAMP`), - update_at: integer("updated_at", { mode: "timestamp" }).notNull().default(sql`CURRENT_TIMESTAMP`), + created_at: integer("created_at", { mode: "timestamp" }) + .notNull() + .default(sql`CURRENT_TIMESTAMP`), + update_at: integer("updated_at", { mode: "timestamp" }) + .notNull() + .default(sql`CURRENT_TIMESTAMP`), }; -export const getUsersTableSchema = (tableNames: tableNames) => sqliteTable(tableNames.users, { - id: text("id").primaryKey().notNull(), - password: text("password"), - email: text("email").notNull().unique(), - ...datesColumns, -}); +export const getUsersTableSchema = (tableNames: tableNames) => + sqliteTable(tableNames.users, { + id: text("id").primaryKey().notNull(), + password: text("password"), + email: text("email").notNull().unique(), + ...datesColumns, + }); -export const getSessionsTableSchema = (tableNames: tableNames) => sqliteTable(tableNames.sessions, { - id: text("id").primaryKey().notNull(), - expires_at: integer("expires_at").notNull(), - ip: text("ip"), - ua: text("ua"), - user_id: text("user_id") - .references(() => getUsersTableSchema(tableNames).id) - .notNull(), - ...datesColumns, -}); +export const getSessionsTableSchema = (tableNames: tableNames) => + sqliteTable(tableNames.sessions, { + id: text("id").primaryKey().notNull(), + expires_at: integer("expires_at").notNull(), + ip: text("ip"), + ua: text("ua"), + user_id: text("user_id") + .references(() => getUsersTableSchema(tableNames).id) + .notNull(), + ...datesColumns, + }); // https://lucia-auth.com/guides/oauth/multiple-providers -export const getOAuthAccountsTableSchema = (tableNames: tableNames) => sqliteTable(tableNames.oauthAccounts, { - provider_id: text("provider_id").notNull(), - provider_user_id: text("provider_user_id").notNull(), - user_id: text("user_id") - .references(() => getUsersTableSchema(tableNames).id) - .notNull(), - ...datesColumns, -}, slipAuthOAuthAccounts => ({ - pk: primaryKey(slipAuthOAuthAccounts.provider_id, slipAuthOAuthAccounts.provider_user_id), -})); +export const getOAuthAccountsTableSchema = (tableNames: tableNames) => + sqliteTable( + tableNames.oauthAccounts, + { + provider_id: text("provider_id").notNull(), + provider_user_id: text("provider_user_id").notNull(), + user_id: text("user_id") + .references(() => getUsersTableSchema(tableNames).id) + .notNull(), + ...datesColumns, + }, + (slipAuthOAuthAccounts) => ({ + pk: primaryKey( + slipAuthOAuthAccounts.provider_id, + slipAuthOAuthAccounts.provider_user_id, + ), + }), + ); diff --git a/src/lib/sqlite-table-checker.ts b/src/lib/sqlite-table-checker.ts index af6beb5..03b157f 100644 --- a/src/lib/sqlite-table-checker.ts +++ b/src/lib/sqlite-table-checker.ts @@ -1,150 +1,234 @@ -import { type PrimaryKey, getTableConfig, type SQLiteColumn, type ForeignKey } from "drizzle-orm/sqlite-core"; +import { + type PrimaryKey, + getTableConfig, + type SQLiteColumn, + type ForeignKey, +} from "drizzle-orm/sqlite-core"; import { getTableName } from "drizzle-orm"; import type { Database } from "db0"; import { z } from "zod"; import { TableChecker } from "./table-checker"; -import { getUsersTableSchema, getOAuthAccountsTableSchema, getSessionsTableSchema } from "./schema"; +import { + getUsersTableSchema, + getOAuthAccountsTableSchema, + getSessionsTableSchema, +} from "./schema"; // #region HELPERS const sqliteTableInfoRowSchema = z.object({ - cid: z.number(), - name: z.string(), - type: z.string(), - notnull: z.number(), - dflt_value: z.any(), - pk: z.number(), + cid: z.number(), + name: z.string(), + type: z.string(), + notnull: z.number(), + dflt_value: z.any(), + pk: z.number(), }); const sqliteDrizzleColumnTypeMapping = { - SQLiteText: "TEXT", - SQLiteInteger: "INTEGER", - SQLiteTimestamp: "TIMESTAMP", + SQLiteText: "TEXT", + SQLiteInteger: "INTEGER", + SQLiteTimestamp: "TIMESTAMP", }; function getSQLiteColumType(drizzleColumnType: string) { - return sqliteDrizzleColumnTypeMapping[drizzleColumnType as keyof typeof sqliteDrizzleColumnTypeMapping] || drizzleColumnType; + return ( + sqliteDrizzleColumnTypeMapping[ + drizzleColumnType as keyof typeof sqliteDrizzleColumnTypeMapping + ] || drizzleColumnType + ); } function createSQLiteTableExistSchema(tableName: string) { - return z - .array(sqliteTableInfoRowSchema) - .min(1, `${tableName} table for SLIP does not exist`); + return z + .array(sqliteTableInfoRowSchema) + .min(1, `${tableName} table for SLIP does not exist`); } -const findColumnInSQLiteTableInfo = (source: T[], columnName: string) => { - return source.find(columnFromSQLite => columnFromSQLite.name === columnName); +const findColumnInSQLiteTableInfo = ( + source: T[], + columnName: string, +) => { + return source.find( + (columnFromSQLite) => columnFromSQLite.name === columnName, + ); }; -const findColumnInSQLiteTableForeignKeys = (source: T[], columnName: string) => { - return source.find(columnFromSQLite => columnFromSQLite.from === columnName); +const findColumnInSQLiteTableForeignKeys = ( + source: T[], + columnName: string, +) => { + return source.find( + (columnFromSQLite) => columnFromSQLite.from === columnName, + ); }; // #endregion -async function validateDabaseWithSchema(db: Database, tableName: string, drizzleTableInfos: { columns: SQLiteColumn[], primaryKeys: PrimaryKey[], foreignKeys: ForeignKey[] }): Promise { - const maybeTableInfo = await db.prepare(`PRAGMA table_info(${tableName})`).all(); - const { success, error, data: tableInfo } = createSQLiteTableExistSchema(tableName).safeParse(maybeTableInfo); - - if (!success) { - throw new Error(error.errors[0].message); - }; - - // Check if all columns from schema exist in SQLite table - for (const columnFromSchema of drizzleTableInfos.columns) { - const correspondingColumn = findColumnInSQLiteTableInfo(tableInfo, columnFromSchema.name); - - if (!correspondingColumn) { - return `${tableName} table must contain a column with name "${columnFromSchema.name}"`; - } - - if (correspondingColumn.type !== getSQLiteColumType(columnFromSchema.columnType)) { - return `${tableName} table must contain a column "${columnFromSchema.name}" with type "${getSQLiteColumType(columnFromSchema.columnType)}"`; - } - - const primaryKeysColumnsNames = drizzleTableInfos.primaryKeys.at(0)?.columns.map(col => col.name); - if ((columnFromSchema.primary || primaryKeysColumnsNames?.includes(columnFromSchema.name)) && correspondingColumn.pk < 1) { - return `${tableName} table must contain a column "${columnFromSchema.name}" as primary key`; - } - - if (columnFromSchema.notNull && correspondingColumn.notnull !== 1) { - return `${tableName} table must contain a column "${columnFromSchema.name}" not nullable`; - } - - const indexesInTableSQLite = (await db - .prepare(`PRAGMA INDEX_LIST(${tableName})`) - .all()) as Array<{ name: string, origin: string, unique: number }>; - - const uniqueIndexesSQLite = await Promise.all(indexesInTableSQLite.filter((indexData) => { - return indexData.origin === "u" && indexData.unique === 1; - }).map((uniqueIndex) => { - return db - .prepare(`PRAGMA index_info(${uniqueIndex.name})`) - .all() as Promise>; - })); - - if (columnFromSchema.isUnique && uniqueIndexesSQLite.find(uniqueIndex => columnFromSchema.name === uniqueIndex.at(0)?.name) === undefined) { - return `${tableName} table must contain a column "${columnFromSchema.name}" unique`; - } - - const defaultData = columnFromSchema.default ? columnFromSchema.default as { queryChunks?: Array<{ value?: string[] }> } : null; - const defaultValue = defaultData?.queryChunks?.at(0)?.value?.at(0); - - if (Boolean(columnFromSchema.hasDefault && defaultValue) && (!correspondingColumn.dflt_value || correspondingColumn.dflt_value !== defaultValue)) { - return `${tableName} table must contain a column with name "${columnFromSchema.name}" with default value of ${defaultValue}`; - } - } - - const foreignKeysTable = drizzleTableInfos.foreignKeys; - const foreignKeysSQLite = (await db - .prepare(`PRAGMA foreign_key_list(${tableName})`) - .all()) as Array<{ table: string, from: string, to: string, name: string }>; - - for (const foreignKeyData of foreignKeysTable) { - const reference = foreignKeyData.reference(); - - for (const foreignKeyColumn of reference.columns) { - const fcorrespondingColumn = findColumnInSQLiteTableForeignKeys(foreignKeysSQLite, foreignKeyColumn.name); - - if (!fcorrespondingColumn) { - return `${tableName} table should have a foreign key "${foreignKeyColumn.name}"`; - } - - const targetTableName = getTableName(reference.foreignTable); - const targetColumnName = reference.foreignColumns[0].name; - if (fcorrespondingColumn.table !== targetTableName || fcorrespondingColumn.to !== targetColumnName) { - return `foreign key "${fcorrespondingColumn.from}" in ${tableName} table should target "${targetColumnName}" column from the "${targetTableName}" table`; - } - } - } - - return null; +async function validateDabaseWithSchema( + db: Database, + tableName: string, + drizzleTableInfos: { + columns: SQLiteColumn[]; + primaryKeys: PrimaryKey[]; + foreignKeys: ForeignKey[]; + }, +): Promise { + const maybeTableInfo = await db + .prepare(`PRAGMA table_info(${tableName})`) + .all(); + const { + success, + error, + data: tableInfo, + } = createSQLiteTableExistSchema(tableName).safeParse(maybeTableInfo); + + if (!success) { + throw new Error(error.errors[0].message); + } + + // Check if all columns from schema exist in SQLite table + for (const columnFromSchema of drizzleTableInfos.columns) { + const correspondingColumn = findColumnInSQLiteTableInfo( + tableInfo, + columnFromSchema.name, + ); + + if (!correspondingColumn) { + return `${tableName} table must contain a column with name "${columnFromSchema.name}"`; + } + + if ( + correspondingColumn.type !== + getSQLiteColumType(columnFromSchema.columnType) + ) { + return `${tableName} table must contain a column "${columnFromSchema.name}" with type "${getSQLiteColumType(columnFromSchema.columnType)}"`; + } + + const primaryKeysColumnsNames = drizzleTableInfos.primaryKeys + .at(0) + ?.columns.map((col) => col.name); + if ( + (columnFromSchema.primary || + primaryKeysColumnsNames?.includes(columnFromSchema.name)) && + correspondingColumn.pk < 1 + ) { + return `${tableName} table must contain a column "${columnFromSchema.name}" as primary key`; + } + + if (columnFromSchema.notNull && correspondingColumn.notnull !== 1) { + return `${tableName} table must contain a column "${columnFromSchema.name}" not nullable`; + } + + const indexesInTableSQLite = (await db + .prepare(`PRAGMA INDEX_LIST(${tableName})`) + .all()) as Array<{ name: string; origin: string; unique: number }>; + + const uniqueIndexesSQLite = await Promise.all( + indexesInTableSQLite + .filter((indexData) => { + return indexData.origin === "u" && indexData.unique === 1; + }) + .map((uniqueIndex) => { + return db + .prepare(`PRAGMA index_info(${uniqueIndex.name})`) + .all() as Promise>; + }), + ); + + if ( + columnFromSchema.isUnique && + uniqueIndexesSQLite.find( + (uniqueIndex) => columnFromSchema.name === uniqueIndex.at(0)?.name, + ) === undefined + ) { + return `${tableName} table must contain a column "${columnFromSchema.name}" unique`; + } + + const defaultData = columnFromSchema.default + ? (columnFromSchema.default as { + queryChunks?: Array<{ value?: string[] }>; + }) + : null; + const defaultValue = defaultData?.queryChunks?.at(0)?.value?.at(0); + + if ( + Boolean(columnFromSchema.hasDefault && defaultValue) && + (!correspondingColumn.dflt_value || + correspondingColumn.dflt_value !== defaultValue) + ) { + return `${tableName} table must contain a column with name "${columnFromSchema.name}" with default value of ${defaultValue}`; + } + } + + const foreignKeysTable = drizzleTableInfos.foreignKeys; + const foreignKeysSQLite = (await db + .prepare(`PRAGMA foreign_key_list(${tableName})`) + .all()) as Array<{ table: string; from: string; to: string; name: string }>; + + for (const foreignKeyData of foreignKeysTable) { + const reference = foreignKeyData.reference(); + + for (const foreignKeyColumn of reference.columns) { + const fcorrespondingColumn = findColumnInSQLiteTableForeignKeys( + foreignKeysSQLite, + foreignKeyColumn.name, + ); + + if (!fcorrespondingColumn) { + return `${tableName} table should have a foreign key "${foreignKeyColumn.name}"`; + } + + const targetTableName = getTableName(reference.foreignTable); + const targetColumnName = reference.foreignColumns[0].name; + if ( + fcorrespondingColumn.table !== targetTableName || + fcorrespondingColumn.to !== targetColumnName + ) { + return `foreign key "${fcorrespondingColumn.from}" in ${tableName} table should target "${targetColumnName}" column from the "${targetTableName}" table`; + } + } + } + + return null; } export class SqliteTableChecker extends TableChecker { - override async checkUserTable() { - const error = await validateDabaseWithSchema(this.dbClient, this.tableNames.users, getTableConfig(getUsersTableSchema(this.tableNames))); - - if (error) { - throw new Error(error); - } - - return true; - } - - override async checkSessionTable() { - const error = await validateDabaseWithSchema(this.dbClient, this.tableNames.sessions, getTableConfig(getSessionsTableSchema(this.tableNames))); - - if (error) { - throw new Error(error); - } - - return true; - } - - override async checkOauthAccountTable() { - const error = await validateDabaseWithSchema(this.dbClient, this.tableNames.oauthAccounts, getTableConfig(getOAuthAccountsTableSchema(this.tableNames))); - - if (error) { - throw new Error(error); - } - - return true; - } + override async checkUserTable() { + const error = await validateDabaseWithSchema( + this.dbClient, + this.tableNames.users, + getTableConfig(getUsersTableSchema(this.tableNames)), + ); + + if (error) { + throw new Error(error); + } + + return true; + } + + override async checkSessionTable() { + const error = await validateDabaseWithSchema( + this.dbClient, + this.tableNames.sessions, + getTableConfig(getSessionsTableSchema(this.tableNames)), + ); + + if (error) { + throw new Error(error); + } + + return true; + } + + override async checkOauthAccountTable() { + const error = await validateDabaseWithSchema( + this.dbClient, + this.tableNames.oauthAccounts, + getTableConfig(getOAuthAccountsTableSchema(this.tableNames)), + ); + + if (error) { + throw new Error(error); + } + + return true; + } } diff --git a/src/lib/table-checker.ts b/src/lib/table-checker.ts index c519218..dfbd388 100644 --- a/src/lib/table-checker.ts +++ b/src/lib/table-checker.ts @@ -2,23 +2,23 @@ import type { Database } from "db0"; import type { tableNames } from ".."; export class TableChecker { - dbClient: Database; - tableNames: tableNames; + dbClient: Database; + tableNames: tableNames; - constructor(dbClient: Database, tableNames: tableNames) { - this.dbClient = dbClient; - this.tableNames = tableNames; - } + constructor(dbClient: Database, tableNames: tableNames) { + this.dbClient = dbClient; + this.tableNames = tableNames; + } - async checkUserTable(): Promise { - throw new Error("checkUserTable not implemented"); - } + async checkUserTable(): Promise { + throw new Error("checkUserTable not implemented"); + } - async checkSessionTable(): Promise { - throw new Error("checkSessionTable not implemented"); - } + async checkSessionTable(): Promise { + throw new Error("checkSessionTable not implemented"); + } - async checkOauthAccountTable(): Promise { - throw new Error("checkOauthAccountTable not implemented"); - } + async checkOauthAccountTable(): Promise { + throw new Error("checkOauthAccountTable not implemented"); + } } diff --git a/tests/database.test.ts b/tests/database.test.ts index b07b985..8851495 100644 --- a/tests/database.test.ts +++ b/tests/database.test.ts @@ -1,79 +1,84 @@ import { describe, it, expect } from "vitest"; import sqlite from "db0/connectors/better-sqlite3"; import { createDatabase } from "db0"; -import { checkDatabaseValidity, checkDbAndTables } from "../../src/runtime/database"; +import { + checkDatabaseValidity, + checkDbAndTables, +} from "../../src/runtime/database"; describe("checkDatabaseValidity", () => { - it("should throw an error when no arguments are provided", () => { - // @ts-expect-error testing no db - expect(() => checkDatabaseValidity()).toThrow( - "No database to check, please provide one", - ); - }); + it("should throw an error when no arguments are provided", () => { + // @ts-expect-error testing no db + expect(() => checkDatabaseValidity()).toThrow( + "No database to check, please provide one", + ); + }); - it("should throw an error when an invalid database is provided", () => { - const invalidDatabase = {}; - expect(() => - checkDatabaseValidity(invalidDatabase, { - users: "slip_users", - sessions: "slip_sessions", - }), - ).toThrowError( - "The provided database is not a valid db0 database, see https://github.com/unjs/db0", - ); - }); + it("should throw an error when an invalid database is provided", () => { + const invalidDatabase = {}; + expect(() => + checkDatabaseValidity(invalidDatabase, { + users: "slip_users", + sessions: "slip_sessions", + }), + ).toThrowError( + "The provided database is not a valid db0 database, see https://github.com/unjs/db0", + ); + }); - it("should throw an error when tableNames are missing", () => { - // @ts-expect-error testing no table names - expect(() => checkDatabaseValidity({})).toThrowError( - "No tableNames provided for SlipAuth: { users: string, sessions: string, oauthAccounts: string }", - ); - }); + it("should throw an error when tableNames are missing", () => { + // @ts-expect-error testing no table names + expect(() => checkDatabaseValidity({})).toThrowError( + "No tableNames provided for SlipAuth: { users: string, sessions: string, oauthAccounts: string }", + ); + }); - it("should throw an error when tableNames are missing users table", () => { - expect(() => - checkDatabaseValidity({}, { sessions: "slip_sessions" }), - ).toThrowError( - "tableNames provided for SlipAuth are incorrect, { users: string, sessions: string }", - ); - }); + it("should throw an error when tableNames are missing users table", () => { + expect(() => + checkDatabaseValidity({}, { sessions: "slip_sessions" }), + ).toThrowError( + "tableNames provided for SlipAuth are incorrect, { users: string, sessions: string }", + ); + }); - it("should throw an error when tableNames are missing sessions table", () => { - expect(() => - checkDatabaseValidity({}, { users: "users_sessions" }), - ).toThrowError( - "tableNames provided for SlipAuth are incorrect, { users: string, sessions: string }", - ); - }); + it("should throw an error when tableNames are missing sessions table", () => { + expect(() => + checkDatabaseValidity({}, { users: "users_sessions" }), + ).toThrowError( + "tableNames provided for SlipAuth are incorrect, { users: string, sessions: string }", + ); + }); - it("should throw an error when tableNames are from an incorrect type", () => { - expect(() => checkDatabaseValidity({}, new Date())).toThrowError( - "tableNames provided for SlipAuth are incorrect, { users: string, sessions: string }", - ); - }); + it("should throw an error when tableNames are from an incorrect type", () => { + expect(() => checkDatabaseValidity({}, new Date())).toThrowError( + "tableNames provided for SlipAuth are incorrect, { users: string, sessions: string }", + ); + }); - it("should throw an error when tableNames have an empty value for a valid key", () => { - expect(() => - checkDatabaseValidity({}, { users: "users_sessions", sessions: "" }), - ).toThrowError( - "tableNames provided for SlipAuth are incorrect, { users: string, sessions: string }", - ); - }); + it("should throw an error when tableNames have an empty value for a valid key", () => { + expect(() => + checkDatabaseValidity({}, { users: "users_sessions", sessions: "" }), + ).toThrowError( + "tableNames provided for SlipAuth are incorrect, { users: string, sessions: string }", + ); + }); }); describe("checkAndCreateDb", () => { - it("should throw an error when unsupported connector are provided", async () => { - const db = createDatabase(sqlite({ - name: "database.test", - })); - await expect( - // @ts-expect-error testing unsupported connector - checkDbAndTables(db, "notsupported", { - users: "slip_users", - sessions: "slip_sessions", - }), - ).rejects.toThrowError( - "Invalid enum value. Expected 'sqlite' | 'libsql' | 'bun-sqlite', received 'notsupported'", - ); - }); + it("should throw an error when unsupported connector are provided", async () => { + const db = createDatabase( + sqlite({ + name: "database.test", + }), + ); + await expect( + // @ts-expect-error testing unsupported connector + checkDbAndTables(db, "notsupported", { + users: "slip_users", + sessions: "slip_sessions", + }), + ).rejects.toThrowError( + "Invalid enum value. Expected 'sqlite' | 'libsql' | 'bun-sqlite', received 'notsupported'", + ); + }); }); diff --git a/tests/libsql.test.ts b/tests/libsql.test.ts index 1e270c0..ab6b9f7 100644 --- a/tests/libsql.test.ts +++ b/tests/libsql.test.ts @@ -4,430 +4,436 @@ import { createDatabase } from "db0"; import { checkDbAndTables } from "../../src/runtime/database"; function testFunction() { - return checkDbAndTables(db, "libsql", { - users: "slip_users", - sessions: "slip_sessions", - oauthAccounts: "slip_oauth_accounts", - }); + return checkDbAndTables(db, "libsql", { + users: "slip_users", + sessions: "slip_sessions", + oauthAccounts: "slip_oauth_accounts", + }); } -const db = createDatabase(libSql({ - url: "file:.data/libsql.test.db" }, -)); +const db = createDatabase( + libSql({ + url: "file:.data/libsql.test.db", + }), +); beforeEach(async () => { - await db.sql`DROP TABLE IF EXISTS slip_users`; - await db.sql`DROP TABLE IF EXISTS slip_sessions`; - await db.sql`DROP TABLE IF EXISTS slip_oauth_accounts`; + await db.sql`DROP TABLE IF EXISTS slip_users`; + await db.sql`DROP TABLE IF EXISTS slip_sessions`; + await db.sql`DROP TABLE IF EXISTS slip_oauth_accounts`; }); describe("sqlite connector", () => { - describe("users table", () => { - describe("id field", () => { - it("should throw an error when users table does not exist in database", async () => { - await expect(testFunction()).rejects.toThrowError("slip_users table for SLIP does not exist"); - }); - - it("should throw an error when users table does not have an id field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("notid" TEXT PRIMARY KEY)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column with name \"id\"", - ); - }); - - it("should throw an error when users table does not have an id field as primary key", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column \"id\" as primary key", - ); - }); - - it("should throw an error when users table does not have an id field with type of text", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" INTEGER PRIMARY KEY)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column \"id\" with type \"TEXT\"", - ); - }); - - it("should throw an error when users table does not have a not nullable id field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT PRIMARY KEY, "email" TEXT)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column \"id\" not nullable", - ); - }); - }); - - describe("password field", () => { - it("should throw an error when users table does not have a password field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column with name \"password\"", - ); - }); - - it("should throw an error when users table does not have an email field with type of text", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "password" INTEGER)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column \"password\" with type \"TEXT\"", - ); - }); - }); - - describe("email field", () => { - it("should throw an error when users table does not have an email field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "password" TEXT)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column with name \"email\"", - ); - }); - - it("should throw an error when users table does not have an email field with type of text", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "password" TEXT, "email" INTEGER)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column \"email\" with type \"TEXT\"", - ); - }); - - it("should throw an error when users table does not have an not nullable email field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "password" TEXT, "email" TEXT)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column \"email\" not nullable", - ); - }); - - it("should throw an error when users table does not have a unique email field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "password" TEXT, "email" TEXT NOT NULL)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column \"email\" unique", - ); - }); - }); - - describe("created_at field", () => { - it("should throw an error when users table does not have an created_at field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column with name \"created_at\"", - ); - }); - - it("should throw an error when users table does not have an created_at field with type of text", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" INTEGER)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column \"created_at\" with type \"TIMESTAMP\"", - ); - }); - - it("should throw an error when users table does not have an not nullable created_at field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column \"created_at\" not nullable", - ); - }); - - it("should throw an error when users table does not have a default value of CURRENT_TIMESTAMP at created_at field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column with name \"created_at\" with default value of CURRENT_TIMESTAMP", - ); - }); - }); - - describe("updated_at field", () => { - it("should throw an error when users table does not have an updated_at field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column with name \"updated_at\"", - ); - }); - - it("should throw an error when users table does not have an updated_at field with type of text", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" INTEGER)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column \"updated_at\" with type \"TIMESTAMP\"", - ); - }); - - it("should throw an error when users table does not have an not nullable updated_at field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column \"updated_at\" not nullable", - ); - }); - - it("should throw an error when users table does not have a default value of CURRENT_TIMESTAMP at updated_at field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column with name \"updated_at\" with default value of CURRENT_TIMESTAMP", - ); - }); - }); - }); - - describe("sessions table", () => { - const validUsersTableSetup = () => - db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP)`; - - beforeEach(async () => { - await validUsersTableSetup(); - }); - - describe("id field", () => { - it("should throw an error when sessions table does not exist in database", async () => { - await expect(testFunction()).rejects.toThrowError("slip_sessions table for SLIP does not exist"); - }); - - it("should throw an error when sessions table does not have an id field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("notid" TEXT)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column with name \"id\"", - ); - }); - - it("should throw an error when sessions table does not have an id field as primary key", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column \"id\" as primary key", - ); - }); - - it("should throw an error when sessions table does not have an id field with type of text", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" INTEGER PRIMARY KEY)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column \"id\" with type \"TEXT\"", - ); - }); - - it("should throw an error when sessions table does not have a not nullable id field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT PRIMARY KEY)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column \"id\" not nullable", - ); - }); - }); - - describe("expires_at field", () => { - it("should throw an error when sessions table does not have an expires_at field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column with name \"expires_at\"", - ); - }); - - it("should throw an error when sessions table does not have an expires_at field with type of number", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" DATE)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column \"expires_at\" with type \"INTEGER\"", - ); - }); - - it("should throw an error when sessions table does not have an not nullable expires_at field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column \"expires_at\" not nullable", - ); - }); - }); - - describe("ip field", () => { - it("should throw an error when sessions table does not have an ip field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column with name \"ip\"", - ); - }); - - it("should throw an error when sessions table does not have an ip field with type of text", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "ip" INTEGER)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column \"ip\" with type \"TEXT\"", - ); - }); - }); - - describe("ua field", () => { - it("should throw an error when sessions table does not have an ua field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "ip" TEXT)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column with name \"ua\"", - ); - }); - - it("should throw an error when sessions table does not have an ua field with type of text", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "ip" TEXT, "ua" INTEGER)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column \"ua\" with type \"TEXT\"", - ); - }); - }); - - describe("user_id field", () => { - it("should throw an error when sessions table does not have a user_id foreign key", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "ip" TEXT, "ua" TEXT)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table should have a foreign key \"user_id\"", - ); - }); - - it("should throw an error when sessions table does not have an user_id field with type of text", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "ip" TEXT, "ua" TEXT, "user_id" BLOB)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column \"user_id\" with type \"TEXT\"", - ); - }); - - it("should throw an error when sessions table does not have an not nullable user_id field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "ip" TEXT, "ua" TEXT, "user_id" TEXT)`; - - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column \"user_id\" not nullable", - ); - }); - - it("should throw an error when sessions table does not have a user_id foreign key", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "ip" TEXT, "ua" TEXT)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table should have a foreign key \"user_id\"", - ); - }); - - it("should throw an error when sessions table does not have a user_id foreign key to user table", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS othertable ("id" TEXT)`; - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "ip" TEXT, "ua" TEXT, FOREIGN KEY (user_id) REFERENCES othertable(id))`; - await expect(testFunction()).rejects.toThrowError( - "foreign key \"user_id\" in slip_sessions table should target \"id\" column from the \"slip_users\" table", - ); - }); - - it("should throw an error when sessions table does not have a user_id foreign key to user table \"id\" column", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "ip" TEXT, "ua" TEXT, FOREIGN KEY (user_id) REFERENCES slip_users(email))`; - await expect(testFunction()).rejects.toThrowError( - "foreign key \"user_id\" in slip_sessions table should target \"id\" column from the \"slip_users\" table", - ); - }); - }); - }); - - describe("slip_oauth_accounts table", () => { - const validUsersTableSetup = () => - db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP)`; - const validSessionsTableSetup = () => - db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "ip" TEXT, "ua" TEXT, FOREIGN KEY (user_id) REFERENCES slip_users(id))`; - - beforeEach(async () => { - await validUsersTableSetup(); - await validSessionsTableSetup(); - }); - - describe("provider_id field", () => { - it("should throw an error when oauth table does not exist in database", async () => { - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table for SLIP does not exist", - ); - }); - - it("should throw an error when oauth table does not have an provider_id field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("notid" TEXT)`; - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table must contain a column with name \"provider_id\"", - ); - }); - - it("should throw an error when oauth table does not have an provider_id field as primary key", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT)`; - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table must contain a column \"provider_id\" as primary key", - ); - }); - - it("should throw an error when oauth table does not have an provider_id field with type of text", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" INTEGER PRIMARY KEY)`; - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table must contain a column \"provider_id\" with type \"TEXT\"", - ); - }); - - it("should throw an error when oauth table does not have a not nullable provider_id field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT PRIMARY KEY)`; - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table must contain a column \"provider_id\" not nullable", - ); - }); - }); - - describe("provider_user_id field", () => { - it("should throw an error when oauth table does not have an provider_user_id field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL PRIMARY KEY)`; - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table must contain a column with name \"provider_user_id\"", - ); - }); - - it("should throw an error when oauth table does not have an provider_user_id field with type of text", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL PRIMARY KEY, "provider_user_id" INTEGER)`; - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table must contain a column \"provider_user_id\" with type \"TEXT\"", - ); - }); - - it("should throw an error when oauth table does not have an provider_user_id field as primary key", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT, PRIMARY KEY (provider_id))`; - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table must contain a column \"provider_user_id\" as primary key", - ); - }); - - it("should throw an error when oauth table does not have a not nullable provider_user_id field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT, PRIMARY KEY (provider_id, provider_user_id))`; - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table must contain a column \"provider_user_id\" not nullable", - ); - }); - }); - - describe("user_id field", () => { - it("should throw an error when slip_oauth_accounts table does not have a user_id foreign key", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (provider_id, provider_user_id))`; - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table should have a foreign key \"user_id\"", - ); - }); - - it("should throw an error when slip_oauth_accounts table does not have an user_id field with type of text", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT NOT NULL, "user_id" BLOB, PRIMARY KEY (provider_id, provider_user_id))`; - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table must contain a column \"user_id\" with type \"TEXT\"", - ); - }); - - it("should throw an error when slip_oauth_accounts table does not have an not nullable user_id field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT NOT NULL, "user_id" TEXT, PRIMARY KEY (provider_id, provider_user_id))`; - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table must contain a column \"user_id\" not nullable", - ); - }); - - it("should throw an error when slip_oauth_accounts table does not have a user_id foreign key", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (provider_id, provider_user_id))`; - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table should have a foreign key \"user_id\"", - ); - }); - - it("should throw an error when slip_oauth_accounts table does not have a user_id foreign key to user table", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS othertable ("id" TEXT)`; - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES othertable(id), PRIMARY KEY (provider_id, provider_user_id))`; - await expect(testFunction()).rejects.toThrowError( - "foreign key \"user_id\" in slip_oauth_accounts table should target \"id\" column from the \"slip_users\" table", - ); - }); - - it("should throw an error when slip_oauth_accounts table does not have a user_id foreign key to user table \"id\" column", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES slip_users(email), PRIMARY KEY (provider_id, provider_user_id))`; - await expect(testFunction()).rejects.toThrowError( - "foreign key \"user_id\" in slip_oauth_accounts table should target \"id\" column from the \"slip_users\" table", - ); - }); - }); - }); + describe("users table", () => { + describe("id field", () => { + it("should throw an error when users table does not exist in database", async () => { + await expect(testFunction()).rejects.toThrowError( + "slip_users table for SLIP does not exist", + ); + }); + + it("should throw an error when users table does not have an id field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("notid" TEXT PRIMARY KEY)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column with name "id"', + ); + }); + + it("should throw an error when users table does not have an id field as primary key", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column "id" as primary key', + ); + }); + + it("should throw an error when users table does not have an id field with type of text", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" INTEGER PRIMARY KEY)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column "id" with type "TEXT"', + ); + }); + + it("should throw an error when users table does not have a not nullable id field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT PRIMARY KEY, "email" TEXT)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column "id" not nullable', + ); + }); + }); + + describe("password field", () => { + it("should throw an error when users table does not have a password field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column with name "password"', + ); + }); + + it("should throw an error when users table does not have an email field with type of text", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "password" INTEGER)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column "password" with type "TEXT"', + ); + }); + }); + + describe("email field", () => { + it("should throw an error when users table does not have an email field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "password" TEXT)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column with name "email"', + ); + }); + + it("should throw an error when users table does not have an email field with type of text", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "password" TEXT, "email" INTEGER)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column "email" with type "TEXT"', + ); + }); + + it("should throw an error when users table does not have an not nullable email field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "password" TEXT, "email" TEXT)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column "email" not nullable', + ); + }); + + it("should throw an error when users table does not have a unique email field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "password" TEXT, "email" TEXT NOT NULL)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column "email" unique', + ); + }); + }); + + describe("created_at field", () => { + it("should throw an error when users table does not have an created_at field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column with name "created_at"', + ); + }); + + it("should throw an error when users table does not have an created_at field with type of text", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" INTEGER)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column "created_at" with type "TIMESTAMP"', + ); + }); + + it("should throw an error when users table does not have an not nullable created_at field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column "created_at" not nullable', + ); + }); + + it("should throw an error when users table does not have a default value of CURRENT_TIMESTAMP at created_at field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column with name "created_at" with default value of CURRENT_TIMESTAMP', + ); + }); + }); + + describe("updated_at field", () => { + it("should throw an error when users table does not have an updated_at field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column with name "updated_at"', + ); + }); + + it("should throw an error when users table does not have an updated_at field with type of text", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" INTEGER)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column "updated_at" with type "TIMESTAMP"', + ); + }); + + it("should throw an error when users table does not have an not nullable updated_at field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column "updated_at" not nullable', + ); + }); + + it("should throw an error when users table does not have a default value of CURRENT_TIMESTAMP at updated_at field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column with name "updated_at" with default value of CURRENT_TIMESTAMP', + ); + }); + }); + }); + + describe("sessions table", () => { + const validUsersTableSetup = () => + db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP)`; + + beforeEach(async () => { + await validUsersTableSetup(); + }); + + describe("id field", () => { + it("should throw an error when sessions table does not exist in database", async () => { + await expect(testFunction()).rejects.toThrowError( + "slip_sessions table for SLIP does not exist", + ); + }); + + it("should throw an error when sessions table does not have an id field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("notid" TEXT)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column with name "id"', + ); + }); + + it("should throw an error when sessions table does not have an id field as primary key", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column "id" as primary key', + ); + }); + + it("should throw an error when sessions table does not have an id field with type of text", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" INTEGER PRIMARY KEY)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column "id" with type "TEXT"', + ); + }); + + it("should throw an error when sessions table does not have a not nullable id field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT PRIMARY KEY)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column "id" not nullable', + ); + }); + }); + + describe("expires_at field", () => { + it("should throw an error when sessions table does not have an expires_at field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column with name "expires_at"', + ); + }); + + it("should throw an error when sessions table does not have an expires_at field with type of number", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" DATE)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column "expires_at" with type "INTEGER"', + ); + }); + + it("should throw an error when sessions table does not have an not nullable expires_at field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column "expires_at" not nullable', + ); + }); + }); + + describe("ip field", () => { + it("should throw an error when sessions table does not have an ip field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column with name "ip"', + ); + }); + + it("should throw an error when sessions table does not have an ip field with type of text", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "ip" INTEGER)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column "ip" with type "TEXT"', + ); + }); + }); + + describe("ua field", () => { + it("should throw an error when sessions table does not have an ua field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "ip" TEXT)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column with name "ua"', + ); + }); + + it("should throw an error when sessions table does not have an ua field with type of text", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "ip" TEXT, "ua" INTEGER)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column "ua" with type "TEXT"', + ); + }); + }); + + describe("user_id field", () => { + it("should throw an error when sessions table does not have a user_id foreign key", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "ip" TEXT, "ua" TEXT)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table should have a foreign key "user_id"', + ); + }); + + it("should throw an error when sessions table does not have an user_id field with type of text", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "ip" TEXT, "ua" TEXT, "user_id" BLOB)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column "user_id" with type "TEXT"', + ); + }); + + it("should throw an error when sessions table does not have an not nullable user_id field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "ip" TEXT, "ua" TEXT, "user_id" TEXT)`; + + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column "user_id" not nullable', + ); + }); + + it("should throw an error when sessions table does not have a user_id foreign key", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "ip" TEXT, "ua" TEXT)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table should have a foreign key "user_id"', + ); + }); + + it("should throw an error when sessions table does not have a user_id foreign key to user table", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS othertable ("id" TEXT)`; + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "ip" TEXT, "ua" TEXT, FOREIGN KEY (user_id) REFERENCES othertable(id))`; + await expect(testFunction()).rejects.toThrowError( + 'foreign key "user_id" in slip_sessions table should target "id" column from the "slip_users" table', + ); + }); + + it('should throw an error when sessions table does not have a user_id foreign key to user table "id" column', async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "ip" TEXT, "ua" TEXT, FOREIGN KEY (user_id) REFERENCES slip_users(email))`; + await expect(testFunction()).rejects.toThrowError( + 'foreign key "user_id" in slip_sessions table should target "id" column from the "slip_users" table', + ); + }); + }); + }); + + describe("slip_oauth_accounts table", () => { + const validUsersTableSetup = () => + db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP)`; + const validSessionsTableSetup = () => + db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "ip" TEXT, "ua" TEXT, FOREIGN KEY (user_id) REFERENCES slip_users(id))`; + + beforeEach(async () => { + await validUsersTableSetup(); + await validSessionsTableSetup(); + }); + + describe("provider_id field", () => { + it("should throw an error when oauth table does not exist in database", async () => { + await expect(testFunction()).rejects.toThrowError( + "slip_oauth_accounts table for SLIP does not exist", + ); + }); + + it("should throw an error when oauth table does not have an provider_id field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("notid" TEXT)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_oauth_accounts table must contain a column with name "provider_id"', + ); + }); + + it("should throw an error when oauth table does not have an provider_id field as primary key", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_oauth_accounts table must contain a column "provider_id" as primary key', + ); + }); + + it("should throw an error when oauth table does not have an provider_id field with type of text", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" INTEGER PRIMARY KEY)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_oauth_accounts table must contain a column "provider_id" with type "TEXT"', + ); + }); + + it("should throw an error when oauth table does not have a not nullable provider_id field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT PRIMARY KEY)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_oauth_accounts table must contain a column "provider_id" not nullable', + ); + }); + }); + + describe("provider_user_id field", () => { + it("should throw an error when oauth table does not have an provider_user_id field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL PRIMARY KEY)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_oauth_accounts table must contain a column with name "provider_user_id"', + ); + }); + + it("should throw an error when oauth table does not have an provider_user_id field with type of text", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL PRIMARY KEY, "provider_user_id" INTEGER)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_oauth_accounts table must contain a column "provider_user_id" with type "TEXT"', + ); + }); + + it("should throw an error when oauth table does not have an provider_user_id field as primary key", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT, PRIMARY KEY (provider_id))`; + await expect(testFunction()).rejects.toThrowError( + 'slip_oauth_accounts table must contain a column "provider_user_id" as primary key', + ); + }); + + it("should throw an error when oauth table does not have a not nullable provider_user_id field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT, PRIMARY KEY (provider_id, provider_user_id))`; + await expect(testFunction()).rejects.toThrowError( + 'slip_oauth_accounts table must contain a column "provider_user_id" not nullable', + ); + }); + }); + + describe("user_id field", () => { + it("should throw an error when slip_oauth_accounts table does not have a user_id foreign key", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (provider_id, provider_user_id))`; + await expect(testFunction()).rejects.toThrowError( + 'slip_oauth_accounts table should have a foreign key "user_id"', + ); + }); + + it("should throw an error when slip_oauth_accounts table does not have an user_id field with type of text", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT NOT NULL, "user_id" BLOB, PRIMARY KEY (provider_id, provider_user_id))`; + await expect(testFunction()).rejects.toThrowError( + 'slip_oauth_accounts table must contain a column "user_id" with type "TEXT"', + ); + }); + + it("should throw an error when slip_oauth_accounts table does not have an not nullable user_id field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT NOT NULL, "user_id" TEXT, PRIMARY KEY (provider_id, provider_user_id))`; + await expect(testFunction()).rejects.toThrowError( + 'slip_oauth_accounts table must contain a column "user_id" not nullable', + ); + }); + + it("should throw an error when slip_oauth_accounts table does not have a user_id foreign key", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (provider_id, provider_user_id))`; + await expect(testFunction()).rejects.toThrowError( + 'slip_oauth_accounts table should have a foreign key "user_id"', + ); + }); + + it("should throw an error when slip_oauth_accounts table does not have a user_id foreign key to user table", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS othertable ("id" TEXT)`; + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES othertable(id), PRIMARY KEY (provider_id, provider_user_id))`; + await expect(testFunction()).rejects.toThrowError( + 'foreign key "user_id" in slip_oauth_accounts table should target "id" column from the "slip_users" table', + ); + }); + + it('should throw an error when slip_oauth_accounts table does not have a user_id foreign key to user table "id" column', async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES slip_users(email), PRIMARY KEY (provider_id, provider_user_id))`; + await expect(testFunction()).rejects.toThrowError( + 'foreign key "user_id" in slip_oauth_accounts table should target "id" column from the "slip_users" table', + ); + }); + }); + }); }); diff --git a/tests/sqlite.test.ts b/tests/sqlite.test.ts index b3a1298..e4025ac 100644 --- a/tests/sqlite.test.ts +++ b/tests/sqlite.test.ts @@ -4,430 +4,436 @@ import { createDatabase } from "db0"; import { checkDbAndTables } from "../../src/runtime/database"; function testFunction() { - return checkDbAndTables(db, "sqlite", { - users: "slip_users", - sessions: "slip_sessions", - oauthAccounts: "slip_oauth_accounts", - }); + return checkDbAndTables(db, "sqlite", { + users: "slip_users", + sessions: "slip_sessions", + oauthAccounts: "slip_oauth_accounts", + }); } -const db = createDatabase(sqlite({ - name: "sqlite.test", -})); +const db = createDatabase( + sqlite({ + name: "sqlite.test", + }), +); beforeEach(async () => { - await db.sql`DROP TABLE IF EXISTS slip_users`; - await db.sql`DROP TABLE IF EXISTS slip_sessions`; - await db.sql`DROP TABLE IF EXISTS slip_oauth_accounts`; + await db.sql`DROP TABLE IF EXISTS slip_users`; + await db.sql`DROP TABLE IF EXISTS slip_sessions`; + await db.sql`DROP TABLE IF EXISTS slip_oauth_accounts`; }); describe("sqlite connector", () => { - describe("users table", () => { - describe("id field", () => { - it("should throw an error when users table does not exist in database", async () => { - await expect(testFunction()).rejects.toThrowError("slip_users table for SLIP does not exist"); - }); - - it("should throw an error when users table does not have an id field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("notid" TEXT PRIMARY KEY)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column with name \"id\"", - ); - }); - - it("should throw an error when users table does not have an id field as primary key", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column \"id\" as primary key", - ); - }); - - it("should throw an error when users table does not have an id field with type of text", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" INTEGER PRIMARY KEY)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column \"id\" with type \"TEXT\"", - ); - }); - - it("should throw an error when users table does not have a not nullable id field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT PRIMARY KEY, "email" TEXT)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column \"id\" not nullable", - ); - }); - }); - - describe("password field", () => { - it("should throw an error when users table does not have a password field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column with name \"password\"", - ); - }); - - it("should throw an error when users table does not have an email field with type of text", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "password" INTEGER)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column \"password\" with type \"TEXT\"", - ); - }); - }); - - describe("email field", () => { - it("should throw an error when users table does not have an email field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "password" TEXT)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column with name \"email\"", - ); - }); - - it("should throw an error when users table does not have an email field with type of text", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "password" TEXT, "email" INTEGER)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column \"email\" with type \"TEXT\"", - ); - }); - - it("should throw an error when users table does not have an not nullable email field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "password" TEXT, "email" TEXT)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column \"email\" not nullable", - ); - }); - - it("should throw an error when users table does not have a unique email field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "password" TEXT, "email" TEXT NOT NULL)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column \"email\" unique", - ); - }); - }); - - describe("created_at field", () => { - it("should throw an error when users table does not have an created_at field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column with name \"created_at\"", - ); - }); - - it("should throw an error when users table does not have an created_at field with type of text", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" INTEGER)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column \"created_at\" with type \"TIMESTAMP\"", - ); - }); - - it("should throw an error when users table does not have an not nullable created_at field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column \"created_at\" not nullable", - ); - }); - - it("should throw an error when users table does not have a default value of CURRENT_TIMESTAMP at created_at field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column with name \"created_at\" with default value of CURRENT_TIMESTAMP", - ); - }); - }); - - describe("updated_at field", () => { - it("should throw an error when users table does not have an updated_at field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column with name \"updated_at\"", - ); - }); - - it("should throw an error when users table does not have an updated_at field with type of text", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" INTEGER)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column \"updated_at\" with type \"TIMESTAMP\"", - ); - }); - - it("should throw an error when users table does not have an not nullable updated_at field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column \"updated_at\" not nullable", - ); - }); - - it("should throw an error when users table does not have a default value of CURRENT_TIMESTAMP at updated_at field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL)`; - await expect(testFunction()).rejects.toThrowError( - "slip_users table must contain a column with name \"updated_at\" with default value of CURRENT_TIMESTAMP", - ); - }); - }); - }); - - describe("sessions table", () => { - const validUsersTableSetup = () => - db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP)`; - - beforeEach(async () => { - await validUsersTableSetup(); - }); - - describe("id field", () => { - it("should throw an error when sessions table does not exist in database", async () => { - await expect(testFunction()).rejects.toThrowError("slip_sessions table for SLIP does not exist"); - }); - - it("should throw an error when sessions table does not have an id field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("notid" TEXT)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column with name \"id\"", - ); - }); - - it("should throw an error when sessions table does not have an id field as primary key", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column \"id\" as primary key", - ); - }); - - it("should throw an error when sessions table does not have an id field with type of text", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" INTEGER PRIMARY KEY)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column \"id\" with type \"TEXT\"", - ); - }); - - it("should throw an error when sessions table does not have a not nullable id field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT PRIMARY KEY)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column \"id\" not nullable", - ); - }); - }); - - describe("expires_at field", () => { - it("should throw an error when sessions table does not have an expires_at field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column with name \"expires_at\"", - ); - }); - - it("should throw an error when sessions table does not have an expires_at field with type of number", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" DATE)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column \"expires_at\" with type \"INTEGER\"", - ); - }); - - it("should throw an error when sessions table does not have an not nullable expires_at field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column \"expires_at\" not nullable", - ); - }); - }); - - describe("ip field", () => { - it("should throw an error when sessions table does not have an ip field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column with name \"ip\"", - ); - }); - - it("should throw an error when sessions table does not have an ip field with type of text", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "ip" INTEGER)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column \"ip\" with type \"TEXT\"", - ); - }); - }); - - describe("ua field", () => { - it("should throw an error when sessions table does not have an ua field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "ip" TEXT)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column with name \"ua\"", - ); - }); - - it("should throw an error when sessions table does not have an ua field with type of text", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "ip" TEXT, "ua" INTEGER)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column \"ua\" with type \"TEXT\"", - ); - }); - }); - - describe("user_id field", () => { - it("should throw an error when sessions table does not have a user_id foreign key", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "ip" TEXT, "ua" TEXT)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table should have a foreign key \"user_id\"", - ); - }); - - it("should throw an error when sessions table does not have an user_id field with type of text", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "ip" TEXT, "ua" TEXT, "user_id" BLOB)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column \"user_id\" with type \"TEXT\"", - ); - }); - - it("should throw an error when sessions table does not have an not nullable user_id field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "ip" TEXT, "ua" TEXT, "user_id" TEXT)`; - - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table must contain a column \"user_id\" not nullable", - ); - }); - - it("should throw an error when sessions table does not have a user_id foreign key", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "ip" TEXT, "ua" TEXT)`; - await expect(testFunction()).rejects.toThrowError( - "slip_sessions table should have a foreign key \"user_id\"", - ); - }); - - it("should throw an error when sessions table does not have a user_id foreign key to user table", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS othertable ("id" TEXT)`; - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "ip" TEXT, "ua" TEXT, FOREIGN KEY (user_id) REFERENCES othertable(id))`; - await expect(testFunction()).rejects.toThrowError( - "foreign key \"user_id\" in slip_sessions table should target \"id\" column from the \"slip_users\" table", - ); - }); - - it("should throw an error when sessions table does not have a user_id foreign key to user table \"id\" column", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "ip" TEXT, "ua" TEXT, FOREIGN KEY (user_id) REFERENCES slip_users(email))`; - await expect(testFunction()).rejects.toThrowError( - "foreign key \"user_id\" in slip_sessions table should target \"id\" column from the \"slip_users\" table", - ); - }); - }); - }); - - describe("slip_oauth_accounts table", () => { - const validUsersTableSetup = () => - db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP)`; - const validSessionsTableSetup = () => - db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "ip" TEXT, "ua" TEXT, FOREIGN KEY (user_id) REFERENCES slip_users(id))`; - - beforeEach(async () => { - await validUsersTableSetup(); - await validSessionsTableSetup(); - }); - - describe("provider_id field", () => { - it("should throw an error when oauth table does not exist in database", async () => { - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table for SLIP does not exist", - ); - }); - - it("should throw an error when oauth table does not have an provider_id field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("notid" TEXT)`; - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table must contain a column with name \"provider_id\"", - ); - }); - - it("should throw an error when oauth table does not have an provider_id field as primary key", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT)`; - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table must contain a column \"provider_id\" as primary key", - ); - }); - - it("should throw an error when oauth table does not have an provider_id field with type of text", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" INTEGER PRIMARY KEY)`; - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table must contain a column \"provider_id\" with type \"TEXT\"", - ); - }); - - it("should throw an error when oauth table does not have a not nullable provider_id field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT PRIMARY KEY)`; - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table must contain a column \"provider_id\" not nullable", - ); - }); - }); - - describe("provider_user_id field", () => { - it("should throw an error when oauth table does not have an provider_user_id field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL PRIMARY KEY)`; - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table must contain a column with name \"provider_user_id\"", - ); - }); - - it("should throw an error when oauth table does not have an provider_user_id field with type of text", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL PRIMARY KEY, "provider_user_id" INTEGER)`; - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table must contain a column \"provider_user_id\" with type \"TEXT\"", - ); - }); - - it("should throw an error when oauth table does not have an provider_user_id field as primary key", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT, PRIMARY KEY (provider_id))`; - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table must contain a column \"provider_user_id\" as primary key", - ); - }); - - it("should throw an error when oauth table does not have a not nullable provider_user_id field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT, PRIMARY KEY (provider_id, provider_user_id))`; - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table must contain a column \"provider_user_id\" not nullable", - ); - }); - }); - - describe("user_id field", () => { - it("should throw an error when slip_oauth_accounts table does not have a user_id foreign key", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (provider_id, provider_user_id))`; - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table should have a foreign key \"user_id\"", - ); - }); - - it("should throw an error when slip_oauth_accounts table does not have an user_id field with type of text", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT NOT NULL, "user_id" BLOB, PRIMARY KEY (provider_id, provider_user_id))`; - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table must contain a column \"user_id\" with type \"TEXT\"", - ); - }); - - it("should throw an error when slip_oauth_accounts table does not have an not nullable user_id field", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT NOT NULL, "user_id" TEXT, PRIMARY KEY (provider_id, provider_user_id))`; - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table must contain a column \"user_id\" not nullable", - ); - }); - - it("should throw an error when slip_oauth_accounts table does not have a user_id foreign key", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (provider_id, provider_user_id))`; - await expect(testFunction()).rejects.toThrowError( - "slip_oauth_accounts table should have a foreign key \"user_id\"", - ); - }); - - it("should throw an error when slip_oauth_accounts table does not have a user_id foreign key to user table", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS othertable ("id" TEXT)`; - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES othertable(id), PRIMARY KEY (provider_id, provider_user_id))`; - await expect(testFunction()).rejects.toThrowError( - "foreign key \"user_id\" in slip_oauth_accounts table should target \"id\" column from the \"slip_users\" table", - ); - }); - - it("should throw an error when slip_oauth_accounts table does not have a user_id foreign key to user table \"id\" column", async () => { - await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES slip_users(email), PRIMARY KEY (provider_id, provider_user_id))`; - await expect(testFunction()).rejects.toThrowError( - "foreign key \"user_id\" in slip_oauth_accounts table should target \"id\" column from the \"slip_users\" table", - ); - }); - }); - }); + describe("users table", () => { + describe("id field", () => { + it("should throw an error when users table does not exist in database", async () => { + await expect(testFunction()).rejects.toThrowError( + "slip_users table for SLIP does not exist", + ); + }); + + it("should throw an error when users table does not have an id field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("notid" TEXT PRIMARY KEY)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column with name "id"', + ); + }); + + it("should throw an error when users table does not have an id field as primary key", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column "id" as primary key', + ); + }); + + it("should throw an error when users table does not have an id field with type of text", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" INTEGER PRIMARY KEY)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column "id" with type "TEXT"', + ); + }); + + it("should throw an error when users table does not have a not nullable id field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT PRIMARY KEY, "email" TEXT)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column "id" not nullable', + ); + }); + }); + + describe("password field", () => { + it("should throw an error when users table does not have a password field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column with name "password"', + ); + }); + + it("should throw an error when users table does not have an email field with type of text", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "password" INTEGER)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column "password" with type "TEXT"', + ); + }); + }); + + describe("email field", () => { + it("should throw an error when users table does not have an email field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "password" TEXT)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column with name "email"', + ); + }); + + it("should throw an error when users table does not have an email field with type of text", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "password" TEXT, "email" INTEGER)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column "email" with type "TEXT"', + ); + }); + + it("should throw an error when users table does not have an not nullable email field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "password" TEXT, "email" TEXT)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column "email" not nullable', + ); + }); + + it("should throw an error when users table does not have a unique email field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "password" TEXT, "email" TEXT NOT NULL)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column "email" unique', + ); + }); + }); + + describe("created_at field", () => { + it("should throw an error when users table does not have an created_at field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column with name "created_at"', + ); + }); + + it("should throw an error when users table does not have an created_at field with type of text", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" INTEGER)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column "created_at" with type "TIMESTAMP"', + ); + }); + + it("should throw an error when users table does not have an not nullable created_at field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column "created_at" not nullable', + ); + }); + + it("should throw an error when users table does not have a default value of CURRENT_TIMESTAMP at created_at field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column with name "created_at" with default value of CURRENT_TIMESTAMP', + ); + }); + }); + + describe("updated_at field", () => { + it("should throw an error when users table does not have an updated_at field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column with name "updated_at"', + ); + }); + + it("should throw an error when users table does not have an updated_at field with type of text", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" INTEGER)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column "updated_at" with type "TIMESTAMP"', + ); + }); + + it("should throw an error when users table does not have an not nullable updated_at field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column "updated_at" not nullable', + ); + }); + + it("should throw an error when users table does not have a default value of CURRENT_TIMESTAMP at updated_at field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_users table must contain a column with name "updated_at" with default value of CURRENT_TIMESTAMP', + ); + }); + }); + }); + + describe("sessions table", () => { + const validUsersTableSetup = () => + db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP)`; + + beforeEach(async () => { + await validUsersTableSetup(); + }); + + describe("id field", () => { + it("should throw an error when sessions table does not exist in database", async () => { + await expect(testFunction()).rejects.toThrowError( + "slip_sessions table for SLIP does not exist", + ); + }); + + it("should throw an error when sessions table does not have an id field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("notid" TEXT)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column with name "id"', + ); + }); + + it("should throw an error when sessions table does not have an id field as primary key", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column "id" as primary key', + ); + }); + + it("should throw an error when sessions table does not have an id field with type of text", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" INTEGER PRIMARY KEY)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column "id" with type "TEXT"', + ); + }); + + it("should throw an error when sessions table does not have a not nullable id field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT PRIMARY KEY)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column "id" not nullable', + ); + }); + }); + + describe("expires_at field", () => { + it("should throw an error when sessions table does not have an expires_at field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column with name "expires_at"', + ); + }); + + it("should throw an error when sessions table does not have an expires_at field with type of number", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" DATE)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column "expires_at" with type "INTEGER"', + ); + }); + + it("should throw an error when sessions table does not have an not nullable expires_at field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column "expires_at" not nullable', + ); + }); + }); + + describe("ip field", () => { + it("should throw an error when sessions table does not have an ip field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column with name "ip"', + ); + }); + + it("should throw an error when sessions table does not have an ip field with type of text", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "ip" INTEGER)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column "ip" with type "TEXT"', + ); + }); + }); + + describe("ua field", () => { + it("should throw an error when sessions table does not have an ua field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "ip" TEXT)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column with name "ua"', + ); + }); + + it("should throw an error when sessions table does not have an ua field with type of text", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "ip" TEXT, "ua" INTEGER)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column "ua" with type "TEXT"', + ); + }); + }); + + describe("user_id field", () => { + it("should throw an error when sessions table does not have a user_id foreign key", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "ip" TEXT, "ua" TEXT)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table should have a foreign key "user_id"', + ); + }); + + it("should throw an error when sessions table does not have an user_id field with type of text", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "ip" TEXT, "ua" TEXT, "user_id" BLOB)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column "user_id" with type "TEXT"', + ); + }); + + it("should throw an error when sessions table does not have an not nullable user_id field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "ip" TEXT, "ua" TEXT, "user_id" TEXT)`; + + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table must contain a column "user_id" not nullable', + ); + }); + + it("should throw an error when sessions table does not have a user_id foreign key", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "ip" TEXT, "ua" TEXT)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_sessions table should have a foreign key "user_id"', + ); + }); + + it("should throw an error when sessions table does not have a user_id foreign key to user table", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS othertable ("id" TEXT)`; + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "ip" TEXT, "ua" TEXT, FOREIGN KEY (user_id) REFERENCES othertable(id))`; + await expect(testFunction()).rejects.toThrowError( + 'foreign key "user_id" in slip_sessions table should target "id" column from the "slip_users" table', + ); + }); + + it('should throw an error when sessions table does not have a user_id foreign key to user table "id" column', async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "ip" TEXT, "ua" TEXT, FOREIGN KEY (user_id) REFERENCES slip_users(email))`; + await expect(testFunction()).rejects.toThrowError( + 'foreign key "user_id" in slip_sessions table should target "id" column from the "slip_users" table', + ); + }); + }); + }); + + describe("slip_oauth_accounts table", () => { + const validUsersTableSetup = () => + db.sql`CREATE TABLE IF NOT EXISTS slip_users ("id" TEXT NOT NULL PRIMARY KEY, "email" TEXT NOT NULL UNIQUE, "password" TEXT, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP)`; + const validSessionsTableSetup = () => + db.sql`CREATE TABLE IF NOT EXISTS slip_sessions ("id" TEXT NOT NULL PRIMARY KEY, "expires_at" INTEGER NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "ip" TEXT, "ua" TEXT, FOREIGN KEY (user_id) REFERENCES slip_users(id))`; + + beforeEach(async () => { + await validUsersTableSetup(); + await validSessionsTableSetup(); + }); + + describe("provider_id field", () => { + it("should throw an error when oauth table does not exist in database", async () => { + await expect(testFunction()).rejects.toThrowError( + "slip_oauth_accounts table for SLIP does not exist", + ); + }); + + it("should throw an error when oauth table does not have an provider_id field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("notid" TEXT)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_oauth_accounts table must contain a column with name "provider_id"', + ); + }); + + it("should throw an error when oauth table does not have an provider_id field as primary key", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_oauth_accounts table must contain a column "provider_id" as primary key', + ); + }); + + it("should throw an error when oauth table does not have an provider_id field with type of text", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" INTEGER PRIMARY KEY)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_oauth_accounts table must contain a column "provider_id" with type "TEXT"', + ); + }); + + it("should throw an error when oauth table does not have a not nullable provider_id field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT PRIMARY KEY)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_oauth_accounts table must contain a column "provider_id" not nullable', + ); + }); + }); + + describe("provider_user_id field", () => { + it("should throw an error when oauth table does not have an provider_user_id field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL PRIMARY KEY)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_oauth_accounts table must contain a column with name "provider_user_id"', + ); + }); + + it("should throw an error when oauth table does not have an provider_user_id field with type of text", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL PRIMARY KEY, "provider_user_id" INTEGER)`; + await expect(testFunction()).rejects.toThrowError( + 'slip_oauth_accounts table must contain a column "provider_user_id" with type "TEXT"', + ); + }); + + it("should throw an error when oauth table does not have an provider_user_id field as primary key", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT, PRIMARY KEY (provider_id))`; + await expect(testFunction()).rejects.toThrowError( + 'slip_oauth_accounts table must contain a column "provider_user_id" as primary key', + ); + }); + + it("should throw an error when oauth table does not have a not nullable provider_user_id field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT, PRIMARY KEY (provider_id, provider_user_id))`; + await expect(testFunction()).rejects.toThrowError( + 'slip_oauth_accounts table must contain a column "provider_user_id" not nullable', + ); + }); + }); + + describe("user_id field", () => { + it("should throw an error when slip_oauth_accounts table does not have a user_id foreign key", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (provider_id, provider_user_id))`; + await expect(testFunction()).rejects.toThrowError( + 'slip_oauth_accounts table should have a foreign key "user_id"', + ); + }); + + it("should throw an error when slip_oauth_accounts table does not have an user_id field with type of text", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT NOT NULL, "user_id" BLOB, PRIMARY KEY (provider_id, provider_user_id))`; + await expect(testFunction()).rejects.toThrowError( + 'slip_oauth_accounts table must contain a column "user_id" with type "TEXT"', + ); + }); + + it("should throw an error when slip_oauth_accounts table does not have an not nullable user_id field", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT NOT NULL, "user_id" TEXT, PRIMARY KEY (provider_id, provider_user_id))`; + await expect(testFunction()).rejects.toThrowError( + 'slip_oauth_accounts table must contain a column "user_id" not nullable', + ); + }); + + it("should throw an error when slip_oauth_accounts table does not have a user_id foreign key", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (provider_id, provider_user_id))`; + await expect(testFunction()).rejects.toThrowError( + 'slip_oauth_accounts table should have a foreign key "user_id"', + ); + }); + + it("should throw an error when slip_oauth_accounts table does not have a user_id foreign key to user table", async () => { + await db.sql`CREATE TABLE IF NOT EXISTS othertable ("id" TEXT)`; + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES othertable(id), PRIMARY KEY (provider_id, provider_user_id))`; + await expect(testFunction()).rejects.toThrowError( + 'foreign key "user_id" in slip_oauth_accounts table should target "id" column from the "slip_users" table', + ); + }); + + it('should throw an error when slip_oauth_accounts table does not have a user_id foreign key to user table "id" column', async () => { + await db.sql`CREATE TABLE IF NOT EXISTS slip_oauth_accounts ("provider_id" TEXT NOT NULL, "provider_user_id" TEXT NOT NULL, "user_id" TEXT NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES slip_users(email), PRIMARY KEY (provider_id, provider_user_id))`; + await expect(testFunction()).rejects.toThrowError( + 'foreign key "user_id" in slip_oauth_accounts table should target "id" column from the "slip_users" table', + ); + }); + }); + }); }); diff --git a/vitest.config.ts b/vitest.config.ts index 1ed4c78..a5be7f8 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,9 +1,9 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ - test: { - outputFile: { - junit: "./junit.xml", - }, - }, + test: { + outputFile: { + junit: "./junit.xml", + }, + }, });