*/ 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(); } }