Snapshot of main at 9f39a6e1e (2026-11-02)
This commit is contained in:
commit
6ccf1792d7
2580 files changed
+1040202
No files matched your search
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Polls;
|
||||
|
||||
use App\Http\Livewire\Posts\Manage as PostsManage;
|
||||
use App\Models\Admin;
|
||||
use App\Models\Poll;
|
||||
use App\Models\PollResponse;
|
||||
use App\Models\Post;
|
||||
use App\Models\PostTranslation;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Livewire\Livewire;
|
||||
use Spatie\Permission\Models\Permission;
|
||||
use Spatie\Permission\Models\Role;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* The Posts page cannot delete or (un)publish the post of an opened poll (security audit R1).
|
||||
*/
|
||||
class PollPostsPageTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
// phpunit.xml configures Redis, which is the dev site's cache too
|
||||
config(['cache.default' => 'array', 'scout.driver' => null]);
|
||||
}
|
||||
|
||||
private function actAsPostsAdmin(array $extra = []): void
|
||||
{
|
||||
$permissions = array_merge(['view posts', 'delete posts', 'publish posts', 'unpublish posts'], $extra);
|
||||
$user = User::factory()->create();
|
||||
$admin = Admin::factory()->create();
|
||||
$admin->users()->attach($user);
|
||||
$role = Role::firstOrCreate(['name' => "Admin\\{$admin->id}\\posts-only", 'guard_name' => 'web']);
|
||||
foreach ($permissions as $name) {
|
||||
Permission::firstOrCreate(['name' => $name, 'guard_name' => 'web']);
|
||||
}
|
||||
$role->syncPermissions($permissions);
|
||||
$user->assignRole($role);
|
||||
$this->actingAs($user, 'web');
|
||||
$this->actingAs($admin, 'admin');
|
||||
session(['activeProfileType' => Admin::class, 'activeProfileId' => $admin->id]);
|
||||
}
|
||||
|
||||
/** @return array{0: ?Poll, 1: int} the poll (if any) and the translation id */
|
||||
private function makePost(?string $pollStatus, bool $withVote = false): array
|
||||
{
|
||||
$postId = DB::table('posts')->insertGetId(['postable_id' => 1, 'postable_type' => Admin::class, 'created_at' => now(), 'updated_at' => now()]);
|
||||
$translationId = DB::table('post_translations')->insertGetId([
|
||||
'post_id' => $postId, 'locale' => 'en', 'slug' => 'posts-page-' . $postId, 'title' => 'Post ' . $postId,
|
||||
'from' => now()->subDay(), 'created_at' => now(), 'updated_at' => now(),
|
||||
]);
|
||||
$poll = $pollStatus ? Poll::create(['post_id' => $postId, 'status' => $pollStatus]) : null;
|
||||
if ($poll && $withVote) {
|
||||
$voter = User::factory()->create();
|
||||
PollResponse::create(['poll_id' => $poll->id, 'voter_id' => $voter->id, 'voter_type' => User::class, 'user_id' => $voter->id,
|
||||
'dedupe_key' => $poll->dedupeKey($voter, $voter->id), 'submitted_at' => now()]);
|
||||
}
|
||||
|
||||
return [$poll, $translationId];
|
||||
}
|
||||
|
||||
public function test_an_open_poll_post_cannot_be_unpublished_or_deleted(): void
|
||||
{
|
||||
$this->actAsPostsAdmin(['manage polls']);
|
||||
[$poll, $translationId] = $this->makePost(Poll::STATUS_OPEN, true);
|
||||
|
||||
Livewire::test(PostsManage::class)->call('stopPublication', $translationId);
|
||||
$this->assertNull(PostTranslation::find($translationId)->till);
|
||||
|
||||
Livewire::test(PostsManage::class)->set('bulkSelected', [$translationId])->call('deleteSelected');
|
||||
$this->assertNotNull(PostTranslation::find($translationId));
|
||||
$this->assertNotNull(Post::find($poll->post_id));
|
||||
}
|
||||
|
||||
public function test_a_closed_poll_post_cannot_be_republished(): void
|
||||
{
|
||||
$this->actAsPostsAdmin();
|
||||
[, $translationId] = $this->makePost(Poll::STATUS_CLOSED, true);
|
||||
PostTranslation::whereKey($translationId)->update(['till' => now()->subHour()]);
|
||||
$till = PostTranslation::find($translationId)->till;
|
||||
|
||||
Livewire::test(PostsManage::class)->call('startPublication', $translationId);
|
||||
$this->assertEquals($till, PostTranslation::find($translationId)->till);
|
||||
}
|
||||
|
||||
public function test_a_draft_poll_post_needs_delete_polls_to_be_deleted(): void
|
||||
{
|
||||
$this->actAsPostsAdmin();
|
||||
[$draft, $translationId] = $this->makePost(Poll::STATUS_DRAFT);
|
||||
Livewire::test(PostsManage::class)->set('bulkSelected', [$translationId])->call('deleteSelected');
|
||||
$this->assertNotNull(PostTranslation::find($translationId));
|
||||
|
||||
$this->actAsPostsAdmin(['delete polls']);
|
||||
Livewire::test(PostsManage::class)->set('bulkSelected', [$translationId])->call('deleteSelected');
|
||||
$this->assertNull(PostTranslation::find($translationId));
|
||||
$this->assertNull(Post::find($draft->post_id));
|
||||
}
|
||||
|
||||
public function test_other_posts_are_unaffected(): void
|
||||
{
|
||||
$this->actAsPostsAdmin();
|
||||
[, $translationId] = $this->makePost(null);
|
||||
|
||||
Livewire::test(PostsManage::class)->call('stopPublication', $translationId);
|
||||
$this->assertNotNull(PostTranslation::find($translationId)->till);
|
||||
|
||||
Livewire::test(PostsManage::class)->set('bulkSelected', [$translationId])->call('deleteSelected');
|
||||
$this->assertNull(PostTranslation::find($translationId));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user