Open a TaskStream board for a project that has none yet
Some checks failed
Tests / lint (push) Has been cancelled
Tests / test (push) Has been cancelled
Tests / test-postgres (push) Has been cancelled
Tests / mcp (push) Has been cancelled

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>
This commit is contained in:
2026-10-12 20:21:38 +02:00
parent 7e7d2847fe
commit 3c119d425e
13 changed files with 207 additions and 28 deletions

View File

@@ -18,8 +18,8 @@ use Tests\FakesPireon;
use Tests\TestCase;
/**
* `taskstream: true` on a new project opens its board in TaskStream too, and
* the answer carries the link.
* `taskstream: true` on a new project, or on one without a board yet, opens
* its board in TaskStream too, and the answer carries the link.
*/
class TaskStreamProjectTest extends TestCase
{
@@ -211,23 +211,64 @@ class TaskStreamProjectTest extends TestCase
}
#[Test]
public function an_update_ignores_the_flag(): void
public function an_update_opens_a_board_for_a_project_without_one(): void
{
$this->fakeTaskStream();
$this->withHeaders($this->apiHeadersFor($this->editor()));
$project = Project::factory()->create(['slug' => ['es' => 'catalogo']]);
$this->patchJson("/api/v1/projects/{$project->id}", ['taskstream' => true, 'needs_help' => true])
->assertOk()
->assertJsonPath('data.taskstream_key', 'CATALOGO')
->assertJsonPath('data.taskstream_url', 'https://taskstream.test/p/CATALOGO')
->assertJsonPath('data.needs_help', true);
$this->assertSame('ts-1', $project->fresh()->taskstream_project_id);
}
#[Test]
public function a_project_with_a_board_keeps_it(): void
{
$this->fakeTaskStream();
$this->withHeaders($this->apiHeadersFor($this->editor()));
$project = Project::factory()->create(['taskstream_project_id' => 'ts-0', 'taskstream_key' => 'CAT']);
$this->patchJson("/api/v1/projects/{$project->id}", ['taskstream' => true, 'taskstream_key' => 'OTRA'])
->assertOk()
->assertJsonPath('data.taskstream_key', 'CAT');
$this->assertSame(0, $this->sentToTaskStream());
}
#[Test]
public function an_update_without_the_flag_ignores_the_key(): void
{
$this->fakeTaskStream();
$this->withHeaders($this->apiHeadersFor($this->editor()));
$project = Project::factory()->create();
$this->patchJson("/api/v1/projects/{$project->id}", [
'taskstream' => true,
'taskstream_key' => 'OTRA',
'needs_help' => true,
])
// What a client sends back after reading a project.
$this->patchJson("/api/v1/projects/{$project->id}", ['taskstream_key' => 'OTRA', 'needs_help' => true])
->assertOk()
->assertJsonPath('data.taskstream_key', null);
$this->assertSame(0, $this->sentToTaskStream());
}
#[Test]
public function a_refused_board_leaves_the_update_unsaved(): void
{
$this->fakeTaskStream(409, ['statusCode' => 409, 'message' => 'Project key WEB is already in use']);
$this->withHeaders($this->apiHeadersFor($this->editor()));
$project = Project::factory()->create(['needs_help' => false]);
$this->patchJson("/api/v1/projects/{$project->id}", ['taskstream' => true, 'taskstream_key' => 'WEB', 'needs_help' => true])
->assertUnprocessable()
->assertJsonValidationErrors('taskstream_key');
$this->assertFalse($project->fresh()->needs_help);
}
#[Test]
public function keys_are_made_from_the_slug_in_taskstreams_style(): void
{