diff --git a/depfile.yaml b/depfile.yaml index 43446a1..66a3c52 100644 --- a/depfile.yaml +++ b/depfile.yaml @@ -115,6 +115,7 @@ ruleset: - "PSR HTTP" - "PSR HTTP Discovery" - PHPUnit + - Guzzle "HTTP Mock Util": ~ "HTTP Mock PSR Middleware": - "PSR HTTP" diff --git a/src/Request/SerializableRequest.php b/src/Request/SerializableRequest.php index 42c0f96..f3fdcf4 100644 --- a/src/Request/SerializableRequest.php +++ b/src/Request/SerializableRequest.php @@ -2,6 +2,8 @@ namespace InterNations\Component\HttpMock\Request; use Serializable; +use Symfony\Component\HttpFoundation\File\UploadedFile; +use Symfony\Component\HttpFoundation\FileBag; use Symfony\Component\HttpFoundation\Request; class SerializableRequest extends Request implements Serializable @@ -9,12 +11,26 @@ class SerializableRequest extends Request implements Serializable public function serialize(): string { $this->getContent(); + $files = []; + + if ($this->files) { + foreach ($this->files as $key => $file) { + // Move the file in another directory + $path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . md5($file->getPathname()); + $file->move(sys_get_temp_dir(), md5($file->getPathname())); + $files[$key] = [ + 'originalName' => $file->getClientOriginalName(), + 'pathName' => $path, + ]; + } + } + return serialize([ 'attributes' => $this->attributes, 'request' => $this->request, 'query' => $this->query, 'server' => $this->server, - 'files' => $this->files, + 'files' => $files, 'cookies' => $this->cookies, 'headers' => $this->headers, 'content' => $this->content, @@ -43,7 +59,16 @@ public function unserialize($data): void // @codingStandardsIgnoreLine $this->request = $attributes['request']; $this->query = $attributes['query']; $this->server = $attributes['server']; - $this->files = $attributes['files']; + $files = $attributes['files']; + $this->files = []; + + if ($files) { + foreach($files as $key => $file) { + $this->files[$key] = new UploadedFile($file['pathName'], $file['originalName'], null, null, true); + } + } + + $this->files = new FileBag($this->files); $this->cookies = $attributes['cookies']; $this->headers = $attributes['headers']; $this->content = $attributes['content']; diff --git a/tests/PHPUnit/HttpMockPHPUnitIntegrationTest.php b/tests/PHPUnit/HttpMockPHPUnitIntegrationTest.php index f8b5db9..64b7e86 100644 --- a/tests/PHPUnit/HttpMockPHPUnitIntegrationTest.php +++ b/tests/PHPUnit/HttpMockPHPUnitIntegrationTest.php @@ -1,8 +1,11 @@ tearDownHttpMock(); } + /** + * Get the content of the file. + * @param $file File The file from which get the content + * @return false|string The file content or false on error. + */ + private function getFileContent(File $file) { + return file_get_contents($file->getPathname()); + } + /** @return array */ public static function getPaths(): array { @@ -272,6 +284,111 @@ public function testPostRequest(): void self::assertSame('post-value', $this->http->requests->latest()->request->get('post-key')); } + public function testPostRequestWithFile(): void + { + $this->http->mock + ->when() + ->methodIs('POST') + ->then() + ->body('BODY') + ->statusCode(201) + ->header('X-Foo', 'Bar') + ->end(); + $this->http->setUp(); + + $streamFactory = $this->getStreamFactory(); + + $f1 = FnStream::decorate($streamFactory->createStream('file-content'), [ + 'getMetadata' => static function () { + return '/foo/bar.txt'; + }, + ]); + + $multipartStream = new MultipartStream([ + [ + 'name' => 'post-key', + 'contents' => 'post-value', + ], + [ + 'name' => 'foo', + 'contents' => $f1, + ], + ]); + $boundary = $multipartStream->getBoundary(); + + $response = $this->http->client->sendRequest( + $this->getServerRequestFactory() + ->createServerRequest('POST', '/') + ->withHeader('Content-Type', 'multipart/form-data; boundary=' . $boundary) + ->withBody($multipartStream) + ); + + $latestRequest = $this->http->requests->latest(); + self::assertSame('BODY', (string) $response->getBody()); + self::assertSame(201, $response->getStatusCode()); + self::assertSame('Bar', $response->getHeaderLine('X-Foo')); + self::assertSame('post-value', $latestRequest->request->get('post-key')); + self::assertSame('file-content', $this->getFileContent($latestRequest->files->get('foo'))); + } + + public function testPostRequestWithMultipleFiles(): void + { + $this->http->mock + ->when() + ->methodIs('POST') + ->then() + ->body('BODY') + ->statusCode(201) + ->header('X-Foo', 'Bar') + ->end(); + $this->http->setUp(); + + $streamFactory = $this->getStreamFactory(); + + $f1 = FnStream::decorate($streamFactory->createStream('first-file-content'), [ + 'getMetadata' => static function () { + return '/foo/bar.txt'; + }, + ]); + + $f2 = FnStream::decorate($streamFactory->createStream('second-file-content'), [ + 'getMetadata' => static function () { + return '/foo/baz.txt'; + }, + ]); + + $multipartStream = new MultipartStream([ + [ + 'name' => 'post-key', + 'contents' => 'post-value', + ], + [ + 'name' => 'foo', + 'contents' => $f1, + ], + [ + 'name' => 'bar', + 'contents' => $f2, + ], + ]); + $boundary = $multipartStream->getBoundary(); + + $response = $this->http->client->sendRequest( + $this->getServerRequestFactory() + ->createServerRequest('POST', '/') + ->withHeader('Content-Type', 'multipart/form-data; boundary=' . $boundary) + ->withBody($multipartStream) + ); + + $latestRequest = $this->http->requests->latest(); + self::assertSame('BODY', (string) $response->getBody()); + self::assertSame(201, $response->getStatusCode()); + self::assertSame('Bar', $response->getHeaderLine('X-Foo')); + self::assertSame('post-value', $latestRequest->request->get('post-key')); + self::assertSame('first-file-content', $this->getFileContent($latestRequest->files->get('foo'))); + self::assertSame('second-file-content', $this->getFileContent($latestRequest->files->get('bar'))); + } + public function testCountRequests(): void { $this->http->mock diff --git a/tests/TestCase.php b/tests/TestCase.php index 91eec83..c81d653 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -5,6 +5,7 @@ use PHPUnit\Framework\TestCase as PhpUnitTestCase; use Psr\Http\Message\RequestFactoryInterface; use Psr\Http\Message\ResponseFactoryInterface; +use Psr\Http\Message\ServerRequestFactoryInterface; use Psr\Http\Message\StreamFactoryInterface; abstract class TestCase extends PhpUnitTestCase @@ -23,4 +24,9 @@ public function getStreamFactory(): StreamFactoryInterface { return Psr17FactoryDiscovery::findStreamFactory(); } + + public function getServerRequestFactory(): ServerRequestFactoryInterface + { + return Psr17FactoryDiscovery::findServerRequestFactory(); + } }