create(['name' => 'alpha']); $beta = Tag::query()->create(['name' => 'beta']); $prompt = Prompt::factory()->create([ 'name' => 'Original', 'description' => 'Original description', 'body' => 'Template {{value}}', 'is_favorite' => true, 'usage_count' => 5, 'last_used_at' => '2026-01-01 00:00:00', ]); $prompt->tags()->attach([$alpha->id, $beta->id]); $response = $this->post("/prompts/{$prompt->id}/duplicate"); $copy = Prompt::query()->where('id', '!=', $prompt->id)->first(); $this->assertNotNull($copy); $response->assertRedirect(route('prompts.show', $copy)); $this->assertSame('Original (copy)', $copy->name); $this->assertSame('Original description', $copy->description); $this->assertSame('Template {{value}}', $copy->body); $this->assertFalse($copy->is_favorite); $this->assertSame(0, $copy->usage_count); $this->assertNull($copy->last_used_at); $this->assertSame(['alpha', 'beta'], $copy->tags->pluck('name')->all()); // The original is untouched. $original = $prompt->fresh(); $this->assertSame('Original', $original->name); $this->assertTrue($original->is_favorite); $this->assertSame(5, $original->usage_count); } public function test_duplicate_works_without_tags(): void { $prompt = Prompt::factory()->create(['name' => 'Bare']); $response = $this->post("/prompts/{$prompt->id}/duplicate"); $copy = Prompt::query()->where('name', 'Bare (copy)')->first(); $this->assertNotNull($copy); $response->assertRedirect(route('prompts.show', $copy)); $this->assertCount(0, $copy->tags); } public function test_duplicate_appends_copy_until_name_is_unique(): void { $original = Prompt::factory()->create(['name' => 'X']); Prompt::factory()->create(['name' => 'X (copy)']); $response = $this->post("/prompts/{$original->id}/duplicate"); $copy = Prompt::query()->where('name', 'X (copy) (copy)')->first(); $this->assertNotNull($copy); $response->assertRedirect(route('prompts.show', $copy)); } public function test_toggle_favorite_flips_state(): void { $prompt = Prompt::factory()->create(['is_favorite' => false]); $this->post("/prompts/{$prompt->id}/toggle-favorite")->assertRedirect(); $this->assertTrue($prompt->fresh()->is_favorite); $this->post("/prompts/{$prompt->id}/toggle-favorite")->assertRedirect(); $this->assertFalse($prompt->fresh()->is_favorite); } public function test_use_renders_prompt_and_records_usage(): void { $prompt = Prompt::factory()->body('Hi {{name}}, welcome to {{place}}.')->create(); $response = $this->postJson("/prompts/{$prompt->id}/use", [ 'values' => ['name' => 'Ada', 'place' => 'Lisbon'], ]); $response->assertOk()->assertJson(['rendered' => 'Hi Ada, welcome to Lisbon.']); $fresh = $prompt->fresh(); $this->assertSame(1, $fresh->usage_count); $this->assertNotNull($fresh->last_used_at); $this->postJson("/prompts/{$prompt->id}/use", [ 'values' => ['name' => 'Grace', 'place' => 'Lagos'], ])->assertOk()->assertJson(['rendered' => 'Hi Grace, welcome to Lagos.']); $this->assertSame(2, $prompt->fresh()->usage_count); } public function test_use_reports_missing_variables_without_recording_usage(): void { $prompt = Prompt::factory()->body('Hi {{name}} from {{place}}.')->create(); $response = $this->postJson("/prompts/{$prompt->id}/use", [ 'values' => ['name' => 'Ada'], ]); $response->assertStatus(422)->assertJson([ 'message' => 'Missing variables: place', 'missing' => ['place'], ]); $fresh = $prompt->fresh(); $this->assertSame(0, $fresh->usage_count); $this->assertNull($fresh->last_used_at); } public function test_use_reports_all_missing_variables_in_template_order(): void { $prompt = Prompt::factory()->body('{{zeta}} then {{alpha}}')->create(); $this->postJson("/prompts/{$prompt->id}/use", ['values' => ['ignored' => 'x']]) ->assertStatus(422) ->assertJson([ 'message' => 'Missing variables: zeta, alpha', 'missing' => ['zeta', 'alpha'], ]); } public function test_use_requires_string_values(): void { $prompt = Prompt::factory()->body('Hi {{name}}.')->create(); $this->postJson("/prompts/{$prompt->id}/use", ['values' => ['name' => 42]]) ->assertStatus(422); $this->postJson("/prompts/{$prompt->id}/use", []) ->assertStatus(422); $this->postJson("/prompts/{$prompt->id}/use", ['values' => 'nope']) ->assertStatus(422); $this->assertSame(0, $prompt->fresh()->usage_count); } public function test_use_404s_for_missing_prompt(): void { $this->postJson('/prompts/99999/use', ['values' => ['a' => 'b']])->assertNotFound(); } }