service = new FinancialAttachmentService(); } public function testSaveUploadedFileReturnsNullForInvalidInput(): void { $this->assertNull($this->service->saveUploadedFile(null, 'payments')); $file = $this->createMock(UploadedFile::class); $file->method('isValid')->willReturn(false); $this->assertNull($this->service->saveUploadedFile($file, 'payments')); } public function testSaveUploadedFileRejectsUnsupportedMimeType(): void { $file = $this->createMock(UploadedFile::class); $file->method('isValid')->willReturn(true); $file->method('hasMoved')->willReturn(false); $file->method('getSize')->willReturn(1000); $file->method('getMimeType')->willReturn('text/plain'); $file->method('getClientExtension')->willReturn('txt'); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Unsupported file type.'); $this->service->saveUploadedFile($file, 'payments'); } public function testSaveUploadedFileRejectsOversizedFiles(): void { $file = $this->createMock(UploadedFile::class); $file->method('isValid')->willReturn(true); $file->method('hasMoved')->willReturn(false); $file->method('getSize')->willReturn(5242881); $file->method('getMimeType')->willReturn('application/pdf'); $file->method('getClientExtension')->willReturn('pdf'); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('File too large.'); $this->service->saveUploadedFile($file, 'payments'); } public function testSaveUploadedFileMovesValidFilesAndReturnsRandomName(): void { $file = $this->createMock(UploadedFile::class); $file->method('isValid')->willReturn(true); $file->method('hasMoved')->willReturn(false); $file->method('getSize')->willReturn(1024); $file->method('getMimeType')->willReturn('application/pdf'); $file->method('getClientExtension')->willReturn('pdf'); $file->method('getRandomName')->willReturn('proof.pdf'); $file->expects($this->once()) ->method('move') ->with($this->stringContains('writable/uploads/payments'), 'proof.pdf'); $this->assertSame('proof.pdf', $this->service->saveUploadedFile($file, 'payments')); } public function testResolvePathUsesBasenameAndReturnsNullForMissingFiles(): void { $dir = $this->service->ensureSubdir('receipts'); $path = $dir . DIRECTORY_SEPARATOR . 'receipt.pdf'; file_put_contents($path, 'pdf'); $this->assertSame($path, $this->service->resolvePath('receipts', '../receipt.pdf')); $this->assertNull($this->service->resolvePath('receipts', 'missing.pdf')); } public function testDetectMimeReturnsKnownMimeForExistingFile(): void { $dir = $this->service->ensureSubdir('mime-tests'); $path = $dir . DIRECTORY_SEPARATOR . 'image.png'; file_put_contents( $path, base64_decode('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+aap8AAAAASUVORK5CYII=') ); $mime = $this->service->detectMime($path); $this->assertStringContainsString('image/', $mime); } }