PromptVault/tests/Feature/Prompts/PromptIndexTest.php

208 lines
8.3 KiB
PHP
Raw Normal View History

2026-10-26 20:12:16 +00:00
<?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 PromptIndexTest extends TestCase
{
use RefreshDatabase;
/**
* GET the index page and return its Inertia props.
*/
private function pageProps(string $url = '/prompts'): array
{
$response = $this->get($url);
$response->assertOk()->assertInertia(fn (Assert $page) => $page->component('Prompts/Index'));
return $response->inertiaProps();
}
private function names(array $props): array
{
return array_column($props['prompts']['data'], 'name');
}
public function test_index_renders_rows_tags_and_filter_state(): void
{
$alpha = Tag::query()->create(['name' => 'alpha']);
$zeta = Tag::query()->create(['name' => 'zeta']);
$alphaPrompt = Prompt::factory()->create(['name' => 'Alpha Prompt', 'body' => 'Body A {{x}}']);
$zetaPrompt = Prompt::factory()->create(['name' => 'Zeta Prompt', 'description' => null, 'body' => 'Body B']);
$alphaPrompt->tags()->attach([$alpha->id, $zeta->id]);
$zetaPrompt->tags()->attach([$zeta->id]);
$props = $this->pageProps();
// Newest first (created_at desc).
$this->assertSame(['Zeta Prompt', 'Alpha Prompt'], $this->names($props));
$this->assertSame(2, $props['prompts']['total']);
$this->assertSame(['alpha', 'zeta'], $props['tags']);
$this->assertSame(['search' => null, 'tag' => null, 'favorites' => false], $props['filters']);
$this->assertSame('created_at', $props['sort']);
$this->assertSame('desc', $props['direction']);
$row = $props['prompts']['data'][0];
$this->assertSame('Zeta Prompt', $row['name']);
$this->assertFalse($row['is_favorite']);
$this->assertSame(0, $row['usage_count']);
$this->assertNull($row['last_used_at']);
$this->assertSame(['zeta'], array_column($row['tags'], 'name'));
}
public function test_index_search_matches_name_description_and_body_case_insensitively(): void
{
Prompt::factory()->create(['name' => 'Regex Explainer', 'description' => null, 'body' => 'Explain {{regex}}']);
Prompt::factory()->create(['name' => 'Unrelated', 'description' => 'notes about regex engines', 'body' => 'x']);
Prompt::factory()->create(['name' => 'Python Helper', 'description' => null, 'body' => 'write python code']);
$props = $this->pageProps('/prompts?search=REGEX');
$this->assertSame(['Unrelated', 'Regex Explainer'], $this->names($props));
$this->assertSame('REGEX', $props['filters']['search']);
}
public function test_index_tag_filter_returns_only_matching_prompts(): void
{
$coding = Tag::query()->create(['name' => 'coding']);
$design = Tag::query()->create(['name' => 'design']);
$codeReviewer = Prompt::factory()->create(['name' => 'Code Reviewer']);
Prompt::factory()->create(['name' => 'No Tags']);
$moodboard = Prompt::factory()->create(['name' => 'Moodboard']);
$codeReviewer->tags()->attach($coding->id);
$moodboard->tags()->attach($design->id);
$props = $this->pageProps('/prompts?tag=coding');
$this->assertSame(['Code Reviewer'], $this->names($props));
$this->assertSame('coding', $props['filters']['tag']);
}
public function test_index_unknown_tag_returns_empty_result_set(): void
{
Prompt::factory()->count(2)->create();
$props = $this->pageProps('/prompts?tag=does-not-exist');
$this->assertSame([], $props['prompts']['data']);
$this->assertSame(0, $props['prompts']['total']);
}
public function test_index_favorites_filter_returns_only_favorites(): void
{
Prompt::factory()->favorite()->create(['name' => 'Starred']);
Prompt::factory()->create(['name' => 'Plain']);
$props = $this->pageProps('/prompts?favorites=1');
$this->assertSame(['Starred'], $this->names($props));
$this->assertTrue($props['filters']['favorites']);
}
public function test_index_sort_by_name_uses_asc_default_and_respects_direction(): void
{
Prompt::factory()->create(['name' => 'Charlie']);
Prompt::factory()->create(['name' => 'alpha']);
Prompt::factory()->create(['name' => 'Bravo']);
$props = $this->pageProps('/prompts?sort=name');
$this->assertSame(['Bravo', 'Charlie', 'alpha'], $this->names($props));
$this->assertSame('name', $props['sort']);
$this->assertSame('asc', $props['direction']);
$props = $this->pageProps('/prompts?sort=name&direction=desc');
$this->assertSame(['alpha', 'Charlie', 'Bravo'], $this->names($props));
$this->assertSame('desc', $props['direction']);
}
public function test_index_sort_by_usage_count(): void
{
Prompt::factory()->create(['name' => 'Rare', 'usage_count' => 1]);
Prompt::factory()->create(['name' => 'Common', 'usage_count' => 42]);
Prompt::factory()->create(['name' => 'Mid', 'usage_count' => 7]);
$props = $this->pageProps('/prompts?sort=usage_count');
$this->assertSame(['Common', 'Mid', 'Rare'], $this->names($props));
$props = $this->pageProps('/prompts?sort=usage_count&direction=asc');
$this->assertSame(['Rare', 'Mid', 'Common'], $this->names($props));
}
public function test_index_sort_by_last_used_at_keeps_nulls_last_in_both_directions(): void
{
Prompt::factory()->create(['name' => 'Older', 'last_used_at' => '2026-01-01 00:00:00']);
Prompt::factory()->create(['name' => 'Recent', 'last_used_at' => '2026-06-01 00:00:00']);
Prompt::factory()->create(['name' => 'Never', 'last_used_at' => null]);
$props = $this->pageProps('/prompts?sort=last_used_at&direction=asc');
$this->assertSame(['Older', 'Recent', 'Never'], $this->names($props));
$props = $this->pageProps('/prompts?sort=last_used_at&direction=desc');
$this->assertSame(['Recent', 'Older', 'Never'], $this->names($props));
}
public function test_index_sort_by_created_at_and_updated_at(): void
{
// created_at desc would list "Older" first; updated_at desc must not.
Prompt::factory()->create(['name' => 'Older', 'created_at' => '2026-09-01 00:00:00', 'updated_at' => '2026-01-01 00:00:00']);
Prompt::factory()->create(['name' => 'Newer', 'created_at' => '2026-08-01 00:00:00', 'updated_at' => '2026-05-01 00:00:00']);
$props = $this->pageProps('/prompts');
$this->assertSame(['Older', 'Newer'], $this->names($props));
$props = $this->pageProps('/prompts?sort=created_at&direction=asc');
$this->assertSame(['Newer', 'Older'], $this->names($props));
$props = $this->pageProps('/prompts?sort=updated_at');
$this->assertSame(['Newer', 'Older'], $this->names($props));
$props = $this->pageProps('/prompts?sort=updated_at&direction=asc');
$this->assertSame(['Older', 'Newer'], $this->names($props));
}
public function test_index_invalid_sort_and_direction_fall_back_to_defaults(): void
{
Prompt::factory()->count(2)->create();
$response = $this->get('/prompts?sort=1%3BDROP&direction=upside');
$response->assertOk();
$props = $response->inertiaProps();
$this->assertSame('created_at', $props['sort']);
$this->assertSame('desc', $props['direction']);
}
public function test_index_paginates_ten_per_page(): void
{
Prompt::factory()->count(12)->create();
$props = $this->pageProps();
$this->assertSame(12, $props['prompts']['total']);
$this->assertSame(2, $props['prompts']['last_page']);
$this->assertCount(10, $props['prompts']['data']);
$this->assertSame(1, $props['prompts']['current_page']);
$props = $this->pageProps('/prompts?page=2');
$this->assertCount(2, $props['prompts']['data']);
$this->assertSame(2, $props['prompts']['current_page']);
}
public function test_index_with_no_prompts_shows_empty_state(): void
{
$props = $this->pageProps();
$this->assertSame([], $props['prompts']['data']);
$this->assertSame(0, $props['prompts']['total']);
$this->assertSame([], $props['tags']);
}
}