A black-box test harness for a running eSignet (thunder-go) deployment. You point it at a deployment, tell it which identity plugin that deployment uses, and it produces one self-contained HTML report covering every test surface you selected.
It tests a deployment from the outside, over HTTP, exactly as a relying party would. It does not build, start or mock eSignet — the deployment under test must already be running and reachable.
New here? Quick start gets a first report out of a fresh clone in three commands. Configuration explains which file a given value belongs in — the one thing worth reading before your first real run.
Test surfaces · Quick start · Project structure · Prerequisites · Configuration · Running · The report · Building · Further documentation
A surface is one independent body of tests. You choose which run; the results merge into a single report.
| Surface | What it verifies | Needs |
|---|---|---|
conformance |
Formal OpenID Connect compliance. Drives the OpenID Conformance Suite, which acts as the OAuth client and grades the result; the harness plays browser-and-user, walking authorize → /flow/execute → callback. |
A running suite + a plan config (setup) |
api |
eSignet's REST endpoints directly, one endpoint at a time: client management (/client-mgmt/client create/get/update, the ACTIVE/INACTIVE status lifecycle, consent and protocol additionalConfig validation) and the flow entry points (/flow/meta, /flow/execute, /oauth2/authorize and /oauth2/introspect validation). Success and rejection cases for each. Written in Gherkin, run by godog. |
A reachable eSignet; Keycloak admin credentials or ADMIN_TOKEN for a target that does not enforce scope |
e2e |
A complete relying-party journey. The harness registers throwaway OIDC clients, then runs authorize (PKCE) → login → token (private_key_jwt) → userinfo and asserts the claims that come back. Covers each authentication factor (OTP, password, biometrics, knowledge-based), positive and negative, plus consent, captcha, token introspection (RFC 7662), whether deactivating a client actually stops it (at authorize, at token and at userinfo), and the per-client protocol switches — PKCE, PAR and DPoP — in combination. |
A reachable eSignet + Keycloak admin credentials or ADMIN_TOKEN + a test identity |
Three identity plugins are supported — mock, sunbird and mosip — selected per run
by the config file you pass. mock needs no identity of your own and is the right starting point.
mosip(MOSIP ID) has extra setup of its own — partner registration, dynamic OTP retrieval. See MOSIP ID plugin; nothing in this README is specific to it.
The shortest useful run is the e2e surface against the mock plugin — no conformance suite, no plan config, no test identity: mock's seeded identity ships in the config.
cd api-test
# 1. Create your credentials overlay and fill in two values:
# esignet.base_url — the deployment to test
# keycloak.client_secret — the admin client secret
cp data/config/config.local.example.json data/config/config.local.json
# 2. See what would run. Executes nothing, needs nothing running.
./run-all.sh -c data/config/config.mock.json -s e2e --check
# 3. Run it.
./run-all.sh -c data/config/config.mock.json -s e2eThe report lands in out/ as a single HTML file. Open it in a browser.
Then widen one step at a time, running --check first each time:
| To also run | Command | What it additionally needs |
|---|---|---|
the api surface |
-s e2e,api |
nothing more |
conformance too |
drop -s entirely |
a running suite and a plan config — see Conformance suite setup |
| a different plugin | -c data/config/config.mosip.json |
that plugin's identity and credentials in your overlay |
api-test/
run-all.sh run selected surfaces + merge into one report
Dockerfile harness image
docker-compose.yml conformance suite + harness, one command
cmd/
conformance/ conformance surface runner
e2e/ e2e surface runner
consolidate/ merge surface results into one HTML report
cfg/ inspect the resolved config (--check, -print-env)
api/ api surface: godog engine (separate Go module)
internal/ shared implementation
data/ everything the run reads that is not code
features/ Gherkin feature files (the api surface's tests)
client-mgmt/ one file per client-management endpoint
flow-execute/ one file per flow endpoint
config/ per-plugin config files (see below)
scenarios/ e2e scenario specs, one per plugin
conformance/ conformance suite profiles + plan-config template
out/ reports land here (git-ignored)
| Surface | Tests defined in | Selected by |
|---|---|---|
conformance |
every module the plan declares (profile: full) |
run.profile, run.modules, plans[] |
api |
data/features/**/*.feature |
api.tags (Gherkin tags) |
e2e |
data/scenarios/e2e-scenarios[-<plugin>].json |
e2e.auth_factors, e2e.include, e2e.exclude |
Feature files are organised by endpoint: create-client.feature holds every create case,
success and rejection alike, so one file describes one endpoint's full behaviour.
Only what your selected surfaces actually need:
| # | Needed for | What |
|---|---|---|
| 1 | every surface | A reachable eSignet. esignet.base_url, e.g. https://esignet-thunder1.esdev.mosip.net/v1/esignet. It must serve /.well-known/openid-configuration. |
| 2 | api (client-management), e2e |
Keycloak admin credentials — a client-credentials grant used to obtain a bearer token: keycloak.token_url, keycloak.client_id, keycloak.client_secret. The e2e surface registers its own throwaway client per run, so nothing needs pre-provisioning. |
| 3 | e2e (except mock) |
A test identity on the target deployment — an identifier plus whichever credentials its authentication factors need. |
| 4 | conformance |
A running conformance suite and a plan config. This is the one genuinely involved prerequisite — see Conformance suite setup. |
./run-all.sh -c <config> --check verifies all of the above without running anything and names
whatever is missing, so on a fresh clone its output is your setup checklist.
Configuration is layered, so you never edit a tracked file to add a secret. Lowest precedence first:
| Layer | In git? | Holds | Example |
|---|---|---|---|
data/config/config.<plugin>.json |
yes | What the tests do: which surfaces, which auth factor, which modules, tags and scenarios | data/config/config.mosip.json |
data/config/config.local.json |
no | What is yours: credentials, target URLs, test identity | your overlay |
| environment variables | — | Always wins. How containers inject secrets and per-deployment URLs | KEYCLOAK_CLIENT_SECRET=… |
Each layer overrides only the keys it names, at any depth.
| File | Purpose |
|---|---|
data/config/config.mock.json |
Ready-to-run config for the mock plugin |
data/config/config.mosip.json |
…for MOSIP ID (extra setup) |
data/config/config.sunbird.json |
…for Sunbird |
data/config/config.local.example.json |
Template for your overlay — copy it to config.local.json |
data/config/config.example.json |
Annotated reference for every available field |
Pick a plugin config with -c; the overlay is picked up automatically from beside it.
cp data/config/config.local.example.json data/config/config.local.jsonA config named explicitly must exist — -c data/config/config.msoip.json is an error, not a
silent fallback to another plugin.
individual_id is required for every plugin except mock, which falls back to its seeded
identity so a first run works out of the box.
Full field-by-field reference, the complete environment-variable table, and the TLS verification knobs: Configuration reference.
Five ways to run, all driven by the same config files.
The normal path. Runs the surfaces listed in the config's run.surfaces and merges them.
./run-all.sh -c data/config/config.mosip.jsonWorks in git-bash on Windows and any POSIX shell.
| Flag | Effect |
|---|---|
-c, --config PATH |
Which plugin config to run |
-s, --surfaces LIST |
Narrow to conformance,api,e2e for this run only |
--check |
Print the resolved plan and exit without running anything |
-h, --help |
The script's own flag and environment reference |
Exit codes: 0 all clear · 1 a test failed · 2 a configuration or setup error.
Useful while iterating on one area.
# conformance
go run ./cmd/conformance -config data/config/config.mosip.json
# api (godog is a separate module, so hand it the resolved config as environment)
eval "$(go run ./cmd/cfg -config data/config/config.mosip.json -print-env)"
( cd api && go test ./... -run TestFeatures -count=1 )
# e2e
go run ./cmd/e2e -config data/config/config.mosip.jsonRun directly like this, each surface writes to its own default out/; run-all.sh is what
threads a shared output directory through all of them.
To narrow the api surface to particular endpoints, set api.tags in the config (comma = OR):
| Tag | Covers |
|---|---|
@flow-execute |
/flow/meta, /flow/execute, /oauth2/introspect request and client-authentication validation |
@flow-authz-neg |
/oauth2/authorize request validation |
@client-mgmt |
client create / get / update, consent configuration, protocol additionalConfig validation (PKCE, PAR, DPoP) |
@client-mgmt-pms |
client registration via PMS (MOSIP ID only) |
Leaving api.tags empty runs whatever your configured credentials can actually drive; anything
skipped for missing configuration is still listed in the report rather than silently omitted.
go run ./cmd/consolidate \
-conformance out/conformance_mock_<timestamp>.json \
-api out/api-envelope.json \
-e2e out/e2e-envelope.json \
-plugin mock -out outAny of the three inputs may be omitted; whatever you pass is merged.
Brings up the OpenID Conformance Suite (MongoDB + server + nginx) alongside the harness and runs
everything. It starts the suite process for you, but it does not create the plan config the
conformance surface needs — that stays a one-time manual step per environment, because the file
holds a private JWKS (Conformance suite setup).
cp data/config/config.local.example.json data/config/config.local.json # required: it is mounted
cp .env.example .env
# In .env: set MOSIP_ESIGNET_BASE_URL, and SURFACES=api,e2e for a first run (see below).
docker compose run --rm --no-deps harness -c /app/config.json --check # dry run
docker compose up --build --abort-on-container-exit --exit-code-from harnessStart with
SURFACES=api,e2e. Every shipped plugin config selects all three surfaces, so an out-of-the-box compose run includesconformanceand stops atNOT RUNNABLE YETuntil a plan config exists inconformance-suite-private/.apiande2eneed onlyMOSIP_ESIGNET_BASE_URLplus the Keycloak credentials, so get those green first and widen once the conformance setup is done. For those two,docker compose run --rm --no-deps --build harness -c /app/config.jsonruns them and writes the same report to./outwithout starting the suite at all —upwould boot mongodb, server and nginx regardless, sinceharnessdeclares them as dependencies.
--no-depson the dry run.--checkresolves configuration and executes nothing, so it does not need the suite. Without the flag Compose pulls and starts mongodb, server and nginx just to print the plan.
On Git Bash (Windows), prefix these with
MSYS_NO_PATHCONV=1. MSYS rewrites the container-side/app/config.jsoninto a Windows path before Docker sees it, and the run fails withconfig file not found: C:/Program Files/Git/app/config.json.
The report appears in ./out on the host. docker compose down -v tears everything down.
Compose does not start eSignet itself — point MOSIP_ESIGNET_BASE_URL at a deployed environment, or
at http://host.docker.internal:8080 for one running on your own machine.
.env knob |
Effect |
|---|---|
CONFIG_FILE |
Which plugin config is mounted (default data/config/config.mock.json) |
MOSIP_ESIGNET_BASE_URL |
The deployment under test |
KEYCLOAK_TOKEN_URL, KEYCLOAK_CLIENT_SECRET |
Admin credentials |
ADMIN_TOKEN |
Skips the Keycloak round-trip above, for a target that does not enforce scope (no ISSUER_URL/JWKS_URL) — a locally started esignet-service, typically. Explicit opt-in, not a fallback |
INDIVIDUAL_ID, FLOW_CLIENT_ID |
Test identity and the pre-registered client for authorize validation |
SURFACES, TEST_PROFILE |
Narrow the run without editing a config |
ESIGNET_TLS_VERIFY, API_TLS_VERIFY |
Certificate verification for the deployment under test — on unless set false |
CONFORMANCE_BASE_URL, CONFORMANCE_TLS_VERIFY |
The suite; compose defaults these to the bundled one |
SUITE_IMAGE_TAG |
Conformance suite image version |
SUITE_WAIT_SECONDS |
How long to wait for the suite to boot (default 150s under compose) |
CONFIG_FILE and SUITE_IMAGE_TAG are read by Compose itself, to pick the bind-mount source and
the image tag. The rest are passed into the container, and only variables listed in the
harness service's environment: block are — so anything not above has to be added there before
.env can reach the harness.
An empty value in
.envis not "unset".KEYCLOAK_CLIENT_SECRET=reaches the container as an empty string and overrides your config file, blanking the secret. To fall back to the config files, leave the line commented out rather than setting it empty..env.exampleships every optional override commented for this reason.
docker run --rm \
-v /opt/esignet-harness/config.mosip.json:/app/config.json:ro \
-v /opt/esignet-harness/out:/app/out \
-e CONFIG=/app/config.json \
-e MOSIP_ESIGNET_BASE_URL=https://esignet-thunder1.esdev.mosip.net/v1/esignet \
-e KEYCLOAK_CLIENT_SECRET=*** \
apitest-esignet:<tag> -c /app/config.jsonFor the conformance surface you also need the plan config mounted — as a Kubernetes Secret or Docker secret, never baked into the image. See Providing the plan config securely.
What needs a rebuild and what does not. Anything mounted is environment data and changes
without rebuilding: the plugin, surfaces, auth factor, identity, scenario filters, tags, and the
plan config. Only image contents need a merge and an image build: Go code, run-all.sh, and the
data/ tree.
One self-contained HTML file per run, in out/. The filename records which surfaces ran, the
plugin, the timestamp and the result counts, so a directory of runs reads at a glance:
out/conformance_api_e2e_mock_20260818-195317_t-57_p-42_f-12_sk-3_ki-0.html
Inside: overall result tiles, then a section per surface, each case expandable to show
- a Validation table of every expected-vs-actual check performed, passes included — not just the final status code,
- the full eSignet request/response trace for that case,
- the conformance suite's own condition log, for conformance rows.
Credentials are redacted by default — OTPs, passwords, tokens, Authorization headers, cookies
and identity inputs are masked before anything is written, since reports are archived as CI
artifacts. The userinfo response claims stay readable: proving the right claims came back is what
the e2e surface exists to evidence. Treat a report as containing subject claims and handle it
accordingly.
To debug a failure a redacted trace cannot explain, set run.debug_show_secrets: true (or
DEBUG_SHOW_SECRETS=true) for a local run. The report then carries a red banner saying so. Never
leave it on for anything CI archives.
go build ./... && go vet ./... && go test ./... # main module
( cd api && go vet ./... && go test ./... ) # api surface moduleTwo Go modules: the main one is stdlib-only; api/ is separate because it pins godog and
gjson. .github/workflows/push-trigger.yml builds and publishes the apitest-esignet image — it
does not execute the tests, which run against deployed environments on their own schedule.
| Document | Covers |
|---|---|
| Configuration reference | Every config field, the full environment-variable table, TLS verification, selecting exactly what runs |
| Conformance suite setup | Standing up the suite, creating the plan config, and providing it securely in a deployment |
| MOSIP ID plugin | Partner registration via PMS, dynamic OTP retrieval, and the settings only this plugin needs |
| e2e scenario model | Writing and filtering e2e scenarios; consent and captcha coverage |
| Troubleshooting | Symptom-to-cause table for common failures |
Design and background: mosip/esignet#2120.