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;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class Authenticate extends Middleware
|
||||
{
|
||||
/**
|
||||
|
|
|
@ -7,6 +7,9 @@ use Closure;
|
|||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class RedirectIfAuthenticated
|
||||
{
|
||||
/**
|
||||
|
|
|
@ -4,6 +4,9 @@ namespace App\Http\Middleware;
|
|||
|
||||
use Illuminate\Http\Middleware\TrustHosts as Middleware;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class TrustHosts extends Middleware
|
||||
{
|
||||
/**
|
||||
|
|
|
@ -60,7 +60,7 @@ class ProcessLike implements ShouldQueue
|
|||
|
||||
//POSSE like
|
||||
try {
|
||||
$response = $client->request(
|
||||
$client->request(
|
||||
'POST',
|
||||
'https://brid.gy/publish/webmention',
|
||||
[
|
||||
|
@ -70,8 +70,8 @@ class ProcessLike implements ShouldQueue
|
|||
],
|
||||
]
|
||||
);
|
||||
} catch (RequestException $exception) {
|
||||
//no biggie
|
||||
} catch (RequestException) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -38,6 +38,8 @@ class AppServiceProvider extends ServiceProvider
|
|||
});
|
||||
|
||||
// Bind the Codebird client
|
||||
// Codebird gets mocked in tests
|
||||
// @codeCoverageIgnoreStart
|
||||
$this->app->bind('Codebird\Codebird', function () {
|
||||
Codebird::setConsumerKey(
|
||||
env('TWITTER_CONSUMER_KEY'),
|
||||
|
@ -53,6 +55,7 @@ class AppServiceProvider extends ServiceProvider
|
|||
|
||||
return $cb;
|
||||
});
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
/**
|
||||
* Paginate a standard Laravel Collection.
|
||||
|
|
|
@ -34,9 +34,7 @@ class HorizonServiceProvider extends HorizonApplicationServiceProvider
|
|||
protected function gate()
|
||||
{
|
||||
Gate::define('viewHorizon', function ($user) {
|
||||
return in_array($user->name, [
|
||||
'jonny',
|
||||
]);
|
||||
return $user->name === 'jonny';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,8 @@ class RouteServiceProvider extends ServiceProvider
|
|||
* Configure the rate limiters for the application.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
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
|
||||
* @return string The uuid for the screenshot
|
||||
* @throws CouldNotTakeBrowsershot
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function saveScreenshot(string $url): string
|
||||
{
|
||||
|
|
|
@ -24,7 +24,7 @@ class WebMentionFactory extends Factory
|
|||
return [
|
||||
'source' => $this->faker->url,
|
||||
'target' => url('notes/1'),
|
||||
'type' => 'reply',
|
||||
'type' => 'in-reply-to',
|
||||
'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 */
|
||||
public function requestForNoteIncludesActivityStreamData(): void
|
||||
{
|
||||
|
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Tests\Feature\Admin;
|
||||
|
||||
use App\Models\User;
|
||||
use Tests\TestCase;
|
||||
|
||||
class AdminTest extends TestCase
|
||||
|
@ -31,4 +32,51 @@ class AdminTest extends TestCase
|
|||
]);
|
||||
$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);
|
||||
}
|
||||
|
||||
/** @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 */
|
||||
public function unknownLikeGivesNotFoundResponse(): void
|
||||
{
|
||||
|
|
|
@ -34,6 +34,50 @@ class MicropubMediaTest extends TestCase
|
|||
$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 */
|
||||
public function clientCanListLastUpload(): void
|
||||
{
|
||||
|
@ -124,6 +168,22 @@ class MicropubMediaTest extends TestCase
|
|||
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 */
|
||||
public function errorResponseForUnknownQValue(): void
|
||||
{
|
||||
|
|
|
@ -79,5 +79,12 @@ class ArticlesTest extends TestCase
|
|||
|
||||
$emptyScope = Article::date()->get();
|
||||
$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();
|
||||
|
||||
$parser = new Parser();
|
||||
$note = Note::factory()->create();
|
||||
$source = 'https://aaronpk.localhost/reply/1';
|
||||
WebMention::factory()->create([
|
||||
'source' => $source,
|
||||
'target' => $note->longurl,
|
||||
]);
|
||||
|
||||
$html = <<<HTML
|
||||
<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>
|
||||
HTML;
|
||||
$html = str_replace('href="', 'href="' . config('app.url'), $html);
|
||||
$mock = new MockHandler([
|
||||
new Response(200, [], $html),
|
||||
]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$note = Note::factory()->create();
|
||||
$source = 'https://aaronpk.localhost/reply/1';
|
||||
|
||||
$job = new ProcessWebMention($note, $source);
|
||||
$job->handle($parser, $client);
|
||||
|
||||
|
@ -114,7 +116,7 @@ class ProcessWebMentionJobTest extends TestCase
|
|||
'source' => $source,
|
||||
'type' => 'in-reply-to',
|
||||
// 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);
|
||||
}
|
||||
|
||||
/** @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 */
|
||||
public function htmlLikeContentIsFiltered(): void
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue