tigrbl-tests 0.3.2.dev1__tar.gz → 0.3.3.dev1__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.
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/PKG-INFO +16 -1
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/README.md +14 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/pyproject.toml +3 -1
- tigrbl_tests-0.3.3.dev1/tests/i9n/test_tigrbl_api_app_usage_uvicorn.py +98 -0
- tigrbl_tests-0.3.3.dev1/tests/i9n/test_tigrbl_api_usage_uvicorn.py +105 -0
- tigrbl_tests-0.3.3.dev1/tests/i9n/test_tigrbl_api_uvicorn.py +67 -0
- tigrbl_tests-0.3.3.dev1/tests/i9n/test_tigrbl_app_include_api_uvicorn.py +149 -0
- tigrbl_tests-0.3.3.dev1/tests/i9n/test_tigrbl_app_multi_api_uvicorn.py +80 -0
- tigrbl_tests-0.3.3.dev1/tests/i9n/test_tigrbl_app_usage_uvicorn.py +103 -0
- tigrbl_tests-0.3.3.dev1/tests/i9n/test_tigrbl_app_uvicorn.py +58 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_spec_app.py +1 -1
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_table_collect_spec.py +1 -1
- tigrbl_tests-0.3.3.dev1/tests/unit/test_table_namespace_init.py +27 -0
- tigrbl_tests-0.3.3.dev1/tests/unit/test_table_namespace_isolation.py +43 -0
- tigrbl_tests-0.3.3.dev1/tests/unit/test_tigrbl_api_app_configuration.py +83 -0
- tigrbl_tests-0.3.3.dev1/tests/unit/test_tigrbl_api_app_instantiation.py +42 -0
- tigrbl_tests-0.3.3.dev1/tests/unit/test_tigrbl_api_app_subclass_definition.py +38 -0
- tigrbl_tests-0.3.3.dev1/tests/unit/test_tigrbl_api_configuration.py +47 -0
- tigrbl_tests-0.3.3.dev1/tests/unit/test_tigrbl_api_instantiation.py +37 -0
- tigrbl_tests-0.3.3.dev1/tests/unit/test_tigrbl_api_subclass_definition.py +37 -0
- tigrbl_tests-0.3.3.dev1/tests/unit/test_tigrbl_app_configuration.py +47 -0
- tigrbl_tests-0.3.3.dev1/tests/unit/test_tigrbl_app_instantiation.py +37 -0
- tigrbl_tests-0.3.3.dev1/tests/unit/test_tigrbl_app_subclass_definition.py +37 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/LICENSE +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/__init__.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/conftest.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/__init__.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_acronym_route_name.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_allow_anon.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_apikey_generation.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_authn_provider_integration.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_bindings_integration.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_bindings_modules.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_bulk_docs_client.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_core_access.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_error_mappings.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_field_spec_effects.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_header_io_uvicorn.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_healthz_methodz_hookz.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_hook_ctx_v3_i9n.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_hook_lifecycle.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_iospec_attributes.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_iospec_integration.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_key_digest_uvicorn.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_list_filters_optional.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_mixins.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_nested_path_schema_and_rpc.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_nested_routing_depth.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_op_ctx_alias_examples.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_op_ctx_behavior.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_op_ctx_core_crud_order.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_openapi_clear_response_schema.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_openapi_schema_examples_presence.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_opspec_effects_i9n_test.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_owner_tenant_policy.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_request_extras.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_request_extras_provider.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_response_extras_provider.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_rest_fallback_serialization.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_rest_row_serialization.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_rest_rpc_parity_v3.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_row_result_serialization.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_schema.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_schema_ctx_attributes_integration.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_schema_ctx_op_ctx_integration.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_schema_ctx_spec_integration.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_sqlite_attachments.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_storage_spec_integration.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_symmetry_parity.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_v3_bulk_rest_endpoints.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_v3_default_rest_ops.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_v3_default_rpc_ops.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_v3_opspec_attributes.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/test_verb_alias_policy.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/i9n/uvicorn_utils.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/perf/__init__.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/perf/test_collect_caching.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/perf/test_hookz_performance.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/perf/test_methodz_performance.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/__init__.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/decorators/test_alias_ctx_bindings.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/decorators/test_engine_ctx_bindings.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/decorators/test_hook_ctx_bindings.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/decorators/test_op_alias_bindings.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/decorators/test_op_ctx_bindings.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/decorators/test_response_ctx_bindings.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/decorators/test_schema_ctx_bindings.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/response_utils.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/runtime/atoms/test_emit_paired_post.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/runtime/atoms/test_emit_paired_pre.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/runtime/atoms/test_emit_readtime_alias.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/runtime/atoms/test_out_masking.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/runtime/atoms/test_refresh_demand.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/runtime/atoms/test_resolve_assemble.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/runtime/atoms/test_resolve_paired_gen.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/runtime/atoms/test_schema_collect_in.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/runtime/atoms/test_schema_collect_out.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/runtime/atoms/test_storage_to_stored.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/runtime/atoms/test_wire_build_in.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/runtime/atoms/test_wire_build_out.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/runtime/atoms/test_wire_dump.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/runtime/atoms/test_wire_validate_in.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/runtime/test_events_phases.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_acol_vcol_knobs.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_alias_ctx_op_alias_attributes.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_alias_ctx_op_attributes.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_api_level_set_auth.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_app_model_defaults.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_app_reexport.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_base_columnspec_inheritance.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_base_facade_initialize.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_build_list_params_spec_model.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_bulk_body_annotation.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_bulk_response_schema.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_colspec_map_isolation.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_column_collect_mixins.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_column_rest_rpc_results.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_column_table_orm_binding.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_config_dataclass_none.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_core_crud_bulk_ops.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_core_crud_default_ops.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_core_crud_methods.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_core_wrap_memoization.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_db_dependency.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_decorator_and_collect.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_default_tags.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_engine_spec_and_shortcuts.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_engine_usage_levels.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_field_spec_attrs.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_file_response.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_handler_step_qualname.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_hook_ctx_attributes.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_hook_ctx_binding.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_hookz_empty_phase.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_hybrid_session_run_sync.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_in_tx.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_include_model_columns_namespace.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_include_models_base_prefix.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_initialize_cross_ddl.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_io_spec_attributes.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_iospec_attributes.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_iospec_effects.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_jsonrpc_id_example.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_jsonrpc_router_default_tag.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_kernel_invoke_ctx.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_kernel_opview_on_demand.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_kernel_plan_labels.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_kernelz_endpoint.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_make_column_shortcuts.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_mixins_sqlalchemy.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_op_alias.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_op_class_engine_binding.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_op_ctx_arity_paths.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_op_ctx_attributes.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_op_ctx_core_crud_integration.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_op_ctx_dynamic_attach.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_op_ctx_persist_options.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_opspec_effects.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_postgres_engine_errors.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_postgres_env_vars.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_relationship_alias_cols.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_request_body_schema.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_request_response_examples.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_resolver_precedence.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_response_alias_table_rpc.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_response_ctx_precedence.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_response_diagnostics_kernelz.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_response_html_jinja_behavior.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_response_parity.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_response_rest.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_response_rpc.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_response_template.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_response_uuid.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_rest_all_default_op_verbs.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_rest_bulk_delete_suppresses_clear.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_rest_no_schema_jsonable.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_rest_operation_id_uniqueness.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_rest_rpc_parity_default_ops.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_rest_rpc_prefixes.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_rest_rpc_symmetry.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_rpc_all_default_op_verbs.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_rpc_default_ops.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_schema_ctx_attributes.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_schema_ctx_plain_class.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_schema_spec_presence.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_schemas_binding.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_security_per_route.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_should_wire_canonical.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_spec_api.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_spec_column.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_spec_engine.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_spec_field.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_spec_hook.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_spec_io.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_spec_op.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_spec_storage.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_spec_table.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_sqlite_attachments.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_sqlite_uuid_type.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_storage_spec_attributes.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_sys_handler_crud.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_sys_run_rollback.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_sys_tx_async_begin.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_sys_tx_begin.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_sys_tx_commit.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_table_base_exports.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_table_columns_namespace.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_v3_favicon_endpoint.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_v3_healthz_endpoint.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_v3_op_alias.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_v3_op_ctx_attributes.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_v3_schemas_and_decorators.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/tests/unit/test_v3_storage_spec_attributes.py +0 -0
- {tigrbl_tests-0.3.2.dev1 → tigrbl_tests-0.3.3.dev1}/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.
|
|
3
|
+
Version: 0.3.3.dev1
|
|
4
4
|
Summary: Test suite and fixtures for the Tigrbl framework.
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
License-File: LICENSE
|
|
@@ -30,6 +30,7 @@ Requires-Dist: python-dotenv
|
|
|
30
30
|
Requires-Dist: requests (>=2.32.3)
|
|
31
31
|
Requires-Dist: ruff (>=0.9.9)
|
|
32
32
|
Requires-Dist: tigrbl
|
|
33
|
+
Requires-Dist: tigrbl_client
|
|
33
34
|
Description-Content-Type: text/markdown
|
|
34
35
|
|
|
35
36
|

|
|
@@ -100,4 +101,18 @@ Use pytest selectors to focus on specific suites:
|
|
|
100
101
|
pytest standards/tigrbl_tests/tests/unit
|
|
101
102
|
```
|
|
102
103
|
|
|
104
|
+
## Examples Curriculum 📚
|
|
105
|
+
|
|
106
|
+
The `examples/` directory contains downstream-facing pytest lessons that
|
|
107
|
+
demonstrate how to implement Tigrbl in real applications. These lessons are
|
|
108
|
+
organized as a multi-module curriculum with uvicorn-backed usage examples and
|
|
109
|
+
system diagnostics validation. See the full curriculum plan for the learning
|
|
110
|
+
sequence and module descriptions.[^tigrbl-examples]
|
|
111
|
+
Run the examples from the `pkgs` directory:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
uv run --package tigrbl-tests --directory standards/tigrbl_tests pytest examples
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
[^tigrbl-examples]: examples/README.md
|
|
103
118
|
|
|
@@ -66,3 +66,17 @@ Use pytest selectors to focus on specific suites:
|
|
|
66
66
|
pytest standards/tigrbl_tests/tests/unit
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
+
## Examples Curriculum 📚
|
|
70
|
+
|
|
71
|
+
The `examples/` directory contains downstream-facing pytest lessons that
|
|
72
|
+
demonstrate how to implement Tigrbl in real applications. These lessons are
|
|
73
|
+
organized as a multi-module curriculum with uvicorn-backed usage examples and
|
|
74
|
+
system diagnostics validation. See the full curriculum plan for the learning
|
|
75
|
+
sequence and module descriptions.[^tigrbl-examples]
|
|
76
|
+
Run the examples from the `pkgs` directory:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
uv run --package tigrbl-tests --directory standards/tigrbl_tests pytest examples
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
[^tigrbl-examples]: examples/README.md
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "tigrbl-tests"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.3.dev1"
|
|
4
4
|
description = "Test suite and fixtures for the Tigrbl framework."
|
|
5
5
|
license = "Apache-2.0"
|
|
6
6
|
readme = "README.md"
|
|
@@ -20,6 +20,7 @@ classifiers = [
|
|
|
20
20
|
authors = [{ name = "Jacob Stewart", email = "jacob@swarmauri.com" }]
|
|
21
21
|
dependencies = [
|
|
22
22
|
"tigrbl",
|
|
23
|
+
"tigrbl_client",
|
|
23
24
|
"psycopg2-binary>=2.9.9",
|
|
24
25
|
"asyncpg>=0.30.0",
|
|
25
26
|
"pytest>=8.0",
|
|
@@ -46,6 +47,7 @@ keywords = [
|
|
|
46
47
|
|
|
47
48
|
[tool.uv.sources]
|
|
48
49
|
tigrbl = { workspace = true }
|
|
50
|
+
tigrbl-client = { workspace = true }
|
|
49
51
|
|
|
50
52
|
[tool.pytest.ini_options]
|
|
51
53
|
norecursedirs = ["combined", "scripts"]
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
import pytest
|
|
3
|
+
import pytest_asyncio
|
|
4
|
+
from fastapi import Security
|
|
5
|
+
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
|
6
|
+
|
|
7
|
+
from tigrbl import Base, TigrblApi, TigrblApp
|
|
8
|
+
from tigrbl.engine.shortcuts import mem
|
|
9
|
+
from tigrbl.orm.mixins import GUIDPk
|
|
10
|
+
from tigrbl.specs import F, IO, S, acol
|
|
11
|
+
from tigrbl.types import Mapped, String
|
|
12
|
+
|
|
13
|
+
from .uvicorn_utils import run_uvicorn_in_task, stop_uvicorn_server
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
bearer = HTTPBearer()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def auth_dependency(
|
|
20
|
+
credentials: HTTPAuthorizationCredentials = Security(bearer),
|
|
21
|
+
) -> HTTPAuthorizationCredentials:
|
|
22
|
+
return credentials
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Kappa(Base, GUIDPk):
|
|
26
|
+
__tablename__ = "kappa_api_app_usage"
|
|
27
|
+
__allow_unmapped__ = True
|
|
28
|
+
|
|
29
|
+
name: Mapped[str] = acol(
|
|
30
|
+
storage=S(type_=String, nullable=False),
|
|
31
|
+
field=F(py_type=str),
|
|
32
|
+
io=IO(in_verbs=("create",), out_verbs=("create", "read", "list")),
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
__tigrbl_cols__ = {"id": GUIDPk.id, "name": name}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class KappaApi(TigrblApi):
|
|
39
|
+
MODELS = (Kappa,)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@pytest_asyncio.fixture()
|
|
43
|
+
async def running_api_app():
|
|
44
|
+
api = KappaApi(engine=mem(async_=False))
|
|
45
|
+
api.set_auth(authn=auth_dependency, allow_anon=False)
|
|
46
|
+
api.include_models([Kappa])
|
|
47
|
+
api.initialize()
|
|
48
|
+
|
|
49
|
+
class KappaApp(TigrblApp):
|
|
50
|
+
APIS = (api,)
|
|
51
|
+
|
|
52
|
+
app = KappaApp(engine=mem(async_=False))
|
|
53
|
+
app.include_router(api)
|
|
54
|
+
|
|
55
|
+
base_url, server, task = await run_uvicorn_in_task(app)
|
|
56
|
+
try:
|
|
57
|
+
yield base_url
|
|
58
|
+
finally:
|
|
59
|
+
await stop_uvicorn_server(server, task)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@pytest.mark.i9n
|
|
63
|
+
@pytest.mark.asyncio
|
|
64
|
+
async def test_tigrbl_api_app_deploys_and_serves_openapi(running_api_app) -> None:
|
|
65
|
+
base_url = running_api_app
|
|
66
|
+
|
|
67
|
+
async with httpx.AsyncClient() as client:
|
|
68
|
+
openapi_resp = await client.get(f"{base_url}/openapi.json")
|
|
69
|
+
|
|
70
|
+
assert openapi_resp.status_code == 200
|
|
71
|
+
openapi = openapi_resp.json()
|
|
72
|
+
paths = openapi["paths"]
|
|
73
|
+
|
|
74
|
+
assert "/kappa" in paths
|
|
75
|
+
assert "/kappa/{item_id}" in paths
|
|
76
|
+
assert {"get", "post", "delete"}.issubset(paths["/kappa"])
|
|
77
|
+
assert {"get", "patch", "put", "delete"}.issubset(paths["/kappa/{item_id}"])
|
|
78
|
+
|
|
79
|
+
security_schemes = openapi.get("components", {}).get("securitySchemes", {})
|
|
80
|
+
assert "HTTPBearer" in security_schemes
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@pytest.mark.i9n
|
|
84
|
+
@pytest.mark.asyncio
|
|
85
|
+
async def test_tigrbl_api_app_handles_authenticated_request(running_api_app) -> None:
|
|
86
|
+
base_url = running_api_app
|
|
87
|
+
headers = {"Authorization": "Bearer demo"}
|
|
88
|
+
|
|
89
|
+
async with httpx.AsyncClient() as client:
|
|
90
|
+
resp = await client.post(
|
|
91
|
+
f"{base_url}/kappa",
|
|
92
|
+
json={"name": "Kappa"},
|
|
93
|
+
headers=headers,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
assert resp.status_code == 201
|
|
97
|
+
payload = resp.json()
|
|
98
|
+
assert payload["name"] == "Kappa"
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
import pytest
|
|
3
|
+
import pytest_asyncio
|
|
4
|
+
from fastapi import Security
|
|
5
|
+
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
|
6
|
+
|
|
7
|
+
from tigrbl import Base, TigrblApi
|
|
8
|
+
from tigrbl.engine.shortcuts import mem
|
|
9
|
+
from tigrbl.orm.mixins import GUIDPk
|
|
10
|
+
from tigrbl.specs import F, IO, S, acol
|
|
11
|
+
from tigrbl.types import App, Mapped, String
|
|
12
|
+
|
|
13
|
+
from .uvicorn_utils import run_uvicorn_in_task, stop_uvicorn_server
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
bearer = HTTPBearer()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def auth_dependency(
|
|
20
|
+
credentials: HTTPAuthorizationCredentials = Security(bearer),
|
|
21
|
+
) -> HTTPAuthorizationCredentials:
|
|
22
|
+
return credentials
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Alpha(Base, GUIDPk):
|
|
26
|
+
__tablename__ = "alpha_api_usage"
|
|
27
|
+
__allow_unmapped__ = True
|
|
28
|
+
|
|
29
|
+
name: Mapped[str] = acol(
|
|
30
|
+
storage=S(type_=String, nullable=False),
|
|
31
|
+
field=F(py_type=str),
|
|
32
|
+
io=IO(in_verbs=("create",), out_verbs=("create", "read", "list")),
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
__tigrbl_cols__ = {"id": GUIDPk.id, "name": name}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Beta(Base, GUIDPk):
|
|
39
|
+
__tablename__ = "beta_api_usage"
|
|
40
|
+
__allow_unmapped__ = True
|
|
41
|
+
|
|
42
|
+
name: Mapped[str] = acol(
|
|
43
|
+
storage=S(type_=String, nullable=False),
|
|
44
|
+
field=F(py_type=str),
|
|
45
|
+
io=IO(in_verbs=("create",), out_verbs=("create", "read", "list")),
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
__tigrbl_cols__ = {"id": GUIDPk.id, "name": name}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@pytest_asyncio.fixture()
|
|
52
|
+
async def running_api():
|
|
53
|
+
app = App()
|
|
54
|
+
api = TigrblApi(engine=mem(async_=False))
|
|
55
|
+
api.set_auth(authn=auth_dependency, allow_anon=False)
|
|
56
|
+
api.include_models([Alpha, Beta])
|
|
57
|
+
api.initialize()
|
|
58
|
+
app.include_router(api)
|
|
59
|
+
|
|
60
|
+
base_url, server, task = await run_uvicorn_in_task(app)
|
|
61
|
+
try:
|
|
62
|
+
yield base_url
|
|
63
|
+
finally:
|
|
64
|
+
await stop_uvicorn_server(server, task)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@pytest.mark.i9n
|
|
68
|
+
@pytest.mark.asyncio
|
|
69
|
+
async def test_tigrbl_api_deploys_and_serves_openapi(running_api) -> None:
|
|
70
|
+
base_url = running_api
|
|
71
|
+
|
|
72
|
+
async with httpx.AsyncClient() as client:
|
|
73
|
+
openapi_resp = await client.get(f"{base_url}/openapi.json")
|
|
74
|
+
|
|
75
|
+
assert openapi_resp.status_code == 200
|
|
76
|
+
openapi = openapi_resp.json()
|
|
77
|
+
paths = openapi["paths"]
|
|
78
|
+
|
|
79
|
+
assert "/alpha" in paths
|
|
80
|
+
assert "/alpha/{item_id}" in paths
|
|
81
|
+
assert "/beta" in paths
|
|
82
|
+
assert "/beta/{item_id}" in paths
|
|
83
|
+
assert {"get", "post", "delete"}.issubset(paths["/alpha"])
|
|
84
|
+
assert {"get", "patch", "put", "delete"}.issubset(paths["/alpha/{item_id}"])
|
|
85
|
+
|
|
86
|
+
security_schemes = openapi.get("components", {}).get("securitySchemes", {})
|
|
87
|
+
assert "HTTPBearer" in security_schemes
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@pytest.mark.i9n
|
|
91
|
+
@pytest.mark.asyncio
|
|
92
|
+
async def test_tigrbl_api_handles_authenticated_request(running_api) -> None:
|
|
93
|
+
base_url = running_api
|
|
94
|
+
headers = {"Authorization": "Bearer demo"}
|
|
95
|
+
|
|
96
|
+
async with httpx.AsyncClient() as client:
|
|
97
|
+
resp = await client.post(
|
|
98
|
+
f"{base_url}/alpha",
|
|
99
|
+
json={"name": "Alpha"},
|
|
100
|
+
headers=headers,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
assert resp.status_code == 201
|
|
104
|
+
payload = resp.json()
|
|
105
|
+
assert payload["name"] == "Alpha"
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
import pytest
|
|
3
|
+
import pytest_asyncio
|
|
4
|
+
|
|
5
|
+
from tigrbl import Base, TigrblApi
|
|
6
|
+
from tigrbl.engine.shortcuts import mem
|
|
7
|
+
from tigrbl.orm.mixins import GUIDPk
|
|
8
|
+
from tigrbl.specs import F, IO, S, acol
|
|
9
|
+
from tigrbl.types import App, Mapped, String
|
|
10
|
+
|
|
11
|
+
from .uvicorn_utils import run_uvicorn_in_task, stop_uvicorn_server
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Gadget(Base, GUIDPk):
|
|
15
|
+
__tablename__ = "gadgets_api"
|
|
16
|
+
__resource__ = "gadget"
|
|
17
|
+
|
|
18
|
+
name: Mapped[str] = acol(
|
|
19
|
+
storage=S(String, nullable=False),
|
|
20
|
+
field=F(py_type=str),
|
|
21
|
+
io=IO(in_verbs=("create",), out_verbs=("create", "read", "list")),
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@pytest_asyncio.fixture()
|
|
26
|
+
async def running_api_app():
|
|
27
|
+
api = TigrblApi(
|
|
28
|
+
engine=mem(async_=False),
|
|
29
|
+
models=[Gadget],
|
|
30
|
+
prefix="/gadgets",
|
|
31
|
+
system_prefix="/diagnostics",
|
|
32
|
+
)
|
|
33
|
+
await api.initialize()
|
|
34
|
+
|
|
35
|
+
app = App()
|
|
36
|
+
app.include_router(api.router)
|
|
37
|
+
api.attach_diagnostics(app=app)
|
|
38
|
+
|
|
39
|
+
base_url, server, task = await run_uvicorn_in_task(app)
|
|
40
|
+
try:
|
|
41
|
+
yield base_url
|
|
42
|
+
finally:
|
|
43
|
+
await stop_uvicorn_server(server, task)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@pytest.mark.i9n
|
|
47
|
+
@pytest.mark.asyncio
|
|
48
|
+
async def test_tigrbl_api_create_gadget(running_api_app):
|
|
49
|
+
async with httpx.AsyncClient() as client:
|
|
50
|
+
response = await client.post(
|
|
51
|
+
f"{running_api_app}/gadgets/gadget", json={"name": "gyro"}
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
assert response.status_code == 201
|
|
55
|
+
payload = response.json()
|
|
56
|
+
assert payload["name"] == "gyro"
|
|
57
|
+
assert "id" in payload
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@pytest.mark.i9n
|
|
61
|
+
@pytest.mark.asyncio
|
|
62
|
+
async def test_tigrbl_api_healthz(running_api_app):
|
|
63
|
+
async with httpx.AsyncClient() as client:
|
|
64
|
+
response = await client.get(f"{running_api_app}/diagnostics/healthz")
|
|
65
|
+
|
|
66
|
+
assert response.status_code == 200
|
|
67
|
+
assert response.json()["ok"] is True
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
import pytest
|
|
3
|
+
import pytest_asyncio
|
|
4
|
+
|
|
5
|
+
from tigrbl import Base, TigrblApi, TigrblApp
|
|
6
|
+
from tigrbl.engine.shortcuts import mem
|
|
7
|
+
from tigrbl.orm.mixins import GUIDPk
|
|
8
|
+
from tigrbl.specs import F, IO, S, acol
|
|
9
|
+
from tigrbl.types import Mapped, String
|
|
10
|
+
|
|
11
|
+
from .uvicorn_utils import run_uvicorn_in_task, stop_uvicorn_server
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class AlphaWidget(Base, GUIDPk):
|
|
15
|
+
__tablename__ = "alpha_widgets_alt"
|
|
16
|
+
__resource__ = "alpha-widget"
|
|
17
|
+
|
|
18
|
+
name: Mapped[str] = acol(
|
|
19
|
+
storage=S(String, nullable=False),
|
|
20
|
+
field=F(py_type=str),
|
|
21
|
+
io=IO(in_verbs=("create",), out_verbs=("create", "read", "list")),
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class BetaWidget(Base, GUIDPk):
|
|
26
|
+
__tablename__ = "beta_widgets_alt"
|
|
27
|
+
__resource__ = "beta-widget"
|
|
28
|
+
|
|
29
|
+
name: Mapped[str] = acol(
|
|
30
|
+
storage=S(String, nullable=False),
|
|
31
|
+
field=F(py_type=str),
|
|
32
|
+
io=IO(in_verbs=("create",), out_verbs=("create", "read", "list")),
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ZetaWidget(Base, GUIDPk):
|
|
37
|
+
__tablename__ = "zeta_widgets_alt"
|
|
38
|
+
__resource__ = "zeta-widget"
|
|
39
|
+
|
|
40
|
+
name: Mapped[str] = acol(
|
|
41
|
+
storage=S(String, nullable=False),
|
|
42
|
+
field=F(py_type=str),
|
|
43
|
+
io=IO(in_verbs=("create",), out_verbs=("create", "read", "list")),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@pytest_asyncio.fixture()
|
|
48
|
+
async def running_app_with_apis():
|
|
49
|
+
engine = mem(async_=False)
|
|
50
|
+
alpha_api = TigrblApi(engine=engine, models=[AlphaWidget], prefix="/alpha")
|
|
51
|
+
beta_api = TigrblApi(engine=engine)
|
|
52
|
+
beta_api.include_model(BetaWidget)
|
|
53
|
+
zeta_api = TigrblApi(engine=engine)
|
|
54
|
+
zeta_api.include_model(ZetaWidget)
|
|
55
|
+
|
|
56
|
+
app = TigrblApp(engine=engine, apis=[alpha_api, (zeta_api, "/zeta")])
|
|
57
|
+
app.include_api(beta_api, prefix="/beta")
|
|
58
|
+
await app.initialize()
|
|
59
|
+
|
|
60
|
+
base_url, server, task = await run_uvicorn_in_task(app)
|
|
61
|
+
try:
|
|
62
|
+
yield base_url
|
|
63
|
+
finally:
|
|
64
|
+
await stop_uvicorn_server(server, task)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@pytest_asyncio.fixture()
|
|
68
|
+
async def running_app_with_api_router():
|
|
69
|
+
engine = mem(async_=False)
|
|
70
|
+
alpha_api = TigrblApi(engine=engine)
|
|
71
|
+
alpha_api.include_model(AlphaWidget)
|
|
72
|
+
beta_api = TigrblApi(engine=engine)
|
|
73
|
+
beta_api.include_model(BetaWidget)
|
|
74
|
+
|
|
75
|
+
app = TigrblApp(engine=engine, apis=[alpha_api])
|
|
76
|
+
app.include_api(beta_api, prefix="/beta")
|
|
77
|
+
app.include_api(alpha_api.router, prefix="/alpha")
|
|
78
|
+
await app.initialize()
|
|
79
|
+
|
|
80
|
+
base_url, server, task = await run_uvicorn_in_task(app)
|
|
81
|
+
try:
|
|
82
|
+
yield base_url
|
|
83
|
+
finally:
|
|
84
|
+
await stop_uvicorn_server(server, task)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@pytest.mark.i9n
|
|
88
|
+
@pytest.mark.asyncio
|
|
89
|
+
async def test_tigrbl_app_api_list_alpha(running_app_with_apis):
|
|
90
|
+
async with httpx.AsyncClient() as client:
|
|
91
|
+
response = await client.post(
|
|
92
|
+
f"{running_app_with_apis}/alpha/alpha-widget", json={"name": "ace"}
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
assert response.status_code == 201
|
|
96
|
+
payload = response.json()
|
|
97
|
+
assert payload["name"] == "ace"
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@pytest.mark.i9n
|
|
101
|
+
@pytest.mark.asyncio
|
|
102
|
+
async def test_tigrbl_app_include_api_beta(running_app_with_apis):
|
|
103
|
+
async with httpx.AsyncClient() as client:
|
|
104
|
+
response = await client.post(
|
|
105
|
+
f"{running_app_with_apis}/beta/beta-widget", json={"name": "bolt"}
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
assert response.status_code == 201
|
|
109
|
+
payload = response.json()
|
|
110
|
+
assert payload["name"] == "bolt"
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@pytest.mark.i9n
|
|
114
|
+
@pytest.mark.asyncio
|
|
115
|
+
async def test_tigrbl_app_api_list_zeta(running_app_with_apis):
|
|
116
|
+
async with httpx.AsyncClient() as client:
|
|
117
|
+
response = await client.post(
|
|
118
|
+
f"{running_app_with_apis}/zeta/zeta-widget", json={"name": "zen"}
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
assert response.status_code == 201
|
|
122
|
+
payload = response.json()
|
|
123
|
+
assert payload["name"] == "zen"
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@pytest.mark.i9n
|
|
127
|
+
@pytest.mark.asyncio
|
|
128
|
+
async def test_tigrbl_app_include_api_router_alpha(running_app_with_api_router):
|
|
129
|
+
async with httpx.AsyncClient() as client:
|
|
130
|
+
response = await client.post(
|
|
131
|
+
f"{running_app_with_api_router}/alpha/alpha-widget", json={"name": "ace"}
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
assert response.status_code == 201
|
|
135
|
+
payload = response.json()
|
|
136
|
+
assert payload["name"] == "ace"
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@pytest.mark.i9n
|
|
140
|
+
@pytest.mark.asyncio
|
|
141
|
+
async def test_tigrbl_app_include_api_router_beta(running_app_with_api_router):
|
|
142
|
+
async with httpx.AsyncClient() as client:
|
|
143
|
+
response = await client.post(
|
|
144
|
+
f"{running_app_with_api_router}/beta/beta-widget", json={"name": "bolt"}
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
assert response.status_code == 201
|
|
148
|
+
payload = response.json()
|
|
149
|
+
assert payload["name"] == "bolt"
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
import pytest
|
|
3
|
+
import pytest_asyncio
|
|
4
|
+
|
|
5
|
+
from tigrbl import Base, TigrblApi, TigrblApp
|
|
6
|
+
from tigrbl.engine.shortcuts import mem
|
|
7
|
+
from tigrbl.orm.mixins import GUIDPk
|
|
8
|
+
from tigrbl.specs import F, IO, S, acol
|
|
9
|
+
from tigrbl.types import Mapped, String
|
|
10
|
+
|
|
11
|
+
from .uvicorn_utils import run_uvicorn_in_task, stop_uvicorn_server
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class AlphaWidget(Base, GUIDPk):
|
|
15
|
+
__tablename__ = "alpha_widgets"
|
|
16
|
+
__resource__ = "alpha-widget"
|
|
17
|
+
|
|
18
|
+
name: Mapped[str] = acol(
|
|
19
|
+
storage=S(String, nullable=False),
|
|
20
|
+
field=F(py_type=str),
|
|
21
|
+
io=IO(in_verbs=("create",), out_verbs=("create", "read", "list")),
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class BetaWidget(Base, GUIDPk):
|
|
26
|
+
__tablename__ = "beta_widgets"
|
|
27
|
+
__resource__ = "beta-widget"
|
|
28
|
+
|
|
29
|
+
name: Mapped[str] = acol(
|
|
30
|
+
storage=S(String, nullable=False),
|
|
31
|
+
field=F(py_type=str),
|
|
32
|
+
io=IO(in_verbs=("create",), out_verbs=("create", "read", "list")),
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@pytest_asyncio.fixture()
|
|
37
|
+
async def running_multi_api_app():
|
|
38
|
+
engine = mem(async_=False)
|
|
39
|
+
alpha_api = TigrblApi(engine=engine)
|
|
40
|
+
alpha_api.include_model(AlphaWidget)
|
|
41
|
+
|
|
42
|
+
beta_api = TigrblApi(engine=engine)
|
|
43
|
+
beta_api.include_model(BetaWidget)
|
|
44
|
+
|
|
45
|
+
app = TigrblApp(engine=engine, apis=[alpha_api])
|
|
46
|
+
app.include_router(beta_api, prefix="/beta")
|
|
47
|
+
app.include_router(alpha_api.router, prefix="/alpha")
|
|
48
|
+
await app.initialize()
|
|
49
|
+
|
|
50
|
+
base_url, server, task = await run_uvicorn_in_task(app)
|
|
51
|
+
try:
|
|
52
|
+
yield base_url
|
|
53
|
+
finally:
|
|
54
|
+
await stop_uvicorn_server(server, task)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@pytest.mark.i9n
|
|
58
|
+
@pytest.mark.asyncio
|
|
59
|
+
async def test_tigrbl_app_routes_alpha_api(running_multi_api_app):
|
|
60
|
+
async with httpx.AsyncClient() as client:
|
|
61
|
+
response = await client.post(
|
|
62
|
+
f"{running_multi_api_app}/alpha/alpha-widget", json={"name": "ace"}
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
assert response.status_code == 201
|
|
66
|
+
payload = response.json()
|
|
67
|
+
assert payload["name"] == "ace"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@pytest.mark.i9n
|
|
71
|
+
@pytest.mark.asyncio
|
|
72
|
+
async def test_tigrbl_app_routes_beta_api(running_multi_api_app):
|
|
73
|
+
async with httpx.AsyncClient() as client:
|
|
74
|
+
response = await client.post(
|
|
75
|
+
f"{running_multi_api_app}/beta/beta-widget", json={"name": "bolt"}
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
assert response.status_code == 201
|
|
79
|
+
payload = response.json()
|
|
80
|
+
assert payload["name"] == "bolt"
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
import pytest
|
|
3
|
+
import pytest_asyncio
|
|
4
|
+
from fastapi import Security
|
|
5
|
+
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
|
6
|
+
|
|
7
|
+
from tigrbl import Base, TigrblApp
|
|
8
|
+
from tigrbl.engine.shortcuts import mem
|
|
9
|
+
from tigrbl.orm.mixins import GUIDPk
|
|
10
|
+
from tigrbl.specs import F, IO, S, acol
|
|
11
|
+
from tigrbl.types import Mapped, String
|
|
12
|
+
|
|
13
|
+
from .uvicorn_utils import run_uvicorn_in_task, stop_uvicorn_server
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
bearer = HTTPBearer()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def auth_dependency(
|
|
20
|
+
credentials: HTTPAuthorizationCredentials = Security(bearer),
|
|
21
|
+
) -> HTTPAuthorizationCredentials:
|
|
22
|
+
return credentials
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Gamma(Base, GUIDPk):
|
|
26
|
+
__tablename__ = "gamma_app_usage"
|
|
27
|
+
__allow_unmapped__ = True
|
|
28
|
+
|
|
29
|
+
name: Mapped[str] = acol(
|
|
30
|
+
storage=S(type_=String, nullable=False),
|
|
31
|
+
field=F(py_type=str),
|
|
32
|
+
io=IO(in_verbs=("create",), out_verbs=("create", "read", "list")),
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
__tigrbl_cols__ = {"id": GUIDPk.id, "name": name}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Delta(Base, GUIDPk):
|
|
39
|
+
__tablename__ = "delta_app_usage"
|
|
40
|
+
__allow_unmapped__ = True
|
|
41
|
+
|
|
42
|
+
name: Mapped[str] = acol(
|
|
43
|
+
storage=S(type_=String, nullable=False),
|
|
44
|
+
field=F(py_type=str),
|
|
45
|
+
io=IO(in_verbs=("create",), out_verbs=("create", "read", "list")),
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
__tigrbl_cols__ = {"id": GUIDPk.id, "name": name}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@pytest_asyncio.fixture()
|
|
52
|
+
async def running_app():
|
|
53
|
+
app = TigrblApp(engine=mem(async_=False))
|
|
54
|
+
app.set_auth(authn=auth_dependency, allow_anon=False)
|
|
55
|
+
app.include_models([Gamma, Delta])
|
|
56
|
+
app.initialize()
|
|
57
|
+
|
|
58
|
+
base_url, server, task = await run_uvicorn_in_task(app)
|
|
59
|
+
try:
|
|
60
|
+
yield base_url
|
|
61
|
+
finally:
|
|
62
|
+
await stop_uvicorn_server(server, task)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@pytest.mark.i9n
|
|
66
|
+
@pytest.mark.asyncio
|
|
67
|
+
async def test_tigrbl_app_deploys_and_serves_openapi(running_app) -> None:
|
|
68
|
+
base_url = running_app
|
|
69
|
+
|
|
70
|
+
async with httpx.AsyncClient() as client:
|
|
71
|
+
openapi_resp = await client.get(f"{base_url}/openapi.json")
|
|
72
|
+
|
|
73
|
+
assert openapi_resp.status_code == 200
|
|
74
|
+
openapi = openapi_resp.json()
|
|
75
|
+
paths = openapi["paths"]
|
|
76
|
+
|
|
77
|
+
assert "/gamma" in paths
|
|
78
|
+
assert "/gamma/{item_id}" in paths
|
|
79
|
+
assert "/delta" in paths
|
|
80
|
+
assert "/delta/{item_id}" in paths
|
|
81
|
+
assert {"get", "post", "delete"}.issubset(paths["/gamma"])
|
|
82
|
+
assert {"get", "patch", "put", "delete"}.issubset(paths["/gamma/{item_id}"])
|
|
83
|
+
|
|
84
|
+
security_schemes = openapi.get("components", {}).get("securitySchemes", {})
|
|
85
|
+
assert "HTTPBearer" in security_schemes
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@pytest.mark.i9n
|
|
89
|
+
@pytest.mark.asyncio
|
|
90
|
+
async def test_tigrbl_app_handles_authenticated_request(running_app) -> None:
|
|
91
|
+
base_url = running_app
|
|
92
|
+
headers = {"Authorization": "Bearer demo"}
|
|
93
|
+
|
|
94
|
+
async with httpx.AsyncClient() as client:
|
|
95
|
+
resp = await client.post(
|
|
96
|
+
f"{base_url}/gamma",
|
|
97
|
+
json={"name": "Gamma"},
|
|
98
|
+
headers=headers,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
assert resp.status_code == 201
|
|
102
|
+
payload = resp.json()
|
|
103
|
+
assert payload["name"] == "Gamma"
|