OneBot: Leantime 改造版源码(BOM/Univer 表格/AI 接管/品牌替换等)
This commit is contained in:
408
tests/Unit/app/Core/Files/FileManagerTest.php
Normal file
408
tests/Unit/app/Core/Files/FileManagerTest.php
Normal file
@@ -0,0 +1,408 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\app\Core\Files;
|
||||
|
||||
use Carbon\CarbonImmutable;
|
||||
use Illuminate\Contracts\Filesystem\Filesystem;
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
use Illuminate\Filesystem\FilesystemManager;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Leantime\Core\Configuration\Environment;
|
||||
use Leantime\Core\Files\Exceptions\FileValidationException;
|
||||
use Leantime\Core\Files\FileManager;
|
||||
use Leantime\Core\Language;
|
||||
use Leantime\Core\Support\CarbonMacros;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Unit\TestCase;
|
||||
|
||||
class FileManagerTest extends TestCase
|
||||
{
|
||||
private $filesystemManager;
|
||||
|
||||
private $config;
|
||||
|
||||
private $fileManager;
|
||||
|
||||
private $storage;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
// Set up session values needed for DateTimeHelper (used by dtHelper())
|
||||
session(['usersettings.timezone' => 'UTC']);
|
||||
session(['usersettings.language' => 'en-US']);
|
||||
session(['usersettings.date_format' => 'Y-m-d']);
|
||||
session(['usersettings.time_format' => 'H:i']);
|
||||
|
||||
// Mock Environment and bind to container for dtHelper()
|
||||
$envMock = $this->createMock(Environment::class);
|
||||
$envMock->defaultTimezone = 'UTC';
|
||||
$envMock->language = 'en-US';
|
||||
app()->instance(Environment::class, $envMock);
|
||||
|
||||
// Mock Language and bind to container
|
||||
$langMock = $this->createMock(Language::class);
|
||||
$langMock->method('__')->willReturnCallback(function ($index) {
|
||||
$map = [
|
||||
'language.dateformat' => 'Y-m-d',
|
||||
'language.timeformat' => 'H:i',
|
||||
];
|
||||
|
||||
return $map[$index] ?? $index;
|
||||
});
|
||||
app()->instance(Language::class, $langMock);
|
||||
|
||||
// Register CarbonMacros for date parsing
|
||||
CarbonImmutable::mixin(new CarbonMacros('UTC', 'en-US', 'Y-m-d', 'H:i'));
|
||||
|
||||
// Mock the FilesystemManager
|
||||
$this->filesystemManager = $this->createMock(FilesystemManager::class);
|
||||
|
||||
// Mock the Environment
|
||||
$this->config = $this->createMock(Environment::class);
|
||||
|
||||
// Mock the storage disk
|
||||
$this->storage = $this->createMock(FilesystemAdapter::class);
|
||||
|
||||
// Setup the FileManager with mocked dependencies
|
||||
$this->fileManager = new FileManager(
|
||||
$this->filesystemManager,
|
||||
$this->config
|
||||
);
|
||||
|
||||
// Create a test file in userfiles directory
|
||||
$testDir = base_path('userfiles/test');
|
||||
if (! is_dir($testDir)) {
|
||||
mkdir($testDir, 0777, true);
|
||||
}
|
||||
file_put_contents($testDir.'/test.txt', 'test content');
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
// Clean up test file
|
||||
@unlink(base_path('userfiles/test/test.txt'));
|
||||
@rmdir(base_path('userfiles/test'));
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function test_upload_file_successfully()
|
||||
{
|
||||
// Mock session data
|
||||
session(['userdata.id' => 123]);
|
||||
|
||||
// Create a mock uploaded file
|
||||
$file = $this->createMock(UploadedFile::class);
|
||||
$file->method('isValid')->willReturn(true);
|
||||
$file->method('getError')->willReturn(0);
|
||||
$file->method('getSize')->willReturn(1000); // 1KB
|
||||
$file->method('getClientOriginalName')->willReturn('test-file.txt');
|
||||
$file->method('getClientOriginalExtension')->willReturn('txt');
|
||||
$file->method('getRealPath')->willReturn(base_path('userfiles/test/test.txt'));
|
||||
|
||||
// Setup filesystem manager to return our mocked storage
|
||||
$this->filesystemManager->method('getDefaultDriver')->willReturn('local');
|
||||
$this->filesystemManager->method('disk')->with('local')->willReturn($this->storage);
|
||||
$this->storage->method('mimeType')->willReturn('text/plain');
|
||||
|
||||
// Setup storage to successfully store the file
|
||||
$this->storage->method('put')->willReturn(true);
|
||||
|
||||
// Mock the PHP stream functions
|
||||
$this->storage->method('put')
|
||||
->with($this->anything(), $this->anything(), $this->anything())
|
||||
->willReturn(true);
|
||||
|
||||
// Execute the method under test
|
||||
$result = $this->fileManager->upload($file);
|
||||
|
||||
// Assert the result is an array with expected keys
|
||||
$this->assertIsArray($result);
|
||||
$this->assertArrayHasKey('fileName', $result);
|
||||
$this->assertArrayHasKey('realName', $result);
|
||||
$this->assertArrayHasKey('extension', $result);
|
||||
$this->assertEquals('test-file.txt', $result['realName']);
|
||||
$this->assertEquals('txt', $result['extension']);
|
||||
}
|
||||
|
||||
public function test_upload_file_with_invalid_file()
|
||||
{
|
||||
// Create a mock uploaded file that is invalid
|
||||
$file = $this->createMock(UploadedFile::class);
|
||||
$file->method('isValid')->willReturn(false);
|
||||
$file->method('getErrorMessage')->willReturn('Test error message');
|
||||
|
||||
// // Mock the Log facade
|
||||
// Log::shouldReceive('error')
|
||||
// ->once()
|
||||
// ->with('File upload failed: Invalid file upload attempt: Test error message', ['exception' => new FileValidationException('test'), 'file'=> '']);
|
||||
|
||||
// Execute the method under test
|
||||
$result = $this->fileManager->upload($file);
|
||||
|
||||
// Assert the result is false
|
||||
$this->assertFalse($result);
|
||||
}
|
||||
|
||||
public function test_upload_file_with_file_too_large()
|
||||
{
|
||||
// Create a mock uploaded file that is too large
|
||||
$file = $this->createMock(UploadedFile::class);
|
||||
$file->method('isValid')->willReturn(true);
|
||||
$file->method('getError')->willReturn(0);
|
||||
$file->method('getSize')->willReturn(PHP_INT_MAX); // Very large file
|
||||
|
||||
// // Mock the Log facade
|
||||
// Log::shouldReceive('error')
|
||||
// ->once()
|
||||
// ->with('File upload failed: File size exceeds the maximum allowed size of', ['exception' => new FileValidationException('test'), 'file'=> '']);
|
||||
|
||||
// Execute the method under test
|
||||
$result = $this->fileManager->upload($file);
|
||||
|
||||
// Assert the result is false
|
||||
$this->assertFalse($result);
|
||||
}
|
||||
|
||||
public function test_get_file_successfully()
|
||||
{
|
||||
// Setup filesystem manager to return our mocked storage
|
||||
$this->filesystemManager->method('getDefaultDriver')->willReturn('local');
|
||||
$this->filesystemManager->method('disk')->with('local')->willReturn($this->storage);
|
||||
|
||||
// Setup storage to successfully find and read the file
|
||||
$this->storage->expects($this->once())->method('exists')->willReturn(true);
|
||||
$this->storage->method('mimeType')->willReturn('text/plain');
|
||||
$this->storage->method('get')->willReturn('file content');
|
||||
$this->storage->method('size')->willReturn(12);
|
||||
$this->storage->method('lastModified')->willReturn(1700000000);
|
||||
|
||||
// Execute the method under test
|
||||
$result = $this->fileManager->getFile('test.txt', 'original-name.txt');
|
||||
|
||||
// Assert the result is a Response with correct headers
|
||||
$this->assertInstanceOf(Response::class, $result);
|
||||
$this->assertEquals('file content', $result->getContent());
|
||||
$this->assertEquals('text/plain', $result->headers->get('Content-Type'));
|
||||
$this->assertEquals('12', $result->headers->get('Content-Length'));
|
||||
$this->assertStringContainsString('original-name.txt', $result->headers->get('Content-Disposition'));
|
||||
}
|
||||
|
||||
public function test_get_file_not_found()
|
||||
{
|
||||
// Setup filesystem manager to return our mocked storage
|
||||
$this->filesystemManager->method('getDefaultDriver')->willReturn('local');
|
||||
$this->filesystemManager->method('disk')->with('local')->willReturn($this->storage);
|
||||
|
||||
// Setup storage to not find the file
|
||||
$this->storage->method('exists')->willReturn(false);
|
||||
|
||||
// Execute the method under test
|
||||
$result = $this->fileManager->getFile('non-existent-file.txt', 'original-name.txt');
|
||||
|
||||
// Assert the result is false
|
||||
$this->assertFalse($result);
|
||||
}
|
||||
|
||||
public function test_get_file_url_local_storage()
|
||||
{
|
||||
// Setup filesystem manager to return our mocked storage
|
||||
$this->filesystemManager->method('getDefaultDriver')->willReturn('local');
|
||||
$this->filesystemManager->method('disk')->with('local')->willReturn($this->storage);
|
||||
$this->storage->method('mimeType')->willReturn('text/plain');
|
||||
|
||||
// Setup storage to successfully find the file and return a URL
|
||||
$this->storage->method('exists')->willReturn(true);
|
||||
$this->storage->method('url')->willReturn('http://example.com/files/test.txt');
|
||||
|
||||
// Configure cache behavior
|
||||
$this->config->method('get')->willReturn(true);
|
||||
Cache::shouldReceive('remember')
|
||||
->once()
|
||||
->andReturn('http://example.com/files/test.txt');
|
||||
|
||||
// Execute the method under test
|
||||
$result = $this->fileManager->getFileUrl('test.txt');
|
||||
|
||||
// Assert the result is the expected URL
|
||||
$this->assertEquals('http://example.com/files/test.txt', $result);
|
||||
}
|
||||
|
||||
public function test_get_file_url_s3_storage()
|
||||
{
|
||||
// Configure environment to use S3
|
||||
$this->config->useS3 = true;
|
||||
$this->config->method('get')->willReturn(60);
|
||||
|
||||
// Setup storage to return mime type
|
||||
$this->storage->method('mimeType')->willReturn('text/plain');
|
||||
// Setup filesystem manager to return our mocked storage
|
||||
$this->filesystemManager->method('disk')->with('s3')->willReturn($this->storage);
|
||||
|
||||
// Setup storage to successfully find the file and return a temporary URL
|
||||
$this->storage->method('exists')->willReturn(true);
|
||||
$this->storage->method('temporaryUrl')->willReturn('https://s3.example.com/files/test.txt?signature=abc123');
|
||||
$this->storage->method('temporaryUrl')->willReturn('https://s3.example.com/files/test.txt?signature=abc123');
|
||||
$this->storage->method('temporaryUrl')->willReturn('https://s3.example.com/files/test.txt?signature=abc123');
|
||||
|
||||
// Execute the method under test
|
||||
$result = $this->fileManager->getFileUrl('test.txt', 's3');
|
||||
|
||||
// Assert the result is the expected URL
|
||||
$this->assertEquals('https://s3.example.com/files/test.txt?signature=abc123', $result);
|
||||
}
|
||||
|
||||
public function test_get_file_url_file_not_found()
|
||||
{
|
||||
// Setup filesystem manager to return our mocked storage
|
||||
$this->filesystemManager->method('getDefaultDriver')->willReturn('local');
|
||||
$this->filesystemManager->method('disk')->with('local')->willReturn($this->storage);
|
||||
|
||||
// Setup storage to not find the file
|
||||
$this->storage->method('exists')->willReturn(false);
|
||||
|
||||
// Execute the method under test
|
||||
$result = $this->fileManager->getFileUrl('non-existent-file.txt');
|
||||
|
||||
// Assert the result is false
|
||||
$this->assertFalse($result);
|
||||
}
|
||||
|
||||
public function test_delete_file_successfully()
|
||||
{
|
||||
// Setup filesystem manager to return our mocked storage
|
||||
$this->filesystemManager->method('getDefaultDriver')->willReturn('local');
|
||||
$this->filesystemManager->method('disk')->with('local')->willReturn($this->storage);
|
||||
|
||||
// Setup storage to successfully find and delete the file
|
||||
$this->storage->method('exists')->willReturn(true);
|
||||
$this->storage->method('delete')->willReturn(true);
|
||||
|
||||
// Execute the method under test
|
||||
$result = $this->fileManager->deleteFile('test.txt');
|
||||
|
||||
// Assert the result is true
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
public function test_delete_file_not_found()
|
||||
{
|
||||
// Setup filesystem manager to return our mocked storage
|
||||
$this->filesystemManager->method('getDefaultDriver')->willReturn('local');
|
||||
$this->filesystemManager->method('disk')->with('local')->willReturn($this->storage);
|
||||
|
||||
// Setup storage to not find the file
|
||||
$this->storage->method('exists')->willReturn(false);
|
||||
|
||||
// Mock the Log facade
|
||||
Log::shouldReceive('info')
|
||||
->once()
|
||||
->with('File not found for deletion: test.txt on disk local');
|
||||
|
||||
// Execute the method under test
|
||||
$result = $this->fileManager->deleteFile('test.txt');
|
||||
|
||||
// Assert the result is false
|
||||
$this->assertFalse($result);
|
||||
}
|
||||
|
||||
public function test_delete_file_with_empty_filename()
|
||||
{
|
||||
// Mock the Log facade
|
||||
Log::shouldReceive('warning')
|
||||
->once()
|
||||
->with('Attempted to delete a file with empty filename');
|
||||
|
||||
// Execute the method under test
|
||||
$result = $this->fileManager->deleteFile('');
|
||||
|
||||
// Assert the result is false
|
||||
$this->assertFalse($result);
|
||||
}
|
||||
|
||||
public function test_get_maximum_file_upload_size()
|
||||
{
|
||||
// Test the static method
|
||||
$result = FileManager::getMaximumFileUploadSize();
|
||||
|
||||
// Assert the result is an integer
|
||||
$this->assertIsInt($result);
|
||||
|
||||
// The result should be the minimum of post_max_size and upload_max_filesize
|
||||
$expected = min(
|
||||
$this->convertPHPSizeToBytes(ini_get('post_max_size')),
|
||||
$this->convertPHPSizeToBytes(ini_get('upload_max_filesize'))
|
||||
);
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to convert PHP size strings to bytes
|
||||
*/
|
||||
private function convertPHPSizeToBytes(string $sSize): int
|
||||
{
|
||||
$sSuffix = strtoupper(substr($sSize, -1));
|
||||
if (! in_array($sSuffix, ['P', 'T', 'G', 'M', 'K'])) {
|
||||
return (int) $sSize;
|
||||
}
|
||||
$iValue = substr($sSize, 0, -1);
|
||||
switch ($sSuffix) {
|
||||
case 'P':
|
||||
$iValue *= 1024;
|
||||
// Fallthrough intended
|
||||
case 'T':
|
||||
$iValue *= 1024;
|
||||
// Fallthrough intended
|
||||
case 'G':
|
||||
$iValue *= 1024;
|
||||
// Fallthrough intended
|
||||
case 'M':
|
||||
$iValue *= 1024;
|
||||
// Fallthrough intended
|
||||
case 'K':
|
||||
$iValue *= 1024;
|
||||
break;
|
||||
}
|
||||
|
||||
return (int) $iValue;
|
||||
}
|
||||
|
||||
public function test_sanitize_filename()
|
||||
{
|
||||
// Use reflection to test private method
|
||||
$reflection = new \ReflectionClass(FileManager::class);
|
||||
$method = $reflection->getMethod('sanitizeFilename');
|
||||
$method->setAccessible(true);
|
||||
|
||||
// Test with a normal filename
|
||||
$result = $method->invoke($this->fileManager, 'test.txt');
|
||||
$this->assertEquals('test.txt', $result);
|
||||
|
||||
// Test with a path
|
||||
$result = $method->invoke($this->fileManager, '/path/to/test.txt');
|
||||
$this->assertEquals('test.txt', $result);
|
||||
|
||||
// Test with special characters
|
||||
$result = $method->invoke($this->fileManager, 'test@file#$.txt');
|
||||
$this->assertEquals('test-file--.txt', $result);
|
||||
|
||||
// Allow chinese characters
|
||||
$result = $method->invoke($this->fileManager, '测试文件.txt');
|
||||
$this->assertEquals('测试文件.txt', $result);
|
||||
}
|
||||
|
||||
public function test_get_avatar_with_cache_hit()
|
||||
{
|
||||
// We already have a test file at userfiles/test/test.txt
|
||||
$testFile = base_path('userfiles/test/test.txt');
|
||||
|
||||
// Verify the test file exists
|
||||
$this->assertFileExists($testFile);
|
||||
|
||||
// Verify content
|
||||
$this->assertEquals('test content', file_get_contents($testFile));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user