registered, you can run against it. A production endpoint receives GET
requests only. A check with a write request or write setup is withheld before sending and is
Not tested with this reason: Not run: this endpoint is marked production, so ChargerDojo sends it no writes. Passing reads leave Partial coverage. Manual writes and all current journeys,
including scheduled journeys, are refused. Test-system runs retain their full behaviour.
The report records the environment at admission. summary.readOnly identifies a read-only
sweep; withheld checks stay in summary.unobservable and never increase summary.passed.
MCP and the GitHub Action print the same reason and Not tested counts.
See what can run
Operation reference:GET /testing/suites.
Run everything
Operation reference:POST /testing/runs.
module and you get every advertised protocol-conformance suite your plan covers for the
connection’s OCPI version. Scenarios are never part of a sweep, for any account: name one to
run it.
Direct Payment and Bookings are both on the Automate plan, so against a real endpoint their
suites run only for accounts that have it. Against the sandbox they are open to everyone, because it costs us nothing
to let you see what they do before you buy. When a run leaves them out, the report says which
modules were withheld, so a smaller total is never a mystery.
Test a payment terminal sets up a PTP endpoint for the Payments
suites and the journey that goes with them.
?wait=<seconds> blocks for up to that long, to a maximum of 55; larger values are
clamped to it, so a script asking for 120 gets 55 and should loop rather than wait longer. Five outcomes, and a CI script has to
tell them apart:
Read
data.status before you read the counters. A 200 alone does not mean there is a
verdict to read, and jq answers null for a summary that is not there, which shell
arithmetic turns into an error rather than a number. The script below checks the status first
for exactly this reason.
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.
An interrupted report has no verdict, even if it later recovers. The CI gate below exits 2 for
that interruption. Wait for the same sandbox report to finish recovery before reading its new
results. ChargerDojo never automatically restarts a run against your own endpoint, because that
would repeat requests there. When an interrupted report offers Restart this report, you can
choose to restart it from the first check on the same reportId; requests already sent to your
endpoint will be repeated.
To retry a start whose reply never arrived, send an Idempotency-Key header with it: up to 128
characters, one fresh key per start, such as your CI job’s run id. A retry with the same key and
the same body answers 202 with the run the first request started, once that run is queued, so a
lost reply never starts a second run. A retry ignores ?wait and does not wait for the run to
finish, and the run it returns may already be finished. If the first request is still starting,
the retry answers 503 with Retry-After and code: "START_PENDING": that run may still start,
so send it again with the same key, never a new one. If the first request gave up before its run
was queued, the 503 has no code and the same key starts one. The same key with a different
body answers 409. A key names its run for 24 hours; after that, the same key starts a new run.
Repeat a run
A run creates objects on your endpoint, and the ids it uses are derived from a seed, a string that names the run. Start another run on the same connection, over the same modules, with the same seed, while ChargerDojo runs the same build with the same set of checks, and it sends the requests the first one sent, byte for byte, apart from the differences described below. That is what turns “it failed yesterday” into something you can put in front of a partner. Operation reference:POST /testing/runs.
seed is optional and is 1 to 128 characters. Omit it and one is generated for you; either way
the seed comes back in data.seed on every answer the start call gives, so keep it with the
report. To repeat a run, send its reportId as replayOf and leave seed out: the new run takes
that run’s seed. Only a completed, graded run can be repeated this way, on the same connection and
with the same module (or none, for a whole sweep); a run that does not match is refused with
400. Run again with this seed on a report does the same.
A finished report carries a request fingerprint in requestStreamHash: a SHA-256 hash of
every request the run issued, in order. Locally cancelled exchanges are excluded because their outcome was not assessed. A request we retried after a 429 counts once. Two runs
that sent the same requests have the same fingerprint, and a replay’s report compares the two
check by check. Where a request differs, it names the check and says which side changed it: us,
your endpoint, or unclear. If ChargerDojo was updated between the two runs, the report says the
runs cannot be compared rather than judging them.
Some requests also carry values your endpoint chose. A run copies the id of a session your
endpoint started, or the URL it gave for a new object, into the requests that follow. If your
endpoint answers the replay with a different one, the requests that carry it differ too, and the
report puts that difference on your side. It can only tell while ChargerDojo is still on the build
that ran both runs: after an update, the report marks such a difference Unclear.
Apart from those, two runs of one seed on the same build may differ in these ways, and only these:
- We send a fresh request id every time, and OCPI 2.2 and later require it to be unique.
- We send a fresh correlation id every time, and OCPI 2.2 and later require it to be unique.
- A reservation ends 24 hours after the run that asked for it, so this moves with the run. A value that is not exactly that run start plus 24 hours is kept and reported.
- A booking period starts an hour after the run that asked for it and lasts an hour, so it moves with the run. A period that is not exactly that is kept and reported.
- Credentials, the Token a command carries, and any other field this installation is configured to hide are masked before the fingerprint sees them, so a difference confined to one of them is invisible to it.
201 Created for a new object, it reports your 200 as a
warning. A new seed creates new objects, and some of them have no DELETE in OCPI (CDRs, Hub client
info parties, and 2.1.1 Locations and Sessions), so they stay on your endpoint after the run.
The seed on a Charging Journey is a different field with the same name. It belongs to the
journey manifest, it shapes the scenario itself rather than object ids, and it is set when you
compile the plan. See Run a charging journey.
Run one module or scenario
Operation reference:POST /testing/runs.
Send one manual OCPI command
Before scripting a whole flow, it is often worth firing a single request and reading the exact exchange: Operation reference:POST /testing/command.
The response includes the request that was sent, the response status and body, and the
latency.
Manage your runs
Operation reference:GET /testing/runs, DELETE /testing/runs/{id}.
404.
Gate your build on it
Exit1 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, and an ungraded run
belongs in the second group: it says nothing about your partner. The GitHub Action and the
script below both exit this way.
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.
Store the key as a repository or organisation secret so the runner masks it. See
Create an API key.
On GitHub Actions
From a new key to a check on every pull request, in four steps:- Create a key. In ChargerDojo, open Settings, then Credentials, and choose Create key. Keys come with the Integrate plan and above. See Create an API key.
-
Store it in the repository. In GitHub, open the repository’s Settings, then
Secrets and variables, then Actions. Add a secret named
CHARGERDOJO_API_KEYholding the key, and a variable namedCHARGERDOJO_CONNECTION_IDholding the connection to test.GET /api/v1/connectionslists your connections with their ids. -
Add the workflow. Save this as
.github/workflows/ocpi-conformance.yml: -
Open a pull request. The conformance check runs the suite against the connection. When a
required check fails, the check turns red and its log names the count and the report:
OCPI conformance: 2 required checks failed. Report: https://chargerdojo.com/ocpi-testing-tool/runs?run=.... Open the report to see which rule broke and the exchange that broke it.
status, passed, failed, report_id and report_url as step outputs. Add module to run
one module or scenario, or seed to repeat a run. Its README lists every input and output, and
it is listed on the
GitHub Marketplace.
Scope a CI run to one module
Addmodule: locations to the Action inputs above to test only Locations on the selected connection:
1; a run that never produces a usable verdict exits 2. Not-tested checks and
withheld modules remain named in the Action log and report. A completed partial run with no
failures exits 0, which is a scoped pass for the graded checks. It proves nothing about other modules or untested
checks. Read the report’s coverage before using that job as a release gate.
On any other CI
The same exit codes from a shell script, for any runner withcurl and jq:
Operation reference: POST /testing/runs.