tigrbl_tests 0.3.10.dev1__tar.gz → 0.3.11.dev2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/PKG-INFO +1 -1
  2. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/pyproject.toml +1 -1
  3. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_hook_lifecycle.py +2 -0
  4. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_mountable_lens_uvicorn.py +1 -1
  5. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_v3_default_rpc_ops.py +115 -0
  6. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/request/test_request_asgi_scope_compat.py +2 -0
  7. tigrbl_tests-0.3.11.dev2/tests/request/test_request_authn_strip.py +33 -0
  8. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/request/test_request_transport_convenience_dot_notation.py +17 -0
  9. tigrbl_tests-0.3.11.dev2/tests/response/test_response_transport_convenience_dot_notation.py +50 -0
  10. tigrbl_tests-0.3.11.dev2/tests/unit/test_jsonrpc_openrpc.py +139 -0
  11. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_openapi_openrpc_schema_separation.py +21 -0
  12. tigrbl_tests-0.3.11.dev2/tests/unit/test_rest_request_mapping_access.py +99 -0
  13. tigrbl_tests-0.3.10.dev1/tests/response/test_response_transport_convenience_dot_notation.py +0 -27
  14. tigrbl_tests-0.3.10.dev1/tests/unit/test_jsonrpc_openrpc.py +0 -60
  15. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/LICENSE +0 -0
  16. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/README.md +0 -0
  17. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/__init__.py +0 -0
  18. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/conftest.py +0 -0
  19. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/hooks/test_hook_ctx_phase_validation.py +0 -0
  20. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/__init__.py +0 -0
  21. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_acronym_route_name.py +0 -0
  22. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_allow_anon.py +0 -0
  23. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_apikey_generation.py +0 -0
  24. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_authn_provider_integration.py +0 -0
  25. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_bindings_integration.py +0 -0
  26. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_bindings_modules.py +0 -0
  27. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_bulk_docs_client.py +0 -0
  28. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_core_access.py +0 -0
  29. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_engine_install_uvicorn.py +0 -0
  30. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_engine_resolver_uvicorn.py +0 -0
  31. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_error_mappings.py +0 -0
  32. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_field_spec_effects.py +0 -0
  33. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_header_io_uvicorn.py +0 -0
  34. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_healthz_methodz_hookz.py +0 -0
  35. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_hook_ctx_v3_i9n.py +0 -0
  36. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_iospec_attributes.py +0 -0
  37. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_iospec_integration.py +0 -0
  38. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_jsonrpc_batch_behavior.py +0 -0
  39. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_key_digest_uvicorn.py +0 -0
  40. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_list_filters_optional.py +0 -0
  41. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_mixins.py +0 -0
  42. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_mountable_favicon_uvicorn.py +0 -0
  43. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_mountable_openapi_uvicorn.py +0 -0
  44. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_mountable_openrpc_uvicorn.py +0 -0
  45. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_mountable_swagger_uvicorn.py +0 -0
  46. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_nested_path_schema_and_rpc.py +0 -0
  47. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_nested_routing_depth.py +0 -0
  48. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_op_ctx_alias_examples.py +0 -0
  49. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_op_ctx_behavior.py +0 -0
  50. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_op_ctx_core_crud_order.py +0 -0
  51. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_openapi_clear_response_schema.py +0 -0
  52. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_openapi_schema_examples_presence.py +0 -0
  53. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_opspec_effects_i9n_test.py +0 -0
  54. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_owner_tenant_policy.py +0 -0
  55. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_request_extras.py +0 -0
  56. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_request_extras_provider.py +0 -0
  57. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_response_extras_provider.py +0 -0
  58. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_rest_fallback_serialization.py +0 -0
  59. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_rest_row_serialization.py +0 -0
  60. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_rest_rpc_parity_v3.py +0 -0
  61. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_row_result_serialization.py +0 -0
  62. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_schema.py +0 -0
  63. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_schema_ctx_attributes_integration.py +0 -0
  64. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_schema_ctx_op_ctx_integration.py +0 -0
  65. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_schema_ctx_spec_integration.py +0 -0
  66. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_sqlite_attachments.py +0 -0
  67. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_storage_spec_integration.py +0 -0
  68. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_symmetry_parity.py +0 -0
  69. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_tigrbl_api_app_usage_uvicorn.py +0 -0
  70. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_tigrbl_api_usage_uvicorn.py +0 -0
  71. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_tigrbl_api_uvicorn.py +0 -0
  72. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_tigrbl_app_include_api_uvicorn.py +0 -0
  73. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_tigrbl_app_multi_api_uvicorn.py +0 -0
  74. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_tigrbl_app_usage_uvicorn.py +0 -0
  75. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_tigrbl_app_uvicorn.py +0 -0
  76. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_v3_bulk_rest_endpoints.py +0 -0
  77. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_v3_default_rest_ops.py +0 -0
  78. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_v3_opspec_attributes.py +0 -0
  79. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/test_verb_alias_policy.py +0 -0
  80. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/i9n/uvicorn_utils.py +0 -0
  81. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/mount/__init__.py +0 -0
  82. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/mount/test_mount_favico.py +0 -0
  83. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/parity/__init__.py +0 -0
  84. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/parity/test_stdapi_openapi_docs.py +0 -0
  85. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/parity/test_stdapi_primitives.py +0 -0
  86. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/parity/test_stdapi_routing.py +0 -0
  87. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/perf/__init__.py +0 -0
  88. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/perf/test_collect_caching.py +0 -0
  89. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/perf/test_hookz_performance.py +0 -0
  90. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/perf/test_methodz_performance.py +0 -0
  91. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/request/test_request_dot_notation.py +0 -0
  92. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/request/test_request_response_conveniences.py +0 -0
  93. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/requests/test_request_json_modes.py +0 -0
  94. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/response/test_response_dot_notation.py +0 -0
  95. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/security/test_dot_notation_schemes.py +0 -0
  96. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/security/test_schemes.py +0 -0
  97. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/__init__.py +0 -0
  98. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/decorators/test_alias_ctx_bindings.py +0 -0
  99. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/decorators/test_engine_ctx_bindings.py +0 -0
  100. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/decorators/test_hook_ctx_bindings.py +0 -0
  101. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/decorators/test_op_alias_bindings.py +0 -0
  102. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/decorators/test_op_ctx_bindings.py +0 -0
  103. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/decorators/test_response_ctx_bindings.py +0 -0
  104. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/decorators/test_schema_ctx_bindings.py +0 -0
  105. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/response_utils.py +0 -0
  106. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/runtime/atoms/test_emit_paired_post.py +0 -0
  107. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/runtime/atoms/test_emit_paired_pre.py +0 -0
  108. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/runtime/atoms/test_emit_readtime_alias.py +0 -0
  109. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/runtime/atoms/test_out_masking.py +0 -0
  110. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/runtime/atoms/test_refresh_demand.py +0 -0
  111. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/runtime/atoms/test_resolve_assemble.py +0 -0
  112. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/runtime/atoms/test_resolve_paired_gen.py +0 -0
  113. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/runtime/atoms/test_schema_collect_in.py +0 -0
  114. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/runtime/atoms/test_schema_collect_out.py +0 -0
  115. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/runtime/atoms/test_storage_to_stored.py +0 -0
  116. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/runtime/atoms/test_wire_build_in.py +0 -0
  117. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/runtime/atoms/test_wire_build_out.py +0 -0
  118. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/runtime/atoms/test_wire_dump.py +0 -0
  119. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/runtime/atoms/test_wire_validate_in.py +0 -0
  120. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/runtime/test_events_phases.py +0 -0
  121. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_acol_vcol_knobs.py +0 -0
  122. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_alias_ctx_op_alias_attributes.py +0 -0
  123. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_alias_ctx_op_attributes.py +0 -0
  124. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_api_level_set_auth.py +0 -0
  125. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_app_model_defaults.py +0 -0
  126. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_app_reexport.py +0 -0
  127. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_base_columnspec_inheritance.py +0 -0
  128. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_base_facade_initialize.py +0 -0
  129. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_build_list_params_spec_model.py +0 -0
  130. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_bulk_body_annotation.py +0 -0
  131. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_bulk_response_schema.py +0 -0
  132. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_colspec_map_isolation.py +0 -0
  133. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_column_collect_mixins.py +0 -0
  134. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_column_rest_rpc_results.py +0 -0
  135. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_column_table_orm_binding.py +0 -0
  136. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_config_dataclass_none.py +0 -0
  137. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_core_crud_bulk_ops.py +0 -0
  138. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_core_crud_default_ops.py +0 -0
  139. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_core_crud_methods.py +0 -0
  140. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_core_wrap_memoization.py +0 -0
  141. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_db_dependency.py +0 -0
  142. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_decorator_and_collect.py +0 -0
  143. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_default_tags.py +0 -0
  144. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_engine_install_api.py +0 -0
  145. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_engine_install_app.py +0 -0
  146. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_engine_install_op.py +0 -0
  147. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_engine_install_table.py +0 -0
  148. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_engine_spec_and_shortcuts.py +0 -0
  149. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_engine_usage_levels.py +0 -0
  150. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_field_spec_attrs.py +0 -0
  151. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_file_response.py +0 -0
  152. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_get_schema.py +0 -0
  153. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_handler_step_qualname.py +0 -0
  154. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_hook_ctx_attributes.py +0 -0
  155. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_hook_ctx_binding.py +0 -0
  156. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_hookz_empty_phase.py +0 -0
  157. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_hybrid_session_run_sync.py +0 -0
  158. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_in_tx.py +0 -0
  159. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_include_model_columns_namespace.py +0 -0
  160. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_include_models_base_prefix.py +0 -0
  161. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_initialize_async_task.py +0 -0
  162. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_initialize_cross_ddl.py +0 -0
  163. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_initialize_mixed_engines.py +0 -0
  164. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_initialize_task_schedule.py +0 -0
  165. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_io_spec_attributes.py +0 -0
  166. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_iospec_attributes.py +0 -0
  167. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_iospec_effects.py +0 -0
  168. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_jsonrpc_id_example.py +0 -0
  169. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_jsonrpc_router_default_tag.py +0 -0
  170. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_kernel_invoke_ctx.py +0 -0
  171. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_kernel_opview_on_demand.py +0 -0
  172. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_kernel_plan_labels.py +0 -0
  173. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_kernelz_endpoint.py +0 -0
  174. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_make_column_shortcuts.py +0 -0
  175. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_middleware_http_and_cors.py +0 -0
  176. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_mixins_sqlalchemy.py +0 -0
  177. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_op_alias.py +0 -0
  178. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_op_class_engine_binding.py +0 -0
  179. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_op_ctx_arity_paths.py +0 -0
  180. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_op_ctx_attributes.py +0 -0
  181. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_op_ctx_core_crud_integration.py +0 -0
  182. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_op_ctx_dynamic_attach.py +0 -0
  183. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_op_ctx_persist_options.py +0 -0
  184. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_opspec_effects.py +0 -0
  185. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_postgres_engine_errors.py +0 -0
  186. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_postgres_env_vars.py +0 -0
  187. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_relationship_alias_cols.py +0 -0
  188. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_request_body_schema.py +0 -0
  189. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_request_response_examples.py +0 -0
  190. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_resolver_interning_warmup.py +0 -0
  191. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_resolver_precedence.py +0 -0
  192. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_response_alias_table_rpc.py +0 -0
  193. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_response_ctx_precedence.py +0 -0
  194. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_response_diagnostics_kernelz.py +0 -0
  195. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_response_html_jinja_behavior.py +0 -0
  196. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_response_parity.py +0 -0
  197. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_response_rest.py +0 -0
  198. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_response_rpc.py +0 -0
  199. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_response_template.py +0 -0
  200. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_response_uuid.py +0 -0
  201. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_rest_all_default_op_verbs.py +0 -0
  202. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_rest_bulk_delete_suppresses_clear.py +0 -0
  203. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_rest_no_schema_jsonable.py +0 -0
  204. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_rest_operation_id_uniqueness.py +0 -0
  205. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_rest_rpc_parity_default_ops.py +0 -0
  206. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_rest_rpc_prefixes.py +0 -0
  207. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_rest_rpc_symmetry.py +0 -0
  208. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_router_compatibility_shims.py +0 -0
  209. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_rpc_all_default_op_verbs.py +0 -0
  210. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_rpc_default_ops.py +0 -0
  211. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_schema_ctx_attributes.py +0 -0
  212. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_schema_ctx_plain_class.py +0 -0
  213. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_schema_spec_presence.py +0 -0
  214. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_schemas_binding.py +0 -0
  215. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_security_per_route.py +0 -0
  216. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_should_wire_canonical.py +0 -0
  217. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_spec_api.py +0 -0
  218. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_spec_app.py +0 -0
  219. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_spec_column.py +0 -0
  220. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_spec_engine.py +0 -0
  221. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_spec_field.py +0 -0
  222. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_spec_hook.py +0 -0
  223. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_spec_io.py +0 -0
  224. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_spec_op.py +0 -0
  225. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_spec_storage.py +0 -0
  226. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_spec_table.py +0 -0
  227. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_sqlite_attachments.py +0 -0
  228. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_sqlite_uuid_type.py +0 -0
  229. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_stdapi_request_injection.py +0 -0
  230. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_stdapi_transport_asgi_wsgi.py +0 -0
  231. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_storage_spec_attributes.py +0 -0
  232. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_sys_handler_crud.py +0 -0
  233. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_sys_run_rollback.py +0 -0
  234. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_sys_tx_async_begin.py +0 -0
  235. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_sys_tx_begin.py +0 -0
  236. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_sys_tx_commit.py +0 -0
  237. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_system_docs_builders.py +0 -0
  238. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_table_base_exports.py +0 -0
  239. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_table_collect_spec.py +0 -0
  240. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_table_columns_namespace.py +0 -0
  241. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_table_namespace_init.py +0 -0
  242. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_table_namespace_isolation.py +0 -0
  243. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_tigrbl_api_app_configuration.py +0 -0
  244. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_tigrbl_api_app_instantiation.py +0 -0
  245. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_tigrbl_api_app_subclass_definition.py +0 -0
  246. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_tigrbl_api_configuration.py +0 -0
  247. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_tigrbl_api_instantiation.py +0 -0
  248. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_tigrbl_api_subclass_definition.py +0 -0
  249. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_tigrbl_app_configuration.py +0 -0
  250. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_tigrbl_app_event_handlers.py +0 -0
  251. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_tigrbl_app_instantiation.py +0 -0
  252. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_tigrbl_app_state.py +0 -0
  253. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_tigrbl_app_subclass_definition.py +0 -0
  254. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_v3_favicon_endpoint.py +0 -0
  255. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_v3_healthz_endpoint.py +0 -0
  256. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_v3_op_alias.py +0 -0
  257. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_v3_op_ctx_attributes.py +0 -0
  258. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_v3_schemas_and_decorators.py +0 -0
  259. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_v3_storage_spec_attributes.py +0 -0
  260. {tigrbl_tests-0.3.10.dev1 → tigrbl_tests-0.3.11.dev2}/tests/unit/test_verbosity.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tigrbl_tests
3
- Version: 0.3.10.dev1
3
+ Version: 0.3.11.dev2
4
4
  Summary: Test suite and fixtures for the Tigrbl framework.
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tigrbl_tests"
3
- version = "0.3.10.dev1"
3
+ version = "0.3.11.dev2"
4
4
  description = "Test suite and fixtures for the Tigrbl framework."
5
5
  license = "Apache-2.0"
6
6
  readme = "README.md"
@@ -98,6 +98,7 @@ async def test_hook_phases_execution_order(db_mode):
98
98
  res = await client.post(
99
99
  "/rpc",
100
100
  json={
101
+ "id": "hook-order-check",
101
102
  "method": "Item.create",
102
103
  "params": {"tenant_id": tid, "name": "test-item"},
103
104
  },
@@ -163,6 +164,7 @@ async def test_hook_parity_crud_vs_rpc(db_mode):
163
164
  await client.post(
164
165
  "/rpc",
165
166
  json={
167
+ "id": "hook-parity-check",
166
168
  "method": "Item.create",
167
169
  "params": {"tenant_id": tid, "name": "rpc-item"},
168
170
  },
@@ -26,7 +26,7 @@ async def test_lens_mountable_on_tigrbl_app_uvicorn():
26
26
  in response.text
27
27
  )
28
28
  assert (
29
- 'href="https://esm.sh/@tigrbljs/tigrbl-lens@latest/dist/tigrbl-lens.css"'
29
+ 'href="https://esm.sh/@tigrbljs/tigrbl-lens@latest/dist/tigrbl-lens.css?css"'
30
30
  in response.text
31
31
  )
32
32
  assert 'import { createRoot } from "react-dom/client";' in response.text
@@ -140,6 +140,96 @@ async def test_bulk_methods_absent(client_and_model):
140
140
  assert not hasattr(Gadget.rpc, name)
141
141
 
142
142
 
143
+ @pytest_asyncio.fixture()
144
+ async def wrapper_field_client_and_model():
145
+ Base3.metadata.clear()
146
+ Base3.registry.dispose()
147
+
148
+ class WrapperNamed(Base3):
149
+ __tablename__ = "wrapper_named"
150
+ __allow_unmapped__ = True
151
+
152
+ id: Mapped[int] = acol(
153
+ storage=S(type_=Integer, primary_key=True, autoincrement=True)
154
+ )
155
+ data: Mapped[str] = acol(
156
+ storage=S(type_=String, nullable=False),
157
+ field=F(required_in=("create",)),
158
+ io=IO(in_verbs=("create", "update"), out_verbs=("read", "list")),
159
+ )
160
+
161
+ __tigrbl_cols__ = {"id": id, "data": data}
162
+
163
+ app = Tigrblv3()
164
+ api = Tigrblv3(engine=mem())
165
+ api.include_model(WrapperNamed, prefix="")
166
+ api.mount_jsonrpc(prefix="/rpc")
167
+ await api.initialize()
168
+ app.include_router(api.router)
169
+ transport = ASGITransport(app=app)
170
+ client = AsyncClient(transport=transport, base_url="http://test")
171
+ try:
172
+ yield client, WrapperNamed
173
+ finally:
174
+ await client.aclose()
175
+
176
+
177
+ @pytest.mark.i9n
178
+ @pytest.mark.asyncio
179
+ async def test_rpc_create_allows_schema_defined_wrapper_like_fields(
180
+ wrapper_field_client_and_model,
181
+ ):
182
+ client, _ = wrapper_field_client_and_model
183
+ payload = {
184
+ "jsonrpc": "2.0",
185
+ "method": "WrapperNamed.create",
186
+ "params": {"data": "legit"},
187
+ "id": 99,
188
+ }
189
+
190
+ response = await client.post("/rpc", json=payload)
191
+
192
+ assert response.status_code == 200
193
+ body = response.json()
194
+ assert "error" not in body
195
+ assert body["result"]["data"] == "legit"
196
+
197
+
198
+ @pytest.mark.i9n
199
+ @pytest.mark.asyncio
200
+ @pytest.mark.parametrize("wrapper_key", ["data", "payload", "body", "item"])
201
+ async def test_rpc_create_rejects_wrapper_object(wrapper_key, client_and_model):
202
+ client, _ = client_and_model
203
+ payload = {
204
+ "jsonrpc": "2.0",
205
+ "method": "Gadget.create",
206
+ "params": {wrapper_key: {"name": "Wrapped", "age": 7}},
207
+ "id": 11,
208
+ }
209
+
210
+ response = await client.post("/rpc", json=payload)
211
+
212
+ assert response.status_code == 200
213
+ error = response.json()["error"]
214
+ assert error["code"] == -32602
215
+ assert error["message"] == "Invalid params"
216
+ assert error["data"]["disallowed_keys"] == [wrapper_key]
217
+
218
+
219
+ @pytest.mark.i9n
220
+ @pytest.mark.asyncio
221
+ @pytest.mark.parametrize("wrapper_key", ["data", "payload", "body", "item"])
222
+ async def test_rest_create_rejects_wrapper_object(wrapper_key, client_and_model):
223
+ client, _ = client_and_model
224
+
225
+ response = await client.post(
226
+ "/gadget",
227
+ json={wrapper_key: {"name": "Wrapped", "age": 7}},
228
+ )
229
+
230
+ assert response.status_code == 422
231
+
232
+
143
233
  @pytest_asyncio.fixture()
144
234
  async def bulk_client_and_model():
145
235
  Base3.metadata.clear()
@@ -184,6 +274,31 @@ async def bulk_client_and_model():
184
274
  await client.aclose()
185
275
 
186
276
 
277
+ @pytest.mark.i9n
278
+ @pytest.mark.asyncio
279
+ @pytest.mark.parametrize("wrapper_key", ["data", "payload", "body", "item"])
280
+ async def test_rpc_bulk_create_rejects_wrapper_object_items(
281
+ wrapper_key,
282
+ bulk_client_and_model,
283
+ ):
284
+ client, _ = bulk_client_and_model
285
+
286
+ payload = {
287
+ "jsonrpc": "2.0",
288
+ "method": "Gadget.bulk_create",
289
+ "params": [{wrapper_key: {"name": "Wrapped", "age": 7}}],
290
+ "id": 20,
291
+ }
292
+
293
+ response = await client.post("/rpc", json=payload)
294
+
295
+ assert response.status_code == 200
296
+ error = response.json()["error"]
297
+ assert error["code"] == -32602
298
+ assert error["message"] == "Invalid params"
299
+ assert error["data"]["disallowed_keys"] == [wrapper_key]
300
+
301
+
187
302
  @pytest.mark.i9n
188
303
  @pytest.mark.asyncio
189
304
  async def test_rpc_bulk_ops(bulk_client_and_model):
@@ -26,6 +26,8 @@ def test_request_accepts_scope_with_receive_kwarg_for_compatibility() -> None:
26
26
  assert request.query == {"a": ["1", "2"], "b": ["3"]}
27
27
  assert request.path_params == {"tenant_id": "abc"}
28
28
  assert request.headers.authorization == "Bearer test-token"
29
+ assert request.headers.get("Authorization") == "Bearer test-token"
30
+ assert request.headers.get("authorization") == "Bearer test-token"
29
31
  assert request.bearer_token == "test-token"
30
32
  assert request.client.ip == "198.51.100.77"
31
33
 
@@ -0,0 +1,33 @@
1
+ from __future__ import annotations
2
+
3
+ from tigrbl.requests import Request
4
+
5
+
6
+ def _make_request(*, headers: dict[str, str]) -> Request:
7
+ return Request(
8
+ method="GET",
9
+ path="/authn",
10
+ headers=headers,
11
+ query={},
12
+ path_params={},
13
+ body=b"",
14
+ )
15
+
16
+
17
+ def test_request_admin_key_is_stripped() -> None:
18
+ request = _make_request(headers={"X-Admin-Key": " admin-secret "})
19
+
20
+ assert request.admin_key == "admin-secret"
21
+
22
+
23
+ def test_request_bearer_session_token_is_stripped() -> None:
24
+ request = _make_request(headers={"Authorization": "Bearer public-token "})
25
+
26
+ assert request.bearer_token == "public-token"
27
+ assert request.session_token == "public-token"
28
+
29
+
30
+ def test_request_cookie_session_token_is_stripped() -> None:
31
+ request = _make_request(headers={"Cookie": "sid= session-cookie-token "})
32
+
33
+ assert request.session_token == "session-cookie-token"
@@ -32,6 +32,23 @@ def test_request_headers_and_cookies_support_nested_dot_notation_access() -> Non
32
32
  assert request.headers.cookie.tenant == "acme"
33
33
 
34
34
 
35
+ def test_request_cookie_header_supports_many_cookies_with_terse_getters() -> None:
36
+ request = _make_request(
37
+ headers={
38
+ "Cookie": "sid=session-token; tenant=acme; theme=dark; mode=compact",
39
+ }
40
+ )
41
+
42
+ assert request.headers.get("cookie", "")
43
+ assert request.headers.cookie.sid == "session-token"
44
+ assert request.headers.cookie.tenant == "acme"
45
+ assert request.headers.cookie.theme == "dark"
46
+ assert request.cookies.get("sid") == "session-token"
47
+ assert request.cookies.get("tenant") == "acme"
48
+ assert request.cookies.get("theme") == "dark"
49
+ assert request.cookies.get("mode") == "compact"
50
+
51
+
35
52
  @pytest.mark.asyncio()
36
53
  async def test_request_exposes_json_and_dot_notation_bearer_conveniences() -> None:
37
54
  request = _make_request(
@@ -0,0 +1,50 @@
1
+ from __future__ import annotations
2
+
3
+ from tigrbl.responses import Response
4
+
5
+
6
+ def test_response_supports_instance_json_method_for_body_payload() -> None:
7
+ response = Response(
8
+ status_code=200,
9
+ headers=[("content-type", "application/json; charset=utf-8")],
10
+ body=b'{"ok":true,"count":1}',
11
+ )
12
+
13
+ assert response.json() == {"ok": True, "count": 1}
14
+
15
+
16
+ def test_response_headers_and_cookies_support_dot_notation() -> None:
17
+ response = Response(
18
+ status_code=200,
19
+ headers=[
20
+ ("x-request-id", "req-123"),
21
+ ("set-cookie", "sid=session-token; Path=/"),
22
+ ],
23
+ body=b"",
24
+ )
25
+
26
+ assert response.headers_map.x_request_id == "req-123"
27
+ assert response.headers.get("x-request-id") == "req-123"
28
+ assert "sid" in response.headers.get("set-cookie", "")
29
+ assert response.cookies.sid == "session-token"
30
+
31
+
32
+ def test_response_set_cookie_supports_many_cookies_and_terse_reads() -> None:
33
+ response = Response(status_code=200, headers=[], body=b"")
34
+
35
+ response.set_cookie("sid", "session-token", path="/")
36
+ response.set_cookie("tenant", "acme", path="/")
37
+ response.set_cookie("theme", "dark", path="/")
38
+
39
+ set_cookie_header = response.headers.get("set-cookie", "")
40
+ assert "sid=session-token" in set_cookie_header
41
+ assert "tenant=acme" in set_cookie_header
42
+ assert "theme=dark" in set_cookie_header
43
+ assert "session-token" in set_cookie_header.get("sid", "")
44
+ assert "acme" in set_cookie_header.get("tenant", "")
45
+ assert "dark" in set_cookie_header.get("theme", "")
46
+ assert set_cookie_header.get("missing") is None
47
+
48
+ assert response.cookies.sid == "session-token"
49
+ assert response.cookies.tenant == "acme"
50
+ assert response.cookies.theme == "dark"
@@ -0,0 +1,139 @@
1
+ from httpx import ASGITransport, Client
2
+ from sqlalchemy import Column, String
3
+ from tigrbl import Base, TigrblApi, TigrblApp
4
+ from tigrbl.engine.shortcuts import mem
5
+ from tigrbl.orm.mixins import GUIDPk
6
+
7
+
8
+ def _build_app():
9
+ class Widget(Base, GUIDPk):
10
+ __tablename__ = "widgets_openrpc"
11
+ name = Column(String, nullable=False)
12
+
13
+ app = TigrblApp(engine=mem(async_=False))
14
+ app.include_model(Widget)
15
+ app.initialize()
16
+ app.mount_jsonrpc()
17
+ return app, Widget
18
+
19
+
20
+ def test_openrpc_endpoint_exposed():
21
+ app, _ = _build_app()
22
+ transport = ASGITransport(app=app)
23
+ with Client(transport=transport, base_url="http://test") as client:
24
+ response = client.get("/openrpc.json")
25
+
26
+ assert response.status_code == 200
27
+ payload = response.json()
28
+ assert payload["openrpc"] == "1.2.6"
29
+ assert payload["servers"] == [{"name": app.title, "url": "/rpc"}]
30
+ assert "methods" in payload
31
+
32
+
33
+ def test_openrpc_server_url_respects_jsonrpc_prefix():
34
+ app, _ = _build_app()
35
+ app.jsonrpc_prefix = "/rpc/custom"
36
+ transport = ASGITransport(app=app)
37
+
38
+ with Client(transport=transport, base_url="http://test") as client:
39
+ payload = client.get("/openrpc.json").json()
40
+
41
+ assert payload["servers"] == [{"name": app.title, "url": "/rpc/custom"}]
42
+
43
+
44
+ def test_openrpc_includes_method_schema():
45
+ app, model = _build_app()
46
+ transport = ASGITransport(app=app)
47
+ with Client(transport=transport, base_url="http://test") as client:
48
+ payload = client.get("/openrpc.json").json()
49
+ methods = {method["name"]: method for method in payload["methods"]}
50
+
51
+ create_method = methods[f"{model.__name__}.create"]
52
+ assert create_method["paramStructure"] == "by-name"
53
+
54
+ params = create_method["params"][0]["schema"]
55
+ assert params["title"].startswith(model.__name__)
56
+ assert "Create" in params["title"]
57
+
58
+ result = create_method["result"]["schema"]
59
+ assert result["title"].startswith(model.__name__)
60
+ assert "Response" in result["title"]
61
+
62
+
63
+ def test_mount_jsonrpc_updates_openrpc_server_url() -> None:
64
+ app, _ = _build_app()
65
+ app.mount_jsonrpc(prefix="/jsonrpc")
66
+ transport = ASGITransport(app=app)
67
+
68
+ with Client(transport=transport, base_url="http://test") as client:
69
+ payload = client.get("/openrpc.json").json()
70
+
71
+ assert payload["servers"] == [{"name": app.title, "url": "/jsonrpc"}]
72
+
73
+
74
+ def test_jsonrpc_create_accepts_nested_params_mapping() -> None:
75
+ app, model = _build_app()
76
+ transport = ASGITransport(app=app)
77
+
78
+ request_payload = {
79
+ "jsonrpc": "2.0",
80
+ "method": f"{model.__name__}.create",
81
+ "params": {"params": {"name": "New Widget"}},
82
+ "id": 1,
83
+ }
84
+
85
+ with Client(transport=transport, base_url="http://test") as client:
86
+ response = client.post("/rpc", json=request_payload)
87
+
88
+ assert response.status_code == 200
89
+ data = response.json()
90
+ assert data["id"] == 1
91
+ assert "error" not in data
92
+ assert data["result"]["name"] == "New Widget"
93
+
94
+
95
+ def test_jsonrpc_notification_without_id_returns_no_content() -> None:
96
+ app, model = _build_app()
97
+ transport = ASGITransport(app=app)
98
+
99
+ request_payload = {
100
+ "jsonrpc": "2.0",
101
+ "method": f"{model.__name__}.create",
102
+ "params": {"name": "Silent Widget"},
103
+ }
104
+
105
+ with Client(transport=transport, base_url="http://test") as client:
106
+ response = client.post("/rpc", json=request_payload)
107
+
108
+ assert response.status_code == 204
109
+ assert response.content == b""
110
+
111
+
112
+ def test_mount_lens_uses_latest_openrpc_path_by_default() -> None:
113
+ app, _ = _build_app()
114
+ app.mount_openrpc(path="/schema/openrpc.json")
115
+ app.mount_lens(path="/lens-custom")
116
+
117
+ transport = ASGITransport(app=app)
118
+ with Client(transport=transport, base_url="http://test") as client:
119
+ html = client.get("/lens-custom").text
120
+
121
+ assert "/schema/openrpc.json" in html
122
+
123
+
124
+ def test_openrpc_server_url_respects_api_mount_jsonrpc_prefix_argument():
125
+ class Widget(Base, GUIDPk):
126
+ __tablename__ = "widgets_openrpc_api_mount_prefix"
127
+ name = Column(String, nullable=False)
128
+
129
+ api = TigrblApi(engine=mem(async_=False))
130
+ api.include_model(Widget)
131
+ api.initialize()
132
+ api.mount_jsonrpc(prefix="/jsonrpc")
133
+ api.mount_openrpc()
134
+
135
+ transport = ASGITransport(app=api)
136
+ with Client(transport=transport, base_url="http://test") as client:
137
+ payload = client.get("/openrpc.json").json()
138
+
139
+ assert payload["servers"] == [{"name": api.title, "url": "/jsonrpc"}]
@@ -48,3 +48,24 @@ def test_openrpc_schema_excludes_openapi_endpoint() -> None:
48
48
  method_names = {method["name"].lower() for method in payload["methods"]}
49
49
  assert all("openapi" not in name for name in method_names)
50
50
  assert all("openrpc" not in name for name in method_names)
51
+
52
+
53
+ def test_openapi_schema_excludes_openrpc_even_if_route_is_schema_visible() -> None:
54
+ app = _build_app()
55
+
56
+ def openrpc_override(_request):
57
+ return {"openrpc": "1.2.6"}
58
+
59
+ app.add_api_route(
60
+ "/openrpc.json",
61
+ openrpc_override,
62
+ methods=["GET"],
63
+ name="openrpc_json",
64
+ include_in_schema=True,
65
+ )
66
+
67
+ transport = ASGITransport(app=app)
68
+ with Client(transport=transport, base_url="http://test") as client:
69
+ payload = client.get("/openapi.json").json()
70
+
71
+ assert "/openrpc.json" not in payload["paths"]
@@ -0,0 +1,99 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from collections.abc import Mapping
5
+ from typing import Any
6
+
7
+ from tigrbl.requests import Request
8
+ from tigrbl.responses import Response
9
+
10
+ ADMIN_KEY = "admin-secret"
11
+
12
+
13
+ def _json_mapping(response: Response) -> Mapping[str, Any]:
14
+ payload = response.json()
15
+ if not isinstance(payload, Mapping):
16
+ raise TypeError("Expected a mapping JSON payload")
17
+ if isinstance(payload.get("result"), Mapping):
18
+ return payload["result"]
19
+ if isinstance(payload.get("data"), Mapping):
20
+ return payload["data"]
21
+ return payload
22
+
23
+
24
+ def _rest_request(
25
+ method: str,
26
+ path: str,
27
+ payload: dict[str, Any],
28
+ response_payload: Mapping[str, Any],
29
+ ) -> tuple[Request, Response, Mapping[str, Any]]:
30
+ request = Request(
31
+ method=method,
32
+ path=path,
33
+ headers={"x-admin-key": ADMIN_KEY, "content-type": "application/json"},
34
+ query={},
35
+ path_params={},
36
+ body=json.dumps(payload).encode("utf-8"),
37
+ )
38
+ response = Response.json(response_payload)
39
+ return request, response, _json_mapping(response)
40
+
41
+
42
+ def test_rest_request_supports_mapping_access_on_result_payload() -> None:
43
+ request, response, result = _rest_request(
44
+ "POST",
45
+ "/users",
46
+ {"email": "a@b.com"},
47
+ {"ok": True, "result": {"id": "u-1", "email": "a@b.com"}},
48
+ )
49
+
50
+ assert request.headers.get("x-admin-key") == ADMIN_KEY
51
+ assert request.json()["email"] == "a@b.com"
52
+ assert response.status_code == 200
53
+ assert isinstance(result, Mapping)
54
+ assert result["id"] == "u-1"
55
+ assert result.get("email") == "a@b.com"
56
+
57
+
58
+ def test_rest_request_supports_mapping_access_on_data_payload() -> None:
59
+ request, _, data = _rest_request(
60
+ "POST",
61
+ "/users",
62
+ {"email": "e@f.com"},
63
+ {"ok": True, "data": {"id": "u-3", "email": "e@f.com"}},
64
+ )
65
+
66
+ assert request.headers.get("x-admin-key") == ADMIN_KEY
67
+ assert request.json().get("email") == "e@f.com"
68
+ assert isinstance(data, Mapping)
69
+ assert data["id"] == "u-3"
70
+ assert data.get("email") == "e@f.com"
71
+
72
+
73
+ def test_rest_request_supports_mapping_access_without_result_envelope() -> None:
74
+ request, _, data = _rest_request(
75
+ "GET",
76
+ "/users/u-2",
77
+ {},
78
+ {"id": "u-2", "email": "c@d.com"},
79
+ )
80
+
81
+ assert request.headers.get("x-admin-key") == ADMIN_KEY
82
+ assert isinstance(data, Mapping)
83
+ assert data["id"] == "u-2"
84
+ assert data.get("email") == "c@d.com"
85
+
86
+
87
+ def test_response_json_is_mapping_and_supports_data_and_result_access() -> None:
88
+ response = Response.json(
89
+ {
90
+ "ok": True,
91
+ "data": {"id": "d-1", "name": "data-object"},
92
+ "result": {"id": "r-1", "name": "result-object"},
93
+ }
94
+ )
95
+
96
+ payload = response.json()
97
+ assert isinstance(payload, Mapping)
98
+ assert payload["data"]["id"] == "d-1"
99
+ assert payload["result"]["id"] == "r-1"
@@ -1,27 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from tigrbl.responses import Response
4
-
5
-
6
- def test_response_supports_instance_json_method_for_body_payload() -> None:
7
- response = Response(
8
- status_code=200,
9
- headers=[("content-type", "application/json; charset=utf-8")],
10
- body=b'{"ok":true,"count":1}',
11
- )
12
-
13
- assert response.json() == {"ok": True, "count": 1}
14
-
15
-
16
- def test_response_headers_and_cookies_support_dot_notation() -> None:
17
- response = Response(
18
- status_code=200,
19
- headers=[
20
- ("x-request-id", "req-123"),
21
- ("set-cookie", "sid=session-token; Path=/"),
22
- ],
23
- body=b"",
24
- )
25
-
26
- assert response.headers_map.x_request_id == "req-123"
27
- assert response.cookies.sid == "session-token"
@@ -1,60 +0,0 @@
1
- from httpx import ASGITransport, Client
2
- from sqlalchemy import Column, String
3
- from tigrbl import Base, TigrblApp
4
- from tigrbl.engine.shortcuts import mem
5
- from tigrbl.orm.mixins import GUIDPk
6
-
7
-
8
- def _build_app():
9
- class Widget(Base, GUIDPk):
10
- __tablename__ = "widgets_openrpc"
11
- name = Column(String, nullable=False)
12
-
13
- app = TigrblApp(engine=mem(async_=False))
14
- app.include_model(Widget)
15
- app.initialize()
16
- app.mount_jsonrpc()
17
- return app, Widget
18
-
19
-
20
- def test_openrpc_endpoint_exposed():
21
- app, _ = _build_app()
22
- transport = ASGITransport(app=app)
23
- with Client(transport=transport, base_url="http://test") as client:
24
- response = client.get("/openrpc.json")
25
-
26
- assert response.status_code == 200
27
- payload = response.json()
28
- assert payload["openrpc"] == "1.2.6"
29
- assert payload["servers"] == [{"name": app.title, "url": "/rpc"}]
30
- assert "methods" in payload
31
-
32
-
33
- def test_openrpc_server_url_respects_jsonrpc_prefix():
34
- app, _ = _build_app()
35
- app.jsonrpc_prefix = "/rpc/custom"
36
- transport = ASGITransport(app=app)
37
-
38
- with Client(transport=transport, base_url="http://test") as client:
39
- payload = client.get("/openrpc.json").json()
40
-
41
- assert payload["servers"] == [{"name": app.title, "url": "/rpc/custom"}]
42
-
43
-
44
- def test_openrpc_includes_method_schema():
45
- app, model = _build_app()
46
- transport = ASGITransport(app=app)
47
- with Client(transport=transport, base_url="http://test") as client:
48
- payload = client.get("/openrpc.json").json()
49
- methods = {method["name"]: method for method in payload["methods"]}
50
-
51
- create_method = methods[f"{model.__name__}.create"]
52
- assert create_method["paramStructure"] == "by-name"
53
-
54
- params = create_method["params"][0]["schema"]
55
- assert params["title"].startswith(model.__name__)
56
- assert "Create" in params["title"]
57
-
58
- result = create_method["result"]["schema"]
59
- assert result["title"].startswith(model.__name__)
60
- assert "Response" in result["title"]