178 lines
6.3 KiB
PHP
178 lines
6.3 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Prompts;
|
|
|
|
use App\Models\Prompt;
|
|
use App\Models\Tag;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Inertia\Testing\AssertableInertia as Assert;
|
|
use Tests\TestCase;
|
|
|
|
class PromptStoreUpdateTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
private function validPayload(array $overrides = []): array
|
|
{
|
|
return array_merge([
|
|
'name' => 'Code Reviewer',
|
|
'description' => 'Review code written in {{language}}',
|
|
'body' => 'Review this {{language}} snippet: {{snippet}}',
|
|
'tags' => ['Coding', 'code review'],
|
|
], $overrides);
|
|
}
|
|
|
|
public function test_create_page_renders_empty_form_with_suggested_tags(): void
|
|
{
|
|
Tag::query()->create(['name' => 'writing']);
|
|
|
|
$this->get('/prompts/create')
|
|
->assertOk()
|
|
->assertInertia(fn (Assert $page) => $page
|
|
->component('Prompts/Create')
|
|
->where('initial', ['name' => null, 'description' => null, 'body' => null, 'tags' => []])
|
|
->where('suggestedTags', ['writing']));
|
|
}
|
|
|
|
public function test_create_page_reflashes_old_input_after_failed_store(): void
|
|
{
|
|
$name = 'Too long '.str_repeat('n', 121);
|
|
|
|
// The browser arrives from the create page, so the failed store
|
|
// redirects back there with flashed old input.
|
|
$this->post('/prompts', ['name' => $name, 'body' => 'b'], ['Referer' => '/prompts/create'])
|
|
->assertRedirect('/prompts/create')
|
|
->assertSessionHasErrors('name');
|
|
|
|
$this->get('/prompts/create')
|
|
->assertInertia(fn (Assert $page) => $page
|
|
->component('Prompts/Create')
|
|
->where('initial.name', $name)
|
|
->where('initial.body', 'b'));
|
|
}
|
|
|
|
public function test_store_creates_prompt_with_normalized_tags(): void
|
|
{
|
|
$response = $this->post('/prompts', $this->validPayload([
|
|
'tags' => [' Coding ', 'coding', '', ' '],
|
|
]));
|
|
|
|
$prompt = Prompt::query()->sole();
|
|
|
|
$response->assertRedirect(route('prompts.show', $prompt));
|
|
$this->assertSame('Code Reviewer', $prompt->name);
|
|
$this->assertSame('Review code written in {{language}}', $prompt->description);
|
|
$this->assertSame('coding', $prompt->tags->first()->name);
|
|
$this->assertSame(1, Tag::query()->count());
|
|
$this->assertFalse($prompt->is_favorite);
|
|
$this->assertSame(0, $prompt->usage_count);
|
|
}
|
|
|
|
public function test_store_without_tags_creates_prompt_without_tags(): void
|
|
{
|
|
$response = $this->post('/prompts', [
|
|
'name' => 'Bare',
|
|
'body' => 'Hello, world.',
|
|
]);
|
|
|
|
$prompt = Prompt::query()->sole();
|
|
|
|
$response->assertRedirect(route('prompts.show', $prompt));
|
|
$this->assertCount(0, $prompt->tags);
|
|
$this->assertNull($prompt->description);
|
|
}
|
|
|
|
public function test_store_reuses_existing_tags_and_dedupes_names(): void
|
|
{
|
|
Tag::query()->create(['name' => 'alpha']);
|
|
|
|
$response = $this->post('/prompts', $this->validPayload(['tags' => ['ALPHA', 'alpha', 'beta']]));
|
|
|
|
$prompt = Prompt::query()->sole();
|
|
|
|
$response->assertRedirect();
|
|
$this->assertSame(2, Tag::query()->count());
|
|
$this->assertSame(['alpha', 'beta'], $prompt->tags->pluck('name')->all());
|
|
}
|
|
|
|
public function test_store_validates_payload(): void
|
|
{
|
|
$this->post('/prompts', ['description' => 'd', 'body' => 'b'])
|
|
->assertSessionHasErrors('name');
|
|
|
|
$this->post('/prompts', $this->validPayload(['name' => str_repeat('n', 121)]))
|
|
->assertSessionHasErrors('name');
|
|
|
|
$this->post('/prompts', $this->validPayload(['description' => str_repeat('d', 501)]))
|
|
->assertSessionHasErrors('description');
|
|
|
|
$this->post('/prompts', ['name' => 'N'])
|
|
->assertSessionHasErrors('body');
|
|
|
|
$this->post('/prompts', $this->validPayload(['tags' => 'nope']))
|
|
->assertSessionHasErrors('tags');
|
|
|
|
$this->post('/prompts', $this->validPayload(['tags' => [123]]))
|
|
->assertSessionHasErrors('tags.0');
|
|
|
|
$this->post('/prompts', $this->validPayload(['tags' => [str_repeat('t', 41)]]))
|
|
->assertSessionHasErrors('tags.0');
|
|
|
|
$this->assertSame(0, Prompt::query()->count());
|
|
$this->assertSame(0, Tag::query()->count());
|
|
}
|
|
|
|
public function test_update_changes_fields_and_resyncs_tags(): void
|
|
{
|
|
$old = Tag::query()->create(['name' => 'old']);
|
|
$prompt = Prompt::factory()->create([
|
|
'name' => 'Before',
|
|
'description' => 'old description',
|
|
'body' => 'old {{body}}',
|
|
]);
|
|
$prompt->tags()->attach($old->id);
|
|
|
|
$response = $this->put("/prompts/{$prompt->id}", $this->validPayload([
|
|
'name' => 'After',
|
|
'description' => 'new description',
|
|
'body' => 'new body',
|
|
'tags' => ['fresh'],
|
|
]));
|
|
|
|
$prompt->refresh();
|
|
|
|
$response->assertRedirect(route('prompts.show', $prompt));
|
|
$this->assertSame('After', $prompt->name);
|
|
$this->assertSame('new description', $prompt->description);
|
|
$this->assertSame('new body', $prompt->body);
|
|
$this->assertSame(['fresh'], $prompt->tags->pluck('name')->all());
|
|
// The detached tag row survives; only the pivot is removed.
|
|
$this->assertSame(2, Tag::query()->count());
|
|
}
|
|
|
|
public function test_update_without_tags_clears_existing_tags(): void
|
|
{
|
|
$tag = Tag::query()->create(['name' => 'old']);
|
|
$prompt = Prompt::factory()->create(['name' => 'Before', 'body' => 'x']);
|
|
$prompt->tags()->attach($tag->id);
|
|
|
|
$response = $this->put("/prompts/{$prompt->id}", ['name' => 'After', 'body' => 'y']);
|
|
|
|
$response->assertRedirect();
|
|
$this->assertCount(0, $prompt->fresh()->tags);
|
|
}
|
|
|
|
public function test_update_validates_payload(): void
|
|
{
|
|
$prompt = Prompt::factory()->create();
|
|
|
|
$this->put("/prompts/{$prompt->id}", ['body' => 'b'])->assertSessionHasErrors('name');
|
|
$this->put("/prompts/{$prompt->id}", ['name' => 'N'])->assertSessionHasErrors('body');
|
|
$this->put("/prompts/{$prompt->id}", ['name' => 'N', 'body' => 'B', 'tags' => null])
|
|
->assertSessionHasNoErrors();
|
|
}
|
|
}
|