chore: snapshot before performance optimization
This commit is contained in:
@@ -1,96 +1,58 @@
|
||||
/**
|
||||
* Test database setup and teardown utilities for migration tests
|
||||
* Provides isolated database environments for each test suite
|
||||
* Updated for PostgreSQL
|
||||
*/
|
||||
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
|
||||
// Environment variables
|
||||
const DATABASE_DIR = path.join(process.cwd(), 'prisma', 'test-databases')
|
||||
const TEST_DATABASE_PATH = path.join(DATABASE_DIR, 'migration-test.db')
|
||||
|
||||
/**
|
||||
* Initialize test environment
|
||||
* Creates test database directory if it doesn't exist
|
||||
*/
|
||||
export async function setupTestEnvironment() {
|
||||
// Ensure test database directory exists
|
||||
if (!fs.existsSync(DATABASE_DIR)) {
|
||||
fs.mkdirSync(DATABASE_DIR, { recursive: true })
|
||||
}
|
||||
|
||||
// Clean up any existing test database
|
||||
if (fs.existsSync(TEST_DATABASE_PATH)) {
|
||||
fs.unlinkSync(TEST_DATABASE_PATH)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Prisma client instance connected to test database
|
||||
* Create a Prisma client instance for testing
|
||||
* Uses DATABASE_URL from environment
|
||||
*/
|
||||
export function createTestPrismaClient(): PrismaClient {
|
||||
return new PrismaClient({
|
||||
datasources: {
|
||||
db: {
|
||||
url: `file:${TEST_DATABASE_PATH}`
|
||||
}
|
||||
}
|
||||
})
|
||||
return new PrismaClient()
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize test database schema from migrations
|
||||
* This applies all migrations to create a clean schema
|
||||
* Initialize test database schema
|
||||
* Runs prisma migrate deploy or db push
|
||||
*/
|
||||
export async function initializeTestDatabase(prisma: PrismaClient) {
|
||||
// Connect to database
|
||||
await prisma.$connect()
|
||||
|
||||
// Read and execute all migration files in order
|
||||
const migrationsDir = path.join(process.cwd(), 'prisma', 'migrations')
|
||||
const migrationFolders = fs.readdirSync(migrationsDir)
|
||||
.filter(name => !name.includes('migration_lock') && fs.statSync(path.join(migrationsDir, name)).isDirectory())
|
||||
.sort()
|
||||
|
||||
// Execute each migration
|
||||
for (const folder of migrationFolders) {
|
||||
const migrationSql = fs.readFileSync(path.join(migrationsDir, folder, 'migration.sql'), 'utf-8')
|
||||
try {
|
||||
await prisma.$executeRawUnsafe(migrationSql)
|
||||
} catch (error) {
|
||||
// Some migrations might fail if tables already exist, which is okay for test setup
|
||||
console.log(`Migration ${folder} note:`, (error as Error).message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup test database
|
||||
* Disconnects Prisma client and removes test database file
|
||||
* Disconnects Prisma client and cleans all data
|
||||
*/
|
||||
export async function cleanupTestDatabase(prisma: PrismaClient) {
|
||||
try {
|
||||
// Delete in dependency order
|
||||
await prisma.aiFeedback.deleteMany()
|
||||
await prisma.memoryEchoInsight.deleteMany()
|
||||
await prisma.noteShare.deleteMany()
|
||||
await prisma.note.deleteMany()
|
||||
await prisma.label.deleteMany()
|
||||
await prisma.notebook.deleteMany()
|
||||
await prisma.userAISettings.deleteMany()
|
||||
await prisma.systemConfig.deleteMany()
|
||||
await prisma.session.deleteMany()
|
||||
await prisma.account.deleteMany()
|
||||
await prisma.verificationToken.deleteMany()
|
||||
await prisma.user.deleteMany()
|
||||
await prisma.$disconnect()
|
||||
} catch (error) {
|
||||
console.error('Error disconnecting Prisma:', error)
|
||||
}
|
||||
|
||||
// Remove test database file
|
||||
if (fs.existsSync(TEST_DATABASE_PATH)) {
|
||||
fs.unlinkSync(TEST_DATABASE_PATH)
|
||||
console.error('Error cleaning up test database:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create sample test data
|
||||
* Generates test notes with various configurations
|
||||
*/
|
||||
export async function createSampleNotes(prisma: PrismaClient, count: number = 10) {
|
||||
const notes = []
|
||||
const userId = 'test-user-123'
|
||||
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const note = await prisma.note.create({
|
||||
data: {
|
||||
@@ -107,18 +69,17 @@ export async function createSampleNotes(prisma: PrismaClient, count: number = 10
|
||||
})
|
||||
notes.push(note)
|
||||
}
|
||||
|
||||
|
||||
return notes
|
||||
}
|
||||
|
||||
/**
|
||||
* Create sample AI-enabled notes
|
||||
* Tests AI field migration scenarios
|
||||
*/
|
||||
export async function createSampleAINotes(prisma: PrismaClient, count: number = 10) {
|
||||
const notes = []
|
||||
const userId = 'test-user-ai'
|
||||
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const note = await prisma.note.create({
|
||||
data: {
|
||||
@@ -137,13 +98,12 @@ export async function createSampleAINotes(prisma: PrismaClient, count: number =
|
||||
})
|
||||
notes.push(note)
|
||||
}
|
||||
|
||||
|
||||
return notes
|
||||
}
|
||||
|
||||
/**
|
||||
* Measure execution time for a function
|
||||
* Useful for performance testing
|
||||
*/
|
||||
export async function measureExecutionTime<T>(fn: () => Promise<T>): Promise<{ result: T; duration: number }> {
|
||||
const start = performance.now()
|
||||
@@ -157,115 +117,98 @@ export async function measureExecutionTime<T>(fn: () => Promise<T>): Promise<{ r
|
||||
|
||||
/**
|
||||
* Verify data integrity after migration
|
||||
* Checks for data loss or corruption
|
||||
*/
|
||||
export async function verifyDataIntegrity(prisma: PrismaClient, expectedNoteCount: number) {
|
||||
const noteCount = await prisma.note.count()
|
||||
|
||||
|
||||
if (noteCount !== expectedNoteCount) {
|
||||
throw new Error(`Data integrity check failed: Expected ${expectedNoteCount} notes, found ${noteCount}`)
|
||||
}
|
||||
|
||||
// Verify no null critical fields
|
||||
const allNotes = await prisma.note.findMany()
|
||||
for (const note of allNotes) {
|
||||
if (!note.title && !note.content) {
|
||||
throw new Error(`Data integrity check failed: Note ${note.id} has neither title nor content`)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if database tables exist
|
||||
* Verifies schema migration success
|
||||
* Check if database table exists (PostgreSQL version)
|
||||
*/
|
||||
export async function verifyTableExists(prisma: PrismaClient, tableName: string): Promise<boolean> {
|
||||
try {
|
||||
const result = await prisma.$queryRawUnsafe<Array<{ name: string }>>(
|
||||
`SELECT name FROM sqlite_master WHERE type='table' AND name=?`,
|
||||
const result = await prisma.$queryRawUnsafe<Array<{ exists: boolean }>>(
|
||||
`SELECT EXISTS (
|
||||
SELECT FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = $1
|
||||
)`,
|
||||
tableName
|
||||
)
|
||||
return result.length > 0
|
||||
return result[0]?.exists ?? false
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if index exists on a table
|
||||
* Verifies index creation migration success
|
||||
* Check if index exists on a table (PostgreSQL version)
|
||||
*/
|
||||
export async function verifyIndexExists(prisma: PrismaClient, tableName: string, indexName: string): Promise<boolean> {
|
||||
try {
|
||||
const result = await prisma.$queryRawUnsafe<Array<{ name: string }>>(
|
||||
`SELECT name FROM sqlite_master WHERE type='index' AND tbl_name=? AND name=?`,
|
||||
const result = await prisma.$queryRawUnsafe<Array<{ exists: boolean }>>(
|
||||
`SELECT EXISTS (
|
||||
SELECT FROM pg_indexes
|
||||
WHERE schemaname = 'public'
|
||||
AND tablename = $1
|
||||
AND indexname = $2
|
||||
)`,
|
||||
tableName,
|
||||
indexName
|
||||
)
|
||||
return result.length > 0
|
||||
return result[0]?.exists ?? false
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify foreign key relationships
|
||||
* Ensures cascade delete works correctly
|
||||
* Check if column exists in table (PostgreSQL version)
|
||||
*/
|
||||
export async function verifyCascadeDelete(prisma: PrismaClient, parentTableName: string, childTableName: string): Promise<boolean> {
|
||||
// This is a basic check - in a real migration test, you would:
|
||||
// 1. Create a parent record
|
||||
// 2. Create related child records
|
||||
// 3. Delete the parent
|
||||
// 4. Verify children are deleted
|
||||
return true
|
||||
export async function verifyColumnExists(prisma: PrismaClient, tableName: string, columnName: string): Promise<boolean> {
|
||||
try {
|
||||
const result = await prisma.$queryRawUnsafe<Array<{ exists: boolean }>>(
|
||||
`SELECT EXISTS (
|
||||
SELECT FROM information_schema.columns
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = $1
|
||||
AND column_name = $2
|
||||
)`,
|
||||
tableName,
|
||||
columnName
|
||||
)
|
||||
return result[0]?.exists ?? false
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get table schema information
|
||||
* Useful for verifying schema migration
|
||||
* Get table schema information (PostgreSQL version)
|
||||
*/
|
||||
export async function getTableSchema(prisma: PrismaClient, tableName: string) {
|
||||
try {
|
||||
const result = await prisma.$queryRawUnsafe<Array<{
|
||||
cid: number
|
||||
name: string
|
||||
type: string
|
||||
notnull: number
|
||||
dflt_value: string | null
|
||||
pk: number
|
||||
column_name: string
|
||||
data_type: string
|
||||
is_nullable: string
|
||||
column_default: string | null
|
||||
}>>(
|
||||
`PRAGMA table_info(${tableName})`
|
||||
`SELECT column_name, data_type, is_nullable, column_default
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = $1
|
||||
ORDER BY ordinal_position`,
|
||||
tableName
|
||||
)
|
||||
return result
|
||||
} catch (error) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if column exists in table
|
||||
* Verifies column migration success
|
||||
*/
|
||||
export async function verifyColumnExists(prisma: PrismaClient, tableName: string, columnName: string): Promise<boolean> {
|
||||
const schema = await getTableSchema(prisma, tableName)
|
||||
if (!schema) return false
|
||||
return schema.some(col => col.name === columnName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get database size in bytes
|
||||
* Useful for performance monitoring
|
||||
*/
|
||||
export async function getDatabaseSize(prisma: PrismaClient): Promise<number> {
|
||||
try {
|
||||
const result = await prisma.$queryRawUnsafe<Array<{ size: number }>>(
|
||||
`SELECT page_count * page_size as size FROM pragma_page_count(), pragma_page_size()`
|
||||
)
|
||||
return result[0]?.size || 0
|
||||
} catch (error) {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user