Improve tests

This commit is contained in:
Jonny Barnes 2022-05-14 17:44:14 +01:00
parent 427debaba4
commit 48d1c9a00b
18 changed files with 267 additions and 14 deletions

View file

@ -4,6 +4,9 @@ namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware; use Illuminate\Auth\Middleware\Authenticate as Middleware;
/**
* @codeCoverageIgnore
*/
class Authenticate extends Middleware class Authenticate extends Middleware
{ {
/** /**

View file

@ -7,6 +7,9 @@ use Closure;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
/**
* @codeCoverageIgnore
*/
class RedirectIfAuthenticated class RedirectIfAuthenticated
{ {
/** /**

View file

@ -4,6 +4,9 @@ namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustHosts as Middleware; use Illuminate\Http\Middleware\TrustHosts as Middleware;
/**
* @codeCoverageIgnore
*/
class TrustHosts extends Middleware class TrustHosts extends Middleware
{ {
/** /**

View file

@ -60,7 +60,7 @@ class ProcessLike implements ShouldQueue
//POSSE like //POSSE like
try { try {
$response = $client->request( $client->request(
'POST', 'POST',
'https://brid.gy/publish/webmention', 'https://brid.gy/publish/webmention',
[ [
@ -70,8 +70,8 @@ class ProcessLike implements ShouldQueue
], ],
] ]
); );
} catch (RequestException $exception) { } catch (RequestException) {
//no biggie return 0;
} }
return 0; return 0;

View file

@ -38,6 +38,8 @@ class AppServiceProvider extends ServiceProvider
}); });
// Bind the Codebird client // Bind the Codebird client
// Codebird gets mocked in tests
// @codeCoverageIgnoreStart
$this->app->bind('Codebird\Codebird', function () { $this->app->bind('Codebird\Codebird', function () {
Codebird::setConsumerKey( Codebird::setConsumerKey(
env('TWITTER_CONSUMER_KEY'), env('TWITTER_CONSUMER_KEY'),
@ -53,6 +55,7 @@ class AppServiceProvider extends ServiceProvider
return $cb; return $cb;
}); });
// @codeCoverageIgnoreEnd
/** /**
* Paginate a standard Laravel Collection. * Paginate a standard Laravel Collection.

View file

@ -34,9 +34,7 @@ class HorizonServiceProvider extends HorizonApplicationServiceProvider
protected function gate() protected function gate()
{ {
Gate::define('viewHorizon', function ($user) { Gate::define('viewHorizon', function ($user) {
return in_array($user->name, [ return $user->name === 'jonny';
'jonny',
]);
}); });
} }
} }

View file

@ -44,6 +44,8 @@ class RouteServiceProvider extends ServiceProvider
* Configure the rate limiters for the application. * Configure the rate limiters for the application.
* *
* @return void * @return void
*
* @codeCoverageIgnore
*/ */
protected function configureRateLimiting() protected function configureRateLimiting()
{ {

View file

@ -79,11 +79,12 @@ class BookmarkService
} }
/** /**
* Given a URL, use browsershot to save an image of the page. * Given a URL, use `browsershot` to save an image of the page.
* *
* @param string $url * @param string $url
* @return string The uuid for the screenshot * @return string The uuid for the screenshot
* @throws CouldNotTakeBrowsershot * @throws CouldNotTakeBrowsershot
* @codeCoverageIgnore
*/ */
public function saveScreenshot(string $url): string public function saveScreenshot(string $url): string
{ {

View file

@ -24,7 +24,7 @@ class WebMentionFactory extends Factory
return [ return [
'source' => $this->faker->url, 'source' => $this->faker->url,
'target' => url('notes/1'), 'target' => url('notes/1'),
'type' => 'reply', 'type' => 'in-reply-to',
'content' => $this->faker->paragraph, 'content' => $this->faker->paragraph,
]; ];
} }

View file

@ -25,6 +25,19 @@ class ActivityStreamTest extends TestCase
]); ]);
} }
/** @test */
public function notesPageContainsAuthorActivityStreamData(): void
{
$response = $this->get('/notes', ['Accept' => 'application/activity+json']);
$response->assertHeader('Content-Type', 'application/activity+json');
$response->assertJson([
'@context' => 'https://www.w3.org/ns/activitystreams',
'id' => config('app.url'),
'type' => 'Person',
'name' => config('user.displayname'),
]);
}
/** @test */ /** @test */
public function requestForNoteIncludesActivityStreamData(): void public function requestForNoteIncludesActivityStreamData(): void
{ {

View file

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Tests\Feature\Admin; namespace Tests\Feature\Admin;
use App\Models\User;
use Tests\TestCase; use Tests\TestCase;
class AdminTest extends TestCase class AdminTest extends TestCase
@ -31,4 +32,51 @@ class AdminTest extends TestCase
]); ]);
$response->assertRedirect('/login'); $response->assertRedirect('/login');
} }
/** @test */
public function loginSucceeds(): void
{
User::factory([
'name' => 'admin',
'password' => bcrypt('password'),
])->create();
$response = $this->post('/login', [
'name' => 'admin',
'password' => 'password',
]);
$response->assertRedirect('/');
}
/** @test */
public function whenLoggedInRedirectsToAdminPage(): void
{
$user = User::factory()->create();
$response = $this->actingAs($user)->get('/login');
$response->assertRedirect('/');
}
/** @test */
public function loggedOutUsersSimplyRedirected(): void
{
$response = $this->get('/logout');
$response->assertRedirect('/');
}
/** @test */
public function loggedInUsersShownLogoutForm(): void
{
$user = User::factory()->create();
$response = $this->actingAs($user)->get('/logout');
$response->assertViewIs('logout');
}
/** @test */
public function loggedInUsersCanLogout(): void
{
$user = User::factory()->create();
$response = $this->actingAs($user)->post('/logout');
$response->assertRedirect('/');
}
} }

View file

@ -0,0 +1,33 @@
<?php
namespace Tests\Feature;
use App\Models\Article;
use App\Models\Bookmark;
use App\Models\Like;
use App\Models\Note;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class FrontPageTest extends TestCase
{
use RefreshDatabase;
/** @test */
public function frontPageLoadsAllContent(): void
{
Note::factory()->create(['note' => 'Note 1']);
Article::factory()->create(['title' => 'Article 1']);
Bookmark::factory()->create(['url' => 'https://example.com']);
Like::factory()->create([
'url' => 'https://example.org/1',
'content' => 'Like 1',
]);
$this->get('/')
->assertSee('Note 1')
->assertSee('Article 1')
->assertSee('https://example.com')
->assertSee('Like 1');
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace Tests\Feature;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
class HorizonTest extends TestCase
{
/**
* Horizon has its own test suite, here we just test it has been installed successfully.
*
* @test
* @return void
*/
public function horizonIsInstalled(): void
{
$user = User::factory()->create([
'name' => 'jonny',
]);
$response = $this->actingAs($user)->get('/horizon');
$response->assertStatus(200);
}
}

View file

@ -233,6 +233,44 @@ class LikesTest extends TestCase
$this->assertEquals('Jonny Barnes', Like::find($id)->author_name); $this->assertEquals('Jonny Barnes', Like::find($id)->author_name);
} }
/** @test */
public function noErrorForFailureToPosseWithBridgy(): void
{
$like = new Like();
$like->url = 'https://twitter.com/jonnybarnes/status/1050823255123251200';
$like->save();
$id = $like->id;
$job = new ProcessLike($like);
$mock = new MockHandler([
new Response(404, [], 'Not found'),
]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$this->app->bind(Client::class, function () use ($client) {
return $client;
});
$info = (object) [
'author_name' => 'Jonny Barnes',
'author_url' => 'https://twitter.com/jonnybarnes',
'html' => '<div>HTML of the tweet embed</div>',
];
$codebirdMock = $this->getMockBuilder(Codebird::class)
->addMethods(['statuses_oembed'])
->getMock();
$codebirdMock->method('statuses_oembed')
->willReturn($info);
$this->app->instance(Codebird::class, $codebirdMock);
$authorship = new Authorship();
$job->handle($client, $authorship);
$this->assertEquals('Jonny Barnes', Like::find($id)->author_name);
}
/** @test */ /** @test */
public function unknownLikeGivesNotFoundResponse(): void public function unknownLikeGivesNotFoundResponse(): void
{ {

View file

@ -34,6 +34,50 @@ class MicropubMediaTest extends TestCase
$response->assertJson(['url' => null]); $response->assertJson(['url' => null]);
} }
/** @test */
public function getRequestWithInvalidTokenReturnsErrorResponse(): void
{
$response = $this->get(
'/api/media?q=last',
['HTTP_Authorization' => 'Bearer abc123']
);
$response->assertStatus(400);
$response->assertJsonFragment(['error_description' => 'The provided token did not pass validation']);
}
/** @test */
public function getRequestWithTokenWithoutScopeReturnsErrorResponse(): void
{
$response = $this->get(
'/api/media?q=last',
['HTTP_Authorization' => 'Bearer ' . $this->getTokenWithNoScope()]
);
$response->assertStatus(400);
$response->assertJsonFragment(['error_description' => 'The provided token has no scopes']);
}
/** @test */
public function getRequestWithTokenWithInsufficientScopeReturnsErrorResponse(): void
{
$response = $this->get(
'/api/media?q=last',
['HTTP_Authorization' => 'Bearer ' . $this->getTokenWithIncorrectScope()]
);
$response->assertStatus(401);
$response->assertJsonFragment(['error_description' => 'The tokens scope does not have the necessary requirements.']);
}
/** @test */
public function emptyGetRequestWithTokenReceivesOkResponse(): void
{
$response = $this->get(
'/api/media',
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$response->assertStatus(200);
$response->assertJson(['status' => 'OK']);
}
/** @test */ /** @test */
public function clientCanListLastUpload(): void public function clientCanListLastUpload(): void
{ {
@ -124,6 +168,22 @@ class MicropubMediaTest extends TestCase
unlink(storage_path('app/') . $filename); unlink(storage_path('app/') . $filename);
} }
/** @test */
public function mediaEndpointUploadRequiresFile(): void
{
$response = $this->post(
'/api/media',
[],
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
);
$response->assertStatus(400);
$response->assertJson([
'response' => 'error',
'error' => 'invalid_request',
'error_description' => 'No file was sent with the request',
]);
}
/** @test */ /** @test */
public function errorResponseForUnknownQValue(): void public function errorResponseForUnknownQValue(): void
{ {

View file

@ -79,5 +79,12 @@ class ArticlesTest extends TestCase
$emptyScope = Article::date()->get(); $emptyScope = Article::date()->get();
$this->assertCount(2, $emptyScope); $this->assertCount(2, $emptyScope);
// Check the December case
$article = Article::factory()->create([
'created_at' => Carbon::now()->setMonth(12)->toDateTimeString(),
'updated_at' => Carbon::now()->setMonth(12)->toDateTimeString(),
]);
$this->assertCount(1, Article::date(date('Y'), 12)->get());
} }
} }

View file

@ -89,23 +89,25 @@ class ProcessWebMentionJobTest extends TestCase
Queue::fake(); Queue::fake();
$parser = new Parser(); $parser = new Parser();
$note = Note::factory()->create();
$source = 'https://aaronpk.localhost/reply/1';
WebMention::factory()->create([
'source' => $source,
'target' => $note->longurl,
]);
$html = <<<HTML $html = <<<HTML
<div class="h-entry"> <div class="h-entry">
<p>In reply to <a class="u-in-reply-to" href="/notes/E">a note</a></p> <p>In reply to <a class="u-in-reply-to" href="{$note->longurl}">a note</a></p>
<div class="e-content">Updated reply</div> <div class="e-content">Updated reply</div>
</div> </div>
HTML; HTML;
$html = str_replace('href="', 'href="' . config('app.url'), $html);
$mock = new MockHandler([ $mock = new MockHandler([
new Response(200, [], $html), new Response(200, [], $html),
]); ]);
$handler = HandlerStack::create($mock); $handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]); $client = new Client(['handler' => $handler]);
$note = Note::factory()->create();
$source = 'https://aaronpk.localhost/reply/1';
$job = new ProcessWebMention($note, $source); $job = new ProcessWebMention($note, $source);
$job->handle($parser, $client); $job->handle($parser, $client);
@ -114,7 +116,7 @@ class ProcessWebMentionJobTest extends TestCase
'source' => $source, 'source' => $source,
'type' => 'in-reply-to', 'type' => 'in-reply-to',
// phpcs:ignore Generic.Files.LineLength.TooLong // phpcs:ignore Generic.Files.LineLength.TooLong
'mf2' => '{"rels": [], "items": [{"type": ["h-entry"], "properties": {"content": [{"html": "Updated reply", "value": "Updated reply"}], "in-reply-to": ["' . config('app.url') . '/notes/E"]}}], "rel-urls": []}', 'mf2' => '{"rels": [], "items": [{"type": ["h-entry"], "properties": {"content": [{"html": "Updated reply", "value": "Updated reply"}], "in-reply-to": ["' . $note->longurl . '"]}}], "rel-urls": []}',
]); ]);
} }

View file

@ -31,6 +31,17 @@ class LikesTest extends TestCase
$this->assertEquals('some plaintext content', $like->content); $this->assertEquals('some plaintext content', $like->content);
} }
/** @test */
public function weCanHandleBlankContent(): void
{
$like = new Like();
$like->url = 'https://example.org/post/123';
$like->content = null;
$like->save();
$this->assertNull($like->content);
}
/** @test */ /** @test */
public function htmlLikeContentIsFiltered(): void public function htmlLikeContentIsFiltered(): void
{ {