pyRPC
← Back to Blog

Testing procedure kinds: what we covered

·8 min read

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.QUERY

Python: 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.procedures

TypeScript: 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 expose useQuery
  • With kinds, mutation procedures expose useMutation
  • 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.

Procedures docs · End-to-end flow