/** * WordPress Utilities for HVAC Testing Framework * * Provides WordPress-specific functionality: * - WP-CLI integration for database management * - Cache clearing and rewrite rule flushing * - Test data seeding and cleanup * - User management and role assignments * * @package HVAC_Community_Events * @version 2.0.0 * @created 2025-08-27 */ const { exec } = require('child_process'); const { promisify } = require('util'); const fs = require('fs').promises; const path = require('path'); const ConfigManager = require('../core/ConfigManager'); const execAsync = promisify(exec); class WordPressUtils { constructor() { this.config = ConfigManager; this.wpCliPath = this.config.get('wordpress.cliPath', 'wp'); this.baseUrl = this.config.get('app.baseUrl'); this.timeout = 30000; // 30 seconds for WP-CLI commands } /** * Execute WP-CLI command with error handling */ async executeWpCli(command, options = {}) { const fullCommand = `${this.wpCliPath} ${command} --url="${this.baseUrl}" --allow-root`; try { console.log(`๐Ÿ”ง WP-CLI: ${command}`); const { stdout, stderr } = await execAsync(fullCommand, { timeout: options.timeout || this.timeout, cwd: options.cwd || process.cwd() }); if (stderr && !options.ignoreStderr) { console.warn(`โš ๏ธ WP-CLI Warning: ${stderr}`); } return stdout.trim(); } catch (error) { console.error(`โŒ WP-CLI Error (${command}):`, error.message); throw new Error(`WP-CLI command failed: ${command} - ${error.message}`); } } /** * Flush WordPress rewrite rules */ async flushRewriteRules() { try { await this.executeWpCli('rewrite flush'); console.log('โœ… Rewrite rules flushed'); } catch (error) { console.warn('Could not flush rewrite rules:', error.message); } } /** * Clear WordPress cache (if caching plugin is available) */ async clearCache() { const cacheCommands = [ 'cache flush', // Object cache 'transient delete --all', // Transients 'rewrite flush' // Rewrite rules ]; for (const command of cacheCommands) { try { await this.executeWpCli(command, { ignoreStderr: true }); } catch (error) { // Cache clearing might fail if no cache plugin is installed console.log(`Cache command skipped: ${command}`); } } console.log('๐Ÿ—‘๏ธ WordPress cache cleared'); } /** * Create test user with specific role */ async createTestUser(username, email, role, password = null) { // Generate password if not provided if (!password) { password = `Test${role}123!`; } try { // Check if user already exists const userExists = await this.executeWpCli(`user get ${username}`, { ignoreStderr: true }) .then(() => true) .catch(() => false); if (userExists) { console.log(`๐Ÿ‘ค User already exists: ${username}`); // Update user role if needed await this.executeWpCli(`user set-role ${username} ${role}`); return { username, email, password, existed: true }; } // Create new user await this.executeWpCli(`user create ${username} ${email} --role=${role} --user_pass="${password}"`); console.log(`โœ… Created test user: ${username} (${role})`); return { username, email, password, existed: false }; } catch (error) { throw new Error(`Failed to create test user ${username}: ${error.message}`); } } /** * Delete test user */ async deleteTestUser(username) { try { await this.executeWpCli(`user delete ${username} --yes`); console.log(`๐Ÿ—‘๏ธ Deleted test user: ${username}`); } catch (error) { console.warn(`Could not delete user ${username}:`, error.message); } } /** * Set up test data for HVAC plugin */ async seedTestData(testId) { console.log(`๐ŸŒฑ Seeding test data for: ${testId}`); try { // Create test events await this.createTestEvents(testId); // Create test venues await this.createTestVenues(testId); // Create test organizers await this.createTestOrganizers(testId); console.log('โœ… Test data seeded successfully'); } catch (error) { console.error('โŒ Failed to seed test data:', error.message); throw error; } } /** * Create test events */ async createTestEvents(testId) { const events = [ { title: `Test HVAC Training Event - ${testId}`, content: 'Test event for automated testing', status: 'publish', meta: { '_hvac_test_id': testId, '_hvac_test_event': '1' } } ]; for (const event of events) { try { // Create the event post const postCommand = `post create --post_type=tribe_events --post_title="${event.title}" --post_content="${event.content}" --post_status=${event.status} --porcelain`; const postId = await this.executeWpCli(postCommand); // Add meta fields for (const [key, value] of Object.entries(event.meta)) { await this.executeWpCli(`post meta add ${postId} ${key} "${value}"`); } console.log(`๐Ÿ“… Created test event: ${event.title} (ID: ${postId})`); } catch (error) { console.warn(`Could not create test event: ${event.title}`, error.message); } } } /** * Create test venues */ async createTestVenues(testId) { const venues = [ { title: `Test Venue - ${testId}`, content: 'Test venue for automated testing', meta: { '_hvac_test_id': testId, '_VenueAddress': '123 Test Street', '_VenueCity': 'Test City', '_VenueState': 'Test State', '_VenueZip': '12345' } } ]; for (const venue of venues) { try { const postCommand = `post create --post_type=tribe_venue --post_title="${venue.title}" --post_content="${venue.content}" --post_status=publish --porcelain`; const postId = await this.executeWpCli(postCommand); // Add meta fields for (const [key, value] of Object.entries(venue.meta)) { await this.executeWpCli(`post meta add ${postId} ${key} "${value}"`); } console.log(`๐Ÿข Created test venue: ${venue.title} (ID: ${postId})`); } catch (error) { console.warn(`Could not create test venue: ${venue.title}`, error.message); } } } /** * Create test organizers */ async createTestOrganizers(testId) { const organizers = [ { title: `Test Organizer - ${testId}`, content: 'Test organizer for automated testing', meta: { '_hvac_test_id': testId, '_OrganizerPhone': '555-123-4567', '_OrganizerEmail': `test-organizer-${testId}@example.com` } } ]; for (const organizer of organizers) { try { const postCommand = `post create --post_type=tribe_organizer --post_title="${organizer.title}" --post_content="${organizer.content}" --post_status=publish --porcelain`; const postId = await this.executeWpCli(postCommand); // Add meta fields for (const [key, value] of Object.entries(organizer.meta)) { await this.executeWpCli(`post meta add ${postId} ${key} "${value}"`); } console.log(`๐Ÿ‘ค Created test organizer: ${organizer.title} (ID: ${postId})`); } catch (error) { console.warn(`Could not create test organizer: ${organizer.title}`, error.message); } } } /** * Clean up test data */ async cleanupTestData(testId) { console.log(`๐Ÿงน Cleaning up test data for: ${testId}`); try { // Find and delete test posts const testPosts = await this.executeWpCli(`post list --meta_key=_hvac_test_id --meta_value="${testId}" --field=ID`); if (testPosts.trim()) { const postIds = testPosts.split('\n').filter(id => id.trim()); for (const postId of postIds) { await this.executeWpCli(`post delete ${postId} --force`); } console.log(`๐Ÿ—‘๏ธ Deleted ${postIds.length} test posts`); } // Clean up orphaned meta await this.executeWpCli(`db query "DELETE FROM wp_postmeta WHERE meta_key = '_hvac_test_id' AND meta_value = '${testId}'"`); } catch (error) { console.warn('Could not fully clean up test data:', error.message); } } /** * Check if plugin is active */ async isPluginActive(pluginName) { try { const activePlugins = await this.executeWpCli('plugin list --status=active --field=name'); return activePlugins.includes(pluginName); } catch (error) { console.warn(`Could not check plugin status for ${pluginName}:`, error.message); return false; } } /** * Activate plugin */ async activatePlugin(pluginName) { try { await this.executeWpCli(`plugin activate ${pluginName}`); console.log(`โœ… Activated plugin: ${pluginName}`); } catch (error) { throw new Error(`Failed to activate plugin ${pluginName}: ${error.message}`); } } /** * Get WordPress version */ async getWordPressVersion() { try { const version = await this.executeWpCli('core version'); return version.trim(); } catch (error) { console.warn('Could not get WordPress version:', error.message); return 'unknown'; } } /** * Update database after schema changes */ async updateDatabase() { try { await this.executeWpCli('core update-db'); console.log('โœ… Database updated'); } catch (error) { console.warn('Could not update database:', error.message); } } /** * Import test data from SQL file */ async importDatabase(sqlFilePath) { try { const absolutePath = path.resolve(sqlFilePath); await this.executeWpCli(`db import "${absolutePath}"`); console.log(`โœ… Imported database from: ${sqlFilePath}`); } catch (error) { throw new Error(`Failed to import database: ${error.message}`); } } /** * Export database to SQL file */ async exportDatabase(outputPath) { try { const absolutePath = path.resolve(outputPath); await this.executeWpCli(`db export "${absolutePath}"`); console.log(`โœ… Exported database to: ${outputPath}`); return absolutePath; } catch (error) { throw new Error(`Failed to export database: ${error.message}`); } } /** * Reset database to clean state */ async resetDatabase() { try { await this.executeWpCli('db reset --yes'); console.log('โœ… Database reset to clean state'); } catch (error) { throw new Error(`Failed to reset database: ${error.message}`); } } /** * Get site URL */ async getSiteUrl() { try { return await this.executeWpCli('option get siteurl'); } catch (error) { console.warn('Could not get site URL:', error.message); return this.baseUrl; } } /** * Set WordPress option */ async setOption(optionName, optionValue) { try { await this.executeWpCli(`option update ${optionName} "${optionValue}"`); console.log(`โœ… Set option: ${optionName} = ${optionValue}`); } catch (error) { console.warn(`Could not set option ${optionName}:`, error.message); } } /** * Get WordPress option */ async getOption(optionName) { try { return await this.executeWpCli(`option get ${optionName}`); } catch (error) { console.warn(`Could not get option ${optionName}:`, error.message); return null; } } /** * Run WordPress cron */ async runCron() { try { await this.executeWpCli('cron event run --due-now'); console.log('โœ… WordPress cron executed'); } catch (error) { console.warn('Could not run cron:', error.message); } } /** * Check WordPress environment health */ async checkHealth() { const health = { wpVersion: 'unknown', pluginsActive: false, databaseConnected: false, writableUploads: false }; try { // Check WordPress version health.wpVersion = await this.getWordPressVersion(); // Check if HVAC plugin is active health.pluginsActive = await this.isPluginActive('hvac-community-events'); // Check database connection try { await this.executeWpCli('db check'); health.databaseConnected = true; } catch (error) { health.databaseConnected = false; } // Check uploads directory try { await this.executeWpCli('eval "wp_upload_dir();"'); health.writableUploads = true; } catch (error) { health.writableUploads = false; } } catch (error) { console.warn('Health check incomplete:', error.message); } console.log('๐Ÿฅ WordPress Health Check:', health); return health; } } module.exports = WordPressUtils;