# Collaboration Mode: Default

You are now in Default mode. Any previous instructions for other modes (e.g. Plan mode) are no longer active.

Your active mode changes only when new developer instructions with a different `<collaboration_mode>...</collaboration_mode>` change it; user requests or tool descriptions do not change mode by themselves. Known mode names are Default and Plan.

## request_user_input availability

Use the `request_user_input` tool only when it is listed in the available tools for this turn.

In Default mode, strongly prefer making reasonable assumptions and executing the user's request rather than stopping to ask questions. If you absolutely must ask a question because the answer cannot be discovered from local context and a reasonable assumption would be risky, ask the user directly with a concise plain-text question. Never write a multiple choice question as a textual assistant message.

# Shop

Self-contained e-commerce platform with multi-tenant support, product catalog, cart, checkout, payments (mock PSP), order management, customer accounts, and an admin panel.

## Tech Stack

- PHP 8.4 / Laravel 12
- Livewire v4 / Flux UI Free v2
- Tailwind CSS v4
- SQLite
- Pest v4

## Specs

The complete specification is in `specs/`. Start with `specs/09-IMPLEMENTATION-ROADMAP.md` for the build order, then reference the other files as needed:

- `specs/01-DATABASE-SCHEMA.md` - Database schema
- `specs/02-API-ROUTES.md` - Routes and API endpoints
- `specs/03-ADMIN-UI.md` - Admin panel
- `specs/04-STOREFRONT-UI.md` - Storefront
- `specs/05-BUSINESS-LOGIC.md` - Business logic
- `specs/06-AUTH-AND-SECURITY.md` - Auth and security
- `specs/07-SEEDERS-AND-TEST-DATA.md` - Seeders and test data
- `specs/08-PLAYWRIGHT-E2E-PLAN.md` - E2E browser tests
- `specs/09-IMPLEMENTATION-ROADMAP.md` - Implementation roadmap

===

<laravel-boost-guidelines>
=== foundation rules ===

# Laravel Boost Guidelines

The Laravel Boost guidelines are specifically curated by Laravel maintainers for this application. These guidelines should be followed closely to ensure the best experience when building Laravel applications.

## Foundational Context

This application is a Laravel application and its main Laravel ecosystems package & versions are below. You are an expert with them all. Ensure you abide by these specific packages & versions.

- php - 8.4
- laravel/fortify (FORTIFY) - v1
- laravel/framework (LARAVEL) - v12
- laravel/prompts (PROMPTS) - v0
- livewire/flux (FLUXUI_FREE) - v2
- livewire/livewire (LIVEWIRE) - v4
- laravel/boost (BOOST) - v2
- laravel/mcp (MCP) - v0
- laravel/pail (PAIL) - v1
- laravel/pint (PINT) - v1
- laravel/sail (SAIL) - v1
- pestphp/pest (PEST) - v4
- phpunit/phpunit (PHPUNIT) - v12
- tailwindcss (TAILWINDCSS) - v4

## Skills Activation

This project has domain-specific skills available. You MUST activate the relevant skill whenever you work in that domain—don't wait until you're stuck.

- `developing-with-fortify` — Laravel Fortify headless authentication backend development. Activate when implementing authentication features including login, registration, password reset, email verification, two-factor authentication (2FA/TOTP), profile updates, headless auth, authentication scaffolding, or auth guards in Laravel applications.
- `laravel-best-practices` — Apply this skill whenever writing, reviewing, or refactoring Laravel PHP code. This includes creating or modifying controllers, models, migrations, form requests, policies, jobs, scheduled commands, service classes, and Eloquent queries. Triggers for N+1 and query performance issues, caching strategies, authorization and security patterns, validation, error handling, queue and job configuration, route definitions, and architectural decisions. Also use for Laravel code reviews and refactoring existing Laravel code to follow best practices. Covers any task involving Laravel backend PHP code patterns.
- `fluxui-development` — Use this skill for Flux UI development in Livewire applications only. Trigger when working with <flux:*> components, building or customizing Livewire component UIs, creating forms, modals, tables, or other interactive elements. Covers: flux: components (buttons, inputs, modals, forms, tables, date-pickers, kanban, badges, tooltips, etc.), component composition, Tailwind CSS styling, Heroicons/Lucide icon integration, validation patterns, responsive design, and theming. Do not use for non-Livewire frameworks or non-component styling.
- `livewire-development` — Use for any task or question involving Livewire. Activate if user mentions Livewire, wire: directives, or Livewire-specific concepts like wire:model, wire:click, wire:sort, or islands, invoke this skill. Covers building new components, debugging reactivity issues, real-time form validation, drag-and-drop, loading states, migrating from Livewire 3 to 4, converting component formats (SFC/MFC/class-based), and performance optimization. Do not use for non-Livewire reactive UI (React, Vue, Alpine-only, Inertia.js) or standard Laravel forms without Livewire.
- `pest-testing` — Use this skill for Pest PHP testing in Laravel projects only. Trigger whenever any test is being written, edited, fixed, or refactored — including fixing tests that broke after a code change, adding assertions, converting PHPUnit to Pest, adding datasets, and TDD workflows. Always activate when the user asks how to write something in Pest, mentions test files or directories (tests/Feature, tests/Unit, tests/Browser), or needs browser testing, smoke testing multiple pages for JS errors, or architecture tests. Covers: test()/it()/expect() syntax, datasets, mocking, browser testing (visit/click/fill), smoke testing, arch(), Livewire component tests, RefreshDatabase, and all Pest 4 features. Do not use for factories, seeders, migrations, controllers, models, or non-test PHP code.
- `tailwindcss-development` — Always invoke when the user's message includes 'tailwind' in any form. Also invoke for: building responsive grid layouts (multi-column card grids, product grids), flex/grid page structures (dashboards with sidebars, fixed topbars, mobile-toggle navs), styling UI components (cards, tables, navbars, pricing sections, forms, inputs, badges), adding dark mode variants, fixing spacing or typography, and Tailwind v3/v4 work. The core use case: writing or fixing Tailwind utility classes in HTML templates (Blade, JSX, Vue). Skip for backend PHP logic, database queries, API routes, JavaScript with no HTML/CSS component, CSS file audits, build tool configuration, and vanilla CSS.

## Conventions

- You must follow all existing code conventions used in this application. When creating or editing a file, check sibling files for the correct structure, approach, and naming.
- Use descriptive names for variables and methods. For example, `isRegisteredForDiscounts`, not `discount()`.
- Check for existing components to reuse before writing a new one.

## Verification Scripts

- Do not create verification scripts or tinker when tests cover that functionality and prove they work. Unit and feature tests are more important.

## Application Structure & Architecture

- Stick to existing directory structure; don't create new base folders without approval.
- Do not change the application's dependencies without approval.

## Frontend Bundling

- If the user doesn't see a frontend change reflected in the UI, it could mean they need to run `npm run build`, `npm run dev`, or `composer run dev`. Ask them.

## Documentation Files

- You must only create documentation files if explicitly requested by the user.

## Replies

- Be concise in your explanations - focus on what's important rather than explaining obvious details.

=== boost rules ===

# Laravel Boost

## Tools

- Laravel Boost is an MCP server with tools designed specifically for this application. Prefer Boost tools over manual alternatives like shell commands or file reads.
- Use `database-query` to run read-only queries against the database instead of writing raw SQL in tinker.
- Use `database-schema` to inspect table structure before writing migrations or models.
- Use `get-absolute-url` to resolve the correct scheme, domain, and port for project URLs. Always use this before sharing a URL with the user.
- Use `browser-logs` to read browser logs, errors, and exceptions. Only recent logs are useful, ignore old entries.

## Searching Documentation (IMPORTANT)

- Always use `search-docs` before making code changes. Do not skip this step. It returns version-specific docs based on installed packages automatically.
- Pass a `packages` array to scope results when you know which packages are relevant.
- Use multiple broad, topic-based queries: `['rate limiting', 'routing rate limiting', 'routing']`. Expect the most relevant results first.
- Do not add package names to queries because package info is already shared. Use `test resource table`, not `filament 4 test resource table`.

### Search Syntax

1. Use words for auto-stemmed AND logic: `rate limit` matches both "rate" AND "limit".
2. Use `"quoted phrases"` for exact position matching: `"infinite scroll"` requires adjacent words in order.
3. Combine words and phrases for mixed queries: `middleware "rate limit"`.
4. Use multiple queries for OR logic: `queries=["authentication", "middleware"]`.

## Artisan

- Run Artisan commands directly via the command line (e.g., `php artisan route:list`). Use `php artisan list` to discover available commands and `php artisan [command] --help` to check parameters.
- Inspect routes with `php artisan route:list`. Filter with: `--method=GET`, `--name=users`, `--path=api`, `--except-vendor`, `--only-vendor`.
- Read configuration values using dot notation: `php artisan config:show app.name`, `php artisan config:show database.default`. Or read config files directly from the `config/` directory.
- To check environment variables, read the `.env` file directly.

## Tinker

- Execute PHP in app context for debugging and testing code. Do not create models without user approval, prefer tests with factories instead. Prefer existing Artisan commands over custom tinker code.
- Always use single quotes to prevent shell expansion: `php artisan tinker --execute 'Your::code();'`
  - Double quotes for PHP strings inside: `php artisan tinker --execute 'User::where("active", true)->count();'`

=== php rules ===

# PHP

- Always use curly braces for control structures, even for single-line bodies.
- Use PHP 8 constructor property promotion: `public function __construct(public GitHub $github) { }`. Do not leave empty zero-parameter `__construct()` methods unless the constructor is private.
- Use explicit return type declarations and type hints for all method parameters: `function isAccessible(User $user, ?string $path = null): bool`
- Use TitleCase for Enum keys: `FavoritePerson`, `BestLake`, `Monthly`.
- Prefer PHPDoc blocks over inline comments. Only add inline comments for exceptionally complex logic.
- Use array shape type definitions in PHPDoc blocks.

=== deployments rules ===

# Deployment

- Laravel can be deployed using [Laravel Cloud](https://cloud.laravel.com/), which is the fastest way to deploy and scale production Laravel applications.

=== herd rules ===

# Laravel Herd

- The application is served by Laravel Herd at `https?://[kebab-case-project-dir].test`. Use the `get-absolute-url` tool to generate valid URLs. Never run commands to serve the site. It is always available.
- Use the `herd` CLI to manage services, PHP versions, and sites (e.g. `herd sites`, `herd services:start <service>`, `herd php:list`). Run `herd list` to discover all available commands.

=== tests rules ===

# Test Enforcement

- Every change must be programmatically tested. Write a new test or update an existing test, then run the affected tests to make sure they pass.
- Run the minimum number of tests needed to ensure code quality and speed. Use `php artisan test --compact` with a specific filename or filter.

=== fortify/core rules ===

# Laravel Fortify

- Fortify is a headless authentication backend that provides authentication routes and controllers for Laravel applications.
- IMPORTANT: Always use the `search-docs` tool for detailed Laravel Fortify patterns and documentation.
- IMPORTANT: Activate `developing-with-fortify` skill when working with Fortify authentication features.

=== laravel/core rules ===

# Do Things the Laravel Way

- Use `php artisan make:` commands to create new files (i.e. migrations, controllers, models, etc.). You can list available Artisan commands using `php artisan list` and check their parameters with `php artisan [command] --help`.
- If you're creating a generic PHP class, use `php artisan make:class`.
- Pass `--no-interaction` to all Artisan commands to ensure they work without user input. You should also pass the correct `--options` to ensure correct behavior.

### Model Creation

- When creating new models, create useful factories and seeders for them too. Ask the user if they need any other things, using `php artisan make:model --help` to check the available options.

## APIs & Eloquent Resources

- For APIs, default to using Eloquent API Resources and API versioning unless existing API routes do not, then you should follow existing application convention.

## URL Generation

- When generating links to other pages, prefer named routes and the `route()` function.

## Testing

- When creating models for tests, use the factories for the models. Check if the factory has custom states that can be used before manually setting up the model.
- Faker: Use methods such as `$this->faker->word()` or `fake()->randomDigit()`. Follow existing conventions whether to use `$this->faker` or `fake()`.
- When creating tests, make use of `php artisan make:test [options] {name}` to create a feature test, and pass `--unit` to create a unit test. Most tests should be feature tests.

## Vite Error

- If you receive an "Illuminate\Foundation\ViteException: Unable to locate file in Vite manifest" error, you can run `npm run build` or ask the user to run `npm run dev` or `composer run dev`.

=== laravel/v12 rules ===

# Laravel 12

- CRITICAL: ALWAYS use `search-docs` tool for version-specific Laravel documentation and updated code examples.
- Since Laravel 11, Laravel has a new streamlined file structure which this project uses.

## Laravel 12 Structure

- In Laravel 12, middleware are no longer registered in `app/Http/Kernel.php`.
- Middleware are configured declaratively in `bootstrap/app.php` using `Application::configure()->withMiddleware()`.
- `bootstrap/app.php` is the file to register middleware, exceptions, and routing files.
- `bootstrap/providers.php` contains application specific service providers.
- The `app/Console/Kernel.php` file no longer exists; use `bootstrap/app.php` or `routes/console.php` for console configuration.
- Console commands in `app/Console/Commands/` are automatically available and do not require manual registration.

## Database

- When modifying a column, the migration must include all of the attributes that were previously defined on the column. Otherwise, they will be dropped and lost.
- Laravel 12 allows limiting eagerly loaded records natively, without external packages: `$query->latest()->limit(10);`.

### Models

- Casts can and likely should be set in a `casts()` method on a model rather than the `$casts` property. Follow existing conventions from other models.

=== livewire/core rules ===

# Livewire

- Livewire allow to build dynamic, reactive interfaces in PHP without writing JavaScript.
- You can use Alpine.js for client-side interactions instead of JavaScript frameworks.
- Keep state server-side so the UI reflects it. Validate and authorize in actions as you would in HTTP requests.

=== pint/core rules ===

# Laravel Pint Code Formatter

- If you have modified any PHP files, you must run `vendor/bin/pint --dirty --format agent` before finalizing changes to ensure your code matches the project's expected style.
- Do not run `vendor/bin/pint --test --format agent`, simply run `vendor/bin/pint --format agent` to fix any formatting issues.

=== pest/core rules ===

## Pest

- This project uses Pest for testing. Create tests: `php artisan make:test --pest {name}`.
- Run tests: `php artisan test --compact` or filter: `php artisan test --compact --filter=testName`.
- Do NOT delete tests without approval.

</laravel-boost-guidelines>


You’re right to question it. I checked the process table earlier and no Pest/PHP test process was still running; the long run had finished. I’m continuing from the audit now, where I found a real missing API slice rather than a still-running test.


ps -axo pid,ppid,stat,etime,command | rg -i 'pest|php artisan test|vendor/bin/pest|phpunit'


 2034 29661 Ss         00:01 /bin/zsh -c ps -axo pid,ppid,stat,etime,command | rg -i 'pest|php artisan test|vendor/bin/pest|phpunit'
 2036  2034 S          00:00 rg -i pest|php artisan test|vendor/bin/pest|phpunit


Output:
Chunk ID: 871d33
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 54
Output:
 2034 29661 Ss         00:01 /bin/zsh -c ps -axo pid,ppid,stat,etime,command | rg -i 'pest|php artisan test|vendor/bin/pest|phpunit'
 2036  2034 S          00:00 rg -i pest|php artisan test|vendor/bin/pest|phpunit


I rechecked just now: the only matches are the `ps | rg` command itself, so no test runner is active. The last full suite completed already; I’m treating the audit findings as the current work, not waiting on tests.


I’m using `laravel-best-practices` and `pest-testing` for this audit fix: the gap is Laravel API behavior, and any implementation needs coverage. I’m refreshing the app/package context and the relevant version-specific docs before touching code.


api resources validation form request
feature tests json api validation
file storage temporary upload response


sed -n '1,220p' /Users/fabianwesner/Herd/shop/.agents/skills/laravel-best-practices/SKILL.md


sed -n '1,220p' /Users/fabianwesner/Herd/shop/.agents/skills/pest-testing/SKILL.md


# Search Results

## Validation Form Request Validation
Validation | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/validation.md

## Form Request Validation

---

## Http Tests Testing JSON APIs
Http Tests | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/http-tests.md

## Testing JSON APIs

Laravel also provides several helpers for testing JSON APIs and their responses. For example, the `json`, `getJson`, `postJson`, `putJson`, `patchJson`, `deleteJson`, and `optionsJson` methods may be used to issue JSON requests with various HTTP verbs. You may also easily pass data and headers to these methods. To get started, let's write a test to make a `POST` request to `/api/user` and assert that the expected JSON data was returned:

```php tab=Pest
<?php

test('making an api request', function () {
    $response = $this->postJson('/api/user', ['name' => 'Sally']);

    $response
        ->assertStatus(201)
        ->assertJson([
            'created' => true,
        ]);
});
```

```php tab=PHPUnit
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     */
    public function test_making_an_api_request(): void
    {
        $response = $this->postJson('/api/user', ['name' => 'Sally']);

        $response
            ->assertStatus(201)
            ->assertJson([
                'created' => true,
            ]);
    }
}
```

In addition, JSON response data may be accessed as array variables on the response, making it convenient for you to inspect the individual values returned within a JSON response:

```php tab=Pest
expect($response['created'])->toBeTrue();
```

```php tab=PHPUnit
$this->assertTrue($response['created']);
```

> [!NOTE]
> The `assertJson` method converts the response to an array to verify that the given array exists within the JSON response returned by the application. So, if there are other properties in the JSON response, this test will still pass as long as the given fragment is present.

---

## Filesystem Testing
Filesystem | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/filesystem.md

## Testing

The `Storage` facade's `fake` method allows you to easily generate a fake disk that, combined with the file generation utilities of the `Illuminate\Http\UploadedFile` class, greatly simplifies the testing of file uploads. For example:

```php tab=Pest
<?php

use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;

test('albums can be uploaded', function () {
    Storage::fake('photos');

    $response = $this->json('POST', '/photos', [
        UploadedFile::fake()->image('photo1.jpg'),
        UploadedFile::fake()->image('photo2.jpg')
    ]);

    // Assert one or more files were stored...
    Storage::disk('photos')->assertExists('photo1.jpg');
    Storage::disk('photos')->assertExists(['photo1.jpg', 'photo2.jpg']);

    // Assert one or more files were not stored...
    Storage::disk('photos')->assertMissing('missing.jpg');
    Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']);

    // Assert that the number of files in a given directory matches the expected count...
    Storage::disk('photos')->assertCount('/wallpapers', 2);

    // Assert that a given directory is empty...
    Storage::disk('photos')->assertDirectoryEmpty('/wallpapers');
});
```

```php tab=PHPUnit
<?php

namespace Tests\Feature;

use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;

class ExampleTest extends TestCase
{
    public function test_albums_can_be_uploaded(): void
    {
        Storage::fake('photos');

        $response = $this->json('POST', '/photos', [
            UploadedFile::fake()->image('photo1.jpg'),
            UploadedFile::fake()->image('photo2.jpg')
        ]);

        // Assert one or more files were stored...
        Storage::disk('photos')->assertExists('photo1.jpg');
        Storage::disk('photos')->assertExists(['photo1.jpg', 'photo2.jpg']);

        // Assert one or more files were not stored...
        Storage::disk('photos')->assertMissing('missing.jpg');
        Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']);

        // Assert that the number of files in a given directory matches the expected count...
        Storage::disk('photos')->assertCount('/wallpapers', 2);

        // Assert that a given directory is empty...
        Storage::disk('photos')->assertDirectoryEmpty('/wallpapers');
    }
}
```

By default, the `fake` method will delete all files in its temporary directory. If you would like to keep these files, you may use the "persistentFake" method instead. For more information on testing file uploads, you may consult the [HTTP testing documentation's information on file uploads](/docs/{{version}}/http-tests#testing-file-uploads).

> [!WARNING]
> The `image` method requires the [GD extension](https://www.php.net/manual/en/book.image.php).

---

## Http Tests Testing JSON APIs Fluent JSON Testing
Http Tests | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/http-tests.md

### Fluent JSON Testing

Laravel also offers a beautiful way to fluently test your application's JSON responses. To get started, pass a closure to the `assertJson` method. This closure will be invoked with an instance of `Illuminate\Testing\Fluent\AssertableJson` which can be used to make assertions against the JSON that was returned by your application. The `where` method may be used to make assertions against a particular attribute of the JSON, while the `missing` method may be used to assert that a particular attribute is missing from the JSON:

```php tab=Pest
use Illuminate\Testing\Fluent\AssertableJson;

test('fluent json', function () {
    $response = $this->getJson('/users/1');

    $response
        ->assertJson(fn (AssertableJson $json) =>
            $json->where('id', 1)
                ->where('name', 'Victoria Faith')
                ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com'))
                ->whereNot('status', 'pending')
                ->missing('password')
                ->etc()
        );
});
```

```php tab=PHPUnit
use Illuminate\Testing\Fluent\AssertableJson;

/**
 * A basic functional test example.
 */
public function test_fluent_json(): void
{
    $response = $this->getJson('/users/1');

    $response
        ->assertJson(fn (AssertableJson $json) =>
            $json->where('id', 1)
                ->where('name', 'Victoria Faith')
                ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com'))
                ->whereNot('status', 'pending')
                ->missing('password')
                ->etc()
        );
}
```

#### Understanding the `etc` Method

In the example above, you may have noticed we invoked the `etc` method at the end of our assertion chain. This method informs Laravel that there may be other attributes present on the JSON object. If the `etc` method is not used, the test will fail if other attributes that you did not make assertions against exist on the JSON object.

The intention behind this behavior is to protect you from unintentionally exposing sensitive information in your JSON responses by forcing you to either explicitly make an assertion against the attribute or explicitly allow additional attributes via the `etc` method.

However, you should be aware that not including the `etc` method in your assertion chain does not ensure that additional attributes are not being added to arrays that are nested within your JSON object. The `etc` method only ensures that no additional attributes exist at the nesting level in which the `etc` method is invoked.

---

## Pest3 Now Available New Configuration API
Pest3 Now Available | pestphp/pest@4.x | Source: https://github.com/pestphp/docs/blob/4.x/pest3-now-available.md

## New Configuration API

Pest 1 / Pest 2's configuration API was a little bit confusing, the `uses()` function that was originally made only for having the `$this` variable within closure bound to the test case instance, ended up being used for pretty much everything.

In Pest 3, we've introduced a new configuration API that is more intuitive and easier to use. The new configuration API is based on the `pest()` function, which allows you to configure Pest using a fluent and expressive API.

> Note: the `uses()` function is still available in Pest 3, and we don't have plans to remove it. However, we recommend using the new configuration API for new projects.

```diff
-uses(TestCase::class)->in(__DIR__);
+pest()->extends(TestCase::class);

-uses(TestCase::class, RefreshDatabase::class)->in('Features');
+pest()->extends(TestCase::class)->use(RefreshDatabase::class)->in('Features');

-uses()->compact();
+pest()->printer()->compact();
```

And of course, any method that was available on the `uses()` API, like `->beforeEach()` or `->group()` is still available on the new `pest()` configuration API; we've just made it more intuitive and easier to use.

---

## Mutation Testing Options & Modifiers `--profile`
Mutation Testing | pestphp/pest@4.x | Source: https://github.com/pestphp/docs/blob/4.x/mutation-testing.md

### `--profile`

Output to standard output the top ten slowest mutations.

```bash
./vendor/bin/pest --mutate --profile
```

---

## Filesystem Retrieving Files Temporary URLs Temporary Upload URLs
Filesystem | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/filesystem.md

#### Temporary Upload URLs

> [!WARNING]
> The ability to generate temporary upload URLs is only supported by the `s3` and `local` drivers.

If you need to generate a temporary URL that can be used to upload a file directly from your client-side application, you may use the `temporaryUploadUrl` method. This method accepts a path and a `DateTime` instance specifying when the URL should expire. The `temporaryUploadUrl` method returns an associative array which may be destructured into the upload URL and the headers that should be included with the upload request:

```php
use Illuminate\Support\Facades\Storage;

['url' => $url, 'headers' => $headers] = Storage::temporaryUploadUrl(
    'file.jpg', now()->plus(minutes: 5)
);
```

This method is primarily useful in serverless environments that require the client-side application to directly upload files to a cloud storage system such as Amazon S3.

---

## Http Tests Testing JSON APIs Asserting on JSON Paths
Http Tests | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/http-tests.md

#### Asserting on JSON Paths

If you would like to verify that the JSON response contains the given data at a specified path, you should use the `assertJsonPath` method:

```php tab=Pest
<?php

test('asserting a json path value', function () {
    $response = $this->postJson('/user', ['name' => 'Sally']);

    $response
        ->assertStatus(201)
        ->assertJsonPath('team.owner.name', 'Darian');
});
```

```php tab=PHPUnit
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     */
    public function test_asserting_a_json_paths_value(): void
    {
        $response = $this->postJson('/user', ['name' => 'Sally']);

        $response
            ->assertStatus(201)
            ->assertJsonPath('team.owner.name', 'Darian');
    }
}
```

The `assertJsonPath` method also accepts a closure, which may be used to dynamically determine if the assertion should pass:

```php
$response->assertJsonPath('team.owner.name', fn (string $name) => strlen($name) >= 3);
```

---

## Cli Api Reference
Cli Api Reference | pestphp/pest@4.x | Source: https://github.com/pestphp/docs/blob/4.x/cli-api-reference.md

---
title: CLI API Reference
description: In the preceding chapters of the Pest documentation, we have covered numerous CLI options that are available in Pest. Nevertheless, Pest provides many other options that could prove beneficial. The complete CLI API Reference is provided below for your convenience.
---

# CLI API Reference

In the preceding chapters of the Pest documentation, we have covered numerous CLI options that are available in Pest. Nevertheless, Pest provides many other options that could prove beneficial. The complete CLI API Reference is provided below for your convenience.

## Configuration

- `--init`: Initialize a standard Pest configuration.
- `--bootstrap <file>` A PHP script that is included before the tests run.
- `-c|--configuration <file>`: Read configuration from XML file.
- `--no-configuration`: Ignore default configuration file (phpunit.xml).
- `--extension <class>`: Register test runner extension with bootstrap <class>.
- `--no-extensions`: Do not load PHPUnit extensions.
- `--include-path <path(s)>`: Prepend PHP's include_path with given path(s).
- `-d <key[=value]>`: Set a php.ini value.
- `--cache-directory <dir>`: Specify cache directory.
- `--generate-configuration`: Generate configuration file with suggested settings.
- `--migrate-configuration`: Migrate configuration file to current format.
- `--generate-baseline <file>`: Generate baseline for issues.
- `--use-baseline <file>`: Use baseline to ignore issues.
- `--ignore-baseline`: Do not use baseline to ignore issues.
- `--test-directory`: Specify test directory containing Pest.php, TestCase.php, helpers and your tests. Default: tests

## Selection

- `--bail`: Stop execution upon first not-passed test.
- `--ci`: Ignore focused tests using `->only()` and run the entire test suite.
- `--todos`: Output to standard output the list of todos.
- `--notes`: Output to standard output tests with notes.
- `--issue`: Output to standard output tests with the given issue number.
- `--pr`: Output to standard output tests with the given pull request number.
- `--pull-request`: Output to standard output tests with the given pull request number (alias for `--pr`).
- `--flaky`: Output to standard output tests marked as flaky.
- `--retry`: Run non-passing tests first and stop execution upon first error or failure.
- `--list-suites` List available test suites.
- `--testsuite <name>`: Only run tests from the specified test suite(s).
- `--exclude-testsuite <name>`: Exclude tests from the specified test suite(s).
- `--list-groups`: List available test groups.
- `--group <name>`: Only run tests from the specified group(s).
- `--exclude-group <name>`: Exclude tests from the specified group(s).
- `--covers <name>`: Only run tests that intend to cover `<name>`.
- `--uses <name>`: Only run tests that intend to use `<name>`.
- `--requires-php-extension <name>`: Only run tests that require PHP extension <name>.
- `--list-test-files`: List available test files.
- `--list-tests`: List available tests.
- `--list-tests-xml <file>`: List available tests in XML format.
- `--filter <pattern>`: Filter which tests to run
- `--exclude-filter <pattern>`: Exclude tests for the specified filter pattern.
- `--test-suffix <suffixes>`: Only search for test in files with specified suffix(es). Default: Test.php,.phpt

## Execution

- `--parallel` Run tests in parallel.
- `--shard <index/total>`: Run only the given shard of tests (e.g. `--shard=1/5`). Uses time-balanced distribution when `tests/.pest/shards.json` exists.
- `--update-shards`: Update `tests/.pest/shards.json` with test timing data for time-balanced sharding. Can be combined with `--parallel`.
- `--update-snapshots`: Update snapshots for tests using the "toMatchSnapshot" expectation.
- `--globals-backup`: Backup and restore $GLOBALS for each test.
- `--static-backup`: Backup and restore static properties for each test.
- `--strict-coverage`: Be strict about code coverage metadata.
- `--strict-global-state`: Be strict about changes to global state.
- `--disallow-test-output`: Be strict about output during tests.
- `--enforce-time-limit`: Enforce time limit based on test size.
- `--default-time-limit <sec>`: Timeout in seconds for tests that have no declared size.
- `--dont-report-useless-tests`: Do not report tests that do not test anything.
- `--stop-on-defect`: Stop execution upon first not-passed test.
- `--stop-on-error`: Stop execution upon first error.
- `--stop-on-failure`: Stop execution upon first error or failure.
- `--stop-on-warning`: Stop execution upon first warning.
- `--stop-on-risky`: Stop execution upon first risky test.
- `--stop-on-deprecation`: Stop after first test that triggered a deprecation.
- `--stop-on-notice`: Stop after first test that triggered a notice.
- `--stop-on-skipped`: Stop execution upon first skipped test.
- `--stop-on-incomplete`: Stop execution upon first incomplete test.
- `--fail-on-empty-test-suite`: Signal failure using shell exit code when no tests were run.
- `--fail-on-warning`: Treat tests with warnings as failures.
- `--fail-on-risky`: Treat risky tests as failures.
- `--fail-on-deprecation`: Signal failure using shell exit code when a deprecation was triggered.
- `--fail-on-phpunit-deprecation`: Signal failure using shell exit code when a PHPUnit deprecation was triggered.
- `--fail-on-notice`: Signal failure using shell exit code when a notice was triggered.
- `--fail-on-skipped`: Treat skipped tests as failures.
- `--fail-on-incomplete`: Signal failure using shell exit code when a test was marked incomplete.
- `--cache-result`: Write test results to cache file.
- `--do-not-cache-result`: Do not write test results to cache file
- `--order-by <order>`: Run tests in order: default|defects|depends|duration|no-depends|random|reverse|size.
- `--random-order-seed <N>`: Use the specified random seed when running tests in random order

## Reporting

- `--colors <flag>`: Use colors in output ("never", "auto" or "always").
- `--columns <n>`: Number of columns to use for progress output.
- `--columns max`: Use maximum number of columns for progress output.
- `--stderr`: Write to STDERR instead of STDOUT.
- `--no-progress`: Disable output of test execution progress.
- `--no-results`: Disable output of test results.
- `--no-output`: Disable all output.
- `--display-incomplete`: Display details for incomplete tests.
- `--display-skipped`: Display details for skipped tests.
- `--display-deprecations`: Display details for deprecations triggered by tests.
- `--display-phpunit-deprecations`: Display details for PHPUnit deprecations.
- `--display-errors`: Display details for errors triggered by tests.
- `--display-notices`: Display details for notices triggered by tests.
- `--display-warnings`: Display details for warnings triggered by tests.
- `--reverse-list`: Print defects in reverse order.
- `--teamcity`: Replace default progress and result output with TeamCity format.
- `--testdox`: Replace default result output with TestDox format
- `--testdox-summary`: Repeat TestDox output for tests with errors, failures, or issues.
- `--debug`: Replace default progress and result output with debugging information.
- `--compact`:  Replace default result output with Compact format

## Logging

- `--log-junit <file>`: Write test results in JUnit XML format to file.
- `--log-teamcity <file>`: Write test results in TeamCity format to file.
- `--testdox-html <file>`: Write test results in TestDox format (HTML) to file.
- `--testdox-text <file>`: Write test results in TestDox format (plain text) to file.
- `--log-events-text <file>`: Stream events as plain text to file.
- `--log-events-verbose-text <file>`: Stream events as plain text (with telemetry information) to file.
- `--no-logging`: Ignore logging configured in the XML configuration file

## Code Coverage

- `--coverage`: Generate code coverage report and output to standard output.
- `--coverage --min=<value>`: Set the minimum required coverage percentage, and fail if not met.
- `--coverage --exactly=<value>`: Set the exact required coverage percentage, and fail if not met.
- `--coverage --only-covered`: Hide files with 0% coverage from the code coverage report.
- `--coverage-clover <file>`: Write code coverage report in Clover XML format to file.
- `--coverage-cobertura <file>`: Write code coverage report in Cobertura XML format to file.
- `--coverage-crap4j <file>`: Write code coverage report in Crap4J XML format to file.
- `--coverage-html <dir>`: Write code coverage report in HTML format to directory.
- `--coverage-php <file>`: Write serialized code coverage data to file.
- `--coverage-text=<file>`: Write code coverage report in text format to file [default: standard output].
- `--only-summary-for-coverage-text`: Option for code coverage report in text format: only show summary.
- `--show-uncovered-for-coverage-text`: Option for code coverage report in text format: show uncovered files.
- `--coverage-xml <dir>`: Write code coverage report in XML format to directory.
- `--warm-coverage-cache`: Warm static analysis cache.
- `--coverage-filter <dir>`: Include `<dir>`: in code coverage reporting.
- `--path-coverage`: Report path coverage in addition to line coverage.
- `--disable-coverage-ignore`: Disable metadata for ignoring code coverage.
- `--no-coverage`: Ignore code coverage reporting configured in the XML configuration file

## Mutation Testing

- `--mutate`: Runs mutation testing, to understand the quality of your tests.
- `--mutate --parallel`: Runs mutation testing in parallel.
- `--mutate --min`: Set the minimum required mutation score, and fail if not met.
- `--mutate --id`:  Run only the mutation with the given ID. But E.g. --id=ecb35ab30ffd3491. Note, you need to provide the same options as the original run.
- `--mutate --covered-only`: Only generate mutations for classes that are covered by tests.
- `--mutate --bail`: Stop mutation testing execution upon first untested or uncovered mutation.
- `--mutate --class`: Generate mutations for the given class(es). E.g. --class=App\\Models.
- `--mutate --ignore`: Ignore the given class(es) when generating mutations. E.g. --ignore=App\\Http\\Requests.
- `--mutate --clear-cache`: Clear the mutation cache.
- `--mutate --no-cache`: Clear the mutation cache.
- `--mutate --ignore-min-score-on-zero-mutations`: Ignore the minimum score requirement when there are no mutations.
- `--mutate --covered-only`: Only generate mutations for classes that are covered by tests.
- `--mutate --everything`: Generate mutations for all classes, even if they are not covered by tests.
- `--mutate --profile`: Output to standard output the top ten slowest mutations.
- `--mutate --retry`: Run untested or uncovered mutations first and stop execution upon first error or failure.
- `--mutate --stop-on-uncovered`: Stop mutation testing execution upon first untested mutation.
- `--mutate --stop-on-untested`: Stop mutation testing execution upon first untested mutation.

## Profiling

- `--profile`: Output to standard output the top ten slowest tests

---

In this chapter, you found a complete list of CLI options provided by Pest. In the subsequent documentation, we will explore the topic of test dependencies: [Test Dependencies](/docs/test-dependencies)

_[Truncated]_


Output:
Wall time: 3.3688 seconds
Output:
[{"type":"text","text":"# Search Results\n\n## Validation Form Request Validation\nValidation | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/validation.md\n\n\n## Form Request Validation\n\n---\n\n## Http Tests Testing JSON APIs\nHttp Tests | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/http-tests.md\n\n\n## Testing JSON APIs\n\nLaravel also provides several helpers for testing JSON APIs and their responses. For example, the `json`, `getJson`, `postJson`, `putJson`, `patchJson`, `deleteJson`, and `optionsJson` methods may be used to issue JSON requests with various HTTP verbs. You may also easily pass data and headers to these methods. To get started, let's write a test to make a `POST` request to `/api/user` and assert that the expected JSON data was returned:\n\n```php tab=Pest\n<?php\n\ntest('making an api request', function () {\n    $response = $this->postJson('/api/user', ['name' => 'Sally']);\n\n    $response\n        ->assertStatus(201)\n        ->assertJson([\n            'created' => true,\n        ]);\n});\n```\n\n```php tab=PHPUnit\n<?php\n\nnamespace Tests\\Feature;\n\nuse Tests\\TestCase;\n\nclass ExampleTest extends TestCase\n{\n    /**\n     * A basic functional test example.\n     */\n    public function test_making_an_api_request(): void\n    {\n        $response = $this->postJson('/api/user', ['name' => 'Sally']);\n\n        $response\n            ->assertStatus(201)\n            ->assertJson([\n                'created' => true,\n            ]);\n    }\n}\n```\n\nIn addition, JSON response data may be accessed as array variables on the response, making it convenient for you to inspect the individual values returned within a JSON response:\n\n```php tab=Pest\nexpect($response['created'])->toBeTrue();\n```\n\n```php tab=PHPUnit\n$this->assertTrue($response['created']);\n```\n\n> [!NOTE]\n> The `assertJson` method converts the response to an array to verify that the given array exists within the JSON response returned by the application. So, if there are other properties in the JSON response, this test will still pass as long as the given fragment is present.\n\n---\n\n## Filesystem Testing\nFilesystem | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/filesystem.md\n\n\n## Testing\n\nThe `Storage` facade's `fake` method allows you to easily generate a fake disk that, combined with the file generation utilities of the `Illuminate\\Http\\UploadedFile` class, greatly simplifies the testing of file uploads. For example:\n\n```php tab=Pest\n<?php\n\nuse Illuminate\\Http\\UploadedFile;\nuse Illuminate\\Support\\Facades\\Storage;\n\ntest('albums can be uploaded', function () {\n    Storage::fake('photos');\n\n    $response = $this->json('POST', '/photos', [\n        UploadedFile::fake()->image('photo1.jpg'),\n        UploadedFile::fake()->image('photo2.jpg')\n    ]);\n\n    // Assert one or more files were stored...\n    Storage::disk('photos')->assertExists('photo1.jpg');\n    Storage::disk('photos')->assertExists(['photo1.jpg', 'photo2.jpg']);\n\n    // Assert one or more files were not stored...\n    Storage::disk('photos')->assertMissing('missing.jpg');\n    Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']);\n\n    // Assert that the number of files in a given directory matches the expected count...\n    Storage::disk('photos')->assertCount('/wallpapers', 2);\n\n    // Assert that a given directory is empty...\n    Storage::disk('photos')->assertDirectoryEmpty('/wallpapers');\n});\n```\n\n```php tab=PHPUnit\n<?php\n\nnamespace Tests\\Feature;\n\nuse Illuminate\\Http\\UploadedFile;\nuse Illuminate\\Support\\Facades\\Storage;\nuse Tests\\TestCase;\n\nclass ExampleTest extends TestCase\n{\n    public function test_albums_can_be_uploaded(): void\n    {\n        Storage::fake('photos');\n\n        $response = $this->json('POST', '/photos', [\n            UploadedFile::fake()->image('photo1.jpg'),\n            UploadedFile::fake()->image('photo2.jpg')\n        ]);\n\n        // Assert one or more files were stored...\n        Storage::disk('photos')->assertExists('photo1.jpg');\n        Storage::disk('photos')->assertExists(['photo1.jpg', 'photo2.jpg']);\n\n        // Assert one or more files were not stored...\n        Storage::disk('photos')->assertMissing('missing.jpg');\n        Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']);\n\n        // Assert that the number of files in a given directory matches the expected count...\n        Storage::disk('photos')->assertCount('/wallpapers', 2);\n\n        // Assert that a given directory is empty...\n        Storage::disk('photos')->assertDirectoryEmpty('/wallpapers');\n    }\n}\n```\n\nBy default, the `fake` method will delete all files in its temporary directory. If you would like to keep these files, you may use the \"persistentFake\" method instead. For more information on testing file uploads, you may consult the [HTTP testing documentation's information on file uploads](/docs/{{version}}/http-tests#testing-file-uploads).\n\n> [!WARNING]\n> The `image` method requires the [GD extension](https://www.php.net/manual/en/book.image.php).\n\n---\n\n## Http Tests Testing JSON APIs Fluent JSON Testing\nHttp Tests | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/http-tests.md\n\n\n### Fluent JSON Testing\n\nLaravel also offers a beautiful way to fluently test your application's JSON responses. To get started, pass a closure to the `assertJson` method. This closure will be invoked with an instance of `Illuminate\\Testing\\Fluent\\AssertableJson` which can be used to make assertions against the JSON that was returned by your application. The `where` method may be used to make assertions against a particular attribute of the JSON, while the `missing` method may be used to assert that a particular attribute is missing from the JSON:\n\n```php tab=Pest\nuse Illuminate\\Testing\\Fluent\\AssertableJson;\n\ntest('fluent json', function () {\n    $response = $this->getJson('/users/1');\n\n    $response\n        ->assertJson(fn (AssertableJson $json) =>\n            $json->where('id', 1)\n                ->where('name', 'Victoria Faith')\n                ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com'))\n                ->whereNot('status', 'pending')\n                ->missing('password')\n                ->etc()\n        );\n});\n```\n\n```php tab=PHPUnit\nuse Illuminate\\Testing\\Fluent\\AssertableJson;\n\n/**\n * A basic functional test example.\n */\npublic function test_fluent_json(): void\n{\n    $response = $this->getJson('/users/1');\n\n    $response\n        ->assertJson(fn (AssertableJson $json) =>\n            $json->where('id', 1)\n                ->where('name', 'Victoria Faith')\n                ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com'))\n                ->whereNot('status', 'pending')\n                ->missing('password')\n                ->etc()\n        );\n}\n```\n\n#### Understanding the `etc` Method\n\nIn the example above, you may have noticed we invoked the `etc` method at the end of our assertion chain. This method informs Laravel that there may be other attributes present on the JSON object. If the `etc` method is not used, the test will fail if other attributes that you did not make assertions against exist on the JSON object.\n\nThe intention behind this behavior is to protect you from unintentionally exposing sensitive information in your JSON responses by forcing you to either explicitly make an assertion against the attribute or explicitly allow additional attributes via the `etc` method.\n\nHowever, you should be aware that not including the `etc` method in your assertion chain does not ensure that additional attributes are not being added to arrays that are nested within your JSON object. The `etc` method only ensures that no additional attributes exist at the nesting level in which the `etc` method is invoked.\n\n---\n\n## Pest3 Now Available New Configuration API\nPest3 Now Available | pestphp/pest@4.x | Source: https://github.com/pestphp/docs/blob/4.x/pest3-now-available.md\n\n\n## New Configuration API\n\nPest 1 / Pest 2's configuration API was a little bit confusing, the `uses()` function that was originally made only for having the `$this` variable within closure bound to the test case instance, ended up being used for pretty much everything.\n\nIn Pest 3, we've introduced a new configuration API that is more intuitive and easier to use. The new configuration API is based on the `pest()` function, which allows you to configure Pest using a fluent and expressive API.\n\n> Note: the `uses()` function is still available in Pest 3, and we don't have plans to remove it. However, we recommend using the new configuration API for new projects.\n\n```diff\n-uses(TestCase::class)->in(__DIR__);\n+pest()->extends(TestCase::class);\n\n-uses(TestCase::class, RefreshDatabase::class)->in('Features');\n+pest()->extends(TestCase::class)->use(RefreshDatabase::class)->in('Features');\n\n-uses()->compact();\n+pest()->printer()->compact();\n```\n\nAnd of course, any method that was available on the `uses()` API, like `->beforeEach()` or `->group()` is still available on the new `pest()` configuration API; we've just made it more intuitive and easier to use.\n\n---\n\n## Mutation Testing Options & Modifiers `--profile`\nMutation Testing | pestphp/pest@4.x | Source: https://github.com/pestphp/docs/blob/4.x/mutation-testing.md\n\n\n### `--profile`\n\nOutput to standard output the top ten slowest mutations.\n\n```bash\n./vendor/bin/pest --mutate --profile\n```\n\n---\n\n## Filesystem Retrieving Files Temporary URLs Temporary Upload URLs\nFilesystem | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/filesystem.md\n\n\n#### Temporary Upload URLs\n\n> [!WARNING]\n> The ability to generate temporary upload URLs is only supported by the `s3` and `local` drivers.\n\nIf you need to generate a temporary URL that can be used to upload a file directly from your client-side application, you may use the `temporaryUploadUrl` method. This method accepts a path and a `DateTime` instance specifying when the URL should expire. The `temporaryUploadUrl` method returns an associative array which may be destructured into the upload URL and the headers that should be included with the upload request:\n\n```php\nuse Illuminate\\Support\\Facades\\Storage;\n\n['url' => $url, 'headers' => $headers] = Storage::temporaryUploadUrl(\n    'file.jpg', now()->plus(minutes: 5)\n);\n```\n\nThis method is primarily useful in serverless environments that require the client-side application to directly upload files to a cloud storage system such as Amazon S3.\n\n---\n\n## Http Tests Testing JSON APIs Asserting on JSON Paths\nHttp Tests | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/http-tests.md\n\n\n#### Asserting on JSON Paths\n\nIf you would like to verify that the JSON response contains the given data at a specified path, you should use the `assertJsonPath` method:\n\n```php tab=Pest\n<?php\n\ntest('asserting a json path value', function () {\n    $response = $this->postJson('/user', ['name' => 'Sally']);\n\n    $response\n        ->assertStatus(201)\n        ->assertJsonPath('team.owner.name', 'Darian');\n});\n```\n\n```php tab=PHPUnit\n<?php\n\nnamespace Tests\\Feature;\n\nuse Tests\\TestCase;\n\nclass ExampleTest extends TestCase\n{\n    /**\n     * A basic functional test example.\n     */\n    public function test_asserting_a_json_paths_value(): void\n    {\n        $response = $this->postJson('/user', ['name' => 'Sally']);\n\n        $response\n            ->assertStatus(201)\n            ->assertJsonPath('team.owner.name', 'Darian');\n    }\n}\n```\n\nThe `assertJsonPath` method also accepts a closure, which may be used to dynamically determine if the assertion should pass:\n\n```php\n$response->assertJsonPath('team.owner.name', fn (string $name) => strlen($name) >= 3);\n```\n\n---\n\n## Cli Api Reference\nCli Api Reference | pestphp/pest@4.x | Source: https://github.com/pestphp/docs/blob/4.x/cli-api-reference.md\n\n---\ntitle: CLI API Reference\ndescription: In the preceding chapters of the Pest documentation, we have covered numerous CLI options that are available in Pest. Nevertheless, Pest provides many other options that could prove beneficial. The complete CLI API Reference is provided below for your convenience.\n---\n\n# CLI API Reference\n\nIn the preceding chapters of the Pest documentation, we have covered numerous CLI options that are available in Pest. Nevertheless, Pest provides many other options that could prove beneficial. The complete CLI API Reference is provided below for your convenience.\n\n## Configuration\n\n- `--init`: Initialize a standard Pest configuration.\n- `--bootstrap <file>` A PHP script that is included before the tests run.\n- `-c|--configuration <file>`: Read configuration from XML file.\n- `--no-configuration`: Ignore default configuration file (phpunit.xml).\n- `--extension <class>`: Register test runner extension with bootstrap <class>.\n- `--no-extensions`: Do not load PHPUnit extensions.\n- `--include-path <path(s)>`: Prepend PHP's include_path with given path(s).\n- `-d <key[=value]>`: Set a php.ini value.\n- `--cache-directory <dir>`: Specify cache directory.\n- `--generate-configuration`: Generate configuration file with suggested settings.\n- `--migrate-configuration`: Migrate configuration file to current format.\n- `--generate-baseline <file>`: Generate baseline for issues.\n- `--use-baseline <file>`: Use baseline to ignore issues.\n- `--ignore-baseline`: Do not use baseline to ignore issues.\n- `--test-directory`: Specify test directory containing Pest.php, TestCase.php, helpers and your tests. Default: tests\n\n## Selection\n\n- `--bail`: Stop execution upon first not-passed test.\n- `--ci`: Ignore focused tests using `->only()` and run the entire test suite.\n- `--todos`: Output to standard output the list of todos.\n- `--notes`: Output to standard output tests with notes.\n- `--issue`: Output to standard output tests with the given issue number.\n- `--pr`: Output to standard output tests with the given pull request number.\n- `--pull-request`: Output to standard output tests with the given pull request number (alias for `--pr`).\n- `--flaky`: Output to standard output tests marked as flaky.\n- `--retry`: Run non-passing tests first and stop execution upon first error or failure.\n- `--list-suites` List available test suites.\n- `--testsuite <name>`: Only run tests from the specified test suite(s).\n- `--exclude-testsuite <name>`: Exclude tests from the specified test suite(s).\n- `--list-groups`: List available test groups.\n- `--group <name>`: Only run tests from the specified group(s).\n- `--exclude-group <name>`: Exclude tests from the specified group(s).\n- `--covers <name>`: Only run tests that intend to cover `<name>`.\n- `--uses <name>`: Only run tests that intend to use `<name>`.\n- `--requires-php-extension <name>`: Only run tests that require PHP extension <name>.\n- `--list-test-files`: List available test files.\n- `--list-tests`: List available tests.\n- `--list-tests-xml <file>`: List available tests in XML format.\n- `--filter <pattern>`: Filter which tests to run\n- `--exclude-filter <pattern>`: Exclude tests for the specified filter pattern.\n- `--test-suffix <suffixes>`: Only search for test in files with specified suffix(es). Default: Test.php,.phpt\n\n## Execution\n\n- `--parallel` Run tests in parallel.\n- `--shard <index/total>`: Run only the given shard of tests (e.g. `--shard=1/5`). Uses time-balanced distribution when `tests/.pest/shards.json` exists.\n- `--update-shards`: Update `tests/.pest/shards.json` with test timing data for time-balanced sharding. Can be combined with `--parallel`.\n- `--update-snapshots`: Update snapshots for tests using the \"toMatchSnapshot\" expectation.\n- `--globals-backup`: Backup and restore $GLOBALS for each test.\n- `--static-backup`: Backup and restore static properties for each test.\n- `--strict-coverage`: Be strict about code coverage metadata.\n- `--strict-global-state`: Be strict about changes to global state.\n- `--disallow-test-output`: Be strict about output during tests.\n- `--enforce-time-limit`: Enforce time limit based on test size.\n- `--default-time-limit <sec>`: Timeout in seconds for tests that have no declared size.\n- `--dont-report-useless-tests`: Do not report tests that do not test anything.\n- `--stop-on-defect`: Stop execution upon first not-passed test.\n- `--stop-on-error`: Stop execution upon first error.\n- `--stop-on-failure`: Stop execution upon first error or failure.\n- `--stop-on-warning`: Stop execution upon first warning.\n- `--stop-on-risky`: Stop execution upon first risky test.\n- `--stop-on-deprecation`: Stop after first test that triggered a deprecation.\n- `--stop-on-notice`: Stop after first test that triggered a notice.\n- `--stop-on-skipped`: Stop execution upon first skipped test.\n- `--stop-on-incomplete`: Stop execution upon first incomplete test.\n- `--fail-on-empty-test-suite`: Signal failure using shell exit code when no tests were run.\n- `--fail-on-warning`: Treat tests with warnings as failures.\n- `--fail-on-risky`: Treat risky tests as failures.\n- `--fail-on-deprecation`: Signal failure using shell exit code when a deprecation was triggered.\n- `--fail-on-phpunit-deprecation`: Signal failure using shell exit code when a PHPUnit deprecation was triggered.\n- `--fail-on-notice`: Signal failure using shell exit code when a notice was triggered.\n- `--fail-on-skipped`: Treat skipped tests as failures.\n- `--fail-on-incomplete`: Signal failure using shell exit code when a test was marked incomplete.\n- `--cache-result`: Write test results to cache file.\n- `--do-not-cache-result`: Do not write test results to cache file\n- `--order-by <order>`: Run tests in order: default|defects|depends|duration|no-depends|random|reverse|size.\n- `--random-order-seed <N>`: Use the specified random seed when running tests in random order\n\n## Reporting\n\n- `--colors <flag>`: Use colors in output (\"never\", \"auto\" or \"always\").\n- `--columns <n>`: Number of columns to use for progress output.\n- `--columns max`: Use maximum number of columns for progress output.\n- `--stderr`: Write to STDERR instead of STDOUT.\n- `--no-progress`: Disable output of test execution progress.\n- `--no-results`: Disable output of test results.\n- `--no-output`: Disable all output.\n- `--display-incomplete`: Display details for incomplete tests.\n- `--display-skipped`: Display details for skipped tests.\n- `--display-deprecations`: Display details for deprecations triggered by tests.\n- `--display-phpunit-deprecations`: Display details for PHPUnit deprecations.\n- `--display-errors`: Display details for errors triggered by tests.\n- `--display-notices`: Display details for notices triggered by tests.\n- `--display-warnings`: Display details for warnings triggered by tests.\n- `--reverse-list`: Print defects in reverse order.\n- `--teamcity`: Replace default progress and result output with TeamCity format.\n- `--testdox`: Replace default result output with TestDox format\n- `--testdox-summary`: Repeat TestDox output for tests with errors, failures, or issues.\n- `--debug`: Replace default progress and result output with debugging information.\n- `--compact`:  Replace default result output with Compact format\n\n## Logging\n\n- `--log-junit <file>`: Write test results in JUnit XML format to file.\n- `--log-teamcity <file>`: Write test results in TeamCity format to file.\n- `--testdox-html <file>`: Write test results in TestDox format (HTML) to file.\n- `--testdox-text <file>`: Write test results in TestDox format (plain text) to file.\n- `--log-events-text <file>`: Stream events as plain text to file.\n- `--log-events-verbose-text <file>`: Stream events as plain text (with telemetry information) to file.\n- `--no-logging`: Ignore logging configured in the XML configuration file\n\n## Code Coverage\n\n- `--coverage`: Generate code coverage report and output to standard output.\n- `--coverage --min=<value>`: Set the minimum required coverage percentage, and fail if not met.\n- `--coverage --exactly=<value>`: Set the exact required coverage percentage, and fail if not met.\n- `--coverage --only-covered`: Hide files with 0% coverage from the code coverage report.\n- `--coverage-clover <file>`: Write code coverage report in Clover XML format to file.\n- `--coverage-cobertura <file>`: Write code coverage report in Cobertura XML format to file.\n- `--coverage-crap4j <file>`: Write code coverage report in Crap4J XML format to file.\n- `--coverage-html <dir>`: Write code coverage report in HTML format to directory.\n- `--coverage-php <file>`: Write serialized code coverage data to file.\n- `--coverage-text=<file>`: Write code coverage report in text format to file [default: standard output].\n- `--only-summary-for-coverage-text`: Option for code coverage report in text format: only show summary.\n- `--show-uncovered-for-coverage-text`: Option for code coverage report in text format: show uncovered files.\n- `--coverage-xml <dir>`: Write code coverage report in XML format to directory.\n- `--warm-coverage-cache`: Warm static analysis cache.\n- `--coverage-filter <dir>`: Include `<dir>`: in code coverage reporting.\n- `--path-coverage`: Report path coverage in addition to line coverage.\n- `--disable-coverage-ignore`: Disable metadata for ignoring code coverage.\n- `--no-coverage`: Ignore code coverage reporting configured in the XML configuration file\n\n## Mutation Testing\n\n- `--mutate`: Runs mutation testing, to understand the quality of your tests.\n- `--mutate --parallel`: Runs mutation testing in parallel.\n- `--mutate --min`: Set the minimum required mutation score, and fail if not met.\n- `--mutate --id`:  Run only the mutation with the given ID. But E.g. --id=ecb35ab30ffd3491. Note, you need to provide the same options as the original run.\n- `--mutate --covered-only`: Only generate mutations for classes that are covered by tests.\n- `--mutate --bail`: Stop mutation testing execution upon first untested or uncovered mutation.\n- `--mutate --class`: Generate mutations for the given class(es). E.g. --class=App\\\\Models.\n- `--mutate --ignore`: Ignore the given class(es) when generating mutations. E.g. --ignore=App\\\\Http\\\\Requests.\n- `--mutate --clear-cache`: Clear the mutation cache.\n- `--mutate --no-cache`: Clear the mutation cache.\n- `--mutate --ignore-min-score-on-zero-mutations`: Ignore the minimum score requirement when there are no mutations.\n- `--mutate --covered-only`: Only generate mutations for classes that are covered by tests.\n- `--mutate --everything`: Generate mutations for all classes, even if they are not covered by tests.\n- `--mutate --profile`: Output to standard output the top ten slowest mutations.\n- `--mutate --retry`: Run untested or uncovered mutations first and stop execution upon first error or failure.\n- `--mutate --stop-on-uncovered`: Stop mutation testing execution upon first untested mutation.\n- `--mutate --stop-on-untested`: Stop mutation testing execution upon first untested mutation.\n\n## Profiling\n\n- `--profile`: Output to standard output the top ten slowest tests\n\n---\n\nIn this chapter, you found a complete list of CLI options provided by Pest. In the subsequent documentation, we will explore the topic of test dependencies: [Test Dependencies](/docs/test-dependencies)\n\n\n_[Truncated]_"}]


sed -n '1,220p' /Users/fabianwesner/Herd/shop/.agents/skills/pest-testing/SKILL.md

---
name: pest-testing
description: "Use this skill for Pest PHP testing in Laravel projects only. Trigger whenever any test is being written, edited, fixed, or refactored — including fixing tests that broke after a code change, adding assertions, converting PHPUnit to Pest, adding datasets, and TDD workflows. Always activate when the user asks how to write something in Pest, mentions test files or directories (tests/Feature, tests/Unit, tests/Browser), or needs browser testing, smoke testing multiple pages for JS errors, or architecture tests. Covers: test()/it()/expect() syntax, datasets, mocking, browser testing (visit/click/fill), smoke testing, arch(), Livewire component tests, RefreshDatabase, and all Pest 4 features. Do not use for factories, seeders, migrations, controllers, models, or non-test PHP code."
license: MIT
metadata:
  author: laravel
---

# Pest Testing 4

## Documentation

Use `search-docs` for detailed Pest 4 patterns and documentation.

## Basic Usage

### Creating Tests

All tests must be written using Pest. Use `php artisan make:test --pest {name}`.

### Test Organization

- Unit/Feature tests: `tests/Feature` and `tests/Unit` directories.
- Browser tests: `tests/Browser/` directory.
- Do NOT remove tests without approval - these are core application code.

### Basic Test Structure

Pest supports both `test()` and `it()` functions. Before writing new tests, check existing test files in the same directory to match the project's convention. Use `test()` if existing tests use `test()`, or `it()` if they use `it()`.

<!-- Basic Pest Test Example -->
```php
it('is true', function () {
    expect(true)->toBeTrue();
});
```

### Running Tests

- Run minimal tests with filter before finalizing: `php artisan test --compact --filter=testName`.
- Run all tests: `php artisan test --compact`.
- Run file: `php artisan test --compact tests/Feature/ExampleTest.php`.

## Assertions

Use specific assertions (`assertSuccessful()`, `assertNotFound()`) instead of `assertStatus()`:

<!-- Pest Response Assertion -->
```php
it('returns all', function () {
    $this->postJson('/api/docs', [])->assertSuccessful();
});
```

| Use | Instead of |
|-----|------------|
| `assertSuccessful()` | `assertStatus(200)` |
| `assertNotFound()` | `assertStatus(404)` |
| `assertForbidden()` | `assertStatus(403)` |

## Mocking

Import mock function before use: `use function Pest\Laravel\mock;`

## Datasets

Use datasets for repetitive tests (validation rules, etc.):

<!-- Pest Dataset Example -->
```php
it('has emails', function (string $email) {
    expect($email)->not->toBeEmpty();
})->with([
    'james' => 'james@laravel.com',
    'taylor' => 'taylor@laravel.com',
]);
```

## Pest 4 Features

| Feature | Purpose |
|---------|---------|
| Browser Testing | Full integration tests in real browsers |
| Smoke Testing | Validate multiple pages quickly |
| Visual Regression | Compare screenshots for visual changes |
| Test Sharding | Parallel CI runs |
| Architecture Testing | Enforce code conventions |

### Browser Test Example

Browser tests run in real browsers for full integration testing:

- Browser tests live in `tests/Browser/`.
- Use Laravel features like `Event::fake()`, `assertAuthenticated()`, and model factories.
- Use `RefreshDatabase` for clean state per test.
- Interact with page: click, type, scroll, select, submit, drag-and-drop, touch gestures.
- Test on multiple browsers (Chrome, Firefox, Safari) if requested.
- Test on different devices/viewports (iPhone 14 Pro, tablets) if requested.
- Switch color schemes (light/dark mode) when appropriate.
- Take screenshots or pause tests for debugging.

<!-- Pest Browser Test Example -->
```php
it('may reset the password', function () {
    Notification::fake();

    $this->actingAs(User::factory()->create());

    $page = visit('/sign-in');

    $page->assertSee('Sign In')
        ->assertNoJavaScriptErrors()
        ->click('Forgot Password?')
        ->fill('email', 'nuno@laravel.com')
        ->click('Send Reset Link')
        ->assertSee('We have emailed your password reset link!');

    Notification::assertSent(ResetPassword::class);
});
```

### Smoke Testing

Quickly validate multiple pages have no JavaScript errors:

<!-- Pest Smoke Testing Example -->
```php
$pages = visit(['/', '/about', '/contact']);

$pages->assertNoJavaScriptErrors()->assertNoConsoleLogs();
```

### Visual Regression Testing

Capture and compare screenshots to detect visual changes.

### Test Sharding

Split tests across parallel processes for faster CI runs.

### Architecture Testing

Pest 4 includes architecture testing (from Pest 3):

<!-- Architecture Test Example -->
```php
arch('controllers')
    ->expect('App\Http\Controllers')
    ->toExtendNothing()
    ->toHaveSuffix('Controller');
```

## Common Pitfalls

- Not importing `use function Pest\Laravel\mock;` before using mock
- Using `assertStatus(200)` instead of `assertSuccessful()`
- Forgetting datasets for repetitive validation tests
- Deleting tests without approval
- Forgetting `assertNoJavaScriptErrors()` in browser tests


sed -n '1,220p' /Users/fabianwesner/Herd/shop/.agents/skills/laravel-best-practices/SKILL.md

---
name: laravel-best-practices
description: "Apply this skill whenever writing, reviewing, or refactoring Laravel PHP code. This includes creating or modifying controllers, models, migrations, form requests, policies, jobs, scheduled commands, service classes, and Eloquent queries. Triggers for N+1 and query performance issues, caching strategies, authorization and security patterns, validation, error handling, queue and job configuration, route definitions, and architectural decisions. Also use for Laravel code reviews and refactoring existing Laravel code to follow best practices. Covers any task involving Laravel backend PHP code patterns."
license: MIT
metadata:
  author: laravel
---

# Laravel Best Practices

Best practices for Laravel, prioritized by impact. Each rule teaches what to do and why. For exact API syntax, verify with `search-docs`.

## Consistency First

Before applying any rule, check what the application already does. Laravel offers multiple valid approaches — the best choice is the one the codebase already uses, even if another pattern would be theoretically better. Inconsistency is worse than a suboptimal pattern.

Check sibling files, related controllers, models, or tests for established patterns. If one exists, follow it — don't introduce a second way. These rules are defaults for when no pattern exists yet, not overrides.

## Quick Reference

### 1. Database Performance → `rules/db-performance.md`

- Eager load with `with()` to prevent N+1 queries
- Enable `Model::preventLazyLoading()` in development
- Select only needed columns, avoid `SELECT *`
- `chunk()` / `chunkById()` for large datasets
- Index columns used in `WHERE`, `ORDER BY`, `JOIN`
- `withCount()` instead of loading relations to count
- `cursor()` for memory-efficient read-only iteration
- Never query in Blade templates

### 2. Advanced Query Patterns → `rules/advanced-queries.md`

- `addSelect()` subqueries over eager-loading entire has-many for a single value
- Dynamic relationships via subquery FK + `belongsTo`
- Conditional aggregates (`CASE WHEN` in `selectRaw`) over multiple count queries
- `setRelation()` to prevent circular N+1 queries
- `whereIn` + `pluck()` over `whereHas` for better index usage
- Two simple queries can beat one complex query
- Compound indexes matching `orderBy` column order
- Correlated subqueries in `orderBy` for has-many sorting (avoid joins)

### 3. Security → `rules/security.md`

- Define `$fillable` or `$guarded` on every model, authorize every action via policies or gates
- No raw SQL with user input — use Eloquent or query builder
- `{{ }}` for output escaping, `@csrf` on all POST/PUT/DELETE forms, `throttle` on auth and API routes
- Validate MIME type, extension, and size for file uploads
- Never commit `.env`, use `config()` for secrets, `encrypted` cast for sensitive DB fields

### 4. Caching → `rules/caching.md`

- `Cache::remember()` over manual get/put
- `Cache::flexible()` for stale-while-revalidate on high-traffic data
- `Cache::memo()` to avoid redundant cache hits within a request
- Cache tags to invalidate related groups
- `Cache::add()` for atomic conditional writes
- `once()` to memoize per-request or per-object lifetime
- `Cache::lock()` / `lockForUpdate()` for race conditions
- Failover cache stores in production

### 5. Eloquent Patterns → `rules/eloquent.md`

- Correct relationship types with return type hints
- Local scopes for reusable query constraints
- Global scopes sparingly — document their existence
- Attribute casts in the `casts()` method
- Cast date columns, use Carbon instances in templates
- `whereBelongsTo($model)` for cleaner queries
- Never hardcode table names — use `(new Model)->getTable()` or Eloquent queries

### 6. Validation & Forms → `rules/validation.md`

- Form Request classes, not inline validation
- Array notation `['required', 'email']` for new code; follow existing convention
- `$request->validated()` only — never `$request->all()`
- `Rule::when()` for conditional validation
- `after()` instead of `withValidator()`

### 7. Configuration → `rules/config.md`

- `env()` only inside config files
- `App::environment()` or `app()->isProduction()`
- Config, lang files, and constants over hardcoded text

### 8. Testing Patterns → `rules/testing.md`

- `LazilyRefreshDatabase` over `RefreshDatabase` for speed
- `assertModelExists()` over raw `assertDatabaseHas()`
- Factory states and sequences over manual overrides
- Use fakes (`Event::fake()`, `Exceptions::fake()`, etc.) — but always after factory setup, not before
- `recycle()` to share relationship instances across factories

### 9. Queue & Job Patterns → `rules/queue-jobs.md`

- `retry_after` must exceed job `timeout`; use exponential backoff `[1, 5, 10]`
- `ShouldBeUnique` to prevent duplicates; `ShouldBeUniqueUntilProcessing` for early lock release
- Always implement `failed()`; with `retryUntil()`, set `$tries = 0`
- `RateLimited` middleware for external API calls; `Bus::batch()` for related jobs
- Horizon for complex multi-queue scenarios

### 10. Routing & Controllers → `rules/routing.md`

- Implicit route model binding
- Scoped bindings for nested resources
- `Route::resource()` or `apiResource()`
- Methods under 10 lines — extract to actions/services
- Type-hint Form Requests for auto-validation

### 11. HTTP Client → `rules/http-client.md`

- Explicit `timeout` and `connectTimeout` on every request
- `retry()` with exponential backoff for external APIs
- Check response status or use `throw()`
- `Http::pool()` for concurrent independent requests
- `Http::fake()` and `preventStrayRequests()` in tests

### 12. Events, Notifications & Mail → `rules/events-notifications.md`, `rules/mail.md`

- Event discovery over manual registration; `event:cache` in production
- `ShouldDispatchAfterCommit` / `afterCommit()` inside transactions
- Queue notifications and mailables with `ShouldQueue`
- On-demand notifications for non-user recipients
- `HasLocalePreference` on notifiable models
- `assertQueued()` not `assertSent()` for queued mailables
- Markdown mailables for transactional emails

### 13. Error Handling → `rules/error-handling.md`

- `report()`/`render()` on exception classes or in `bootstrap/app.php` — follow existing pattern
- `ShouldntReport` for exceptions that should never log
- Throttle high-volume exceptions to protect log sinks
- `dontReportDuplicates()` for multi-catch scenarios
- Force JSON rendering for API routes
- Structured context via `context()` on exception classes

### 14. Task Scheduling → `rules/scheduling.md`

- `withoutOverlapping()` on variable-duration tasks
- `onOneServer()` on multi-server deployments
- `runInBackground()` for concurrent long tasks
- `environments()` to restrict to appropriate environments
- `takeUntilTimeout()` for time-bounded processing
- Schedule groups for shared configuration

### 15. Architecture → `rules/architecture.md`

- Single-purpose Action classes; dependency injection over `app()` helper
- Prefer official Laravel packages and follow conventions, don't override defaults
- Default to `ORDER BY id DESC` or `created_at DESC`; `mb_*` for UTF-8 safety
- `defer()` for post-response work; `Context` for request-scoped data; `Concurrency::run()` for parallel execution

### 16. Migrations → `rules/migrations.md`

- Generate migrations with `php artisan make:migration`
- `constrained()` for foreign keys
- Never modify migrations that have run in production
- Add indexes in the migration, not as an afterthought
- Mirror column defaults in model `$attributes`
- Reversible `down()` by default; forward-fix migrations for intentionally irreversible changes
- One concern per migration — never mix DDL and DML

### 17. Collections → `rules/collections.md`

- Higher-order messages for simple collection operations
- `cursor()` vs. `lazy()` — choose based on relationship needs
- `lazyById()` when updating records while iterating
- `toQuery()` for bulk operations on collections

### 18. Blade & Views → `rules/blade-views.md`

- `$attributes->merge()` in component templates
- Blade components over `@include`; `@pushOnce` for per-component scripts
- View Composers for shared view data
- `@aware` for deeply nested component props

### 19. Conventions & Style → `rules/style.md`

- Follow Laravel naming conventions for all entities
- Prefer Laravel helpers (`Str`, `Arr`, `Number`, `Uri`, `Str::of()`, `$request->string()`) over raw PHP functions
- No JS/CSS in Blade, no HTML in PHP classes
- Code should be readable; comments only for config files

## How to Apply

Always use a sub-agent to read rule files and explore this skill's content.

1. Identify the file type and select relevant sections (e.g., migration → §16, controller → §1, §3, §5, §6, §10)
2. Check sibling files for existing patterns — follow those first per Consistency First
3. Verify API syntax with `search-docs` for the installed Laravel version


Output:
Chunk ID: fb0260
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 2249
Output:
---
name: laravel-best-practices
description: "Apply this skill whenever writing, reviewing, or refactoring Laravel PHP code. This includes creating or modifying controllers, models, migrations, form requests, policies, jobs, scheduled commands, service classes, and Eloquent queries. Triggers for N+1 and query performance issues, caching strategies, authorization and security patterns, validation, error handling, queue and job configuration, route definitions, and architectural decisions. Also use for Laravel code reviews and refactoring existing Laravel code to follow best practices. Covers any task involving Laravel backend PHP code patterns."
license: MIT
metadata:
  author: laravel
---

# Laravel Best Practices

Best practices for Laravel, prioritized by impact. Each rule teaches what to do and why. For exact API syntax, verify with `search-docs`.

## Consistency First

Before applying any rule, check what the application already does. Laravel offers multiple valid approaches — the best choice is the one the codebase already uses, even if another pattern would be theoretically better. Inconsistency is worse than a suboptimal pattern.

Check sibling files, related controllers, models, or tests for established patterns. If one exists, follow it — don't introduce a second way. These rules are defaults for when no pattern exists yet, not overrides.

## Quick Reference

### 1. Database Performance → `rules/db-performance.md`

- Eager load with `with()` to prevent N+1 queries
- Enable `Model::preventLazyLoading()` in development
- Select only needed columns, avoid `SELECT *`
- `chunk()` / `chunkById()` for large datasets
- Index columns used in `WHERE`, `ORDER BY`, `JOIN`
- `withCount()` instead of loading relations to count
- `cursor()` for memory-efficient read-only iteration
- Never query in Blade templates

### 2. Advanced Query Patterns → `rules/advanced-queries.md`

- `addSelect()` subqueries over eager-loading entire has-many for a single value
- Dynamic relationships via subquery FK + `belongsTo`
- Conditional aggregates (`CASE WHEN` in `selectRaw`) over multiple count queries
- `setRelation()` to prevent circular N+1 queries
- `whereIn` + `pluck()` over `whereHas` for better index usage
- Two simple queries can beat one complex query
- Compound indexes matching `orderBy` column order
- Correlated subqueries in `orderBy` for has-many sorting (avoid joins)

### 3. Security → `rules/security.md`

- Define `$fillable` or `$guarded` on every model, authorize every action via policies or gates
- No raw SQL with user input — use Eloquent or query builder
- `{{ }}` for output escaping, `@csrf` on all POST/PUT/DELETE forms, `throttle` on auth and API routes
- Validate MIME type, extension, and size for file uploads
- Never commit `.env`, use `config()` for secrets, `encrypted` cast for sensitive DB fields

### 4. Caching → `rules/caching.md`

- `Cache::remember()` over manual get/put
- `Cache::flexible()` for stale-while-revalidate on high-traffic data
- `Cache::memo()` to avoid redundant cache hits within a request
- Cache tags to invalidate related groups
- `Cache::add()` for atomic conditional writes
- `once()` to memoize per-request or per-object lifetime
- `Cache::lock()` / `lockForUpdate()` for race conditions
- Failover cache stores in production

### 5. Eloquent Patterns → `rules/eloquent.md`

- Correct relationship types with return type hints
- Local scopes for reusable query constraints
- Global scopes sparingly — document their existence
- Attribute casts in the `casts()` method
- Cast date columns, use Carbon instances in templates
- `whereBelongsTo($model)` for cleaner queries
- Never hardcode table names — use `(new Model)->getTable()` or Eloquent queries

### 6. Validation & Forms → `rules/validation.md`

- Form Request classes, not inline validation
- Array notation `['required', 'email']` for new code; follow existing convention
- `$request->validated()` only — never `$request->all()`
- `Rule::when()` for conditional validation
- `after()` instead of `withValidator()`

### 7. Configuration → `rules/config.md`

- `env()` only inside config files
- `App::environment()` or `app()->isProduction()`
- Config, lang files, and constants over hardcoded text

### 8. Testing Patterns → `rules/testing.md`

- `LazilyRefreshDatabase` over `RefreshDatabase` for speed
- `assertModelExists()` over raw `assertDatabaseHas()`
- Factory states and sequences over manual overrides
- Use fakes (`Event::fake()`, `Exceptions::fake()`, etc.) — but always after factory setup, not before
- `recycle()` to share relationship instances across factories

### 9. Queue & Job Patterns → `rules/queue-jobs.md`

- `retry_after` must exceed job `timeout`; use exponential backoff `[1, 5, 10]`
- `ShouldBeUnique` to prevent duplicates; `ShouldBeUniqueUntilProcessing` for early lock release
- Always implement `failed()`; with `retryUntil()`, set `$tries = 0`
- `RateLimited` middleware for external API calls; `Bus::batch()` for related jobs
- Horizon for complex multi-queue scenarios

### 10. Routing & Controllers → `rules/routing.md`

- Implicit route model binding
- Scoped bindings for nested resources
- `Route::resource()` or `apiResource()`
- Methods under 10 lines — extract to actions/services
- Type-hint Form Requests for auto-validation

### 11. HTTP Client → `rules/http-client.md`

- Explicit `timeout` and `connectTimeout` on every request
- `retry()` with exponential backoff for external APIs
- Check response status or use `throw()`
- `Http::pool()` for concurrent independent requests
- `Http::fake()` and `preventStrayRequests()` in tests

### 12. Events, Notifications & Mail → `rules/events-notifications.md`, `rules/mail.md`

- Event discovery over manual registration; `event:cache` in production
- `ShouldDispatchAfterCommit` / `afterCommit()` inside transactions
- Queue notifications and mailables with `ShouldQueue`
- On-demand notifications for non-user recipients
- `HasLocalePreference` on notifiable models
- `assertQueued()` not `assertSent()` for queued mailables
- Markdown mailables for transactional emails

### 13. Error Handling → `rules/error-handling.md`

- `report()`/`render()` on exception classes or in `bootstrap/app.php` — follow existing pattern
- `ShouldntReport` for exceptions that should never log
- Throttle high-volume exceptions to protect log sinks
- `dontReportDuplicates()` for multi-catch scenarios
- Force JSON rendering for API routes
- Structured context via `context()` on exception classes

### 14. Task Scheduling → `rules/scheduling.md`

- `withoutOverlapping()` on variable-duration tasks
- `onOneServer()` on multi-server deployments
- `runInBackground()` for concurrent long tasks
- `environments()` to restrict to appropriate environments
- `takeUntilTimeout()` for time-bounded processing
- Schedule groups for shared configuration

### 15. Architecture → `rules/architecture.md`

- Single-purpose Action classes; dependency injection over `app()` helper
- Prefer official Laravel packages and follow conventions, don't override defaults
- Default to `ORDER BY id DESC` or `created_at DESC`; `mb_*` for UTF-8 safety
- `defer()` for post-response work; `Context` for request-scoped data; `Concurrency::run()` for parallel execution

### 16. Migrations → `rules/migrations.md`

- Generate migrations with `php artisan make:migration`
- `constrained()` for foreign keys
- Never modify migrations that have run in production
- Add indexes in the migration, not as an afterthought
- Mirror column defaults in model `$attributes`
- Reversible `down()` by default; forward-fix migrations for intentionally irreversible changes
- One concern per migration — never mix DDL and DML

### 17. Collections → `rules/collections.md`

- Higher-order messages for simple collection operations
- `cursor()` vs. `lazy()` — choose based on relationship needs
- `lazyById()` when updating records while iterating
- `toQuery()` for bulk operations on collections

### 18. Blade & Views → `rules/blade-views.md`

- `$attributes->merge()` in component templates
- Blade components over `@include`; `@pushOnce` for per-component scripts
- View Composers for shared view data
- `@aware` for deeply nested component props

### 19. Conventions & Style → `rules/style.md`

- Follow Laravel naming conventions for all entities
- Prefer Laravel helpers (`Str`, `Arr`, `Number`, `Uri`, `Str::of()`, `$request->string()`) over raw PHP functions
- No JS/CSS in Blade, no HTML in PHP classes
- Code should be readable; comments only for config files

## How to Apply

Always use a sub-agent to read rule files and explore this skill's content.

1. Identify the file type and select relevant sections (e.g., migration → §16, controller → §1, §3, §5, §6, §10)
2. Check sibling files for existing patterns — follow those first per Consistency First
3. Verify API syntax with `search-docs` for the installed Laravel version


Output:
Chunk ID: f3e04f
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1275
Output:
---
name: pest-testing
description: "Use this skill for Pest PHP testing in Laravel projects only. Trigger whenever any test is being written, edited, fixed, or refactored — including fixing tests that broke after a code change, adding assertions, converting PHPUnit to Pest, adding datasets, and TDD workflows. Always activate when the user asks how to write something in Pest, mentions test files or directories (tests/Feature, tests/Unit, tests/Browser), or needs browser testing, smoke testing multiple pages for JS errors, or architecture tests. Covers: test()/it()/expect() syntax, datasets, mocking, browser testing (visit/click/fill), smoke testing, arch(), Livewire component tests, RefreshDatabase, and all Pest 4 features. Do not use for factories, seeders, migrations, controllers, models, or non-test PHP code."
license: MIT
metadata:
  author: laravel
---

# Pest Testing 4

## Documentation

Use `search-docs` for detailed Pest 4 patterns and documentation.

## Basic Usage

### Creating Tests

All tests must be written using Pest. Use `php artisan make:test --pest {name}`.

### Test Organization

- Unit/Feature tests: `tests/Feature` and `tests/Unit` directories.
- Browser tests: `tests/Browser/` directory.
- Do NOT remove tests without approval - these are core application code.

### Basic Test Structure

Pest supports both `test()` and `it()` functions. Before writing new tests, check existing test files in the same directory to match the project's convention. Use `test()` if existing tests use `test()`, or `it()` if they use `it()`.

<!-- Basic Pest Test Example -->
```php
it('is true', function () {
    expect(true)->toBeTrue();
});
```

### Running Tests

- Run minimal tests with filter before finalizing: `php artisan test --compact --filter=testName`.
- Run all tests: `php artisan test --compact`.
- Run file: `php artisan test --compact tests/Feature/ExampleTest.php`.

## Assertions

Use specific assertions (`assertSuccessful()`, `assertNotFound()`) instead of `assertStatus()`:

<!-- Pest Response Assertion -->
```php
it('returns all', function () {
    $this->postJson('/api/docs', [])->assertSuccessful();
});
```

| Use | Instead of |
|-----|------------|
| `assertSuccessful()` | `assertStatus(200)` |
| `assertNotFound()` | `assertStatus(404)` |
| `assertForbidden()` | `assertStatus(403)` |

## Mocking

Import mock function before use: `use function Pest\Laravel\mock;`

## Datasets

Use datasets for repetitive tests (validation rules, etc.):

<!-- Pest Dataset Example -->
```php
it('has emails', function (string $email) {
    expect($email)->not->toBeEmpty();
})->with([
    'james' => 'james@laravel.com',
    'taylor' => 'taylor@laravel.com',
]);
```

## Pest 4 Features

| Feature | Purpose |
|---------|---------|
| Browser Testing | Full integration tests in real browsers |
| Smoke Testing | Validate multiple pages quickly |
| Visual Regression | Compare screenshots for visual changes |
| Test Sharding | Parallel CI runs |
| Architecture Testing | Enforce code conventions |

### Browser Test Example

Browser tests run in real browsers for full integration testing:

- Browser tests live in `tests/Browser/`.
- Use Laravel features like `Event::fake()`, `assertAuthenticated()`, and model factories.
- Use `RefreshDatabase` for clean state per test.
- Interact with page: click, type, scroll, select, submit, drag-and-drop, touch gestures.
- Test on multiple browsers (Chrome, Firefox, Safari) if requested.
- Test on different devices/viewports (iPhone 14 Pro, tablets) if requested.
- Switch color schemes (light/dark mode) when appropriate.
- Take screenshots or pause tests for debugging.

<!-- Pest Browser Test Example -->
```php
it('may reset the password', function () {
    Notification::fake();

    $this->actingAs(User::factory()->create());

    $page = visit('/sign-in');

    $page->assertSee('Sign In')
        ->assertNoJavaScriptErrors()
        ->click('Forgot Password?')
        ->fill('email', 'nuno@laravel.com')
        ->click('Send Reset Link')
        ->assertSee('We have emailed your password reset link!');

    Notification::assertSent(ResetPassword::class);
});
```

### Smoke Testing

Quickly validate multiple pages have no JavaScript errors:

<!-- Pest Smoke Testing Example -->
```php
$pages = visit(['/', '/about', '/contact']);

$pages->assertNoJavaScriptErrors()->assertNoConsoleLogs();
```

### Visual Regression Testing

Capture and compare screenshots to detect visual changes.

### Test Sharding

Split tests across parallel processes for faster CI runs.

### Architecture Testing

Pest 4 includes architecture testing (from Pest 3):

<!-- Architecture Test Example -->
```php
arch('controllers')
    ->expect('App\Http\Controllers')
    ->toExtendNothing()
    ->toHaveSuffix('Controller');
```

## Common Pitfalls

- Not importing `use function Pest\Laravel\mock;` before using mock
- Using `assertStatus(200)` instead of `assertSuccessful()`
- Forgetting datasets for repetitive validation tests
- Deleting tests without approval
- Forgetting `assertNoJavaScriptErrors()` in browser tests


The docs line up with the route gap: JSON tests and form requests are the right fit, and Laravel has a native temporary upload URL shape if the app is configured for it. I’m reading the existing product API, Livewire product form, and tests now so the API writes match local behavior instead of inventing a second catalog path.


sed -n '1,260p' app/Http/Controllers/Api/Admin/V1/ProductController.php


sed -n '1,260p' app/Http/Resources/Admin/V1/ProductResource.php


sed -n '1,280p' tests/Feature/Api/AdminCatalogApiTest.php


sed -n '1,320p' app/Livewire/Admin/Products/Form.php


sed -n '1,260p' app/Http/Controllers/Api/Admin/V1/ProductController.php

<?php

namespace App\Http\Controllers\Api\Admin\V1;

use App\Http\Controllers\Controller;
use App\Http\Resources\Admin\V1\ProductResource;
use App\Models\Product;
use App\Models\Store;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Validation\Rule;

class ProductController extends Controller
{
    public function index(Request $request, Store $store): AnonymousResourceCollection
    {
        $this->authorizeStore($request, $store);

        $validated = $request->validate([
            'status' => ['nullable', Rule::in(['draft', 'active', 'archived'])],
            'query' => ['nullable', 'string', 'max:255'],
            'collection_id' => ['nullable', 'integer'],
            'sort' => ['nullable', Rule::in(['title_asc', 'title_desc', 'created_at_asc', 'created_at_desc', 'updated_at_desc'])],
            'per_page' => ['nullable', 'integer', 'min:1', 'max:100'],
        ]);

        $query = Product::withoutGlobalScopes()
            ->with([
                'media',
                'variants.inventoryItem',
            ])
            ->withCount('variants')
            ->where('store_id', $store->getKey())
            ->when(data_get($validated, 'status'), fn (Builder $query, string $status) => $query->where('status', $status))
            ->when(data_get($validated, 'collection_id'), function (Builder $query, int $collectionId): void {
                $query->whereHas('collections', fn (Builder $query) => $query->withoutGlobalScopes()->whereKey($collectionId));
            })
            ->when(data_get($validated, 'query'), function (Builder $query, string $search): void {
                $like = '%'.$search.'%';

                $query->where(function (Builder $query) use ($like): void {
                    $query
                        ->where('title', 'like', $like)
                        ->orWhere('vendor', 'like', $like)
                        ->orWhereHas('variants', fn (Builder $query) => $query->withoutGlobalScopes()->where('sku', 'like', $like));
                });
            });

        $this->applySort($query, (string) data_get($validated, 'sort', 'updated_at_desc'));

        return ProductResource::collection($query->paginate((int) data_get($validated, 'per_page', 25)));
    }

    public function show(Request $request, Store $store, Product $product): ProductResource
    {
        $this->authorizeStore($request, $store);
        $this->abortUnlessProductBelongsToStore($product, $store);

        return ProductResource::make($this->loadProduct($product));
    }

    private function authorizeStore(Request $request, Store $store): void
    {
        if (! $request->attributes->has('admin_api_oauth_token')) {
            abort_unless($request->user()?->stores()->whereKey($store->getKey())->exists(), 403);
        }

        app()->instance('current_store', $store);
    }

    private function abortUnlessProductBelongsToStore(Product $product, Store $store): void
    {
        abort_unless((int) $product->store_id === $store->getKey(), 404);
    }

    private function loadProduct(Product $product): Product
    {
        return $product->load([
            'collections',
            'media',
            'options.values',
            'variants.inventoryItem',
            'variants.optionValues.option',
        ])->loadCount('variants');
    }

    private function applySort(Builder $query, string $sort): void
    {
        match ($sort) {
            'title_asc' => $query->orderBy('title')->orderBy('id'),
            'title_desc' => $query->orderByDesc('title')->orderByDesc('id'),
            'created_at_asc' => $query->orderBy('created_at')->orderBy('id'),
            'created_at_desc' => $query->orderByDesc('created_at')->orderByDesc('id'),
            default => $query->orderByDesc('updated_at')->orderByDesc('id'),
        };
    }
}