354 lines
11 KiB
PHP
354 lines
11 KiB
PHP
<?php
|
|
|
|
use App\Http\Controllers\PostController;
|
|
use App\Models\Attachment;
|
|
use App\Models\AttachmentGroup;
|
|
use App\Models\Forum;
|
|
use App\Models\Post;
|
|
use App\Models\Rank;
|
|
use App\Models\Role;
|
|
use App\Models\Setting;
|
|
use App\Models\Thread;
|
|
use App\Models\User;
|
|
use Illuminate\Http\Request;
|
|
|
|
beforeEach(function (): void {
|
|
$parserProp = new ReflectionProperty(\App\Actions\BbcodeFormatter::class, 'parser');
|
|
$parserProp->setAccessible(true);
|
|
$parserProp->setValue(
|
|
\Mockery::mock(\s9e\TextFormatter\Parser::class)
|
|
->shouldReceive('parse')
|
|
->andReturn('<r/>')
|
|
->getMock()
|
|
);
|
|
|
|
$rendererProp = new ReflectionProperty(\App\Actions\BbcodeFormatter::class, 'renderer');
|
|
$rendererProp->setAccessible(true);
|
|
$rendererProp->setValue(
|
|
\Mockery::mock(\s9e\TextFormatter\Renderer::class)
|
|
->shouldReceive('render')
|
|
->andReturn('<p></p>')
|
|
->getMock()
|
|
);
|
|
});
|
|
|
|
afterEach(function (): void {
|
|
\Mockery::close();
|
|
});
|
|
|
|
function makeForumForPostController(): Forum
|
|
{
|
|
$category = Forum::create([
|
|
'name' => 'Category',
|
|
'description' => null,
|
|
'type' => 'category',
|
|
'parent_id' => null,
|
|
'position' => 1,
|
|
]);
|
|
|
|
return Forum::create([
|
|
'name' => 'Forum',
|
|
'description' => null,
|
|
'type' => 'forum',
|
|
'parent_id' => $category->id,
|
|
'position' => 1,
|
|
]);
|
|
}
|
|
|
|
it('returns unauthorized on update when no user', function (): void {
|
|
$controller = new PostController();
|
|
$forum = makeForumForPostController();
|
|
$thread = Thread::create([
|
|
'forum_id' => $forum->id,
|
|
'user_id' => null,
|
|
'title' => 'Thread',
|
|
'body' => 'Body',
|
|
]);
|
|
$post = Post::create([
|
|
'thread_id' => $thread->id,
|
|
'user_id' => null,
|
|
'body' => 'Body',
|
|
]);
|
|
|
|
$request = Request::create('/api/posts/'.$post->id, 'PATCH', ['body' => 'x']);
|
|
$request->setUserResolver(fn () => null);
|
|
|
|
$response = $controller->update($request, $post);
|
|
|
|
expect($response->getStatusCode())->toBe(401);
|
|
});
|
|
|
|
it('returns forbidden on update when user is not owner or admin', function (): void {
|
|
$controller = new PostController();
|
|
$forum = makeForumForPostController();
|
|
$owner = User::factory()->create();
|
|
$viewer = User::factory()->create();
|
|
$thread = Thread::create([
|
|
'forum_id' => $forum->id,
|
|
'user_id' => $owner->id,
|
|
'title' => 'Thread',
|
|
'body' => 'Body',
|
|
]);
|
|
$post = Post::create([
|
|
'thread_id' => $thread->id,
|
|
'user_id' => $owner->id,
|
|
'body' => 'Body',
|
|
]);
|
|
|
|
$request = Request::create('/api/posts/'.$post->id, 'PATCH', ['body' => 'x']);
|
|
$request->setUserResolver(fn () => $viewer);
|
|
|
|
$response = $controller->update($request, $post);
|
|
|
|
expect($response->getStatusCode())->toBe(403);
|
|
});
|
|
|
|
it('updates post when user is owner', function (): void {
|
|
$controller = new PostController();
|
|
$forum = makeForumForPostController();
|
|
$owner = User::factory()->create();
|
|
$thread = Thread::create([
|
|
'forum_id' => $forum->id,
|
|
'user_id' => $owner->id,
|
|
'title' => 'Thread',
|
|
'body' => 'Body',
|
|
]);
|
|
$post = Post::create([
|
|
'thread_id' => $thread->id,
|
|
'user_id' => $owner->id,
|
|
'body' => 'Body',
|
|
]);
|
|
|
|
$request = Request::create('/api/posts/'.$post->id, 'PATCH', ['body' => 'Updated']);
|
|
$request->setUserResolver(fn () => $owner);
|
|
|
|
$response = $controller->update($request, $post);
|
|
|
|
expect($response->getStatusCode())->toBe(200);
|
|
$post->refresh();
|
|
expect($post->body)->toBe('Updated');
|
|
});
|
|
|
|
it('parseIriId handles empty and numeric values', function (): void {
|
|
$controller = new PostController();
|
|
$ref = new ReflectionMethod($controller, 'parseIriId');
|
|
$ref->setAccessible(true);
|
|
|
|
expect($ref->invoke($controller, null))->toBeNull();
|
|
expect($ref->invoke($controller, ''))->toBeNull();
|
|
expect($ref->invoke($controller, '/threads/12'))->toBe(12);
|
|
expect($ref->invoke($controller, '7'))->toBe(7);
|
|
expect($ref->invoke($controller, 'abc'))->toBeNull();
|
|
});
|
|
|
|
it('serializes posts with attachments and rank data', function (): void {
|
|
$forum = makeForumForPostController();
|
|
$role = Role::create(['name' => 'ROLE_MOD', 'color' => '#00ff00']);
|
|
$rank = Rank::create(['name' => 'Gold', 'badge_image_path' => 'ranks/badge.png']);
|
|
$user = User::factory()->create([
|
|
'rank_id' => $rank->id,
|
|
'avatar_path' => 'avatars/u.png',
|
|
'location' => 'Here',
|
|
]);
|
|
$user->roles()->attach($role);
|
|
|
|
$thread = Thread::create([
|
|
'forum_id' => $forum->id,
|
|
'user_id' => $user->id,
|
|
'title' => 'Thread',
|
|
'body' => 'Body',
|
|
]);
|
|
$post = Post::create([
|
|
'thread_id' => $thread->id,
|
|
'user_id' => $user->id,
|
|
'body' => 'See [attachment]file.png[/attachment]',
|
|
]);
|
|
|
|
$group = AttachmentGroup::create([
|
|
'name' => 'Images',
|
|
'max_size_kb' => 100,
|
|
'is_active' => true,
|
|
]);
|
|
|
|
$attachment = Attachment::create([
|
|
'thread_id' => null,
|
|
'post_id' => $post->id,
|
|
'attachment_extension_id' => null,
|
|
'attachment_group_id' => $group->id,
|
|
'user_id' => $user->id,
|
|
'disk' => 'local',
|
|
'path' => 'attachments/posts/'.$post->id.'/file.png',
|
|
'thumbnail_path' => 'attachments/posts/'.$post->id.'/thumb.png',
|
|
'original_name' => 'file.png',
|
|
'extension' => 'png',
|
|
'mime_type' => 'image/png',
|
|
'size_bytes' => 10,
|
|
]);
|
|
|
|
$post->load(['user.rank', 'user.roles', 'attachments.group']);
|
|
|
|
$controller = new PostController();
|
|
$ref = new ReflectionMethod($controller, 'serializePost');
|
|
$ref->setAccessible(true);
|
|
|
|
$payload = $ref->invoke($controller, $post);
|
|
|
|
expect($payload['user_rank_badge_url'])->not->toBeNull();
|
|
expect($payload['user_group_color'])->toBe('#00ff00');
|
|
expect($payload['attachments'][0]['group']['name'])->toBe('Images');
|
|
expect($payload['attachments'][0]['thumbnail_url'])->toContain('/thumbnail');
|
|
});
|
|
|
|
it('serializes posts with null user and no attachments', function (): void {
|
|
$forum = makeForumForPostController();
|
|
$thread = Thread::create([
|
|
'forum_id' => $forum->id,
|
|
'user_id' => null,
|
|
'title' => 'Thread',
|
|
'body' => 'Body',
|
|
]);
|
|
$post = Post::create([
|
|
'thread_id' => $thread->id,
|
|
'user_id' => null,
|
|
'body' => 'Body',
|
|
]);
|
|
|
|
$controller = new PostController();
|
|
$ref = new ReflectionMethod($controller, 'serializePost');
|
|
$ref->setAccessible(true);
|
|
|
|
$payload = $ref->invoke($controller, $post);
|
|
|
|
expect($payload['user_avatar_url'])->toBeNull();
|
|
expect($payload['user_rank_badge_url'])->toBeNull();
|
|
expect($payload['user_group_color'])->toBeNull();
|
|
expect($payload['attachments'])->toBe([]);
|
|
});
|
|
|
|
it('replaceAttachmentTags handles inline images and links', function (): void {
|
|
$controller = new PostController();
|
|
$ref = new ReflectionMethod($controller, 'replaceAttachmentTags');
|
|
$ref->setAccessible(true);
|
|
|
|
Setting::updateOrCreate(['key' => 'attachments.display_images_inline'], ['value' => '1']);
|
|
|
|
$attachment = new Attachment([
|
|
'id' => 1,
|
|
'original_name' => 'file.png',
|
|
'mime_type' => 'image/png',
|
|
'thumbnail_path' => null,
|
|
]);
|
|
|
|
$body = 'See [attachment]file.png[/attachment]';
|
|
$result = $ref->invoke($controller, $body, collect([$attachment]));
|
|
expect($result)->toContain('[img]');
|
|
|
|
$attachment->thumbnail_path = 'thumb';
|
|
$result = $ref->invoke($controller, $body, collect([$attachment]));
|
|
expect($result)->toContain('[url=');
|
|
|
|
Setting::updateOrCreate(['key' => 'attachments.display_images_inline'], ['value' => '0']);
|
|
$result = $ref->invoke($controller, $body, collect([$attachment]));
|
|
expect($result)->toContain('[url=');
|
|
|
|
$result = $ref->invoke($controller, 'No match', collect([$attachment]));
|
|
expect($result)->toContain('No match');
|
|
});
|
|
|
|
it('replaceAttachmentTags returns original tag when attachment name missing in map', function (): void {
|
|
$controller = new PostController();
|
|
$ref = new ReflectionMethod($controller, 'replaceAttachmentTags');
|
|
$ref->setAccessible(true);
|
|
|
|
Setting::updateOrCreate(['key' => 'attachments.display_images_inline'], ['value' => '1']);
|
|
|
|
$attachment = new Attachment([
|
|
'id' => 2,
|
|
'original_name' => 'actual.txt',
|
|
'mime_type' => 'text/plain',
|
|
]);
|
|
|
|
$body = 'See [attachment]missing.txt[/attachment]';
|
|
$result = $ref->invoke($controller, $body, collect([$attachment]));
|
|
|
|
expect($result)->toBe($body);
|
|
});
|
|
|
|
it('replaceAttachmentTags renders non-image attachments as links', function (): void {
|
|
$controller = new PostController();
|
|
$ref = new ReflectionMethod($controller, 'replaceAttachmentTags');
|
|
$ref->setAccessible(true);
|
|
|
|
Setting::updateOrCreate(['key' => 'attachments.display_images_inline'], ['value' => 'yes']);
|
|
|
|
$attachment = new Attachment([
|
|
'id' => 3,
|
|
'original_name' => 'doc.txt',
|
|
'mime_type' => 'text/plain',
|
|
]);
|
|
|
|
$body = 'See [attachment]doc.txt[/attachment]';
|
|
$result = $ref->invoke($controller, $body, collect([$attachment]));
|
|
|
|
expect($result)->toContain('[url=');
|
|
expect($result)->toContain('doc.txt');
|
|
});
|
|
|
|
it('replaceAttachmentTags returns body when no attachments or map empty', function (): void {
|
|
$controller = new PostController();
|
|
$ref = new ReflectionMethod($controller, 'replaceAttachmentTags');
|
|
$ref->setAccessible(true);
|
|
|
|
expect($ref->invoke($controller, 'Body', []))->toBe('Body');
|
|
|
|
$attachment = new Attachment([
|
|
'original_name' => '',
|
|
]);
|
|
expect($ref->invoke($controller, 'Body', collect([$attachment])))->toBe('Body');
|
|
});
|
|
|
|
it('displayImagesInline defaults to true when missing setting', function (): void {
|
|
Setting::where('key', 'attachments.display_images_inline')->delete();
|
|
|
|
$controller = new PostController();
|
|
$ref = new ReflectionMethod($controller, 'displayImagesInline');
|
|
$ref->setAccessible(true);
|
|
|
|
expect($ref->invoke($controller))->toBeTrue();
|
|
});
|
|
|
|
it('displayImagesInline returns false for off values', function (): void {
|
|
Setting::updateOrCreate(['key' => 'attachments.display_images_inline'], ['value' => 'off']);
|
|
|
|
$controller = new PostController();
|
|
$ref = new ReflectionMethod($controller, 'displayImagesInline');
|
|
$ref->setAccessible(true);
|
|
|
|
expect($ref->invoke($controller))->toBeFalse();
|
|
});
|
|
|
|
it('resolveGroupColor returns null for missing roles', function (): void {
|
|
$controller = new PostController();
|
|
$user = User::factory()->create();
|
|
$user->setRelation('roles', null);
|
|
|
|
$ref = new ReflectionMethod($controller, 'resolveGroupColor');
|
|
$ref->setAccessible(true);
|
|
|
|
expect($ref->invoke($controller, $user))->toBeNull();
|
|
});
|
|
|
|
it('resolveGroupColor returns first sorted role color', function (): void {
|
|
$controller = new PostController();
|
|
$user = User::factory()->create();
|
|
$roleB = Role::create(['name' => 'ROLE_B', 'color' => '#bbbbbb']);
|
|
$roleA = Role::create(['name' => 'ROLE_A', 'color' => '#aaaaaa']);
|
|
$user->roles()->attach([$roleB->id, $roleA->id]);
|
|
$user->load('roles');
|
|
|
|
$ref = new ReflectionMethod($controller, 'resolveGroupColor');
|
|
$ref->setAccessible(true);
|
|
|
|
expect($ref->invoke($controller, $user))->toBe('#aaaaaa');
|
|
});
|