203 lines
7.1 KiB
JavaScript
203 lines
7.1 KiB
JavaScript
|
|
#!/usr/bin/env node
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Media Library Database Test Script
|
|||
|
|
* Tests all database operations for uploads and folders
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
const { pool } = require("./config/database");
|
|||
|
|
const logger = require("./config/logger");
|
|||
|
|
|
|||
|
|
async function testDatabaseOperations() {
|
|||
|
|
console.log("\n🧪 Testing Media Library Database Operations\n");
|
|||
|
|
console.log("=".repeat(60));
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// Test 1: Check database connection
|
|||
|
|
console.log("\n1️⃣ Testing Database Connection...");
|
|||
|
|
const connectionTest = await pool.query("SELECT NOW()");
|
|||
|
|
console.log(" ✅ Database connected:", connectionTest.rows[0].now);
|
|||
|
|
|
|||
|
|
// Test 2: Check uploads table structure
|
|||
|
|
console.log("\n2️⃣ Checking uploads table structure...");
|
|||
|
|
const uploadsSchema = await pool.query(`
|
|||
|
|
SELECT column_name, data_type, is_nullable
|
|||
|
|
FROM information_schema.columns
|
|||
|
|
WHERE table_name = 'uploads'
|
|||
|
|
ORDER BY ordinal_position
|
|||
|
|
`);
|
|||
|
|
console.log(" ✅ Uploads table columns:");
|
|||
|
|
uploadsSchema.rows.forEach((col) => {
|
|||
|
|
console.log(
|
|||
|
|
` - ${col.column_name} (${col.data_type}, nullable: ${col.is_nullable})`
|
|||
|
|
);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Test 3: Check media_folders table structure
|
|||
|
|
console.log("\n3️⃣ Checking media_folders table structure...");
|
|||
|
|
const foldersSchema = await pool.query(`
|
|||
|
|
SELECT column_name, data_type, is_nullable
|
|||
|
|
FROM information_schema.columns
|
|||
|
|
WHERE table_name = 'media_folders'
|
|||
|
|
ORDER BY ordinal_position
|
|||
|
|
`);
|
|||
|
|
console.log(" ✅ Media folders table columns:");
|
|||
|
|
foldersSchema.rows.forEach((col) => {
|
|||
|
|
console.log(
|
|||
|
|
` - ${col.column_name} (${col.data_type}, nullable: ${col.is_nullable})`
|
|||
|
|
);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Test 4: Check foreign key constraints
|
|||
|
|
console.log("\n4️⃣ Checking foreign key constraints...");
|
|||
|
|
const constraints = await pool.query(`
|
|||
|
|
SELECT
|
|||
|
|
tc.constraint_name,
|
|||
|
|
tc.table_name,
|
|||
|
|
kcu.column_name,
|
|||
|
|
ccu.table_name AS foreign_table_name,
|
|||
|
|
ccu.column_name AS foreign_column_name
|
|||
|
|
FROM information_schema.table_constraints AS tc
|
|||
|
|
JOIN information_schema.key_column_usage AS kcu
|
|||
|
|
ON tc.constraint_name = kcu.constraint_name
|
|||
|
|
AND tc.table_schema = kcu.table_schema
|
|||
|
|
JOIN information_schema.constraint_column_usage AS ccu
|
|||
|
|
ON ccu.constraint_name = tc.constraint_name
|
|||
|
|
AND ccu.table_schema = tc.table_schema
|
|||
|
|
WHERE tc.constraint_type = 'FOREIGN KEY'
|
|||
|
|
AND tc.table_name IN ('uploads', 'media_folders')
|
|||
|
|
`);
|
|||
|
|
console.log(" ✅ Foreign key constraints:");
|
|||
|
|
constraints.rows.forEach((fk) => {
|
|||
|
|
console.log(
|
|||
|
|
` - ${fk.table_name}.${fk.column_name} → ${fk.foreign_table_name}.${fk.foreign_column_name}`
|
|||
|
|
);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Test 5: Count existing data
|
|||
|
|
console.log("\n5️⃣ Counting existing data...");
|
|||
|
|
const fileCount = await pool.query("SELECT COUNT(*) as count FROM uploads");
|
|||
|
|
const folderCount = await pool.query(
|
|||
|
|
"SELECT COUNT(*) as count FROM media_folders"
|
|||
|
|
);
|
|||
|
|
console.log(` ✅ Total files: ${fileCount.rows[0].count}`);
|
|||
|
|
console.log(` ✅ Total folders: ${folderCount.rows[0].count}`);
|
|||
|
|
|
|||
|
|
// Test 6: List all files
|
|||
|
|
if (parseInt(fileCount.rows[0].count) > 0) {
|
|||
|
|
console.log("\n6️⃣ Listing all files in database...");
|
|||
|
|
const files = await pool.query(`
|
|||
|
|
SELECT id, original_name, file_size, folder_id, uploaded_by, created_at
|
|||
|
|
FROM uploads
|
|||
|
|
ORDER BY created_at DESC
|
|||
|
|
LIMIT 10
|
|||
|
|
`);
|
|||
|
|
console.log(" ✅ Recent files:");
|
|||
|
|
files.rows.forEach((file) => {
|
|||
|
|
const size = (file.file_size / 1024).toFixed(2) + " KB";
|
|||
|
|
const folder = file.folder_id ? `Folder #${file.folder_id}` : "Root";
|
|||
|
|
console.log(
|
|||
|
|
` - [ID: ${file.id}] ${file.original_name} (${size}) - ${folder}`
|
|||
|
|
);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Test 7: List all folders
|
|||
|
|
if (parseInt(folderCount.rows[0].count) > 0) {
|
|||
|
|
console.log("\n7️⃣ Listing all folders in database...");
|
|||
|
|
const folders = await pool.query(`
|
|||
|
|
SELECT id, name, parent_id, created_by, created_at,
|
|||
|
|
(SELECT COUNT(*) FROM uploads WHERE folder_id = media_folders.id) as file_count
|
|||
|
|
FROM media_folders
|
|||
|
|
ORDER BY created_at DESC
|
|||
|
|
`);
|
|||
|
|
console.log(" ✅ Folders:");
|
|||
|
|
folders.rows.forEach((folder) => {
|
|||
|
|
const parent = folder.parent_id
|
|||
|
|
? `Parent #${folder.parent_id}`
|
|||
|
|
: "Root";
|
|||
|
|
console.log(
|
|||
|
|
` - [ID: ${folder.id}] ${folder.name} (${folder.file_count} files) - ${parent}`
|
|||
|
|
);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Test 8: Test folder query with file counts
|
|||
|
|
console.log("\n8️⃣ Testing folder query with file counts...");
|
|||
|
|
const foldersWithCounts = await pool.query(`
|
|||
|
|
SELECT
|
|||
|
|
mf.*,
|
|||
|
|
COUNT(u.id) as file_count
|
|||
|
|
FROM media_folders mf
|
|||
|
|
LEFT JOIN uploads u ON u.folder_id = mf.id
|
|||
|
|
GROUP BY mf.id
|
|||
|
|
ORDER BY mf.created_at DESC
|
|||
|
|
`);
|
|||
|
|
console.log(
|
|||
|
|
` ✅ Query returned ${foldersWithCounts.rows.length} folders with accurate file counts`
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// Test 9: Test cascade delete (dry run)
|
|||
|
|
console.log("\n9️⃣ Testing cascade delete rules...");
|
|||
|
|
const cascadeRules = await pool.query(`
|
|||
|
|
SELECT
|
|||
|
|
tc.constraint_name,
|
|||
|
|
rc.delete_rule
|
|||
|
|
FROM information_schema.table_constraints tc
|
|||
|
|
JOIN information_schema.referential_constraints rc
|
|||
|
|
ON tc.constraint_name = rc.constraint_name
|
|||
|
|
WHERE tc.table_name IN ('uploads', 'media_folders')
|
|||
|
|
AND tc.constraint_type = 'FOREIGN KEY'
|
|||
|
|
`);
|
|||
|
|
console.log(" ✅ Delete rules:");
|
|||
|
|
cascadeRules.rows.forEach((rule) => {
|
|||
|
|
console.log(` - ${rule.constraint_name}: ${rule.delete_rule}`);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Test 10: Verify indexes
|
|||
|
|
console.log("\n🔟 Checking database indexes...");
|
|||
|
|
const indexes = await pool.query(`
|
|||
|
|
SELECT
|
|||
|
|
tablename,
|
|||
|
|
indexname,
|
|||
|
|
indexdef
|
|||
|
|
FROM pg_indexes
|
|||
|
|
WHERE tablename IN ('uploads', 'media_folders')
|
|||
|
|
AND schemaname = 'public'
|
|||
|
|
ORDER BY tablename, indexname
|
|||
|
|
`);
|
|||
|
|
console.log(" ✅ Indexes:");
|
|||
|
|
indexes.rows.forEach((idx) => {
|
|||
|
|
console.log(` - ${idx.tablename}.${idx.indexname}`);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
console.log("\n" + "=".repeat(60));
|
|||
|
|
console.log("✅ All database tests passed successfully!\n");
|
|||
|
|
|
|||
|
|
console.log("📊 Summary:");
|
|||
|
|
console.log(` - Database: Connected and operational`);
|
|||
|
|
console.log(
|
|||
|
|
` - Tables: uploads (${uploadsSchema.rows.length} columns), media_folders (${foldersSchema.rows.length} columns)`
|
|||
|
|
);
|
|||
|
|
console.log(
|
|||
|
|
` - Data: ${fileCount.rows[0].count} files, ${folderCount.rows[0].count} folders`
|
|||
|
|
);
|
|||
|
|
console.log(
|
|||
|
|
` - Constraints: ${constraints.rows.length} foreign keys configured`
|
|||
|
|
);
|
|||
|
|
console.log(` - Indexes: ${indexes.rows.length} indexes for performance`);
|
|||
|
|
console.log(
|
|||
|
|
"\n✅ Media library database is properly configured and operational!\n"
|
|||
|
|
);
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error("\n❌ Database test failed:", error.message);
|
|||
|
|
console.error("Stack trace:", error.stack);
|
|||
|
|
process.exit(1);
|
|||
|
|
} finally {
|
|||
|
|
await pool.end();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Run tests
|
|||
|
|
testDatabaseOperations();
|