mockstack 0.14.0__tar.gz → 0.14.1__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 (148) hide show
  1. {mockstack-0.14.0 → mockstack-0.14.1}/CLAUDE.md +3 -2
  2. {mockstack-0.14.0 → mockstack-0.14.1}/Dockerfile +1 -1
  3. {mockstack-0.14.0 → mockstack-0.14.1}/PKG-INFO +3 -3
  4. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/main.py +0 -2
  5. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/conftest.py +14 -1
  6. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/routers/test_catchall.py +11 -10
  7. mockstack-0.14.1/mockstack/tests/test_main.py +20 -0
  8. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/test_middleware.py +4 -3
  9. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack.egg-info/PKG-INFO +3 -3
  10. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack.egg-info/SOURCES.txt +1 -2
  11. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack.egg-info/requires.txt +2 -2
  12. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack.egg-info/scm_file_list.json +1 -2
  13. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack.egg-info/scm_version.json +2 -2
  14. {mockstack-0.14.0 → mockstack-0.14.1}/pyproject.toml +4 -6
  15. {mockstack-0.14.0 → mockstack-0.14.1}/uv.lock +4 -328
  16. mockstack-0.14.0/mockstack/routers/homepage.py +0 -19
  17. mockstack-0.14.0/mockstack/tests/routers/test_homepage.py +0 -15
  18. {mockstack-0.14.0 → mockstack-0.14.1}/.dockerignore +0 -0
  19. {mockstack-0.14.0 → mockstack-0.14.1}/.env.example +0 -0
  20. {mockstack-0.14.0 → mockstack-0.14.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  21. {mockstack-0.14.0 → mockstack-0.14.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  22. {mockstack-0.14.0 → mockstack-0.14.1}/.github/workflows/ci.yml +0 -0
  23. {mockstack-0.14.0 → mockstack-0.14.1}/.github/workflows/publish-docs.yml +0 -0
  24. {mockstack-0.14.0 → mockstack-0.14.1}/.github/workflows/publish-to-pypi.yml +0 -0
  25. {mockstack-0.14.0 → mockstack-0.14.1}/.github/workflows/smoke-test-pypi.yml +0 -0
  26. {mockstack-0.14.0 → mockstack-0.14.1}/.gitignore +0 -0
  27. {mockstack-0.14.0 → mockstack-0.14.1}/.pre-commit-config.yaml +0 -0
  28. {mockstack-0.14.0 → mockstack-0.14.1}/CODE_OF_CONDUCT.md +0 -0
  29. {mockstack-0.14.0 → mockstack-0.14.1}/CONTRIBUTING.md +0 -0
  30. {mockstack-0.14.0 → mockstack-0.14.1}/LICENSE +0 -0
  31. {mockstack-0.14.0 → mockstack-0.14.1}/README.md +0 -0
  32. {mockstack-0.14.0 → mockstack-0.14.1}/SECURITY.md +0 -0
  33. {mockstack-0.14.0 → mockstack-0.14.1}/docker-entrypoint.sh +0 -0
  34. {mockstack-0.14.0 → mockstack-0.14.1}/docs/assets/favicon.ico +0 -0
  35. {mockstack-0.14.0 → mockstack-0.14.1}/docs/assets/logo.png +0 -0
  36. {mockstack-0.14.0 → mockstack-0.14.1}/docs/assets/mockstack.png +0 -0
  37. {mockstack-0.14.0 → mockstack-0.14.1}/docs/configuration.md +0 -0
  38. {mockstack-0.14.0 → mockstack-0.14.1}/docs/guides/proxyrules-cookbook.md +0 -0
  39. {mockstack-0.14.0 → mockstack-0.14.1}/docs/ollama.md +0 -0
  40. {mockstack-0.14.0 → mockstack-0.14.1}/docs/strategies/base.md +0 -0
  41. {mockstack-0.14.0 → mockstack-0.14.1}/docs/strategies/filefixtures.md +0 -0
  42. {mockstack-0.14.0 → mockstack-0.14.1}/docs/strategies/proxyrules.md +0 -0
  43. {mockstack-0.14.0 → mockstack-0.14.1}/examples/filefixtures-with-templates/.env.example +0 -0
  44. {mockstack-0.14.0 → mockstack-0.14.1}/examples/filefixtures-with-templates/README.md +0 -0
  45. {mockstack-0.14.0 → mockstack-0.14.1}/examples/filefixtures-with-templates/templates/servicename-api-v1-items.j2 +0 -0
  46. {mockstack-0.14.0 → mockstack-0.14.1}/examples/filefixtures-with-templates/templates/servicename2-api-v2-user.533ec889-7c68-45c8-b21e-4a7e455d1234.j2 +0 -0
  47. {mockstack-0.14.0 → mockstack-0.14.1}/examples/llm/README.md +0 -0
  48. {mockstack-0.14.0 → mockstack-0.14.1}/examples/llm/mockstack-langchain-example.ipynb +0 -0
  49. {mockstack-0.14.0 → mockstack-0.14.1}/examples/llm/templates/ollama-openai-v1-chat-completions.j2 +0 -0
  50. {mockstack-0.14.0 → mockstack-0.14.1}/examples/llm/templates/openai-v1-chat-completions.j2 +0 -0
  51. {mockstack-0.14.0 → mockstack-0.14.1}/examples/llm/templates/openai-v1-deployments-gpt-4o-chat-completions.j2 +0 -0
  52. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/01-tagged-traffic/fixtures/projects/project.json.j2 +0 -0
  53. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/01-tagged-traffic/rules.yml +0 -0
  54. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/02-scenario-directories/fixtures/archived/projects/project.json.j2 +0 -0
  55. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/02-scenario-directories/fixtures/healthy/projects/project.json.j2 +0 -0
  56. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/02-scenario-directories/rules.yml +0 -0
  57. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/03-sql-gateway/fixtures/analytics/sales_facts.json.j2 +0 -0
  58. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/03-sql-gateway/rules.yml +0 -0
  59. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/04-json-literals/fixtures/orders/search.json.j2 +0 -0
  60. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/04-json-literals/rules.yml +0 -0
  61. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/05-query-parameters/fixtures/users/archived.json.j2 +0 -0
  62. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/05-query-parameters/fixtures/users/page.json.j2 +0 -0
  63. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/05-query-parameters/rules.yml +0 -0
  64. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/06-asserting-in-tests/fixture_assertions.py +0 -0
  65. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/06-asserting-in-tests/fixtures/orders/echo.json.j2 +0 -0
  66. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/06-asserting-in-tests/fixtures/projects/project.json.j2 +0 -0
  67. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/06-asserting-in-tests/fixtures/users/user-1.json.j2 +0 -0
  68. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/06-asserting-in-tests/rules.yml +0 -0
  69. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/07-redirect-mode/fixtures/users/user-1.json.j2 +0 -0
  70. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/07-redirect-mode/rules.yml +0 -0
  71. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/08-status-and-headers/fixtures/errors/rate-limited.json.j2 +0 -0
  72. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/08-status-and-headers/fixtures/errors/unavailable.json.j2 +0 -0
  73. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/08-status-and-headers/fixtures/orders/created.json.j2 +0 -0
  74. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/08-status-and-headers/rules.yml +0 -0
  75. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/09-recording/rules.yml +0 -0
  76. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/09-recording/scrubbers.py +0 -0
  77. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/README.md +0 -0
  78. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/upstream.py +0 -0
  79. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-eval-isolation/.env.example +0 -0
  80. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-eval-isolation/README.md +0 -0
  81. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-eval-isolation/fixtures/healthy/analytics/sales_facts.json.j2 +0 -0
  82. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-eval-isolation/fixtures/healthy/projects/project.json.j2 +0 -0
  83. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-eval-isolation/rules.yml +0 -0
  84. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-eval-isolation/upstream.py +0 -0
  85. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-with-rules-file/.env.example +0 -0
  86. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-with-rules-file/README.md +0 -0
  87. {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-with-rules-file/rules.yml +0 -0
  88. {mockstack-0.14.0 → mockstack-0.14.1}/mkdocs.yml +0 -0
  89. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/__init__.py +0 -0
  90. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/config.py +0 -0
  91. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/constants.py +0 -0
  92. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/display.py +0 -0
  93. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/exceptions.py +0 -0
  94. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/identifiers.py +0 -0
  95. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/intent.py +0 -0
  96. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/lifespan.py +0 -0
  97. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/llm/__init__.py +0 -0
  98. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/llm/ollama.py +0 -0
  99. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/middleware.py +0 -0
  100. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/recording.py +0 -0
  101. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/routers/__init__.py +0 -0
  102. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/routers/catchall.py +0 -0
  103. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/rules.py +0 -0
  104. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/strategies/__init__.py +0 -0
  105. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/strategies/base.py +0 -0
  106. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/strategies/create_mixin.py +0 -0
  107. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/strategies/factory.py +0 -0
  108. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/strategies/filefixtures.py +0 -0
  109. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/strategies/proxyrules.py +0 -0
  110. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/telemetry.py +0 -0
  111. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/templating.py +0 -0
  112. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/__init__.py +0 -0
  113. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/fixtures/__init__.py +0 -0
  114. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/fixtures/proxyrules.yml +0 -0
  115. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/fixtures/templates/__init__.py +0 -0
  116. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/fixtures/templates/example-template.j2 +0 -0
  117. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/live/__init__.py +0 -0
  118. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/live/conftest.py +0 -0
  119. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/live/test_cookbook.py +0 -0
  120. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/live/test_example_eval_isolation.py +0 -0
  121. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/live/test_fixture_status_live.py +0 -0
  122. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/live/test_predicates_live.py +0 -0
  123. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/live/test_record_mode_live.py +0 -0
  124. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/live/test_reverse_proxy_live.py +0 -0
  125. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/llm/__init__.py +0 -0
  126. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/llm/test_ollama.py +0 -0
  127. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/routers/__init__.py +0 -0
  128. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/strategies/__init__.py +0 -0
  129. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/strategies/conftest.py +0 -0
  130. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/strategies/test_create_mixin.py +0 -0
  131. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/strategies/test_filefixtures.py +0 -0
  132. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/strategies/test_filefixtures_example.py +0 -0
  133. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/strategies/test_proxyrules.py +0 -0
  134. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/strategies/test_proxyrules_record.py +0 -0
  135. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/test_config.py +0 -0
  136. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/test_display.py +0 -0
  137. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/test_docs_settings.py +0 -0
  138. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/test_docs_yaml.py +0 -0
  139. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/test_identifiers.py +0 -0
  140. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/test_intent.py +0 -0
  141. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/test_recording.py +0 -0
  142. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/test_rules.py +0 -0
  143. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/test_telemetry.py +0 -0
  144. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/test_templating.py +0 -0
  145. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack.egg-info/dependency_links.txt +0 -0
  146. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack.egg-info/entry_points.txt +0 -0
  147. {mockstack-0.14.0 → mockstack-0.14.1}/mockstack.egg-info/top_level.txt +0 -0
  148. {mockstack-0.14.0 → mockstack-0.14.1}/setup.cfg +0 -0
@@ -32,9 +32,10 @@ CLI flags, `MOCKSTACK__*` environment variables, or a `.env` file.
32
32
  - `mockstack/templating.py`: Jinja environment and path-to-template-name resolution
33
33
  - `mockstack/intent.py`, `mockstack/identifiers.py`: POST intent (search, command,
34
34
  create) and path-identifier heuristics
35
- - `mockstack/routers/`: catch-all and homepage routes
35
+ - `mockstack/routers/`: the catch-all route; every path, `/` included, reaches the strategy
36
36
  - `mockstack/tests/`: unit tests; `conftest.py` holds fixtures shared by unit and live
37
- tests (`make_settings`, `make_request`, `write_rules`, `write_template`, `span`), and
37
+ tests (`make_settings`, `make_request`, `write_rules`, `write_template`, `span`, and
38
+ `asgi_client`, an httpx client that calls the `app` fixture in-process), and
38
39
  `strategies/conftest.py` adds strategy helpers (`traced_request`,
39
40
  `proxyrules_strategy`, `apply_rule`, `upstream_send`, `span_attributes`). Build settings with
40
41
  `make_settings`: it ignores
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Define build argument for version
4
4
  ARG PYTHON_IMAGE_VERSION=3.13-slim
5
- ARG MOCKSTACK_VERSION=0.14.0
5
+ ARG MOCKSTACK_VERSION=0.14.1
6
6
 
7
7
  # ---------------------------- Base Image --------------------------------
8
8
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mockstack
3
- Version: 0.14.0
3
+ Version: 0.14.1
4
4
  Summary: An API mocking workhorse
5
5
  Author-email: Adam Ever-Hadani <mockstack.contact@gmail.com>
6
6
  License-Expression: MIT
@@ -17,7 +17,7 @@ Requires-Python: >=3.13
17
17
  Description-Content-Type: text/markdown
18
18
  License-File: LICENSE
19
19
  Requires-Dist: colorama>=0.4.6
20
- Requires-Dist: fastapi[standard]>=0.141.1
20
+ Requires-Dist: fastapi>=0.141.1
21
21
  Requires-Dist: httpx>=0.28.1
22
22
  Requires-Dist: jinja2>=3.1.6
23
23
  Requires-Dist: opentelemetry-distro[otlp]>=0.65b0
@@ -27,7 +27,7 @@ Requires-Dist: pydantic-settings>=2.15.0
27
27
  Requires-Dist: pyyaml>=6.0.3
28
28
  Requires-Dist: rich>=15.0.0
29
29
  Requires-Dist: typer>=0.27.2
30
- Requires-Dist: uvicorn>=0.53.0
30
+ Requires-Dist: uvicorn[standard]>=0.53.0
31
31
  Provides-Extra: llm
32
32
  Requires-Dist: ollama>=0.6.2; extra == "llm"
33
33
  Dynamic: license-file
@@ -11,7 +11,6 @@ from mockstack.config import CliSettings, Settings, settings_provider
11
11
  from mockstack.lifespan import lifespan_provider
12
12
  from mockstack.middleware import middleware_provider
13
13
  from mockstack.routers.catchall import catchall_router_provider
14
- from mockstack.routers.homepage import homepage_router_provider
15
14
  from mockstack.strategies.factory import strategy_provider
16
15
  from mockstack.telemetry import opentelemetry_provider
17
16
 
@@ -26,7 +25,6 @@ def create_app(settings: Settings | None = None) -> FastAPI:
26
25
  middleware_provider(app, settings)
27
26
  opentelemetry_provider(app, settings)
28
27
 
29
- homepage_router_provider(app, settings)
30
28
  catchall_router_provider(app, settings)
31
29
 
32
30
  return app
@@ -1,12 +1,14 @@
1
1
  """Fixtures shared by the unit tests and the live tests."""
2
2
 
3
3
  import os
4
- from collections.abc import Callable, Iterable, Mapping
4
+ from collections.abc import AsyncIterator, Callable, Iterable, Mapping
5
5
  from pathlib import Path
6
6
  from typing import Any
7
7
  from unittest.mock import MagicMock
8
8
 
9
+ import httpx
9
10
  import pytest
11
+ import pytest_asyncio
10
12
  import yaml
11
13
  from fastapi import FastAPI, Request
12
14
  from starlette.types import Message
@@ -121,6 +123,17 @@ def app(settings, span):
121
123
  return app
122
124
 
123
125
 
126
+ @pytest_asyncio.fixture
127
+ async def asgi_client(app) -> AsyncIterator[httpx.AsyncClient]:
128
+ """An ``httpx.AsyncClient`` that calls the ``app`` fixture in-process.
129
+
130
+ Requests go through ``httpx.ASGITransport``, so routes and middleware added to
131
+ ``app`` before the first request are served. The app's lifespan does not run.
132
+ """
133
+ async with httpx.AsyncClient(transport=httpx.ASGITransport(app=app), base_url="http://testserver") as client:
134
+ yield client
135
+
136
+
124
137
  @pytest.fixture
125
138
  def make_request() -> Callable[..., Request]:
126
139
  """Factory: a Starlette ``Request`` with a real ASGI ``receive`` channel.
@@ -4,7 +4,6 @@ from unittest.mock import AsyncMock
4
4
 
5
5
  import pytest
6
6
  from fastapi.responses import JSONResponse
7
- from starlette.testclient import TestClient
8
7
 
9
8
  from mockstack.routers.catchall import catchall_router_provider
10
9
 
@@ -19,41 +18,43 @@ def mock_strategy():
19
18
 
20
19
 
21
20
  @pytest.fixture
22
- def client(app, settings, mock_strategy):
21
+ def client(app, settings, mock_strategy, asgi_client):
23
22
  """A test client for an app whose catch-all router uses ``mock_strategy``."""
24
23
  app.state.strategy = mock_strategy
25
24
  catchall_router_provider(app, settings)
26
- return TestClient(app)
25
+ return asgi_client
27
26
 
28
27
 
28
+ @pytest.mark.asyncio
29
29
  @pytest.mark.parametrize("method", ["GET", "POST", "PUT", "DELETE", "PATCH"])
30
- def test_catchall_router_provider(client, mock_strategy, method):
30
+ async def test_catchall_router_provider(client, mock_strategy, method):
31
31
  """Test that the catchall router provider sets up routes correctly."""
32
- response = client.request(method, "/test/path")
32
+ response = await client.request(method, "/test/path")
33
33
 
34
34
  assert response.status_code == 200
35
35
  assert response.json() == {"message": "Mock response"}
36
36
  mock_strategy.apply.assert_called_once()
37
37
 
38
38
 
39
+ @pytest.mark.asyncio
39
40
  @pytest.mark.parametrize("method", ["HEAD", "OPTIONS"])
40
- def test_catchall_router_routes_head_and_options_to_strategy(client, mock_strategy, method):
41
+ async def test_catchall_router_routes_head_and_options_to_strategy(client, mock_strategy, method):
41
42
  """HEAD and OPTIONS must reach the strategy rather than get a router-level 405."""
42
- response = client.request(method, "/test/path")
43
+ response = await client.request(method, "/test/path")
43
44
 
44
45
  assert response.status_code == 200
45
46
  mock_strategy.apply.assert_called_once()
46
47
  assert mock_strategy.apply.call_args.args[0].method == method
47
48
 
48
49
 
50
+ @pytest.mark.asyncio
49
51
  @pytest.mark.parametrize("method", ["HEAD", "OPTIONS"])
50
- def test_catchall_router_filefixtures_still_rejects_head_and_options(app, settings, method):
52
+ async def test_catchall_router_filefixtures_still_rejects_head_and_options(app, settings, asgi_client, method):
51
53
  """Routed to the filefixtures strategy (the ``app`` fixture's), HEAD and OPTIONS are
52
54
  still answered 405 -- now by the strategy itself rather than by the router."""
53
55
  catchall_router_provider(app, settings)
54
- client = TestClient(app, raise_server_exceptions=True)
55
56
 
56
- response = client.request(method, "/test/path")
57
+ response = await asgi_client.request(method, "/test/path")
57
58
 
58
59
  assert response.status_code == 405
59
60
  if method == "OPTIONS":
@@ -0,0 +1,20 @@
1
+ """Tests for the application factory."""
2
+
3
+ import httpx
4
+ import pytest
5
+
6
+ from mockstack.main import create_app
7
+
8
+
9
+ @pytest.mark.asyncio
10
+ async def test_create_app_routes_root_path_to_strategy(make_settings, write_template, tmp_path):
11
+ """``GET /`` reaches the strategy through the catch-all route: the app registers no
12
+ route of its own at the root path."""
13
+ write_template("index.j2", '{"message": "root fixture"}')
14
+ app = create_app(make_settings(strategy="filefixtures", templates_dir=tmp_path))
15
+
16
+ async with httpx.AsyncClient(transport=httpx.ASGITransport(app=app), base_url="http://testserver") as client:
17
+ response = await client.get("/")
18
+
19
+ assert response.status_code == 200
20
+ assert response.json() == {"message": "root fixture"}
@@ -1,11 +1,12 @@
1
1
  """Tests for the middleware module."""
2
2
 
3
- from starlette.testclient import TestClient
3
+ import pytest
4
4
 
5
5
  from mockstack.middleware import middleware_provider
6
6
 
7
7
 
8
- def test_middleware_provider_process_time(app, settings):
8
+ @pytest.mark.asyncio
9
+ async def test_middleware_provider_process_time(app, settings, asgi_client):
9
10
  """Test that the middleware provider adds the process time header."""
10
11
  middleware_provider(app, settings)
11
12
 
@@ -13,7 +14,7 @@ def test_middleware_provider_process_time(app, settings):
13
14
  def route():
14
15
  return {"message": "test"}
15
16
 
16
- response = TestClient(app).get("/test")
17
+ response = await asgi_client.get("/test")
17
18
 
18
19
  assert response.status_code == 200
19
20
  assert response.json() == {"message": "test"}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mockstack
3
- Version: 0.14.0
3
+ Version: 0.14.1
4
4
  Summary: An API mocking workhorse
5
5
  Author-email: Adam Ever-Hadani <mockstack.contact@gmail.com>
6
6
  License-Expression: MIT
@@ -17,7 +17,7 @@ Requires-Python: >=3.13
17
17
  Description-Content-Type: text/markdown
18
18
  License-File: LICENSE
19
19
  Requires-Dist: colorama>=0.4.6
20
- Requires-Dist: fastapi[standard]>=0.141.1
20
+ Requires-Dist: fastapi>=0.141.1
21
21
  Requires-Dist: httpx>=0.28.1
22
22
  Requires-Dist: jinja2>=3.1.6
23
23
  Requires-Dist: opentelemetry-distro[otlp]>=0.65b0
@@ -27,7 +27,7 @@ Requires-Dist: pydantic-settings>=2.15.0
27
27
  Requires-Dist: pyyaml>=6.0.3
28
28
  Requires-Dist: rich>=15.0.0
29
29
  Requires-Dist: typer>=0.27.2
30
- Requires-Dist: uvicorn>=0.53.0
30
+ Requires-Dist: uvicorn[standard]>=0.53.0
31
31
  Provides-Extra: llm
32
32
  Requires-Dist: ollama>=0.6.2; extra == "llm"
33
33
  Dynamic: license-file
@@ -99,7 +99,6 @@ mockstack/llm/__init__.py
99
99
  mockstack/llm/ollama.py
100
100
  mockstack/routers/__init__.py
101
101
  mockstack/routers/catchall.py
102
- mockstack/routers/homepage.py
103
102
  mockstack/strategies/__init__.py
104
103
  mockstack/strategies/base.py
105
104
  mockstack/strategies/create_mixin.py
@@ -114,6 +113,7 @@ mockstack/tests/test_docs_settings.py
114
113
  mockstack/tests/test_docs_yaml.py
115
114
  mockstack/tests/test_identifiers.py
116
115
  mockstack/tests/test_intent.py
116
+ mockstack/tests/test_main.py
117
117
  mockstack/tests/test_middleware.py
118
118
  mockstack/tests/test_recording.py
119
119
  mockstack/tests/test_rules.py
@@ -135,7 +135,6 @@ mockstack/tests/llm/__init__.py
135
135
  mockstack/tests/llm/test_ollama.py
136
136
  mockstack/tests/routers/__init__.py
137
137
  mockstack/tests/routers/test_catchall.py
138
- mockstack/tests/routers/test_homepage.py
139
138
  mockstack/tests/strategies/__init__.py
140
139
  mockstack/tests/strategies/conftest.py
141
140
  mockstack/tests/strategies/test_create_mixin.py
@@ -1,5 +1,5 @@
1
1
  colorama>=0.4.6
2
- fastapi[standard]>=0.141.1
2
+ fastapi>=0.141.1
3
3
  httpx>=0.28.1
4
4
  jinja2>=3.1.6
5
5
  opentelemetry-distro[otlp]>=0.65b0
@@ -9,7 +9,7 @@ pydantic-settings>=2.15.0
9
9
  pyyaml>=6.0.3
10
10
  rich>=15.0.0
11
11
  typer>=0.27.2
12
- uvicorn>=0.53.0
12
+ uvicorn[standard]>=0.53.0
13
13
 
14
14
  [llm]
15
15
  ollama>=0.6.2
@@ -88,7 +88,6 @@
88
88
  "mockstack/recording.py",
89
89
  "mockstack/routers/__init__.py",
90
90
  "mockstack/routers/catchall.py",
91
- "mockstack/routers/homepage.py",
92
91
  "mockstack/rules.py",
93
92
  "mockstack/strategies/__init__.py",
94
93
  "mockstack/strategies/base.py",
@@ -116,7 +115,6 @@
116
115
  "mockstack/tests/llm/test_ollama.py",
117
116
  "mockstack/tests/routers/__init__.py",
118
117
  "mockstack/tests/routers/test_catchall.py",
119
- "mockstack/tests/routers/test_homepage.py",
120
118
  "mockstack/tests/strategies/__init__.py",
121
119
  "mockstack/tests/strategies/conftest.py",
122
120
  "mockstack/tests/strategies/test_create_mixin.py",
@@ -130,6 +128,7 @@
130
128
  "mockstack/tests/test_docs_yaml.py",
131
129
  "mockstack/tests/test_identifiers.py",
132
130
  "mockstack/tests/test_intent.py",
131
+ "mockstack/tests/test_main.py",
133
132
  "mockstack/tests/test_middleware.py",
134
133
  "mockstack/tests/test_recording.py",
135
134
  "mockstack/tests/test_rules.py",
@@ -1,7 +1,7 @@
1
1
  {
2
- "tag": "0.14.0",
2
+ "tag": "0.14.1",
3
3
  "distance": 0,
4
- "node": "g96e7689044f1b72430a7acb6ab3cec1989805660",
4
+ "node": "g6d3026d1e83dfcb152634ce3af4505beac16a54e",
5
5
  "dirty": false,
6
6
  "branch": "HEAD",
7
7
  "node_date": "2026-09-15"
@@ -21,7 +21,7 @@ classifiers = [
21
21
  requires-python = ">=3.13"
22
22
  dependencies = [
23
23
  "colorama>=0.4.6",
24
- "fastapi[standard]>=0.141.1",
24
+ "fastapi>=0.141.1",
25
25
  "httpx>=0.28.1",
26
26
  "jinja2>=3.1.6",
27
27
  "opentelemetry-distro[otlp]>=0.65b0",
@@ -31,7 +31,8 @@ dependencies = [
31
31
  "pyyaml>=6.0.3",
32
32
  "rich>=15.0.0",
33
33
  "typer>=0.27.2",
34
- "uvicorn>=0.53.0",
34
+ # The standard extra installs uvloop and httptools, which uvicorn uses when present.
35
+ "uvicorn[standard]>=0.53.0",
35
36
  ]
36
37
  dynamic = ["version"]
37
38
 
@@ -94,11 +95,8 @@ addopts = "-m 'not slow'"
94
95
  # strict_config, strict_markers, strict_xfail and strict_parametrization_ids.
95
96
  strict = true
96
97
  filterwarnings = [
97
- # A warning raised from mockstack's own modules (tests included) is an error...
98
+ # A warning raised from mockstack's own modules (tests included) is an error.
98
99
  "error:::mockstack",
99
- # ...except Starlette's deprecation of httpx in its test client, which it attributes
100
- # to the module importing `starlette.testclient`.
101
- "default:Using `httpx` with `starlette.testclient` is deprecated",
102
100
  ]
103
101
 
104
102
  [tool.coverage.run]