On this page
What to testWhere the edges are (things likely to be under-tested)
For Quality
What to test
Consent and scope enforcement (highest priority — this is the trust boundary)
- Every scope's actual API behavior matches its documented grant. In particular:
patients:read without .pii must return masked phone/no name; adding patients:read.pii must reveal them. Walk the full scope table in Scopes & consent and spot-check several tiers, not just the obvious ones.
- A missing-scope call returns
403 missing_scope with the correct details.missingScopes — for every route, not just a sample.
- A plugin requesting a scope or event outside the platform's known dictionary (
scopeDescriptions.ts) must have Install disabled, not just warned — confirm this is a hard block via findUnknownConsentKeys, and try to construct a listing (via the admin tool) that triggers it.
- Re-consent on scope escalation: per
docs/plugin-platform/compliance.md, a listing update that adds scopes should suspend existing installations until the clinic re-consents. This is a compliance-critical behavior worth an explicit end-to-end test — install a plugin, escalate its scopes via the admin tool, confirm the existing installation is affected.
optionalScopes/branch-scoped tokens exist at the API layer but have no UI. If QA can drive installPlugin/createPluginToken directly (GraphQL), verify partial-scope grants and branch-narrowed tokens actually behave as documented even without a UI path to them yet — this is exactly the kind of code that bit-rots if untested because "nobody uses it."
Idempotency and money paths
POST /payments: same Idempotency-Key submitted twice within 24h returns the identical stored response (Idempotent-Replay: true); a different idempotency key racing concurrently for the same logical payment should hit the id-derivation collision path, not double-record. This is the platform's actual double-charge defense — test it directly, don't assume the two layers (response replay + id derivation) are redundant in every race.
amount_exceeds_pending, invoice_already_paid, treasury_required — verify each typed error and its details payload, not just the HTTP status code.
- Payment links:
payment_provider_not_configured when no gateway is set up; confirm onlinepayment.succeeded's paymentId links correctly and that nothing double-records if a plugin (incorrectly) also calls payments.create() after a link succeeds.
Webhook delivery semantics
- Retry backoff and the exhaustion threshold: confirm a delivery genuinely retries per the exponential schedule and stops at
PLUGIN_WEBHOOK_MAX_CONSECUTIVE_FAILURES (default 20), auto-disabling the endpoint.
- Signature verification: a tampered payload or wrong secret must fail
constructEvent(); a timestamp older than 5 minutes must be rejected (replay protection) — test both the SDK helper and a hand-rolled verification against the documented HMAC scheme.
- SSRF guard: attempt to register a webhook endpoint pointing at a private/loopback/link-local address outside test mode and confirm it's rejected; confirm the sandbox relaxation genuinely only applies in test mode, not accidentally in production config.
measurement.recorded's measurementName key and einvoice.cleared's status: SUBMITTED quirk (see Webhooks) are exactly the kind of documented-but-easy-to-regress detail worth a dedicated payload-shape test each.
Settings schema
- Every validation rule in Settings schema (key pattern/uniqueness, section/field limits,
select/multiselect requiring options, secret disallowing defaults, visibleIf referencing a real field) — both accept and reject cases.
- Server-side and client-side (SDK and admin-editor) validation should agree on every case; since the admin editor's copy is explicitly a hand-synced mirror of the canonical implementation, a targeted test suite that runs the same fixture schemas against all three validators (server, SDK, admin) would catch drift early.
- The
"__SET__" secret sentinel round-trip: save a secret, confirm subsequent reads show the sentinel, confirm re-submitting the sentinel unchanged doesn't corrupt or clear the stored secret, confirm the plugin's own GET /installation call still gets the real value.
Admin registry workflow
- Status-transition edges: confirm Publish is genuinely blocked from
PUBLISHED and DEPRECATED states, and Suspend is genuinely blocked from anything but PUBLISHED, matching PUBLISHABLE_STATUSES/SUSPENDABLE_STATUSES.
- Suspending a listing should invalidate every token under it immediately, platform-wide — test this against an installation actively mid-session, not just a fresh one.
- Slug immutability: confirm the slug field is genuinely locked after creation both in the UI (disabled input) and at the API layer (a direct mutation attempt should also be rejected, not just hidden client-side).
Where the edges are (things likely to be under-tested)
- The admin tool's constants files are hand-synced mirrors, not generated from the server catalog (
pluginAdminConstants.js, validateSettingsSchema.js in clinic-web). If a new scope or event was added to the server catalog late in this PR's development, check whether it's actually present in the admin multi-select — this class of bug won't show up in normal testing, only in a deliberate cross-file diff.
quotation.viewed is registered as a real event a plugin can subscribe to, but the platform will never deliver it. Confirm subscribing doesn't error, and confirm no code path anywhere accidentally fires it prematurely.
- Known emit gaps are documented, not fixed:
docs/plugin-platform/compliance.md names editInvoiceWithPayments's paid-transitions as not yet firing invoice.paid. If QA has bandwidth, confirming the gap is exactly as scoped (and not wider) is more useful than re-discovering it as a "new" bug.
- The install-flow and detail-page consent screens are two separate render sites (
PluginDetail.tsx and InstallPluginModal.tsx) using the same underlying ConsentLists.tsx components — worth a snapshot/visual-regression test pinning both, since a change to one that doesn't update the other would silently create a mismatch between "what you were shown" and "what you're being asked to confirm."
- Sandbox-only relaxations (HTTP webhook URLs, SSRF allowance, lower rate limits,
PLUGIN_TEST_MODE_ENABLED) are exactly the kind of environment-gated logic worth a config-audit test to ensure the relaxation can never leak into a production deploy through a misconfigured env var.