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.
- {mockstack-0.14.0 → mockstack-0.14.1}/CLAUDE.md +3 -2
- {mockstack-0.14.0 → mockstack-0.14.1}/Dockerfile +1 -1
- {mockstack-0.14.0 → mockstack-0.14.1}/PKG-INFO +3 -3
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/main.py +0 -2
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/conftest.py +14 -1
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/routers/test_catchall.py +11 -10
- mockstack-0.14.1/mockstack/tests/test_main.py +20 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/test_middleware.py +4 -3
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack.egg-info/PKG-INFO +3 -3
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack.egg-info/SOURCES.txt +1 -2
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack.egg-info/requires.txt +2 -2
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack.egg-info/scm_file_list.json +1 -2
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack.egg-info/scm_version.json +2 -2
- {mockstack-0.14.0 → mockstack-0.14.1}/pyproject.toml +4 -6
- {mockstack-0.14.0 → mockstack-0.14.1}/uv.lock +4 -328
- mockstack-0.14.0/mockstack/routers/homepage.py +0 -19
- mockstack-0.14.0/mockstack/tests/routers/test_homepage.py +0 -15
- {mockstack-0.14.0 → mockstack-0.14.1}/.dockerignore +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/.env.example +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/.github/workflows/ci.yml +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/.github/workflows/publish-docs.yml +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/.github/workflows/publish-to-pypi.yml +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/.github/workflows/smoke-test-pypi.yml +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/.gitignore +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/.pre-commit-config.yaml +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/CODE_OF_CONDUCT.md +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/CONTRIBUTING.md +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/LICENSE +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/README.md +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/SECURITY.md +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/docker-entrypoint.sh +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/docs/assets/favicon.ico +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/docs/assets/logo.png +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/docs/assets/mockstack.png +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/docs/configuration.md +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/docs/guides/proxyrules-cookbook.md +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/docs/ollama.md +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/docs/strategies/base.md +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/docs/strategies/filefixtures.md +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/docs/strategies/proxyrules.md +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/filefixtures-with-templates/.env.example +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/filefixtures-with-templates/README.md +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/filefixtures-with-templates/templates/servicename-api-v1-items.j2 +0 -0
- {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
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/llm/README.md +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/llm/mockstack-langchain-example.ipynb +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/llm/templates/ollama-openai-v1-chat-completions.j2 +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/llm/templates/openai-v1-chat-completions.j2 +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/llm/templates/openai-v1-deployments-gpt-4o-chat-completions.j2 +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/01-tagged-traffic/fixtures/projects/project.json.j2 +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/01-tagged-traffic/rules.yml +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/02-scenario-directories/fixtures/archived/projects/project.json.j2 +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/02-scenario-directories/fixtures/healthy/projects/project.json.j2 +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/02-scenario-directories/rules.yml +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/03-sql-gateway/fixtures/analytics/sales_facts.json.j2 +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/03-sql-gateway/rules.yml +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/04-json-literals/fixtures/orders/search.json.j2 +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/04-json-literals/rules.yml +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/05-query-parameters/fixtures/users/archived.json.j2 +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/05-query-parameters/fixtures/users/page.json.j2 +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/05-query-parameters/rules.yml +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/06-asserting-in-tests/fixture_assertions.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/06-asserting-in-tests/fixtures/orders/echo.json.j2 +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/06-asserting-in-tests/fixtures/projects/project.json.j2 +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/06-asserting-in-tests/fixtures/users/user-1.json.j2 +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/06-asserting-in-tests/rules.yml +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/07-redirect-mode/fixtures/users/user-1.json.j2 +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/07-redirect-mode/rules.yml +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/08-status-and-headers/fixtures/errors/rate-limited.json.j2 +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/08-status-and-headers/fixtures/errors/unavailable.json.j2 +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/08-status-and-headers/fixtures/orders/created.json.j2 +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/08-status-and-headers/rules.yml +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/09-recording/rules.yml +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/09-recording/scrubbers.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/README.md +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-cookbook/upstream.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-eval-isolation/.env.example +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-eval-isolation/README.md +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-eval-isolation/fixtures/healthy/analytics/sales_facts.json.j2 +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-eval-isolation/fixtures/healthy/projects/project.json.j2 +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-eval-isolation/rules.yml +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-eval-isolation/upstream.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-with-rules-file/.env.example +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-with-rules-file/README.md +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/examples/proxyrules-with-rules-file/rules.yml +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mkdocs.yml +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/__init__.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/config.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/constants.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/display.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/exceptions.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/identifiers.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/intent.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/lifespan.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/llm/__init__.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/llm/ollama.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/middleware.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/recording.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/routers/__init__.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/routers/catchall.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/rules.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/strategies/__init__.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/strategies/base.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/strategies/create_mixin.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/strategies/factory.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/strategies/filefixtures.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/strategies/proxyrules.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/telemetry.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/templating.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/__init__.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/fixtures/__init__.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/fixtures/proxyrules.yml +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/fixtures/templates/__init__.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/fixtures/templates/example-template.j2 +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/live/__init__.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/live/conftest.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/live/test_cookbook.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/live/test_example_eval_isolation.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/live/test_fixture_status_live.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/live/test_predicates_live.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/live/test_record_mode_live.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/live/test_reverse_proxy_live.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/llm/__init__.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/llm/test_ollama.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/routers/__init__.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/strategies/__init__.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/strategies/conftest.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/strategies/test_create_mixin.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/strategies/test_filefixtures.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/strategies/test_filefixtures_example.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/strategies/test_proxyrules.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/strategies/test_proxyrules_record.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/test_config.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/test_display.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/test_docs_settings.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/test_docs_yaml.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/test_identifiers.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/test_intent.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/test_recording.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/test_rules.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/test_telemetry.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack/tests/test_templating.py +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack.egg-info/dependency_links.txt +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack.egg-info/entry_points.txt +0 -0
- {mockstack-0.14.0 → mockstack-0.14.1}/mockstack.egg-info/top_level.txt +0 -0
- {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
|
|
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
|
|
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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mockstack
|
|
3
|
-
Version: 0.14.
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
3
|
+
import pytest
|
|
4
4
|
|
|
5
5
|
from mockstack.middleware import middleware_provider
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
|
|
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 =
|
|
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.
|
|
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
|
|
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
|
|
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",
|
|
@@ -21,7 +21,7 @@ classifiers = [
|
|
|
21
21
|
requires-python = ">=3.13"
|
|
22
22
|
dependencies = [
|
|
23
23
|
"colorama>=0.4.6",
|
|
24
|
-
"fastapi
|
|
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
|
-
|
|
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]
|