<?php namespace Tests\Unit\Services; use App\ContentProvider; use App\MetadataResolver\MetadataResolver; use App\MetadataResolver\ResolverCircuitBreakException; use App\MetadataResolver\UncaughtResolverException; use App\Services\MetadataResolveService; use Carbon\Carbon; use GuzzleHttp\Client; use GuzzleHttp\Exception\ClientException; use GuzzleHttp\Exception\ServerException; use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\HandlerStack; use GuzzleHttp\Psr7\Response; use Illuminate\Foundation\Testing\RefreshDatabase; use Mockery\MockInterface; use Tests\TestCase; class MetadataResolverServiceTest extends TestCase { use RefreshDatabase; protected function setUp(): void { parent::setUp(); $this->seed(); Carbon::setTestNow('2020-07-21 19:19:19'); // FIXME: 今書かれてるテストはresolveのHTTPリクエストのみを考慮しているので、ContentProviderにデータがないとリクエスト回数がずれる factory(ContentProvider::class)->create(); } protected function tearDown(): void { parent::tearDown(); Carbon::setTestNow(); } public function testOnRuntimeException() { $this->mock(MetadataResolver::class, function (MockInterface $mock) { $mock->shouldReceive('resolve')->andReturnUsing(function ($url) { throw new \RuntimeException('Something happened!'); }); }); try { $service = app()->make(MetadataResolveService::class); $service->execute('http://example.com'); } catch (UncaughtResolverException $e) { $this->assertDatabaseHas('metadata', [ 'url' => 'http://example.com', 'error_at' => new Carbon('2020-07-21 19:19:19'), 'error_count' => 1, 'error_exception_class' => \RuntimeException::class, 'error_http_code' => null, 'error_body' => 'Something happened!', ]); return; } $this->fail(); } public function testOnHttpClientError() { $handler = HandlerStack::create(new MockHandler([new Response(404)])); $client = new Client(['handler' => $handler]); $this->instance(Client::class, $client); try { $service = app()->make(MetadataResolveService::class); $service->execute('http://example.com'); } catch (UncaughtResolverException $e) { $this->assertDatabaseHas('metadata', [ 'url' => 'http://example.com', 'error_at' => new Carbon('2020-07-21 19:19:19'), 'error_count' => 1, 'error_exception_class' => ClientException::class, 'error_http_code' => 404, ]); return; } $this->fail(); } public function testOnHttpServerError() { $handler = HandlerStack::create(new MockHandler([new Response(503), new Response(503)])); $client = new Client(['handler' => $handler]); $this->instance(Client::class, $client); try { $service = app()->make(MetadataResolveService::class); $service->execute('http://example.com'); } catch (UncaughtResolverException $e) { $this->assertDatabaseHas('metadata', [ 'url' => 'http://example.com', 'error_at' => new Carbon('2020-07-21 19:19:19'), 'error_count' => 1, 'error_exception_class' => ServerException::class, 'error_http_code' => 503, ]); return; } $this->fail(); } public function testCircuitBreak() { $this->mock(MetadataResolver::class, function (MockInterface $mock) { $mock->shouldReceive('resolve')->andReturnUsing(function ($url) { throw new \RuntimeException('Something happened!'); }); }); try { for ($i = 0; $i < 6; $i++) { try { $service = app()->make(MetadataResolveService::class); $service->execute('http://example.com'); } catch (UncaughtResolverException $e) { } } } catch (ResolverCircuitBreakException $e) { $this->assertDatabaseHas('metadata', [ 'url' => 'http://example.com', 'error_at' => new Carbon('2020-07-21 19:19:19'), 'error_count' => 5, 'error_exception_class' => \RuntimeException::class, 'error_http_code' => null, 'error_body' => 'Something happened!', ]); return; } $this->fail(); } public function testOnResurrect() { $successBody = <<<HTML <!doctype html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="og:title" content="OGP Title"> <meta name="og:description" content="OGP Description"> <title>Test Document</title> </head> <body> </body> </html> HTML; $handler = HandlerStack::create(new MockHandler([ new Response(404), new Response(200, ['Content-Type' => 'text/html'], $successBody), ])); $client = new Client(['handler' => $handler]); $this->instance(Client::class, $client); for ($i = 0; $i < 2; $i++) { try { $service = app()->make(MetadataResolveService::class); $service->execute('http://example.com'); } catch (UncaughtResolverException $e) { } } $this->assertDatabaseHas('metadata', [ 'url' => 'http://example.com', 'title' => 'OGP Title', 'description' => 'OGP Description', 'image' => '', 'error_at' => null, 'error_count' => 0, 'error_exception_class' => null, 'error_http_code' => null, 'error_body' => null, ]); } }