webmentions/tests/EndpointTest.php

608 lines
22 KiB
PHP
Raw Normal View History

<?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 PHPUnit\Framework\Attributes\TestWith;
use PHPUnit\Framework\TestCase;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;
class EndpointTest extends TestCase
{
private function objectContains(string $key, mixed $expected)
{
return $this->callback(function (object $obj) use ($expected, $key) {
$val = $obj->$key;
$type = gettype($val);
return match ($type) {
"object" => $val == $expected,
"array" => count(array_diff($val, $expected)) === 0,
default => $val === $expected
};
});
2023-03-14 13:54:05 +00:00
}
#[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>
2023-03-14 21:08:33 +00:00
<article>
<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>
</article>
</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')
2023-03-14 13:54:05 +00:00
->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>
2023-03-14 21:08:33 +00:00
<article class="h-entry">
<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>
</article>
</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')
2023-03-14 13:54:05 +00:00
->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>
2023-03-14 21:08:33 +00:00
<article class="h-entry">
<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>
</article>
</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')
2023-03-14 13:54:05 +00:00
->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>
2023-03-14 21:08:33 +00:00
<article="h-entry">
<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>
</article>
2023-03-14 13:54:05 +00:00
</body>
</html>
XML;
$mockClient = $this->createMock(HttpClientInterface::class);
$mockResponse = $this->createMock(ResponseInterface::class);
$mockGateway = $this->createMock(WebmentionGatewayInterface::class);
2023-03-14 13:54:05 +00:00
$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));
2023-03-14 21:08:33 +00:00
$endpoint = new Endpoint($mockClient, $mockGateway);
$endpoint->receiveWebmention($source, $target);
}
public function testItShouldParseARepostsContent()
{
$source = "https://my-valid-source-url.com";
$target = "https://lewisdale.dev/post/a-post-page";
$content = <<<XML
<html>
<head>
</head>
<body>
<article="h-entry">
<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>
</article>
</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('content', "Reposted this post"));
$endpoint = new Endpoint($mockClient, $mockGateway);
$endpoint->receiveWebmention($source, $target);
}
public function testItShouldParseALikeContent()
{
$source = "https://my-valid-source-url.com";
$target = "https://lewisdale.dev/post/a-post-page";
$content = <<<HTML
<html>
<head>
</head>
<body>
<span class="h-entry">
<a class="u-like-of" href="$target">A Cool Post</a><a class="u-url" href="/"></a>
</span>
</body>
</html>
HTML;
$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('content', "Liked this post"));
$endpoint = new Endpoint($mockClient, $mockGateway);
$endpoint->receiveWebmention($source, $target);
}
public function testItShouldParseAReplyContent()
{
$source = "https://my-valid-source-url.com";
$target = "https://lewisdale.dev/post/a-post-page";
$content = <<<HTML
<html>
<head>
</head>
<body>
<article class="h-entry">
<a class="u-in-reply-to" rel="in-reply-to" href="$target">@post</a>: That's a great idea!<a class="u-url" href="/"></a>
</article>
</body>
</html>
HTML;
$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('content', "@post: That's a great idea!"));
$endpoint = new Endpoint($mockClient, $mockGateway);
$endpoint->receiveWebmention($source, $target);
}
public function testItShouldParseAnAuthorCardWithANameUrlAndPhoto()
{
$source = "https://my-valid-source-url.com";
$target = "https://lewisdale.dev/post/a-post-page";
$content = <<<HTML
<html>
<head>
</head>
<body>
<article class="h-entry">
<a class="u-in-reply-to" rel="in-reply-to" href="$target">@post</a>: That's a great idea!<a class="u-url" href="/"></a>
<div class="h-card">
<p class="p-name">Anne Author</p> who can be found at <a class="u-url" href="https://my-blog.com">my-blog.com</a>.
<img src="https://dummyimage.com/100x100/fff/aaa" class="u-photo" alt="My profile picture" />
</div>
</article>
</body>
</html>
HTML;
$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);
$expected = new \Lewisdale\Webmentions\Models\Author(
null,
"Anne Author",
"https://my-blog.com",
"https://dummyimage.com/100x100/fff/aaa"
);
$mockGateway->expects($this->once())
->method('save')
->with($this->objectContains('author', $expected));
$endpoint = new Endpoint($mockClient, $mockGateway);
$endpoint->receiveWebmention($source, $target);
}
public function testItShouldParseAnAuthorCardWithAMinimalHCard()
{
$source = "https://my-valid-source-url.com";
$target = "https://lewisdale.dev/post/a-post-page";
$content = <<<HTML
<html>
<head>
</head>
<body>
<article class="h-entry">
<a class="u-in-reply-to" rel="in-reply-to" href="$target">@post</a>: That's a great idea!<a class="u-url" href="/"></a>
<span class="h-card">
<a class="p-name p-org u-url" href="https://microformats.org/">microformats.org</a>
</span>
</article>
</body>
</html>
HTML;
$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);
$expected = new \Lewisdale\Webmentions\Models\Author(
null,
"microformats.org",
"https://microformats.org/",
""
);
$mockGateway->expects($this->once())
->method('save')
->with($this->objectContains('author', $expected));
$endpoint = new Endpoint($mockClient, $mockGateway);
$endpoint->receiveWebmention($source, $target);
}
public function testItShouldParseAnAuthorCardWithAVeryMinimalHCard()
{
$source = "https://my-valid-source-url.com";
$target = "https://lewisdale.dev/post/a-post-page";
$content = <<<HTML
<html>
<head>
</head>
<body>
<article class="h-entry">
<a class="u-in-reply-to" rel="in-reply-to" href="$target">@post</a>: That's a great idea!<a class="u-url" href="/"></a>
<a class="h-card" href="https://microformats.org/">microformats.org</a>
</article>
</body>
</html>
HTML;
$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);
$expected = new \Lewisdale\Webmentions\Models\Author(
null,
"microformats.org",
"https://microformats.org/",
""
);
$mockGateway->expects($this->once())
->method('save')
->with($this->objectContains('author', $expected));
$endpoint = new Endpoint($mockClient, $mockGateway);
$endpoint->receiveWebmention($source, $target);
}
public function testItShouldParseAnAuthorCardOutsideTheEntrry()
{
$source = "https://my-valid-source-url.com";
$target = "https://lewisdale.dev/post/a-post-page";
$content = <<<HTML
<html>
<head>
</head>
<body>
<article class="h-entry">
<a class="u-in-reply-to" rel="in-reply-to" href="$target">@post</a>: That's a great idea!<a class="u-url" href="/"></a>
</article>
<div class="h-card">
<p class="p-name">Anne Author</p> who can be found at <a class="u-url" href="https://my-blog.com">my-blog.com</a>.
<img src="https://dummyimage.com/100x100/fff/aaa" class="u-photo" alt="My profile picture" />
</div>
</body>
</html>
HTML;
$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);
$expected = new \Lewisdale\Webmentions\Models\Author(
null,
"Anne Author",
"https://my-blog.com",
"https://dummyimage.com/100x100/fff/aaa"
);
$mockGateway->expects($this->once())
->method('save')
->with($this->objectContains('author', $expected));
$endpoint = new Endpoint($mockClient, $mockGateway);
$endpoint->receiveWebmention($source, $target);
}
}