<?php declare(strict_types=1); use Lewisdale\Webmentions\Endpoint; use Lewisdale\Webmentions\Exceptions\InvalidTargetException; use Lewisdale\Webmentions\Exceptions\InvalidUrlException; use Lewisdale\Webmentions\Exceptions\SourceNotFoundException; use Lewisdale\Webmentions\Exceptions\TargetNotMentionedException; use Lewisdale\Webmentions\Gateways\WebmentionGatewayInterface; use Lewisdale\Webmentions\Models\MentionType; use Lewisdale\Webmentions\Models\Webmention; use PHPUnit\Framework\TestCase; use PHPUnit\Framework\Attributes\TestWith; use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\ResponseInterface; class EndpointTest extends TestCase { private function objectContains(string $key, mixed $value) { return $this->callback(fn(object $obj) => $obj->$key === $value); } #[TestWith(["https://my.url.com", true])] #[TestWith(["my.url.com", false])] public function testValidatesUrls(string $url, bool $expected) { $endpoint = new Endpoint($this->createMock(HttpClientInterface::class), $this->createMock(WebmentionGatewayInterface::class)); $this->assertEquals($expected, $endpoint->validateUrl($url), "Expected $url"); } #[TestWith(["https://my-valid-source.url", "htt://my-invalid-target.com"])] #[TestWith(["my-invalid-source", "https://my-valid-target.com"])] #[TestWith(["http:///an-invalid-source", "an-invalid-target"])] public function testThrowsInvalidUrlExceptionIfTheUrlIsInvalid(string $source, string $target) { $this->expectException(InvalidUrlException::class); $endpoint = new Endpoint($this->createMock(HttpClientInterface::class), $this->createMock(WebmentionGatewayInterface::class)); $endpoint->receiveWebmention($source, $target); } public function testThrowsInvalidTargetExceptionIfTheTargetUrlIsNotOnCurrentSite() { $this->expectException(InvalidTargetException::class); $endpoint = new Endpoint($this->createMock(HttpClientInterface::class), $this->createMock(WebmentionGatewayInterface::class)); $endpoint->receiveWebmention("https://a-valid-source.url", "https://not-my-site.com"); } #[TestWith([404])] #[TestWith([401])] #[TestWith([500])] #[TestWith([501])] #[TestWith([502])] #[TestWith([503])] #[TestWith([500])] public function testItShouldThrowASourceNotFoundExceptionIfTheSourceUrlIsUnreachable(int $statusCode) { $source = "https://my-valid-source-url.com"; $target = "https://lewisdale.dev/post/a-post-page"; $mockClient = $this->createMock(HttpClientInterface::class); $mockResponse = $this->createMock(ResponseInterface::class); $mockClient->expects($this->once()) ->method('request') ->with($this->identicalTo('GET'), $this->identicalTo($source)) ->will($this->returnValue($mockResponse)); $mockResponse->method('getStatusCode') ->will($this->returnValue($statusCode)); $this->expectException(SourceNotFoundException::class); $endpoint = new Endpoint($mockClient, $this->createMock(WebmentionGatewayInterface::class)); $endpoint->receiveWebmention($source, $target); } public function testItShouldThrowATargetNotMentionedErrorIfTheSourceDoesNotMentionTheTarget() { $source = "https://my-valid-source-url.com"; $target = "https://lewisdale.dev/post/a-post-page"; $content = <<<XML <html> <head> </head> <body> <h1>Some content</h1> <p>Here's some body content. It <a href="/another/page">contains a url</a>.</p> <p>But it does not mention the target.</p> </body> </html> XML; $mockClient = $this->createMock(HttpClientInterface::class); $mockResponse = $this->createMock(ResponseInterface::class); $mockClient->expects($this->once()) ->method('request') ->with($this->identicalTo('GET'), $this->identicalTo($source)) ->will($this->returnValue($mockResponse)); $mockResponse->method('getStatusCode') ->will($this->returnValue(200)); $mockResponse->method('getContent') ->willReturn($content); $this->expectException(TargetNotMentionedException::class); $endpoint = new Endpoint($mockClient, $this->createMock(WebmentionGatewayInterface::class)); $endpoint->receiveWebmention($source, $target); } public function testItShouldParseAWebmentionAsALikeIfItHasTheCorrectMicroFormat() { $source = "https://my-valid-source-url.com"; $target = "https://lewisdale.dev/post/a-post-page"; $content = <<<XML <html> <head> </head> <body> <h1>Some content</h1> <p>Here's some body content. It <a href="/another/page">contains a url</a>.</p> <p>I'm liking <a href="$target" class="u-like-of">this post</a>.</p> </body> </html> XML; $mockClient = $this->createMock(HttpClientInterface::class); $mockResponse = $this->createMock(ResponseInterface::class); $mockGateway = $this->createMock(WebmentionGatewayInterface::class); $mockClient->expects($this->once()) ->method('request') ->with($this->identicalTo('GET'), $this->identicalTo($source)) ->will($this->returnValue($mockResponse)); $mockResponse->method('getStatusCode') ->will($this->returnValue(200)); $mockResponse->method('getContent') ->willReturn($content); $mockGateway->expects($this->once()) ->method('save') ->with($this->objectContains('type', MentionType::Like)); $endpoint = new Endpoint($mockClient, $mockGateway); $endpoint->receiveWebmention($source, $target); } public function testItShouldParseAWebmentionAsAMentionIfItHasNoMicroformat() { $source = "https://my-valid-source-url.com"; $target = "https://lewisdale.dev/post/a-post-page"; $content = <<<XML <html> <head> </head> <body> <h1>Some content</h1> <p>Here's some body content. It <a href="/another/page">contains a url</a>.</p> <p>I'm writing about <a href="$target" class="my-link">this post</a>.</p> </body> </html> XML; $mockClient = $this->createMock(HttpClientInterface::class); $mockResponse = $this->createMock(ResponseInterface::class); $mockGateway = $this->createMock(WebmentionGatewayInterface::class); $mockClient->expects($this->once()) ->method('request') ->with($this->identicalTo('GET'), $this->identicalTo($source)) ->will($this->returnValue($mockResponse)); $mockResponse->method('getStatusCode') ->will($this->returnValue(200)); $mockResponse->method('getContent') ->willReturn($content); $mockGateway->expects($this->once()) ->method('save') ->with($this->objectContains('type', MentionType::Mention)); $endpoint = new Endpoint($mockClient, $mockGateway); $endpoint->receiveWebmention($source, $target); } public function testItShouldParseAWebmentionAsAReplyIfItHasTheCorrectMicroFormat() { $source = "https://my-valid-source-url.com"; $target = "https://lewisdale.dev/post/a-post-page"; $content = <<<XML <html> <head> </head> <body> <h1>Some content</h1> <p>Here's some body content. It <a href="/another/page">contains a url</a>.</p> <p>I'm writing about <a href="$target" class="u-in-reply-to">this post</a>.</p> </body> </html> XML; $mockClient = $this->createMock(HttpClientInterface::class); $mockResponse = $this->createMock(ResponseInterface::class); $mockGateway = $this->createMock(WebmentionGatewayInterface::class); $mockClient->expects($this->once()) ->method('request') ->with($this->identicalTo('GET'), $this->identicalTo($source)) ->will($this->returnValue($mockResponse)); $mockResponse->method('getStatusCode') ->will($this->returnValue(200)); $mockResponse->method('getContent') ->willReturn($content); $mockGateway->expects($this->once()) ->method('save') ->with($this->objectContains('type', MentionType::Reply)); $endpoint = new Endpoint($mockClient, $mockGateway); $endpoint->receiveWebmention($source, $target); } public function testItShouldParseAWebmentionAsARepost() { $source = "https://my-valid-source-url.com"; $target = "https://lewisdale.dev/post/a-post-page"; $content = <<<XML <html> <head> </head> <body> <h1>Some content</h1> <p>Here's some body content. It <a href="/another/page">contains a url</a>.</p> <p>I'm writing about <a href="$target" class="u-repost-of">this post</a>.</p> </body> </html> XML; $mockClient = $this->createMock(HttpClientInterface::class); $mockResponse = $this->createMock(ResponseInterface::class); $mockGateway = $this->createMock(WebmentionGatewayInterface::class); $mockClient->expects($this->once()) ->method('request') ->with($this->identicalTo('GET'), $this->identicalTo($source)) ->will($this->returnValue($mockResponse)); $mockResponse->method('getStatusCode') ->will($this->returnValue(200)); $mockResponse->method('getContent') ->willReturn($content); $mockGateway->expects($this->once()) ->method('save') ->with($this->objectContains('type', MentionType::Repost)); $endpoint = new Endpoint($mockClient, $mockGateway); $endpoint->receiveWebmention($source, $target); } } ?>