> ## Documentation Index
> Fetch the complete documentation index at: https://evsim.synergyboat.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Run a conformance suite

> Drive the full compliance suite, a single module or scenario, or one manual OCPI command against a registered partner connection, from the command line.

Once a connection is `registered`, you can run against it.

## See what can run

```bash theme={null}
curl -fsS "https://evsim.synergyboat.com/api/v1/testing/suites" \
  -H "Authorization: Bearer $EVRT_KEY"
```

Returns the available suites. A run only executes suites for modules the selected OCPI
version **advertises**. If a version does not advertise a module, the run refuses it
rather than executing a suite that was never made to pass against those routes, so a
module absent from a version has no suite there, not a failing one. The advertised set
differs by version; 2.3.0 advertises the widest set today, including the Booking and
Payments (Direct Payment) suites.

## Run everything

```bash theme={null}
curl -fsS -X POST "https://evsim.synergyboat.com/api/v1/testing/runs?wait=45" \
  -H "Authorization: Bearer $EVRT_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"connectionId\": \"$CONNECTION_ID\"}"
```

Omit `module` and you get the full compliance suite for the connection's OCPI version.

`?wait=<seconds>` blocks for **up to** that long. Two outcomes, and a CI script has to
tell them apart:

| Status | Meaning                                                     | Body                                                                                                                |
| ------ | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `200`  | The run finished.                                           | A summary in `data.summary` (passed, failed and warned counts). The per-check report is at `GET /testing/runs/:id`. |
| `202`  | The run is **still going**. Your wait expired, not the run. | `data.reportId` and `data.status`. No `summary`.                                                                    |

A `202` is not a failure. Treat it as one and you will report a partner as broken because
your timeout was short, which is the same class of mistake as failing them for a declined
recommendation. Poll `GET /api/v1/testing/runs/$REPORT_ID?wait=<seconds>` until the run
reaches a terminal state. Without `?wait`, the start call returns immediately and you poll
the same way.

## Run one module or scenario

```bash theme={null}
curl -fsS -X POST "https://evsim.synergyboat.com/api/v1/testing/runs" \
  -H "Authorization: Bearer $EVRT_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"connectionId\": \"$CONNECTION_ID\", \"module\": \"scenario_ptp_e2e\"}"
```

Useful when you are iterating on one thing and do not want to wait for the rest. A
scenario walks an end-to-end story (a session that produces a CDR, a command with its
asynchronous callback) rather than checking endpoints in isolation, and it chains its own
output: each step feeds the next. That is why a scenario can fail on a late step with a
perfectly healthy endpoint; something upstream handed it nothing to work with. Read the
exchange on the first red step, not the last.

## Send one manual OCPI command

Before scripting a whole flow, it is often worth firing a single request and reading the
exact exchange:

```bash theme={null}
curl -fsS -X POST "https://evsim.synergyboat.com/api/v1/testing/command" \
  -H "Authorization: Bearer $EVRT_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"connectionId\": \"$CONNECTION_ID\", \"method\": \"GET\", \"endpoint\": \"/locations\"}"
```

| Field            | Notes                                                             |
| ---------------- | ----------------------------------------------------------------- |
| `connectionId`   | A registered connection.                                          |
| `method`         | `GET`, `POST`, `PUT`, `PATCH` or `DELETE`.                        |
| `endpoint`       | Module-relative OCPI path, for example `/commands/START_SESSION`. |
| `body`           | Request body for write methods. Optional.                         |
| `expectedStatus` | Expected HTTP status, or every status the spec permits. Optional. |

The response includes the request that was sent, the response status and body, and the
latency.

## Manage your runs

```bash theme={null}
# List your runs, optionally filtered
curl -fsS "https://evsim.synergyboat.com/api/v1/testing/runs?connectionId=$CONNECTION_ID" \
  -H "Authorization: Bearer $EVRT_KEY"

# Delete one run
curl -fsS -X DELETE "https://evsim.synergyboat.com/api/v1/testing/runs/$REPORT_ID" \
  -H "Authorization: Bearer $EVRT_KEY"
```

Runs are owner scoped: another owner's run id answers `404`.

## Gate your build on it

```bash theme={null}
#!/usr/bin/env bash
set -euo pipefail

BASE="https://evsim.synergyboat.com"
AUTH="Authorization: Bearer $EVRT_KEY"

# Capture the HTTP status as well as the body. Without it, an expired wait (202), a rate
# limit (429) or an auth error (401) all look identical to a failing run, and you would
# tell your partner they are broken when the truth is that your script never got an answer.
RESPONSE=$(curl -sS -w '\n%{http_code}' -X POST "$BASE/api/v1/testing/runs?wait=90" \
  -H "$AUTH" \
  -H "Content-Type: application/json" \
  -d "{\"connectionId\": \"$CONNECTION_ID\"}")

STATUS=$(printf '%s' "$RESPONSE" | tail -n1)
BODY=$(printf '%s' "$RESPONSE" | sed '$d')

case "$STATUS" in
  200) ;;
  202) echo "Run did not finish within the wait. Poll it; do not treat this as a failure."
       exit 2 ;;
  *)   echo "Could not run conformance (HTTP $STATUS): $BODY"
       exit 2 ;;
esac

FAILED=$(printf '%s' "$BODY" | jq '.data.summary.failed')

if [ "$FAILED" -gt 0 ]; then
  echo "OCPI conformance: $FAILED required checks failed"
  exit 1
fi

echo "OCPI conformance: compliant"
```

Exit `1` means "your partner broke a rule". Exit `2` means "we did not get an answer".
Those are different problems and a build log should not conflate them.

**Gate on `failed`, not on warnings.** A warning means a recommendation was declined, and
a declined recommendation breaks no rule. If you fail your build on warnings you will be
fixing code that already works, which is the exact mistake this tool refuses to make on
your behalf. See [Read your report](/docs/guide/read-your-report).

## Next

[Read your report](/docs/guide/read-your-report).
