<NME> ResolverTest.php
<BEF> <?php

namespace Bolt\Tests\Embed;

use Bolt\Common\Json;
use Bolt\Embed;
use Bolt\Filesystem\Adapter\Local;
use Bolt\Filesystem\Filesystem;
use Embed\Exceptions\EmbedException;
use Embed\Exceptions\InvalidUrlException;
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7;
use PHPUnit\Framework\TestCase;

/**
 * @covers \Bolt\Embed\Resolver
 *
 * @author Gawain Lynch <gawain.lynch@gmail.com>
 */
class ResolverTest extends TestCase
{
    public function testEmbed()
    {
        $requestUrl = 'https://www.youtube.com/watch?v=x4IDM3ltTYo';
        $content = [
            'thumbnail_height' => 360,
            'thumbnail_url'    => 'https://i.ytimg.com/vi/x4IDM3ltTYo/hqdefault.jpg',
            'provider_url'     => 'https://www.youtube.com/',
            'type'             => 'video',
            'author_name'      => 'Silversun Pickups',
            'height'           => 270,
            'version'          => '1.0',
            'thumbnail_width'  => 480,
            'title'            => 'Silversun Pickups - Nightlight (Official Video)',
            'author_url'       => 'https://www.youtube.com/user/Silversunpickups',
            'provider_name'    => 'YouTube',
            'width'            => 480,
            'html'             => '<iframe width="480" height="270" src="https://www.youtube.com/embed/x4IDM3ltTYo?feature=oembed" frameborder="0" allowfullscreen></iframe>',
        ];

        $factory = function ($url, $options = []) use ($content) {
        $client = new Client(['handler' => $handler]);

        $factory = function ($url, $options = []) use ($requestUrl, $client) {
            $dispatcher = new Embed\GuzzleDispatcher($client);
            /** @var \Embed\Adapters\Adapter $info */
            return \Embed\Embed::create($url, $options, $dispatcher);
        };

            $dispatcher = new Embed\GuzzleDispatcher($client, $handler);
            /** @var \Embed\Adapters\Adapter $info */
            return \Embed\Embed::create($url, $options, $dispatcher);
        };
        $url = new Psr7\Uri($requestUrl);
        $resolver = new Embed\Resolver($factory);
        $response = $resolver->embed($url, 'oembed');

        $this->assertArrayHasKey('type', $response);
        $this->assertArrayHasKey('title', $response);
        $this->assertArrayHasKey('height', $response);
        $this->assertArrayHasKey('width', $response);
        $this->assertSame($content, $response);

        $response = $resolver->embed($url, 'koala');
        $this->assertSame([], $response);
    }

    public function testImage()
    {
        $requestUrl = 'https://www.youtube.com/watch?v=x4IDM3ltTYo';
        $content = [
            'thumbnail_url' => 'https://i.ytimg.com/vi/x4IDM3ltTYo/generic-logo.png',
        ];

        $factory = function ($url, $options = []) use ($requestUrl, $content) {
            $mock = new MockHandler([
                new Psr7\Response(200),
                new Psr7\Response(200, ['Content-Type' => 'application/json'], Json::dump($content)),
                new Psr7\Response(200, ['Content-Type' => 'image/png'], file_get_contents(__DIR__ . '/../resources/generic-logo.png')),
            ]);

            $handler = HandlerStack::create($mock);
            $client = new Client(['handler' => $handler]);

            $dispatcher = new Embed\GuzzleDispatcher($client, $handler);
            /** @var \Embed\Adapters\Adapter $info */
            return \Embed\Embed::create($url, $options, $dispatcher);
        };
        $url = new Psr7\Uri($requestUrl);
        $resolver = new Embed\Resolver($factory);
        $response = $resolver->image($url);

        $this->assertSame($content['thumbnail_url'], $response);
    }

    public function testImages()
    {
        $requestUrl = 'http://www.youtube.com/watch?v=x4IDM3ltTYo';
        $content = [
            'image'         => 'https://www.youtube.com/yts/img/image.png',
            'thumbnail'     => 'https://www.youtube.com/yts/img/thumbnail.png',
        ];
        $filesystem = new Filesystem(new Local(__DIR__ . '/../resources'));
        /** @var \Bolt\Filesystem\Handler\Image $mockFile */
        $mockFile = $filesystem->getFile('generic-logo.png');
        $fileData = $mockFile->readStream();

        $factory = function ($url, $options = []) use ($requestUrl, $content, $fileData) {
            $mock = new MockHandler([
                new Psr7\Response(200),
                new Psr7\Response(200, ['Content-Type' => 'application/json'], Json::dump($content)),
                new Psr7\Response(200, ['Content-Type' => 'image/png'], $fileData),
                new Psr7\Response(200, ['Content-Type' => 'image/png'], $fileData),
            ]);

            $handler = HandlerStack::create($mock);
            $client = new Client(['handler' => $handler]);

            $dispatcher = new Embed\GuzzleDispatcher($client, $handler);
            /** @var \Embed\Adapters\Adapter $info */
            return \Embed\Embed::create($url, $options, $dispatcher);
        };
        $url = new Psr7\Uri($requestUrl);
        $resolver = new Embed\Resolver($factory);
        $response = $resolver->images($url);

        foreach (['url', 'width', 'height', 'size', 'mime'] as $key) {
            $this->assertArrayHasKey($key, $response[0]);
            $this->assertArrayHasKey($key, $response[1]);
        }

        $this->assertSame($content['image'], $response[0]['url']);
        $this->assertSame('image/png', $response[0]['mime'][0]);
        $this->assertSame($content['thumbnail'], $response[1]['url']);
        $this->assertSame('image/png', $response[1]['mime'][0]);
    }

    /**
     * @expectedException \Bolt\Exception\EmbedResolverException
     */
    public function testInvalidUrl()
    {
        $requestUrl = 'https://example.com/watch?v=nothing';
        $factory = function () {
            throw new InvalidUrlException('');
        };
        $url = new Psr7\Uri($requestUrl);
        $resolver = new Embed\Resolver($factory);
        $resolver->image($url);
    }

    /**
     * @expectedException \Bolt\Exception\EmbedResolverException
     */
    public function testEmbedException()
    {
        $requestUrl = 'https://example.com/watch?v=nothing';
        $factory = function () {
            throw new EmbedException('');
        };
        $url = new Psr7\Uri($requestUrl);
        $resolver = new Embed\Resolver($factory);
        $resolver->image($url);
    }
}

<MSG> Merge pull request #6644 from GawainLynch/hotfix/embed

[3.2] Embed updates
<DFF> @@ -44,7 +44,7 @@ class ResolverTest extends BoltUnitTest
         $client = new Client(['handler' => $handler]);
 
         $factory = function ($url, $options = []) use ($requestUrl, $client) {
-            $dispatcher = new Embed\GuzzleDispatcher($client);
+            $dispatcher = new Embed\GuzzleDispatcher($client, HandlerStack::create());
             /** @var \Embed\Adapters\Adapter $info */
             return \Embed\Embed::create($url, $options, $dispatcher);
         };

{'commit': "diff --git a/src/Embed/GuzzleDispatcher.php b/src/Embed/GuzzleDispatcher.php\nindex fd13e401f..7a9a8ec0a 100644\n--- a/src/Embed/GuzzleDispatcher.php\n+++ b/src/Embed/GuzzleDispatcher.php\n@@ -7,7 +7,12 @@ use Embed\\Http\\ImageResponse;\n use Embed\\Http\\Response;\n use Embed\\Http\\Url;\n use GuzzleHttp\\Client;\n+use GuzzleHttp\\Handler\\CurlHandler;\n+use GuzzleHttp\\HandlerStack;\n+use GuzzleHttp\\Middleware;\n+use GuzzleHttp\\Promise;\n use GuzzleHttp\\Psr7;\n+use Psr\\Http\\Message\\ResponseInterface;\n \n /**\n  * Guzzle dispatcher for the embed/embed service.\n@@ -19,18 +24,22 @@ use GuzzleHttp\\Psr7;\n class GuzzleDispatcher implements DispatcherInterface\n {\n     /** @var Client */\n-    protected $client;\n+    private $client;\n     /** @var \\Embed\\Http\\AbstractResponse[] */\n     private $responses = [];\n+    /** @var HandlerStack */\n+    private $handlerStack;\n \n     /**\n      * Constructor.\n      *\n-     * @param Client $client\n+     * @param Client       $client\n+     * @param HandlerStack $handlerStack\n      */\n-    public function __construct(Client $client)\n+    public function __construct(Client $client, HandlerStack $handlerStack)\n     {\n         $this->client = $client;\n+        $this->handlerStack = $handlerStack;\n     }\n \n     /**\n@@ -56,7 +65,70 @@ class GuzzleDispatcher implements DispatcherInterface\n      */\n     public function dispatchImages(array $urls)\n     {\n-        throw new \\RuntimeException(sprintf('%s is not currently implemented', __METHOD__));\n+        $this->handlerStack->setHandler(new CurlHandler());\n+        $responses = [];\n+        $promises = [];\n+        $mimeTypes = [\n+            'image/jpeg',\n+            'image/png',\n+            'image/gif',\n+            'image/bmp',\n+            'image/x-icon',\n+        ];\n+\n+        /** @var Url $url */\n+        foreach ($urls as $key => $url) {\n+            if ($url->getScheme() === 'data') {\n+                $response = ImageResponse::createFromBase64($url);\n+                if ($response) {\n+                    $responses[$key] = $response;\n+                }\n+\n+                continue;\n+            }\n+            $callable = function (ResponseInterface $response) use ($url) {\n+                return $response->withHeader('X-Embed-Request-Uri', $url);\n+            };\n+            $this->handlerStack->push(Middleware::mapResponse($callable), 'embed-request');\n+            $promises[$key] = $this->client->getAsync((string) $url);\n+            $this->handlerStack->remove('embed-request');\n+        }\n+\n+        $results = Promise\\settle($promises)->wait();\n+        foreach ($results as $result) {\n+            if (!isset($result['value'])) {\n+                continue;\n+            }\n+\n+            /** @var Psr7\\Response $response */\n+            $response = $result['value'];\n+            $body = $response->getBody();\n+            $baseUrl = (array) $response->getHeader('X-Embed-Request-Uri');\n+            $redirectUriHistory = $response->getHeader('X-Guzzle-Redirect-History');\n+            $redirectUri = $redirectUriHistory ? array_pop($redirectUriHistory) : reset($baseUrl);\n+\n+            $mimeType = $this->getMimeType($response);\n+            if (!in_array($mimeType, $mimeTypes)) {\n+                continue;\n+            }\n+\n+            $imageInfo = getimagesizefromstring($body);\n+            if ($imageInfo === false) {\n+                continue;\n+            }\n+\n+            $responses[] = $this->responses[] = new ImageResponse(\n+                Url::create(reset($baseUrl)),\n+                Url::create($redirectUri),\n+                $response->getStatusCode(),\n+                $response->getHeader('Content-Type'),\n+                $imageInfo,\n+                $response->getHeaders(),\n+                []\n+            );\n+        }\n+\n+        return array_values($responses);\n     }\n \n     /**\n@@ -74,7 +146,8 @@ class GuzzleDispatcher implements DispatcherInterface\n             $options['allow_redirects'] = [\n                 'max' => 10,\n             ];\n-            $guzzleResponse = $this->client->get((string) $url, $options);\n+            $response = $this->client->get((string) $url, $options);\n+            $redirectUri = $response->getEffectiveUrl() ?: (string) $url;\n         } else {\n             $allowRedirects = $this->client->getConfig('allow_redirects');\n             $allowRedirects['max'] = 10;\n@@ -83,19 +156,38 @@ class GuzzleDispatcher implements DispatcherInterface\n \n             $uri = new Psr7\\Uri((string) $url);\n             $request = new Psr7\\Request('GET', $uri, ['Accept' => 'text/html']);\n-            /** @var Psr7\\Response $guzzleResponse */\n-            $guzzleResponse = $this->client->send($request, $options);\n+            /** @var Psr7\\Response $response */\n+            $response = $this->client->send($request, $options);\n+            $redirectUriHistory = $response->getHeader('X-Guzzle-Redirect-History');\n+            $redirectUri = $redirectUriHistory ? array_pop($redirectUriHistory) : (string) $url;\n         }\n \n-        $contentType = $guzzleResponse->getHeader('Content-Type');\n-\n         return [\n-            'url'         => (string) $url,\n-            'statusCode'  => $guzzleResponse->getStatusCode(),\n-            'contentType' => is_array($contentType) ? reset($contentType) : $contentType,\n-            'content'     => $guzzleResponse->getBody()->getContents(),\n-            'headers'     => $guzzleResponse->getHeaders(),\n+            'url'         => $redirectUri,\n+            'statusCode'  => $response->getStatusCode(),\n+            'contentType' => $this->getMimeType($response),\n+            'content'     => $response->getBody()->getContents(),\n+            'headers'     => $response->getHeaders(),\n             'info'        => [],\n         ];\n     }\n+\n+    /**\n+     * @param Psr7\\Response $response\n+     *\n+     * @return string\n+     */\n+    private function getMimeType(Psr7\\Response $response)\n+    {\n+        $header = $response->getHeader('Content-Type');\n+        if ($header === null) {\n+            $finfo = finfo_open(FILEINFO_MIME_TYPE);\n+            $mime = finfo_buffer($finfo, $response->getBody());\n+            finfo_close($finfo);\n+\n+            return $mime;\n+        }\n+\n+        return reset($header);\n+    }\n }\ndiff --git a/src/Embed/Resolver.php b/src/Embed/Resolver.php\nindex 86ca606f1..b29601971 100644\n--- a/src/Embed/Resolver.php\n+++ b/src/Embed/Resolver.php\n@@ -31,7 +31,7 @@ class Resolver\n     }\n \n     /**\n-     * Return oEmbed information from a given URL.\n+     * Return embed information from a given URL.\n      *\n      * @param UriInterface $url\n      * @param string       $providerName\n@@ -40,7 +40,8 @@ class Resolver\n      */\n     public function embed(UriInterface $url, $providerName)\n     {\n-        $providers = $this->getEmbedProviders($url);\n+        $adapter = $this->getUrlAdapter($url);\n+        $providers = $adapter->getProviders();\n         if (!isset($providers[$providerName])) {\n             return [];\n         }\n@@ -51,25 +52,31 @@ class Resolver\n     }\n \n     /**\n-     * Retrieve all of the embed providers from a given URL.\n+     * Return the best embeded image from a given URL.\n      *\n      * @param UriInterface $url\n      *\n-     * @throws EmbedResolverException\n+     * @return string\n+     */\n+    public function image(UriInterface $url)\n+    {\n+        $adapter = $this->getUrlAdapter($url);\n+\n+        return $adapter->getImage();\n+    }\n+\n+    /**\n+     * Return embed images from a given URL.\n      *\n-     * @return \\Embed\\Providers\\Provider[]\n+     * @param UriInterface $url\n+     *\n+     * @return array Values: url, width, height, size, mime (array)\n      */\n-    private function getEmbedProviders(UriInterface $url)\n+    public function images(UriInterface $url)\n     {\n-        try {\n-            $info = $this->getUrlAdapter($url);\n-        } catch (InvalidUrlException $e) {\n-            throw new EmbedResolverException($e->getMessage(), 1, $e);\n-        } catch (EmbedException $e) {\n-            throw new EmbedResolverException('Provider exception occurred', 2, $e);\n-        }\n+        $adapter = $this->getUrlAdapter($url);\n \n-        return $info->getProviders();\n+        return $adapter->getImages();\n     }\n \n     /**\n@@ -84,6 +91,14 @@ class Resolver\n         $embedFactory = $this->embedFactory;\n         $url = Url::create((string) $url);\n \n-        return $embedFactory($url);\n+        try {\n+            $adapter = $embedFactory($url);\n+        } catch (InvalidUrlException $e) {\n+            throw new EmbedResolverException($e->getMessage(), 1, $e);\n+        } catch (EmbedException $e) {\n+            throw new EmbedResolverException('Provider exception occurred', 2, $e);\n+        }\n+\n+        return $adapter;\n     }\n }\ndiff --git a/src/Provider/EmbedServiceProvider.php b/src/Provider/EmbedServiceProvider.php\nindex 8fb4c3fbb..3c0e0f314 100644\n--- a/src/Provider/EmbedServiceProvider.php\n+++ b/src/Provider/EmbedServiceProvider.php\n@@ -2,8 +2,8 @@\n \n namespace Bolt\\Provider;\n \n-use Bolt\\Embed\\Resolver;\n use Bolt\\Embed\\GuzzleDispatcher;\n+use Bolt\\Embed\\Resolver;\n use Embed\\Embed;\n use Silex\\Application;\n use Silex\\ServiceProviderInterface;\n@@ -25,13 +25,28 @@ class EmbedServiceProvider implements ServiceProviderInterface\n \n         $app['embed.dispatcher'] = $app->share(\n             function ($app) {\n-                return new GuzzleDispatcher($app['guzzle.client']);\n+                return new GuzzleDispatcher($app['guzzle.client'], $app['guzzle.handler_stack']);\n             }\n         );\n \n         $app['embed.factory.config'] = $app->share(\n-            function () {\n-                return [];\n+            function ($app) {\n+                return [\n+                    'min_image_width'     => 60,\n+                    'min_image_height'    => 60,\n+                    'images_blacklist'    => null,\n+                    'choose_bigger_image' => false,\n+                    'html'                => [\n+                        'max_images'      => 10,\n+                        'external_images' => false,\n+                    ],\n+                    'oembed' => [\n+                        'parameters' => [],\n+                    ],\n+                    'google' => [\n+                        'key' => $app['config']->get('general/google_api_key'),\n+                    ],\n+                ];\n             }\n         );\n \ndiff --git a/tests/phpunit/unit/Embed/ResolverTest.php b/tests/phpunit/unit/Embed/ResolverTest.php\nindex 2bfe7467f..8a2c75bf6 100644\n--- a/tests/phpunit/unit/Embed/ResolverTest.php\n+++ b/tests/phpunit/unit/Embed/ResolverTest.php\n@@ -44,7 +44,7 @@ class ResolverTest extends BoltUnitTest\n         $client = new Client(['handler' => $handler]);\n \n         $factory = function ($url, $options = []) use ($requestUrl, $client) {\n-            $dispatcher = new Embed\\GuzzleDispatcher($client);\n+            $dispatcher = new Embed\\GuzzleDispatcher($client, HandlerStack::create());\n             /** @var \\Embed\\Adapters\\Adapter $info */\n             return \\Embed\\Embed::create($url, $options, $dispatcher);\n         };\n", 'commit_subject': 'Merge pull request #6644 from GawainLynch/hotfix/embed', 'repo_name': 'bolt/bolt', 'committer': {'name': 'GitHub', 'email': '1505422b2465e9a84f6fdfaa161078890c593f06@github.com', 'time_sec': '1494414553', 'tz_offset': '120', 'date': {'seconds': '1494414553'}}, 'commit_tree': '380592cf06efcede5dd755b23428df9cc2797b0a', 'commit_message': 'Merge pull request #6644 from GawainLynch/hotfix/embed\n\n[3.2] Embed updates', 'language_name': 'HTML', 'license': 'mit', 'hunks': "@@ -44,7 +44,7 @@ class ResolverTest extends BoltUnitTest\n         $client = new Client(['handler' => $handler]);\n \n         $factory = function ($url, $options = []) use ($requestUrl, $client) {\n-            $dispatcher = new Embed\\GuzzleDispatcher($client);\n+            $dispatcher = new Embed\\GuzzleDispatcher($client, HandlerStack::create());\n             /** @var \\Embed\\Adapters\\Adapter $info */\n             return \\Embed\\Embed::create($url, $options, $dispatcher);\n         };\n", 'addition_count': 1, 'deletion_count': 1, 'src_file': 'a/tests/phpunit/unit/Embed/ResolverTest.php', 'tgt_file': 'b/tests/phpunit/unit/Embed/ResolverTest.php', 'file_extension': '.php', 'before_file': '<?php\n\nnamespace Bolt\\Tests\\Embed;\n\nuse Bolt\\Common\\Json;\nuse Bolt\\Embed;\nuse Bolt\\Filesystem\\Adapter\\Local;\nuse Bolt\\Filesystem\\Filesystem;\nuse Embed\\Exceptions\\EmbedException;\nuse Embed\\Exceptions\\InvalidUrlException;\nuse GuzzleHttp\\Client;\nuse GuzzleHttp\\Handler\\MockHandler;\nuse GuzzleHttp\\HandlerStack;\nuse GuzzleHttp\\Psr7;\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n * @covers \\Bolt\\Embed\\Resolver\n *\n * @author Gawain Lynch <gawain.lynch@gmail.com>\n */\nclass ResolverTest extends TestCase\n{\n    public function testEmbed()\n    {\n        $requestUrl = \'https://www.youtube.com/watch?v=x4IDM3ltTYo\';\n        $content = [\n            \'thumbnail_height\' => 360,\n            \'thumbnail_url\'    => \'https://i.ytimg.com/vi/x4IDM3ltTYo/hqdefault.jpg\',\n            \'provider_url\'     => \'https://www.youtube.com/\',\n            \'type\'             => \'video\',\n            \'author_name\'      => \'Silversun Pickups\',\n            \'height\'           => 270,\n            \'version\'          => \'1.0\',\n            \'thumbnail_width\'  => 480,\n            \'title\'            => \'Silversun Pickups - Nightlight (Official Video)\',\n            \'author_url\'       => \'https://www.youtube.com/user/Silversunpickups\',\n            \'provider_name\'    => \'YouTube\',\n            \'width\'            => 480,\n            \'html\'             => \'<iframe width="480" height="270" src="https://www.youtube.com/embed/x4IDM3ltTYo?feature=oembed" frameborder="0" allowfullscreen></iframe>\',\n        ];\n\n        $factory = function ($url, $options = []) use ($content) {\n        $client = new Client([\'handler\' => $handler]);\n\n        $factory = function ($url, $options = []) use ($requestUrl, $client) {\n            $dispatcher = new Embed\\GuzzleDispatcher($client);\n            /** @var \\Embed\\Adapters\\Adapter $info */\n            return \\Embed\\Embed::create($url, $options, $dispatcher);\n        };\n\n            $dispatcher = new Embed\\GuzzleDispatcher($client, $handler);\n            /** @var \\Embed\\Adapters\\Adapter $info */\n            return \\Embed\\Embed::create($url, $options, $dispatcher);\n        };\n        $url = new Psr7\\Uri($requestUrl);\n        $resolver = new Embed\\Resolver($factory);\n        $response = $resolver->embed($url, \'oembed\');\n\n        $this->assertArrayHasKey(\'type\', $response);\n        $this->assertArrayHasKey(\'title\', $response);\n        $this->assertArrayHasKey(\'height\', $response);\n        $this->assertArrayHasKey(\'width\', $response);\n        $this->assertSame($content, $response);\n\n        $response = $resolver->embed($url, \'koala\');\n        $this->assertSame([], $response);\n    }\n\n    public function testImage()\n    {\n        $requestUrl = \'https://www.youtube.com/watch?v=x4IDM3ltTYo\';\n        $content = [\n            \'thumbnail_url\' => \'https://i.ytimg.com/vi/x4IDM3ltTYo/generic-logo.png\',\n        ];\n\n        $factory = function ($url, $options = []) use ($requestUrl, $content) {\n            $mock = new MockHandler([\n                new Psr7\\Response(200),\n                new Psr7\\Response(200, [\'Content-Type\' => \'application/json\'], Json::dump($content)),\n                new Psr7\\Response(200, [\'Content-Type\' => \'image/png\'], file_get_contents(__DIR__ . \'/../resources/generic-logo.png\')),\n            ]);\n\n            $handler = HandlerStack::create($mock);\n            $client = new Client([\'handler\' => $handler]);\n\n            $dispatcher = new Embed\\GuzzleDispatcher($client, $handler);\n            /** @var \\Embed\\Adapters\\Adapter $info */\n            return \\Embed\\Embed::create($url, $options, $dispatcher);\n        };\n        $url = new Psr7\\Uri($requestUrl);\n        $resolver = new Embed\\Resolver($factory);\n        $response = $resolver->image($url);\n\n        $this->assertSame($content[\'thumbnail_url\'], $response);\n    }\n\n    public function testImages()\n    {\n        $requestUrl = \'http://www.youtube.com/watch?v=x4IDM3ltTYo\';\n        $content = [\n            \'image\'         => \'https://www.youtube.com/yts/img/image.png\',\n            \'thumbnail\'     => \'https://www.youtube.com/yts/img/thumbnail.png\',\n        ];\n        $filesystem = new Filesystem(new Local(__DIR__ . \'/../resources\'));\n        /** @var \\Bolt\\Filesystem\\Handler\\Image $mockFile */\n        $mockFile = $filesystem->getFile(\'generic-logo.png\');\n        $fileData = $mockFile->readStream();\n\n        $factory = function ($url, $options = []) use ($requestUrl, $content, $fileData) {\n            $mock = new MockHandler([\n                new Psr7\\Response(200),\n                new Psr7\\Response(200, [\'Content-Type\' => \'application/json\'], Json::dump($content)),\n                new Psr7\\Response(200, [\'Content-Type\' => \'image/png\'], $fileData),\n                new Psr7\\Response(200, [\'Content-Type\' => \'image/png\'], $fileData),\n            ]);\n\n            $handler = HandlerStack::create($mock);\n            $client = new Client([\'handler\' => $handler]);\n\n            $dispatcher = new Embed\\GuzzleDispatcher($client, $handler);\n            /** @var \\Embed\\Adapters\\Adapter $info */\n            return \\Embed\\Embed::create($url, $options, $dispatcher);\n        };\n        $url = new Psr7\\Uri($requestUrl);\n        $resolver = new Embed\\Resolver($factory);\n        $response = $resolver->images($url);\n\n        foreach ([\'url\', \'width\', \'height\', \'size\', \'mime\'] as $key) {\n            $this->assertArrayHasKey($key, $response[0]);\n            $this->assertArrayHasKey($key, $response[1]);\n        }\n\n        $this->assertSame($content[\'image\'], $response[0][\'url\']);\n        $this->assertSame(\'image/png\', $response[0][\'mime\'][0]);\n        $this->assertSame($content[\'thumbnail\'], $response[1][\'url\']);\n        $this->assertSame(\'image/png\', $response[1][\'mime\'][0]);\n    }\n\n    /**\n     * @expectedException \\Bolt\\Exception\\EmbedResolverException\n     */\n    public function testInvalidUrl()\n    {\n        $requestUrl = \'https://example.com/watch?v=nothing\';\n        $factory = function () {\n            throw new InvalidUrlException(\'\');\n        };\n        $url = new Psr7\\Uri($requestUrl);\n        $resolver = new Embed\\Resolver($factory);\n        $resolver->image($url);\n    }\n\n    /**\n     * @expectedException \\Bolt\\Exception\\EmbedResolverException\n     */\n    public function testEmbedException()\n    {\n        $requestUrl = \'https://example.com/watch?v=nothing\';\n        $factory = function () {\n            throw new EmbedException(\'\');\n        };\n        $url = new Psr7\\Uri($requestUrl);\n        $resolver = new Embed\\Resolver($factory);\n        $resolver->image($url);\n    }\n}\n', 'message': 'Merge pull request #6644 from GawainLynch/hotfix/embed\n\n[3.2] Embed updates', 'author_name': 'GitHub', 'lang': 'php'}