Revisions now call publishing only what publishes: a project's status is whether the work is active, and a service is published by its flag. The fake identity server answers by token, so a test can sign in a second person. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
212 lines
6.8 KiB
PHP
212 lines
6.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Feature\Api;
|
|
|
|
use App\Enums\PageStatus;
|
|
use App\Models\Page;
|
|
use App\Models\Project;
|
|
use App\Models\Service;
|
|
use App\Models\User;
|
|
use App\Support\PreviewLink;
|
|
use Database\Seeders\RolePermissionSeeder;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Notification;
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
use Tests\FakesPireon;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* History, review, publication and preview links through the admin API.
|
|
*/
|
|
class ReviewAndHistoryApiTest extends TestCase
|
|
{
|
|
use FakesPireon, RefreshDatabase;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
$this->seed(RolePermissionSeeder::class);
|
|
Notification::fake();
|
|
}
|
|
|
|
protected function actingWithRole(string $role): User
|
|
{
|
|
$user = User::factory()->create();
|
|
$user->syncRoles([$role]);
|
|
|
|
$this->withHeaders($this->apiHeadersFor($user));
|
|
|
|
return $user;
|
|
}
|
|
|
|
#[Test]
|
|
public function the_history_lists_each_change_with_who_made_it(): void
|
|
{
|
|
$editor = $this->actingWithRole('editor');
|
|
|
|
$project = Project::factory()->create(['status' => 'active']);
|
|
|
|
$this->patchJson("/api/v1/projects/{$project->id}", ['status' => 'paused'])->assertOk();
|
|
|
|
$this->getJson("/api/v1/projects/{$project->id}/revisions")
|
|
->assertOk()
|
|
->assertJsonPath('data.0.event', 'updated')
|
|
->assertJsonPath('data.0.author.id', $editor->id)
|
|
->assertJsonPath('data.0.changes.status.new', 'paused');
|
|
}
|
|
|
|
#[Test]
|
|
public function a_change_is_undone_and_the_undoing_is_recorded(): void
|
|
{
|
|
$this->actingWithRole('editor');
|
|
|
|
$service = Service::factory()->create(['tagline' => ['es' => 'Antes']]);
|
|
|
|
$this->patchJson("/api/v1/services/{$service->id}", ['tagline' => ['es' => 'Después']])->assertOk();
|
|
|
|
$revision = $service->revisions()->first();
|
|
|
|
$this->postJson("/api/v1/revisions/{$revision->id}/restore")
|
|
->assertOk()
|
|
->assertJsonPath('data.changes.tagline.new.es', 'Antes');
|
|
|
|
$this->assertSame('Antes', $service->fresh()->getTranslation('tagline', 'es'));
|
|
$this->assertSame(3, $service->revisions()->count());
|
|
}
|
|
|
|
#[Test]
|
|
public function undoing_a_publication_takes_the_publish_permission(): void
|
|
{
|
|
$page = Page::factory()->draft()->create();
|
|
|
|
$this->actingAs($editor = User::factory()->create()->syncRoles(['editor']));
|
|
$page->update(['status' => PageStatus::Published, 'published_at' => now()]);
|
|
|
|
$this->actingWithRole('writer');
|
|
|
|
$this->postJson('/api/v1/revisions/'.$page->revisions()->first()->id.'/restore')
|
|
->assertUnprocessable()
|
|
->assertJsonValidationErrors('revision');
|
|
|
|
$this->assertSame(PageStatus::Published, $page->fresh()->status);
|
|
}
|
|
|
|
#[Test]
|
|
public function a_writer_asks_for_review_and_an_editor_sees_it_waiting(): void
|
|
{
|
|
$writer = $this->actingWithRole('writer');
|
|
|
|
$page = Page::factory()->draft()->create();
|
|
|
|
$this->postJson("/api/v1/pages/{$page->id}/review", ['note' => 'Lista para revisar.'])
|
|
->assertOk()
|
|
->assertJsonPath('data.awaiting_review', true)
|
|
->assertJsonPath('data.requested_by.id', $writer->id);
|
|
|
|
$this->assertSame(PageStatus::Review, $page->fresh()->status);
|
|
|
|
$this->postJson("/api/v1/pages/{$page->id}/review")->assertUnprocessable();
|
|
|
|
$this->actingWithRole('editor');
|
|
|
|
$this->getJson('/api/v1/reviews')
|
|
->assertOk()
|
|
->assertJsonCount(1, 'data')
|
|
->assertJsonPath('data.0.type', 'pages')
|
|
->assertJsonPath('data.0.id', $page->id);
|
|
|
|
$this->getJson("/api/v1/pages/{$page->id}/reviews")
|
|
->assertJsonPath('data.0.event', 'requested')
|
|
->assertJsonPath('data.0.body', 'Lista para revisar.');
|
|
}
|
|
|
|
#[Test]
|
|
public function an_editor_returns_it_with_a_reason(): void
|
|
{
|
|
$this->actingAs(User::factory()->create()->syncRoles(['writer']));
|
|
$page = Page::factory()->draft()->create();
|
|
$page->requestReview();
|
|
|
|
$this->actingWithRole('editor');
|
|
|
|
$this->postJson("/api/v1/pages/{$page->id}/return", [])->assertUnprocessable();
|
|
|
|
$this->postJson("/api/v1/pages/{$page->id}/return", ['note' => 'Falta la imagen.'])
|
|
->assertOk()
|
|
->assertJsonPath('data.awaiting_review', false);
|
|
|
|
$this->assertSame(PageStatus::Draft, $page->fresh()->status);
|
|
}
|
|
|
|
#[Test]
|
|
public function publishing_later_answers_the_review_now(): void
|
|
{
|
|
$this->actingAs(User::factory()->create()->syncRoles(['writer']));
|
|
$project = Project::factory()->draft()->create();
|
|
$project->requestReview();
|
|
|
|
$this->actingWithRole('editor');
|
|
|
|
$at = now()->addDays(3)->startOfMinute();
|
|
|
|
$this->postJson("/api/v1/projects/{$project->id}/publish", ['at' => $at->toIso8601String()])
|
|
->assertOk()
|
|
->assertJsonPath('data.published', false)
|
|
->assertJsonPath('data.awaiting_review', false)
|
|
->assertJsonPath('data.published_at', $at->toIso8601String());
|
|
|
|
$this->postJson("/api/v1/projects/{$project->id}/unpublish")
|
|
->assertOk()
|
|
->assertJsonPath('data.published_at', null);
|
|
}
|
|
|
|
#[Test]
|
|
public function a_writer_cannot_publish_or_return(): void
|
|
{
|
|
$this->actingWithRole('writer');
|
|
|
|
$service = Service::factory()->create(['is_published' => false]);
|
|
|
|
$this->postJson("/api/v1/services/{$service->id}/publish")->assertForbidden();
|
|
$this->postJson("/api/v1/services/{$service->id}/return", ['note' => 'x'])->assertForbidden();
|
|
|
|
$this->assertFalse($service->fresh()->is_published);
|
|
}
|
|
|
|
#[Test]
|
|
public function preview_links_open_that_draft_only(): void
|
|
{
|
|
config(['site.url' => 'https://pireon.test']);
|
|
|
|
$this->actingWithRole('writer');
|
|
|
|
$page = Page::factory()->draft()->create();
|
|
|
|
$links = $this->postJson("/api/v1/pages/{$page->id}/preview")
|
|
->assertCreated()
|
|
->json('data.links');
|
|
|
|
$this->assertArrayHasKey('es', $links);
|
|
$this->assertStringStartsWith('https://pireon.test/', $links['es']);
|
|
|
|
parse_str((string) parse_url($links['es'], PHP_URL_QUERY), $query);
|
|
|
|
$this->assertSame(['type' => 'page', 'id' => $page->id, 'locale' => 'es'], PreviewLink::open($query['preview']));
|
|
}
|
|
|
|
#[Test]
|
|
public function comments_join_the_thread(): void
|
|
{
|
|
$this->actingWithRole('editor');
|
|
|
|
$page = Page::factory()->create();
|
|
|
|
$this->postJson("/api/v1/pages/{$page->id}/comments", ['body' => '¿Revisamos el título?'])
|
|
->assertCreated()
|
|
->assertJsonPath('data.event', 'commented');
|
|
}
|
|
}
|