testing-guide.md 24 KB

QDR Temporary Shared Storage - Testing Guide

This document outlines a comprehensive testing strategy for the QDR Temporary Shared Storage plugin to ensure all components function correctly after the latest updates and refactoring.

Manual Testing Procedures

1. Installation and Activation Testing

  • Upload plugin to a test WordPress site (WordPress 6.8.1+ and PHP 8.2+)
  • Activate the plugin
  • Verify that activation creates:
    • The custom database table with proper schema (including fileid field)
    • The required upload directories with .htaccess protection
    • The download page with [qdr_tss_download] shortcode
    • Default plugin settings including auto-generated API key
    • Scheduled cron job for cleanup

2. Admin Interface Testing

2.1 Settings Page (Settings > Shared Storage)

  • Navigate to Settings > Shared Storage
  • Verify all settings fields are displayed correctly:
    • API Key field with current key displayed
    • Media Category field
    • Maximum Storage Size field with current usage display
  • Test generating a new API key using "Generate New Key" button
  • Change settings values and save
  • Refresh page and verify settings were saved correctly
  • Test the "Purge Expired Files" button functionality
  • Verify storage usage bar displays correctly

2.2 Media Page (Media > Shared Storage)

  • Navigate to Media > Shared Storage
  • Verify the table loads correctly (initially empty)
  • Upload files through the REST API (see REST API testing section)
  • Verify files appear in the table with correct data:
    • File ID (new fileid format)
    • Original file name
    • Description and message
    • Active from/to dates
    • Reference
    • Upload date
    • Download count
    • Last download
  • Test sorting by different columns (click column headers)
  • Test pagination with multiple files
  • Test searching by filename and reference
  • Test new action buttons:
    • Copy Permalink button - verify it copies the correct URL to clipboard
    • Force Activate button - verify it resets the active period from current time
    • View Details button - verify modal opens with complete file information
    • Edit button - verify modal allows property editing
    • Delete button - verify confirmation dialog and file deletion

2.3 Enhanced Admin Features

  • Test responsive design on mobile devices
  • Verify modal dialogs work correctly
  • Test AJAX operations don't cause page refreshes
  • Verify loading indicators appear during operations
  • Test error handling in admin interface

3. REST API Testing

3.1 Authentication

  • Attempt API requests without an API key (should return 403)
  • Attempt API requests with an invalid API key (should return 403)
  • Attempt API requests with the correct API key (should succeed)
  • Test API key validation with various header formats

3.2 File Upload (Chunked)

Test the complete chunked upload process:

  • Initialize upload (POST /upload/init):
    • Valid file name and size
    • Invalid parameters (missing fields)
    • File size exceeding storage limit
  • Upload chunks (POST /upload/chunk):
    • Sequential chunk upload
    • Out-of-order chunk upload
    • Missing chunks
    • Invalid chunk data
  • Finalize upload (POST /upload/finalize):
    • All required fields provided
    • Missing required fields
    • Invalid date formats
    • Verify file stored in correct date-based directory structure
    • Verify database record created with new fileid format
    • Verify response includes fileid, permalink, and shortcode

Test with various file types and sizes:

  • Small file (< 1MB) - should work with single chunk
  • Medium file (1-10MB) - test multiple chunks
  • Large file (> 10MB) - test many chunks
  • PDF documents
  • Image files (JPEG, PNG, GIF)
  • Office documents (DOCX, XLSX, PPTX)
  • Archive files (ZIP, RAR)
  • Text files
  • Video files (if within size limits)

3.3 File Management API

  • Get file details (GET /media/{fileid}):
    • Valid file ID
    • Invalid/non-existent file ID
    • Verify password is not included in response
  • Update file properties (PUT /media/{fileid}):
    • Update message
    • Update active dates
    • Update reference
    • Update description
    • Invalid date formats
    • Empty update request
  • Delete file (DELETE /media/{fileid}):
    • Valid file ID
    • Invalid/non-existent file ID
    • Verify file deleted from storage
    • Verify database record removed
  • List files (GET /media):
    • Default pagination
    • Custom pagination parameters
    • Sorting by different fields
    • Filtering by filename
    • Filtering by reference
    • Combined filters and sorting

4. Public Interface Testing

4.1 Download Page Functionality

  • Access download page at /shared-file-download/
  • Direct file access via /shared-file-download/{fileid}
  • Download form testing:
    • Correct password and reference (should allow download)
    • Incorrect password (should show error)
    • Incorrect reference when required (should show error)
    • Missing password (should show error)
    • Empty form submission
  • File status testing:
    • Active file (current time between active_from and active_to)
    • Expired file (current time > active_to)
    • Future file (current time < active_from)
  • Download process:
    • Verify correct file headers are sent
    • Verify original filename is preserved
    • Verify download count increments
    • Verify last download time updates
  • Shortcode testing:
    • [qdr_tss_download] - generic form
    • [qdr_tss_download id="fileid"] - specific file form

4.2 URL Rewriting and Pretty URLs

  • Test pretty URLs: /shared-file-download/{fileid}
  • Test legacy URLs with query parameters
  • Test URL redirection and rewrite rules
  • Verify 404 handling for invalid file IDs

5. Scheduled Tasks and Cleanup Testing

  • Manual cron trigger:
    • Use admin "Purge Expired Files" button
    • Verify expired files are deleted from storage
    • Verify expired records are deleted from database
    • Verify non-expired files are not affected
  • Automatic cron job:
    • Wait for hourly cron execution or trigger manually
    • Test with files having different expiration dates
    • Verify empty directories are cleaned up
  • Storage management:
    • Test storage limit enforcement
    • Verify storage usage calculations
    • Test behavior when approaching storage limit

6. Security Testing

6.1 File Access Security

  • Direct file access attempts:
    • Try to access files directly via URL (should be blocked)
    • Verify .htaccess rules are working
    • Test directory browsing prevention
  • Download security:
    • Password protection enforcement
    • Reference code validation
    • Time-based access control
    • CSRF protection on download forms

6.2 API Security

  • Authentication bypass attempts:
    • Missing API key header
    • Invalid API key values
    • API key in wrong header format
  • Input validation:
    • SQL injection attempts in API parameters
    • XSS attempts in file names and descriptions
    • Path traversal attempts in file operations
    • Large payload attacks
  • AJAX Security:
    • Nonce verification for admin AJAX requests
    • Permission checks for admin operations
    • Rate limiting considerations

7. Edge Cases and Error Handling

7.1 File System Edge Cases

  • Storage scenarios:
    • Storage limit reached during upload
    • Disk space full
    • Permission issues on upload directory
    • Corrupted chunk uploads
  • Concurrent operations:
    • Multiple simultaneous uploads
    • Upload while cleanup is running
    • Edit file while someone is downloading

7.2 Database Edge Cases

  • Data integrity:
    • Database connection failures during upload
    • Orphaned files (in storage but not in database)
    • Orphaned records (in database but no file)
    • Character encoding issues in file names

7.3 Network and Performance

  • Network issues:
    • Interrupted chunk uploads
    • Slow network connections
    • Timeout scenarios
  • Performance:
    • Large file uploads (test with largest possible file)
    • High number of files in storage
    • Database query performance with many records

8. Internationalization Testing

  • Translation testing:
    • Switch WordPress language to Czech (cs_CZ)
    • Verify admin interface displays Czech translations
    • Test download page in Czech
    • Verify JavaScript strings are translated
  • Character encoding:
    • Upload files with non-ASCII names
    • Test descriptions with special characters
    • Test various character sets

Automated Testing

Setting Up the Test Environment

  1. Install PHPUnit and Dependencies:

    composer require --dev phpunit/phpunit
    composer require --dev brain/monkey
    composer require --dev mockery/mockery
    
  2. WordPress Test Library Setup:

    bash bin/install-wp-tests.sh wordpress_test root '' localhost latest
    
  3. PHPUnit Configuration (phpunit.xml):

    <phpunit
       bootstrap="tests/bootstrap.php"
       backupGlobals="false"
       colors="true"
       convertErrorsToExceptions="true"
       convertNoticesToExceptions="true"
       convertWarningsToExceptions="true"
    >
       <testsuites>
           <testsuite name="QDR Temporary Shared Storage Test Suite">
               <directory prefix="test-" suffix=".php">./tests/</directory>
           </testsuite>
       </testsuites>
       <filter>
           <whitelist>
               <directory suffix=".php">./includes/</directory>
               <directory suffix=".php">./admin/</directory>
               <directory suffix=".php">./public/</directory>
               <directory suffix=".php">./rest-api/</directory>
           </whitelist>
       </filter>
    </phpunit>
    

Unit Test Examples

Database Manager Tests (tests/test-db-manager.php)

<?php
class TestDbManager extends WP_UnitTestCase {
    private $db_manager;

    public function setUp(): void {
        parent::setUp();
        $this->db_manager = QDR_TSS_DB_Manager::instance();
    }

    public function test_create_table() {
        global $wpdb;
        
        $table_name = $wpdb->prefix . QDR_TSS_PLUGIN_TABLE;
        $wpdb->query("DROP TABLE IF EXISTS $table_name");
        
        $result = $this->db_manager->create_table();
        
        $table_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'") === $table_name;
        $this->assertTrue($table_exists);
        
        // Test table structure
        $columns = $wpdb->get_results("DESCRIBE $table_name");
        $column_names = wp_list_pluck($columns, 'Field');
        
        $this->assertContains('fileid', $column_names);
        $this->assertContains('original_file_name', $column_names);
        $this->assertContains('active_from', $column_names);
        $this->assertContains('active_to', $column_names);
    }

    public function test_insert_and_get_item() {
        $data = array(
            'fileid' => 'test123456789',
            'original_file_name' => 'test.pdf',
            'description' => 'Test description',
            'message' => 'Test message',
            'active_from' => current_time('mysql'),
            'active_to' => date('Y-m-d H:i:s', strtotime('+30 days')),
            'password' => wp_hash_password('test_password'),
            'reference' => 'TEST123',
            'file_path' => 'test/path/test.pdf',
            'file_size' => 1024
        );
        
        $item_id = $this->db_manager->insert_item($data);
        $this->assertNotFalse($item_id);
        $this->assertGreaterThan(0, $item_id);
        
        // Test get by ID
        $item = $this->db_manager->get_item($item_id);
        $this->assertEquals($data['fileid'], $item->fileid);
        $this->assertEquals($data['original_file_name'], $item->original_file_name);
        
        // Test get by fileid
        $item_by_fileid = $this->db_manager->get_item_by_fileid($data['fileid']);
        $this->assertEquals($item_id, $item_by_fileid->id);
    }

    public function test_get_items_with_pagination() {
        // Insert test data
        for ($i = 1; $i <= 15; $i++) {
            $this->db_manager->insert_item([
                'fileid' => 'test' . $i,
                'original_file_name' => "test{$i}.pdf",
                'description' => "Test file {$i}",
                'active_from' => current_time('mysql'),
                'active_to' => date('Y-m-d H:i:s', strtotime('+30 days')),
                'password' => wp_hash_password('test'),
                'file_path' => "test/test{$i}.pdf",
                'file_size' => 1024 * $i
            ]);
        }
        
        // Test pagination
        $result = $this->db_manager->get_items(['per_page' => 10, 'page' => 1]);
        $this->assertEquals(10, count($result['items']));
        $this->assertEquals(15, $result['total']);
        $this->assertEquals(2, $result['total_pages']);
        
        // Test second page
        $result = $this->db_manager->get_items(['per_page' => 10, 'page' => 2]);
        $this->assertEquals(5, count($result['items']));
    }
}

File Manager Tests (tests/test-file-manager.php)

<?php
class TestFileManager extends WP_UnitTestCase {
    private $file_manager;

    public function setUp(): void {
        parent::setUp();
        $this->file_manager = QDR_TSS_File_Manager::instance();
    }

    public function test_init_file_system() {
        $result = $this->file_manager->init_file_system();
        
        $this->assertTrue($result);
        $this->assertTrue(file_exists(QDR_TSS_UPLOADS_DIR));
        $this->assertTrue(file_exists(QDR_TSS_UPLOADS_DIR . 'tmp/'));
        $this->assertTrue(file_exists(QDR_TSS_UPLOADS_DIR . '.htaccess'));
    }

    public function test_generate_fileid() {
        $fileid1 = $this->file_manager->generate_fileid();
        $fileid2 = $this->file_manager->generate_fileid();
        
        $this->assertNotEquals($fileid1, $fileid2);
        $this->assertTrue(strlen($fileid1) > 40);
        $this->assertMatchesRegularExpression('/^[a-zA-Z0-9]+$/', $fileid1);
    }

    public function test_chunked_upload_process() {
        // Initialize upload
        $upload_id = $this->file_manager->init_chunked_upload();
        $this->assertNotFalse($upload_id);
        
        // Create test chunk data
        $chunk_data = 'This is test chunk data';
        $temp_file = tempnam(sys_get_temp_dir(), 'test_chunk');
        file_put_contents($temp_file, $chunk_data);
        
        $file_array = [
            'tmp_name' => $temp_file,
            'name' => 'test_chunk',
            'size' => strlen($chunk_data)
        ];
        
        // Store chunk
        $result = $this->file_manager->store_chunk($upload_id, 0, $file_array);
        $this->assertTrue($result);
        
        // Check all chunks (should return true for single chunk)
        $all_chunks = $this->file_manager->check_all_chunks($upload_id, 1);
        $this->assertTrue($all_chunks);
        
        // Finalize upload
        $final_result = $this->file_manager->finalize_chunked_upload($upload_id, 1, 'test.txt');
        $this->assertIsArray($final_result);
        $this->assertArrayHasKey('file_path', $final_result);
        $this->assertArrayHasKey('file_size', $final_result);
        
        // Cleanup
        unlink($temp_file);
    }
}

REST API Tests (tests/test-rest-api.php)

<?php
class TestRestApi extends WP_UnitTestCase {
    private $server;
    private $api_key;

    public function setUp(): void {
        parent::setUp();
        global $wp_rest_server;
        $this->server = $wp_rest_server = new WP_REST_Server;
        do_action('rest_api_init');
        
        // Set up API key
        $settings = QDR_TSS_Settings::instance();
        $this->api_key = $settings->generate_api_key();
        $settings->update_setting('api_key', $this->api_key);
    }

    public function test_unauthorized_access() {
        $request = new WP_REST_Request('GET', '/qdr-tss/v1/media');
        $response = $this->server->dispatch($request);
        
        $this->assertEquals(403, $response->get_status());
    }

    public function test_authorized_access() {
        $request = new WP_REST_Request('GET', '/qdr-tss/v1/media');
        $request->add_header('X-Api-Key', $this->api_key);
        $response = $this->server->dispatch($request);
        
        $this->assertEquals(200, $response->get_status());
    }

    public function test_upload_init() {
        $request = new WP_REST_Request('POST', '/qdr-tss/v1/upload/init');
        $request->add_header('X-Api-Key', $this->api_key);
        $request->set_param('original_file_name', 'test.pdf');
        $request->set_param('file_size', 1024);
        
        $response = $this->server->dispatch($request);
        
        $this->assertEquals(200, $response->get_status());
        
        $data = $response->get_data();
        $this->assertArrayHasKey('upload_id', $data);
        $this->assertArrayHasKey('status', $data);
        $this->assertEquals('initialized', $data['status']);
    }
}

Integration Testing Scripts

API Client Test Script (tests/integration/api-client-test.php)

<?php
/**
 * Integration test script for API client functionality
 */

class QDR_TSS_Integration_Test {
    private $api_url;
    private $api_key;
    
    public function __construct($api_url, $api_key) {
        $this->api_url = rtrim($api_url, '/');
        $this->api_key = $api_key;
    }
    
    public function run_all_tests() {
        echo "Starting QDR TSS API Integration Tests\n";
        echo "=====================================\n\n";
        
        try {
            $this->test_complete_upload_workflow();
            $this->test_file_management();
            $this->test_error_handling();
            
            echo "\n✅ All tests passed!\n";
        } catch (Exception $e) {
            echo "\n❌ Test failed: " . $e->getMessage() . "\n";
        }
    }
    
    private function test_complete_upload_workflow() {
        echo "Testing complete upload workflow...\n";
        
        // Create test file
        $test_content = "This is a test file for QDR TSS integration testing.";
        $temp_file = tempnam(sys_get_temp_dir(), 'qdr_test');
        file_put_contents($temp_file, $test_content);
        
        try {
            $result = $this->upload_file($temp_file, 'Integration Test File', 'test_password', 'INT_TEST_001');
            
            echo "  ✓ File uploaded successfully\n";
            echo "  ✓ File ID: {$result['fileid']}\n";
            echo "  ✓ Permalink: {$result['permalink']}\n";
            
            // Store fileid for later tests
            $this->test_fileid = $result['fileid'];
            
        } finally {
            unlink($temp_file);
        }
    }
    
    private function test_file_management() {
        echo "Testing file management operations...\n";
        
        if (!isset($this->test_fileid)) {
            throw new Exception("No test file ID available");
        }
        
        // Test get file details
        $details = $this->get_file_details($this->test_fileid);
        echo "  ✓ Retrieved file details\n";
        
        // Test update file
        $updated = $this->update_file($this->test_fileid, [
            'message' => 'Updated via integration test'
        ]);
        echo "  ✓ Updated file properties\n";
        
        // Test delete file
        $this->delete_file($this->test_fileid);
        echo "  ✓ Deleted file\n";
    }
    
    private function test_error_handling() {
        echo "Testing error handling...\n";
        
        // Test with invalid API key
        $old_key = $this->api_key;
        $this->api_key = 'invalid_key';
        
        try {
            $this->get_file_list();
            throw new Exception("Should have failed with invalid API key");
        } catch (Exception $e) {
            if (strpos($e->getMessage(), '403') !== false) {
                echo "  ✓ Invalid API key properly rejected\n";
            } else {
                throw $e;
            }
        }
        
        $this->api_key = $old_key;
        
        // Test with non-existent file
        try {
            $this->get_file_details('nonexistent123');
            throw new Exception("Should have failed with non-existent file");
        } catch (Exception $e) {
            if (strpos($e->getMessage(), '404') !== false) {
                echo "  ✓ Non-existent file properly handled\n";
            } else {
                throw $e;
            }
        }
    }
    
    // API helper methods...
    private function upload_file($file_path, $description, $password, $reference) {
        // Implementation similar to previous examples
        // ... (chunked upload implementation)
    }
    
    private function get_file_details($fileid) {
        // ... (implementation)
    }
    
    private function update_file($fileid, $data) {
        // ... (implementation)
    }
    
    private function delete_file($fileid) {
        // ... (implementation)
    }
    
    private function get_file_list() {
        // ... (implementation)
    }
}

// Run tests
$tester = new QDR_TSS_Integration_Test(
    'https://your-test-site.com/wp-json/qdr-tss/v1',
    'your-test-api-key'
);

$tester->run_all_tests();

Performance Testing

Load Testing Script

#!/bin/bash
# Simple load test for file uploads

API_URL="https://your-test-site.com/wp-json/qdr-tss/v1"
API_KEY="your-test-api-key"
CONCURRENT_UPLOADS=5
TEST_FILE_SIZE=1048576  # 1MB

echo "Starting load test with $CONCURRENT_UPLOADS concurrent uploads"

for i in $(seq 1 $CONCURRENT_UPLOADS); do
    (
        # Create test file
        dd if=/dev/zero of="test_file_$i.bin" bs=$TEST_FILE_SIZE count=1 2>/dev/null
        
        # Upload file
        echo "Starting upload $i"
        start_time=$(date +%s)
        
        # Your upload implementation here
        # ...
        
        end_time=$(date +%s)
        duration=$((end_time - start_time))
        
        echo "Upload $i completed in ${duration}s"
        
        # Cleanup
        rm "test_file_$i.bin"
    ) &
done

wait
echo "All uploads completed"

Browser Testing Checklist

Admin Interface Browser Testing

Test the admin interface across different browsers:

  • Chrome (latest)
  • Firefox (latest)
  • Safari (latest)
  • Edge (latest)
  • Mobile Chrome
  • Mobile Safari

For each browser, test:

  • Media page layout and functionality
  • Settings page functionality
  • Modal dialogs
  • Action buttons (Copy Permalink, Force Activate)
  • AJAX operations
  • JavaScript error console (should be clean)

Final Testing Checklist

Before deploying to production:

  • All manual tests pass
  • Unit tests pass (./vendor/bin/phpunit)
  • Integration tests pass
  • Security tests pass
  • Performance is acceptable
  • Documentation is updated
  • Translation files are current
  • Browser compatibility verified
  • Mobile responsiveness tested
  • Error logging reviewed
  • Backup and restore procedures tested

Troubleshooting Common Issues

Database Issues

  • Problem: Table not created during activation
  • Solution: Check database user permissions, verify WordPress table prefix

File Upload Issues

  • Problem: Chunks not uploading
  • Solution: Check file permissions, PHP memory limits, server timeout settings

API Issues

  • Problem: 403 errors with valid API key
  • Solution: Verify header format, check for mod_security rules, test with different HTTP clients

Download Issues

  • Problem: Files not downloading
  • Solution: Check file permissions, verify .htaccess rules, test rewrite rules

This comprehensive testing approach ensures the plugin works reliably across all components and integrates properly with WordPress and client applications.