# Artisan Migrate Specification

## Purpose

The Artisan Migrate capability exposes a single HTTP endpoint, `GET /artisan`, that applies all pending database migrations non-interactively. It is an operations convenience allowing an authenticated administrator to trigger schema migrations from a browser without shell access to the host.

## Requirements

### Requirement: Admin Authentication Gate

The system SHALL redirect any unauthenticated request to `auth/login` with an HTTP `302` response and SHALL NOT execute any migration logic for that request. (Implemented in `routes/web.php:20,23-29`, `config/admin.php:29`)

#### Scenario: Unauthenticated request is blocked

- **GIVEN** a caller has no authenticated admin session
- **WHEN** the caller sends `GET /artisan`
- **THEN** the system returns HTTP `302` redirecting to `auth/login` and no migration is applied

### Requirement: Non-Interactive Migration Execution

The system SHALL invoke all pending database migrations without requiring interactive confirmation when an authenticated admin sends `GET /artisan`. (Implemented in `routes/web.php:32-34`)

#### Scenario: Pending migrations are applied

- **GIVEN** an authenticated administrator and one or more unapplied migration files
- **WHEN** the administrator sends `GET /artisan`
- **THEN** every pending migration is applied, each is recorded in the `migrations` ledger table, and the request completes without blocking on a production confirmation prompt

### Requirement: Static Completion Response

The system SHALL return HTTP `200` with a `text/html` body containing exactly `Migrating completed<br>` after `Artisan::call` returns, regardless of the migrator's exit code. (Implemented in `routes/web.php:36`)

#### Scenario: Successful migration run returns completion message

- **GIVEN** an authenticated administrator
- **WHEN** `GET /artisan` is called and migrations complete without throwing
- **THEN** the response is HTTP `200` with body `Migrating completed<br>`

#### Scenario: Non-zero exit code does not alter the response

- **GIVEN** an authenticated administrator and the migrator returns a non-zero exit code without throwing an exception
- **WHEN** `GET /artisan` is called
- **THEN** the response is still HTTP `200` with body `Migrating completed<br>`; the exit code is not surfaced to the caller

### Requirement: Idempotent Behavior When No Migrations Are Pending

The system SHALL apply no migrations and SHALL still return HTTP `200` with body `Migrating completed<br>` when there are no pending migrations. (Implemented in `routes/web.php:32-36`)

#### Scenario: Repeated call with no pending migrations

- **GIVEN** an authenticated administrator and all migrations have already been applied
- **WHEN** `GET /artisan` is called
- **THEN** no new migration is applied, the `migrations` table is unchanged, and the response is HTTP `200` with body `Migrating completed<br>`

### Requirement: Exception Propagation on Migration Failure

The system SHALL propagate any exception thrown during migration execution as an HTTP `500` response; the static completion message SHALL NOT be returned in this case. (Implemented in `routes/web.php:32-36`)

#### Scenario: A broken migration causes a 500 response

- **GIVEN** an authenticated administrator and a pending migration that throws an exception during execution
- **WHEN** `GET /artisan` is called
- **THEN** the system returns HTTP `500` and the body `Migrating completed<br>` is not sent
