root = sys_get_temp_dir() . '/php_s3_limits_' . bin2hex(random_bytes(6)); @mkdir($this->root, 0750, true); } protected function tearDown(): void { $this->recursiveRmdir($this->root); } public function testStageRejectsBodiesOverMaxBytesWithoutContentLength(): void { $storage = new LocalFilesystemStorage($this->root); $stream = fopen('php://temp', 'r+'); fwrite($stream, str_repeat('x', 2048)); rewind($stream); try { $storage->stage($stream, null, 1024); self::fail('Expected EntityTooLarge when the body exceeds maxBytes'); } catch (S3Exception $e) { self::assertSame('EntityTooLarge', $e->errorCode); self::assertSame(400, $e->status); } finally { fclose($stream); } // The rejected upload must not leave a staging file behind. self::assertSame([], glob($this->root . '/tmp/*') ?: []); } public function testStageAcceptsBodiesExactlyAtTheMaxBytesBoundary(): void { $storage = new LocalFilesystemStorage($this->root); $stream = fopen('php://temp', 'r+'); fwrite($stream, str_repeat('y', 1024)); rewind($stream); $staged = $storage->stage($stream, null, 1024); fclose($stream); self::assertSame(1024, $staged->size); $storage->discard($staged); } public function testStageStillVerifiesDeclaredContentLength(): void { $storage = new LocalFilesystemStorage($this->root); $stream = fopen('php://temp', 'r+'); fwrite($stream, str_repeat('z', 12)); rewind($stream); try { $storage->stage($stream, 10, 1024); self::fail('Expected IncompleteBody for a length mismatch'); } catch (S3Exception $e) { self::assertSame('IncompleteBody', $e->errorCode); } finally { fclose($stream); } self::assertSame([], glob($this->root . '/tmp/*') ?: []); } private function recursiveRmdir(string $dir): void { if (!is_dir($dir)) { return; } foreach (scandir($dir) ?: [] as $file) { if ($file === '.' || $file === '..') { continue; } $path = $dir . '/' . $file; if (is_dir($path)) { $this->recursiveRmdir($path); } else { @unlink($path); } } @rmdir($dir); } }