Improve tests
This commit is contained in:
parent
427debaba4
commit
48d1c9a00b
18 changed files with 267 additions and 14 deletions
|
@ -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
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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',
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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('/');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
33
tests/Feature/FrontPageTest.php
Normal file
33
tests/Feature/FrontPageTest.php
Normal 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');
|
||||||
|
}
|
||||||
|
}
|
28
tests/Feature/HorizonTest.php
Normal file
28
tests/Feature/HorizonTest.php
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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 token’s 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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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": []}',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Reference in a new issue