Adding procedure kinds touched four layers: Python core, codegen, TypeScript types, and the adapter proxy. Each layer needed its own tests. Here is what we wrote and why.
Python: test_decorators.py
Tests that @rpc.query and @rpc.mutation set the correct kind on the procedure:
def test_query_sets_kind():
@rpc.query
def get_user(user_id: int) -> dict: ...
assert get_user.kind == ProcedureKind.QUERY
def test_mutation_sets_kind():
@rpc.mutation
def update_user(user_id: int, name: str) -> dict: ...
assert update_user.kind == ProcedureKind.MUTATION
def test_bare_rpc_defaults_to_query():
@rpc
def greet(name: str) -> str: ...
assert greet.kind == ProcedureKind.QUERYPython: test_introspection.py
Tests that the introspection schema includes the kind field:
def test_introspection_includes_kind():
schema = get_registry_schema()
assert schema["procedures"]["greet"]["kind"] == "query"
assert schema["procedures"]["update_user"]["kind"] == "mutation"Python: test_registry.py (Router.merge)
Tests that Router.merge() combines procedures correctly:
def test_merge_combines_procedures():
r1 = Router()
@r1.rpc
def a() -> str: ...
r2 = Router()
@r2.rpc
def b() -> str: ...
main = Router()
main.merge(r1)
main.merge(r2)
assert "a" in main.procedures
assert "b" in main.proceduresTypeScript: codegen tests
Tests that codegen outputs ProcedureKinds and procedureKinds correctly:
test("emits procedureKinds in generated output", () => {
const output = codegen(schema)
expect(output).toContain("ProcedureKinds")
expect(output).toContain('greet: "query"')
expect(output).toContain('update_user: "mutation"')
})TypeScript: adapter tests
Tests for createReactClient, createNextClient, etc. verify that:
- With
kinds, query procedures exposeuseQuery - With
kinds, mutation procedures exposeuseMutation - Without
kinds, both hooks exist - Provider is exported and renders children
Why this matters
Each layer is tested independently. If someone changes the Python decorator, the decorator tests catch it. If codegen output breaks, the codegen tests catch it. If the adapter's Proxy misresolves kinds, the adapter tests catch it. No single test tries to cover the whole stack.

pyRPC