testing/rpc_envelope_cases.ts

The malformed-envelope and invalid-params cases for a JSON-RPC endpoint, as data.

Hono-free, so the in-process describe_rpc_attack_surface_tests and the cross-process describe_rpc_adversarial_cross_tests run one set: the envelope cases (create_rpc_envelope_cases) and the per-method params cases (generate_rpc_params_test_cases).

view source

Declarations
#

9 declarations

assert_rpc_envelope_refusal
#

testing/rpc_envelope_cases.ts view source

(status: number, body: unknown, c: RpcEnvelopeCase): void import {assert_rpc_envelope_refusal} from '@fuzdev/fuz_app/testing/rpc_envelope_cases.js';

Assert a response body is the refusal an envelope case owes: a well-formed JSON-RPC error envelope with the case's code, echoed id, and data.reason.

status

the response's HTTP status

type number

body

the parsed response body

type unknown

c

the case

returns

void

create_rpc_envelope_cases
#

testing/rpc_envelope_cases.ts view source

(endpoint: AppSurfaceRpcEndpoint, options?: RpcEnvelopeCaseOptions): RpcEnvelopeCase[] import {create_rpc_envelope_cases} from '@fuzdev/fuz_app/testing/rpc_envelope_cases.js';

Build the malformed-envelope cases for one RPC endpoint — the dispatcher's envelope parse, method lookup, and GET read restriction.

The POST cases and the GET read cases ride the endpoint's first read method (side_effects: false), the GET mutation cases its first mutation that needs an account. _testing_* and protocol methods are never chosen. A case whose method the endpoint lacks is left out.

Every read (side_effects: false) gets a GET with each of RPC_NON_OBJECT_PARAMS → invalid_params: the query string is no envelope, so the dispatcher's params validation is what refuses it.

Three cases pin an order against auth, sent with no credential: a GET of a mutating method is refused invalid_request (the read restriction runs after the method lookup and before authorization), a batch array is refused invalid_request with id: null (the envelope parse runs first, and batches are unsupported), and a GET with non-object params on a read requiring an identity is the 401 (params validation runs after authorization).

endpoint

the endpoint from the generated surface

options?

methods never chosen

optional default {}

returns

RpcEnvelopeCase[]

the cases, in a fixed order

generate_rpc_params_test_cases
#

testing/rpc_envelope_cases.ts view source

(input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>, options?: InputTestCaseOptions | undefined): InputTestCase[] import {generate_rpc_params_test_cases} from '@fuzdev/fuz_app/testing/rpc_envelope_cases.js';

Generate the schema-invalid params cases for one RPC method's input.

generate_input_test_cases minus its structural cases: a non-object body is a non-object params, which the JSON-RPC envelope refuses (invalid_request) before params validation runs, and the envelope cases cover it. Every case returned owes 400 invalid_params.

input

the method's input schema

type ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>

options?

a valid base params to seed the cases (InputTestCaseOptions.base), and the method name for error messages

optional

returns

InputTestCase[]

the cases, each a params object

throws

  • Error - if the base `params`, seeded or generated, fails the input schema

RPC_ENVELOPE_PROBE_ID
#

testing/rpc_envelope_cases.ts view source

"envelope_probe" import {RPC_ENVELOPE_PROBE_ID} from '@fuzdev/fuz_app/testing/rpc_envelope_cases.js';

The request id every envelope case sends where it sends one.

rpc_get_side_effects_reason
#

testing/rpc_envelope_cases.ts view source

(method: string): string import {rpc_get_side_effects_reason} from '@fuzdev/fuz_app/testing/rpc_envelope_cases.js';

The data.reason the dispatchers answer a GET of a side_effects method with.

method

type string

returns

string

RPC_NON_OBJECT_PARAMS
#

testing/rpc_envelope_cases.ts view source

readonly string[] import {RPC_NON_OBJECT_PARAMS} from '@fuzdev/fuz_app/testing/rpc_envelope_cases.js';

The JSON values that are neither an object nor null, one per JSON type — what a GET ?params= can carry that no input schema admits.

RpcEnvelopeCase
#

testing/rpc_envelope_cases.ts view source

RpcEnvelopeCase import type {RpcEnvelopeCase} from '@fuzdev/fuz_app/testing/rpc_envelope_cases.js';

One malformed-envelope request and the refusal it owes.

Data, so the in-process group here and the cross-process describe_rpc_adversarial_cross_tests run the same set.

name

type string

readonly

as

Who sends it: the principal method's auth admits, so the envelope check is what refuses, or no credential at all — the ordering cases, where an answer of 401 would mean auth ran before the envelope check.

type "authorized" | "anonymous"

readonly

method

The declared method whose auth picks the principal ('authorized').

type AppSurfaceRpcMethod

readonly

request

A raw POST body, or a GET query string starting with ?.

type { readonly verb: "POST"; readonly body: string; } | { readonly verb: "GET"; readonly query: string; }

readonly

expect

type { readonly status: number; readonly code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<"JsonrpcServerErrorCode">); readonly id: string | number | null; readonly reason: string | null; }

readonly

RpcEnvelopeCaseOptions
#

to_rpc_envelope_request
#

testing/rpc_envelope_cases.ts view source

(endpoint_path: string, c: RpcEnvelopeCase, headers?: Record<string, string>): { url: string; init: RequestInit; } import {to_rpc_envelope_request} from '@fuzdev/fuz_app/testing/rpc_envelope_cases.js';

Build the RequestInit + path suffix for an envelope case.

endpoint_path

the RPC endpoint path

type string

c

the case

headers?

auth headers to merge in

type Record<string, string>
optional default {}

returns

{ url: string; init: RequestInit; }

the URL (path + query for GET) and the init

Depends on
#

Imported by
#