feat: Add comprehensive documentation suite and reorganize project structure
- Created complete documentation in docs/ directory - Added PROJECT_OVERVIEW.md with feature highlights and getting started guide - Added ARCHITECTURE.md with system design and technical details - Added SECURITY.md with comprehensive security implementation guide - Added DEVELOPMENT.md with development workflows and best practices - Added DEPLOYMENT.md with production deployment instructions - Added API.md with complete REST API documentation - Added CONTRIBUTING.md with contribution guidelines - Added CHANGELOG.md with version history and migration notes - Reorganized all documentation files into docs/ directory for better organization - Updated README.md with proper documentation links and quick navigation - Enhanced project structure with professional documentation standards
This commit is contained in:
157
tests/Unit/ContentUploadTest.php
Normal file
157
tests/Unit/ContentUploadTest.php
Normal file
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ContentUploadTest extends TestCase
|
||||
{
|
||||
private $storageDir;
|
||||
private $originalUploadDir;
|
||||
private $originalUploadUrl;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->storageDir = __DIR__ . '/../temp/uploads';
|
||||
if (!is_dir($this->storageDir)) {
|
||||
mkdir($this->storageDir, 0755, true);
|
||||
}
|
||||
|
||||
global $cfg;
|
||||
$this->originalUploadDir = $cfg['upload_files_dir'] ?? null;
|
||||
$this->originalUploadUrl = $cfg['upload_files_url'] ?? null;
|
||||
$cfg['upload_files_dir'] = $this->storageDir;
|
||||
$cfg['upload_files_url'] = 'https://example.com/uploads';
|
||||
|
||||
// Reset singleton for clean state between tests
|
||||
$reflection = new ReflectionProperty(VContent::class, 'instance');
|
||||
$reflection->setAccessible(true);
|
||||
$reflection->setValue(null);
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
global $cfg;
|
||||
if ($this->originalUploadDir !== null) {
|
||||
$cfg['upload_files_dir'] = $this->originalUploadDir;
|
||||
}
|
||||
if ($this->originalUploadUrl !== null) {
|
||||
$cfg['upload_files_url'] = $this->originalUploadUrl;
|
||||
}
|
||||
|
||||
if (is_dir($this->storageDir)) {
|
||||
$this->removeDirectory($this->storageDir);
|
||||
}
|
||||
|
||||
$reflection = new ReflectionProperty(VContent::class, 'instance');
|
||||
$reflection->setAccessible(true);
|
||||
$reflection->setValue(null);
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testHandleUploadStoresFileSuccessfully(): void
|
||||
{
|
||||
$content = VContent::getInstance();
|
||||
|
||||
$tmpFile = tempnam(sys_get_temp_dir(), 'upload');
|
||||
file_put_contents($tmpFile, str_repeat('A', 1024));
|
||||
|
||||
$file = [
|
||||
'name' => 'test-video.mp4',
|
||||
'tmp_name' => $tmpFile,
|
||||
'size' => filesize($tmpFile),
|
||||
'error' => UPLOAD_ERR_OK,
|
||||
'type' => 'video/mp4'
|
||||
];
|
||||
|
||||
$result = $content->handleUpload($file, 123, [
|
||||
'type' => 'video',
|
||||
'store_metadata' => false
|
||||
]);
|
||||
|
||||
$this->assertTrue($result['success'], 'Expected upload to succeed');
|
||||
$this->assertArrayHasKey('data', $result);
|
||||
|
||||
$data = $result['data'];
|
||||
$this->assertEquals('video', $data['type']);
|
||||
$this->assertNotEmpty($data['stored_path']);
|
||||
|
||||
$storedPath = $this->storageDir . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $data['stored_path']);
|
||||
$this->assertFileExists($storedPath, 'Stored file should exist on disk');
|
||||
|
||||
$progress = $content->getUploadProgress($data['upload_id']);
|
||||
$this->assertEquals('completed', $progress['status']);
|
||||
$this->assertEquals(100, $progress['progress']);
|
||||
}
|
||||
|
||||
public function testHandleUploadRejectsInvalidMimeType(): void
|
||||
{
|
||||
$content = VContent::getInstance();
|
||||
|
||||
$tmpFile = tempnam(sys_get_temp_dir(), 'upload');
|
||||
file_put_contents($tmpFile, "<?php echo 'malicious'; ?>");
|
||||
|
||||
$file = [
|
||||
'name' => 'malware.php',
|
||||
'tmp_name' => $tmpFile,
|
||||
'size' => filesize($tmpFile),
|
||||
'error' => UPLOAD_ERR_OK,
|
||||
'type' => 'text/x-php'
|
||||
];
|
||||
|
||||
$result = $content->handleUpload($file, 321, [
|
||||
'type' => 'document',
|
||||
'store_metadata' => false
|
||||
]);
|
||||
|
||||
$this->assertFalse($result['success'], 'Upload should fail for dangerous MIME types');
|
||||
$this->assertNotEmpty($result['message']);
|
||||
}
|
||||
|
||||
public function testProgressLifecycle(): void
|
||||
{
|
||||
$content = VContent::getInstance();
|
||||
$uploadId = 'progress_test_' . bin2hex(random_bytes(4));
|
||||
|
||||
$content->initProgress($uploadId, 2000);
|
||||
$progress = $content->getUploadProgress($uploadId);
|
||||
$this->assertEquals('in_progress', $progress['status']);
|
||||
$this->assertEquals(0, $progress['progress']);
|
||||
|
||||
$content->updateProgress($uploadId, 1000, 2000);
|
||||
$progress = $content->getUploadProgress($uploadId);
|
||||
$this->assertEquals(50, $progress['progress']);
|
||||
|
||||
$content->completeProgress($uploadId);
|
||||
$progress = $content->getUploadProgress($uploadId);
|
||||
$this->assertEquals('completed', $progress['status']);
|
||||
$this->assertEquals(100, $progress['progress']);
|
||||
|
||||
$content->cleanupProgress($uploadId);
|
||||
$progress = $content->getUploadProgress($uploadId);
|
||||
$this->assertEquals('unknown', $progress['status']);
|
||||
}
|
||||
|
||||
private function removeDirectory($dir): void
|
||||
{
|
||||
if (!is_dir($dir)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$items = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
|
||||
RecursiveIteratorIterator::CHILD_FIRST
|
||||
);
|
||||
|
||||
foreach ($items as $item) {
|
||||
if ($item->isDir()) {
|
||||
@rmdir($item->getRealPath());
|
||||
} else {
|
||||
@unlink($item->getRealPath());
|
||||
}
|
||||
}
|
||||
|
||||
@rmdir($dir);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user