The project form gets «Crear en TaskStream» while the project has no board, and `taskstream: true` on a PATCH does the same through the API. A refusal leaves the edit unsaved; a project with a board keeps it, and `taskstream_key` without the flag is still ignored. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
173 lines
6.0 KiB
PHP
173 lines
6.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Feature\Panel;
|
|
|
|
use App\Filament\Resources\Projects\Pages\CreateProject;
|
|
use App\Filament\Resources\Projects\Pages\EditProject;
|
|
use App\Models\Project;
|
|
use App\Services\Pireon\AccessToken;
|
|
use App\Services\Pireon\TokenStore;
|
|
use Database\Seeders\RolePermissionSeeder;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Http\Client\Request;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Livewire\Livewire;
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
use Tests\FakesPireon;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* The panel's way to open a project's board in TaskStream, as the person
|
|
* signed in: a toggle on the create form, or an action on a project that
|
|
* has none yet.
|
|
*/
|
|
class TaskStreamPanelTest extends TestCase
|
|
{
|
|
use FakesPireon, RefreshDatabase;
|
|
|
|
protected string $taskstream = 'https://api.taskstream.test';
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
$this->seed(RolePermissionSeeder::class);
|
|
$this->actingAs($this->userWithRoles('editor'));
|
|
|
|
config([
|
|
'services.taskstream.api_url' => $this->taskstream,
|
|
'services.taskstream.web_url' => 'https://taskstream.test',
|
|
]);
|
|
|
|
// What signing in to the panel leaves in the session.
|
|
app(TokenStore::class)->put(new AccessToken('session-token', null, now()->addHour()->toImmutable()));
|
|
}
|
|
|
|
protected function fakeTaskStream(int $status = 201, array $body = []): void
|
|
{
|
|
Http::fake([
|
|
"{$this->taskstream}/api/projects" => fn (Request $request) => Http::response(
|
|
$status === 201 ? ['id' => 'ts-1', 'key' => $request['key'], 'version' => 1] : $body,
|
|
$status,
|
|
),
|
|
]);
|
|
}
|
|
|
|
#[Test]
|
|
public function the_toggle_opens_a_board_with_the_session_token(): void
|
|
{
|
|
$this->fakeTaskStream();
|
|
|
|
Livewire::test(CreateProject::class)
|
|
->fillForm([
|
|
'title' => ['es' => 'Catálogo abierto'],
|
|
'slug' => ['es' => 'catalogo'],
|
|
'taskstream' => true,
|
|
])
|
|
->assertFormFieldExists('taskstream_key')
|
|
->call('create')
|
|
->assertHasNoFormErrors()
|
|
->assertNotified();
|
|
|
|
Http::assertSent(fn (Request $request) => $request->url() === "{$this->taskstream}/api/projects"
|
|
&& $request->hasHeader('Authorization', 'Bearer session-token')
|
|
&& $request['key'] === 'CATALOGO'
|
|
&& $request['isPublic'] === true);
|
|
|
|
$project = Project::query()->sole();
|
|
$this->assertSame('CATALOGO', $project->taskstream_key);
|
|
|
|
Livewire::test(EditProject::class, ['record' => $project->getRouteKey()])
|
|
->assertActionVisible('taskstream')
|
|
->assertActionHasUrl('taskstream', 'https://taskstream.test/p/CATALOGO')
|
|
->assertFormFieldDoesNotExist('taskstream');
|
|
}
|
|
|
|
#[Test]
|
|
public function without_the_toggle_taskstream_is_left_alone(): void
|
|
{
|
|
$this->fakeTaskStream();
|
|
|
|
Livewire::test(CreateProject::class)
|
|
->fillForm(['title' => ['es' => 'Solo aquí'], 'slug' => ['es' => 'solo-aqui']])
|
|
->assertFormFieldHidden('taskstream_key')
|
|
->call('create')
|
|
->assertHasNoFormErrors();
|
|
|
|
Http::assertNothingSent();
|
|
|
|
Livewire::test(EditProject::class, ['record' => Project::query()->sole()->getRouteKey()])
|
|
->assertActionHidden('taskstream');
|
|
}
|
|
|
|
#[Test]
|
|
public function a_refusal_shows_on_the_key_and_saves_nothing(): void
|
|
{
|
|
$this->fakeTaskStream(409, ['statusCode' => 409, 'message' => 'Project key WEB is already in use']);
|
|
|
|
Livewire::test(CreateProject::class)
|
|
->fillForm([
|
|
'title' => ['es' => 'Web'],
|
|
'slug' => ['es' => 'web'],
|
|
'taskstream' => true,
|
|
'taskstream_key' => 'web',
|
|
])
|
|
->call('create')
|
|
->assertHasFormErrors(['taskstream_key']);
|
|
|
|
$this->assertSame(0, Project::withTrashed()->count());
|
|
}
|
|
|
|
#[Test]
|
|
public function taskstream_being_down_saves_nothing(): void
|
|
{
|
|
Http::fake(["{$this->taskstream}/*" => Http::failedConnection()]);
|
|
|
|
Livewire::test(CreateProject::class)
|
|
->fillForm(['title' => ['es' => 'Web'], 'slug' => ['es' => 'web'], 'taskstream' => true])
|
|
->call('create')
|
|
->assertNotified(__('rules.taskstream.unavailable'));
|
|
|
|
$this->assertSame(0, Project::withTrashed()->count());
|
|
}
|
|
|
|
#[Test]
|
|
public function a_project_without_a_board_can_get_one_later(): void
|
|
{
|
|
$this->fakeTaskStream();
|
|
$project = Project::factory()->create(['slug' => ['es' => 'catalogo']]);
|
|
|
|
Livewire::test(EditProject::class, ['record' => $project->getRouteKey()])
|
|
->assertActionHidden('taskstream')
|
|
->assertActionVisible('createInTaskStream')
|
|
->callAction('createInTaskStream', data: ['key' => 'cat'])
|
|
->assertHasNoActionErrors()
|
|
->assertNotified();
|
|
|
|
Http::assertSent(fn (Request $request) => $request->hasHeader('Authorization', 'Bearer session-token')
|
|
&& $request['key'] === 'CAT'
|
|
&& $request['isPublic'] === true);
|
|
|
|
$this->assertSame('CAT', $project->fresh()->taskstream_key);
|
|
|
|
Livewire::test(EditProject::class, ['record' => $project->getRouteKey()])
|
|
->assertActionVisible('taskstream')
|
|
->assertActionHidden('createInTaskStream');
|
|
}
|
|
|
|
#[Test]
|
|
public function a_refusal_later_keeps_the_project_without_a_board(): void
|
|
{
|
|
$this->fakeTaskStream(409, ['statusCode' => 409, 'message' => 'Project key WEB is already in use']);
|
|
$project = Project::factory()->create();
|
|
|
|
Livewire::test(EditProject::class, ['record' => $project->getRouteKey()])
|
|
->callAction('createInTaskStream', data: ['key' => 'WEB'])
|
|
->assertNotified(__('rules.taskstream.refused', ['reason' => 'Project key WEB is already in use']));
|
|
|
|
$this->assertNull($project->fresh()->taskstream_key);
|
|
}
|
|
}
|