Testing
The /_emulator control surface makes time, failures, and authorization
deterministic — so behavior that would take days or aggressive load in real
Key Vault is testable in milliseconds. These routes are local plumbing, not
part of the Key Vault contract, and take no auth.
Controllable clock
Section titled “Controllable clock”Every timestamp the emulator stamps — secret nbf/exp windows, soft-delete
scheduledPurgeDate, certificate validity, and token exp/nbf checks —
flows through one clock you control.
# freeze, then jump past a 90-day purge deadline instantlycurl -sk -X POST https://localhost:8444/_emulator/clock -d '{"freeze": true}'curl -sk -X POST https://localhost:8444/_emulator/clock -d '{"advance": 7776000}' # +90 dayscurl -sk https://localhost:8444/_emulator/clock # {offset, frozen, now}Fields: advance (± seconds), offset (absolute seconds from real time),
freeze (bool). Uses:
- Soft-delete purge — delete a secret, advance past
scheduledPurgeDate, confirm it’s gone and the name is reusable. - Token expiry — mint a token, advance past its lifetime, confirm the vault
now
401s the same token (validation runs on this clock). - Certificate lifetime — issue with a short validity, advance, inspect.
Fault injection
Section titled “Fault injection”# make the next request 429 with Retry-After (test SDK retry/backoff)curl -sk -X POST https://localhost:8444/_emulator/faults -d '{"throttleNextRequests": 1}'
# make the next request a 500curl -sk -X POST https://localhost:8444/_emulator/faults -d '{"rejectNextRequests": 1}'Throttling injection matters because real Key Vault throttles aggressively, and an SDK’s retry/backoff behavior is otherwise untestable offline. Faults fire before auth, so they exercise the transport path regardless of token state.
Permission map
Section titled “Permission map”Restrict a principal to an operation set to test authorization-denied paths — see Authentication § Authorization.
curl -sk -X POST https://localhost:8444/_emulator/permissions \ -d '{"<principal-oid>": ["secrets/get"]}' # {} restores full accessIn your own tests
Section titled “In your own tests”- Any language — point the SDK at the emulator (localhost or DNS-pinned),
drive the clock/faults over
/_emulatorwith a plain HTTP call. - Go, in-process — the emulator’s own e2e starts entra-emulator in-process
and drives the real
azsecrets/azkeys/azcertificatesSDKs against the vault; seeinternal/server/*_test.gofor the fixture pattern.
What the project itself verifies
Section titled “What the project itself verifies”- Real-SDK e2e for all three object types (
azsecrets,azkeys,azcertificates) completing challenge-based auth against in-process entra-emulator. - The real-SDK witness matrix (
e2e/sdk/run.py): Microsoft’s Python (azure-keyvault-*), JavaScript (@azure/keyvault-*) and .NET (Azure.Security.KeyVault.*) SDKs, pinned, each completing challenge auth and exercising secrets (soft-delete → recover → purge), RSA crypto throughCryptographyClient(with a tampered-signature negative), and the self-signed certificate LRO — on Linux, macOS and Windows in CI. - The three-emulator chain (
e2e/chain/run.py). - A ≥90% coverage floor, enforced in CI.
- Every 🟢 claim in the parity map names its witness, enforced by
scripts/check_witnesses.py --strictin CI.
Not every witness is equal evidence
Section titled “Not every witness is equal evidence”Witnesses are named with their kind, ranked deliberately:
| kind | what it means |
|---|---|
ci:<job> | a CI job driving a packaged external client over a real network — the Python, JavaScript and .NET matrices above |
sdk:<Test> | a Go test in which Azure’s own client does the talking: azsecrets, azkeys, azcertificates over the vault’s wire, in-process |
go:<Test> | a Go test using our own client — real HTTP and real tokens, but our reading of the contract on both ends |
boundary:… | the claim is scoped by a documented limitation |
sdk: exists because the vocabulary was understating this repo. The first
bullet above — real-SDK e2e for all three object types — was real all along,
but had no kind that fitted, so those tests were filed as go:, whose own
definition reads “our own client rather than a third party’s”. Twenty
citations counted Azure’s clients as ours. Third-party evidence is 71 of the
128 witness citations, not 51.