# Monthly LLM Governance — cURL Test Plan

Base URL: `https://projectlando.net/genie/`

All tests use the Otto chat endpoint (POST). Adjust `ai_settings` in the database before each test (no DB structure changes; only UPDATE/INSERT of existing columns: `monthly_max_spend`, `alert_threshold_percent`, `slack_webhook_url`, `last_alert_sent`).

---

## Test 1: Governance disabled — normal execution

**Setup:** Set `monthly_max_spend = 0` (or NULL) so governance is disabled.

```bash
# After setting monthly_max_spend = 0 in ai_settings (id = 1):
curl -s -X POST "https://projectlando.net/genie/fitment/otto_chat.php" \
  -H "Content-Type: application/json" \
  -d '{"session_id":"gov-test-1","message":"Hello, say OK"}' | jq .
```

**Expect:** Normal JSON response with `content` / `raw_text` from the LLM (no `status: spend_limit_reached`). HTTP 200.

---

## Test 2: Hard cap — spend_limit_reached

**Setup:** Set `monthly_max_spend` to a small value (e.g. `0.01`) so current month spend is already >= cap (or ensure `llm_usage_log` has enough cost this month to exceed 0.01).

```bash
# After setting monthly_max_spend = 0.01 and ensuring current month spend >= 0.01:
curl -s -w "\nHTTP_CODE:%{http_code}\n" -X POST "https://projectlando.net/genie/fitment/otto_chat.php" \
  -H "Content-Type: application/json" \
  -d '{"session_id":"gov-test-2","message":"Hello"}' | tail -20
```

**Expect:** Response body includes `"status":"spend_limit_reached"` (or endpoint returns 429 with blocked message). No provider call; no new row in `llm_usage_log` for this request.

---

## Test 3: Threshold alert (Slack)

**Setup:** Set `monthly_max_spend` to a value above current month spend (so cap is not hit), and set `alert_threshold_percent` to a low value (e.g. `10`) so that current month spend is >= 10% of cap. Ensure `last_alert_sent` is not today (e.g. NULL or a past date) so one alert is sent.

```bash
# Example: monthly_max_spend = 100, alert_threshold_percent = 10, current month spend e.g. 15.
# After setting last_alert_sent to yesterday or NULL:
curl -s -X POST "https://projectlando.net/genie/fitment/otto_chat.php" \
  -H "Content-Type: application/json" \
  -d '{"session_id":"gov-test-3","message":"Hi"}' | jq .
```

**Expect:** Normal LLM response (HTTP 200). Slack receives one alert: "⚠️ Otto LLM spend has reached 10% of monthly cap..." (if webhook configured). `ai_settings.last_alert_sent` updated to NOW(). Second request same day does not send another Slack alert.

---

## Optional: Chat endpoint (alternative)

```bash
# If your app uses the chat subfolder:
curl -s -X POST "https://projectlando.net/genie/chat/otto_chat.php" \
  -H "Content-Type: application/json" \
  -d '{"session_id":"gov-test-1","message":"Hello"}' | jq .
```

Governance runs inside `LLMService::chatWithTelemetry()`, so any endpoint that uses `LLMService` (fitment/otto_chat.php, chat/otto_chat.php, chat/chat_controller.php) is covered.
