mockstack 0.12.0__tar.gz → 0.13.0__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.13.0/.env.example +38 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/.github/workflows/ci.yml +26 -10
- {mockstack-0.12.0 → mockstack-0.13.0}/.github/workflows/publish-docs.yml +3 -3
- {mockstack-0.12.0 → mockstack-0.13.0}/.github/workflows/publish-to-pypi.yml +6 -6
- mockstack-0.13.0/.github/workflows/smoke-test-pypi.yml +83 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/.gitignore +3 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/.pre-commit-config.yaml +14 -8
- mockstack-0.13.0/CLAUDE.md +98 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/CONTRIBUTING.md +17 -13
- {mockstack-0.12.0 → mockstack-0.13.0}/Dockerfile +1 -1
- {mockstack-0.12.0/mockstack.egg-info → mockstack-0.13.0}/PKG-INFO +65 -24
- mockstack-0.12.0/PKG-INFO → mockstack-0.13.0/README.md +50 -43
- mockstack-0.13.0/docs/configuration.md +127 -0
- mockstack-0.13.0/docs/guides/proxyrules-cookbook.md +418 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/docs/ollama.md +5 -1
- {mockstack-0.12.0 → mockstack-0.13.0}/docs/strategies/base.md +1 -0
- mockstack-0.13.0/docs/strategies/filefixtures.md +136 -0
- mockstack-0.13.0/docs/strategies/proxyrules.md +447 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/examples/filefixtures-with-templates/.env.example +1 -1
- {mockstack-0.12.0 → mockstack-0.13.0}/examples/filefixtures-with-templates/README.md +2 -2
- mockstack-0.13.0/examples/filefixtures-with-templates/templates/servicename-api-v1-items.j2 +20 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/examples/llm/mockstack-langchain-example.ipynb +15 -12
- mockstack-0.13.0/examples/proxyrules-cookbook/01-tagged-traffic/fixtures/projects/project.json.j2 +1 -0
- mockstack-0.13.0/examples/proxyrules-cookbook/01-tagged-traffic/rules.yml +11 -0
- mockstack-0.13.0/examples/proxyrules-cookbook/02-scenario-directories/fixtures/archived/projects/project.json.j2 +1 -0
- mockstack-0.13.0/examples/proxyrules-cookbook/02-scenario-directories/fixtures/healthy/projects/project.json.j2 +1 -0
- mockstack-0.13.0/examples/proxyrules-cookbook/02-scenario-directories/rules.yml +11 -0
- mockstack-0.13.0/examples/proxyrules-cookbook/03-sql-gateway/fixtures/analytics/sales_facts.json.j2 +1 -0
- mockstack-0.13.0/examples/proxyrules-cookbook/03-sql-gateway/rules.yml +11 -0
- mockstack-0.13.0/examples/proxyrules-cookbook/04-json-literals/fixtures/orders/search.json.j2 +1 -0
- mockstack-0.13.0/examples/proxyrules-cookbook/04-json-literals/rules.yml +32 -0
- mockstack-0.13.0/examples/proxyrules-cookbook/05-query-parameters/fixtures/users/archived.json.j2 +1 -0
- mockstack-0.13.0/examples/proxyrules-cookbook/05-query-parameters/fixtures/users/page.json.j2 +1 -0
- mockstack-0.13.0/examples/proxyrules-cookbook/05-query-parameters/rules.yml +18 -0
- mockstack-0.13.0/examples/proxyrules-cookbook/06-asserting-in-tests/fixture_assertions.py +40 -0
- mockstack-0.13.0/examples/proxyrules-cookbook/06-asserting-in-tests/fixtures/orders/echo.json.j2 +1 -0
- mockstack-0.13.0/examples/proxyrules-cookbook/06-asserting-in-tests/fixtures/projects/project.json.j2 +1 -0
- mockstack-0.13.0/examples/proxyrules-cookbook/06-asserting-in-tests/fixtures/users/user-1.json.j2 +1 -0
- mockstack-0.13.0/examples/proxyrules-cookbook/06-asserting-in-tests/rules.yml +44 -0
- mockstack-0.13.0/examples/proxyrules-cookbook/07-redirect-mode/fixtures/users/user-1.json.j2 +1 -0
- mockstack-0.13.0/examples/proxyrules-cookbook/07-redirect-mode/rules.yml +9 -0
- mockstack-0.13.0/examples/proxyrules-cookbook/README.md +54 -0
- mockstack-0.13.0/examples/proxyrules-cookbook/upstream.py +40 -0
- mockstack-0.13.0/examples/proxyrules-eval-isolation/.env.example +11 -0
- mockstack-0.13.0/examples/proxyrules-eval-isolation/README.md +78 -0
- mockstack-0.13.0/examples/proxyrules-eval-isolation/fixtures/healthy/analytics/sales_facts.json.j2 +1 -0
- mockstack-0.13.0/examples/proxyrules-eval-isolation/fixtures/healthy/projects/project.json.j2 +1 -0
- mockstack-0.13.0/examples/proxyrules-eval-isolation/rules.yml +26 -0
- mockstack-0.13.0/examples/proxyrules-eval-isolation/upstream.py +27 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/examples/proxyrules-with-rules-file/.env.example +2 -2
- mockstack-0.13.0/examples/proxyrules-with-rules-file/README.md +16 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/mkdocs.yml +5 -2
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/config.py +21 -21
- mockstack-0.13.0/mockstack/constants.py +54 -0
- mockstack-0.13.0/mockstack/display.py +37 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/exceptions.py +3 -1
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/identifiers.py +9 -10
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/lifespan.py +3 -3
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/llm/ollama.py +9 -8
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/main.py +10 -10
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/middleware.py +11 -10
- mockstack-0.13.0/mockstack/routers/catchall.py +23 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/routers/homepage.py +3 -2
- mockstack-0.13.0/mockstack/rules.py +319 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/strategies/__init__.py +1 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/strategies/base.py +4 -4
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/strategies/create_mixin.py +12 -19
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/strategies/factory.py +1 -3
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/strategies/filefixtures.py +51 -21
- mockstack-0.13.0/mockstack/strategies/proxyrules.py +535 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/telemetry.py +9 -20
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/templating.py +19 -21
- mockstack-0.13.0/mockstack/tests/conftest.py +189 -0
- mockstack-0.13.0/mockstack/tests/live/conftest.py +253 -0
- mockstack-0.13.0/mockstack/tests/live/test_cookbook.py +523 -0
- mockstack-0.13.0/mockstack/tests/live/test_example_eval_isolation.py +89 -0
- mockstack-0.13.0/mockstack/tests/live/test_predicates_live.py +58 -0
- mockstack-0.13.0/mockstack/tests/live/test_reverse_proxy_live.py +116 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/tests/llm/test_ollama.py +32 -36
- mockstack-0.13.0/mockstack/tests/routers/test_catchall.py +60 -0
- mockstack-0.13.0/mockstack/tests/routers/test_homepage.py +15 -0
- mockstack-0.13.0/mockstack/tests/strategies/__init__.py +0 -0
- mockstack-0.13.0/mockstack/tests/strategies/conftest.py +52 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/tests/strategies/test_create_mixin.py +19 -75
- mockstack-0.13.0/mockstack/tests/strategies/test_filefixtures.py +294 -0
- mockstack-0.13.0/mockstack/tests/strategies/test_filefixtures_example.py +41 -0
- mockstack-0.13.0/mockstack/tests/strategies/test_proxyrules.py +1110 -0
- mockstack-0.13.0/mockstack/tests/test_config.py +58 -0
- mockstack-0.13.0/mockstack/tests/test_docs_settings.py +73 -0
- mockstack-0.13.0/mockstack/tests/test_docs_yaml.py +54 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/tests/test_identifiers.py +29 -40
- mockstack-0.13.0/mockstack/tests/test_intent.py +76 -0
- mockstack-0.13.0/mockstack/tests/test_middleware.py +23 -0
- mockstack-0.13.0/mockstack/tests/test_rules.py +750 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/tests/test_telemetry.py +40 -73
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/tests/test_templating.py +75 -123
- mockstack-0.12.0/README.md → mockstack-0.13.0/mockstack.egg-info/PKG-INFO +84 -10
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack.egg-info/SOURCES.txt +43 -0
- mockstack-0.13.0/mockstack.egg-info/requires.txt +15 -0
- mockstack-0.13.0/mockstack.egg-info/scm_file_list.json +130 -0
- mockstack-0.13.0/mockstack.egg-info/scm_version.json +8 -0
- mockstack-0.13.0/pyproject.toml +167 -0
- mockstack-0.13.0/uv.lock +1936 -0
- mockstack-0.12.0/.env.example +0 -25
- mockstack-0.12.0/docs/configuration.md +0 -103
- mockstack-0.12.0/docs/strategies/filefixtures.md +0 -109
- mockstack-0.12.0/docs/strategies/proxyrules.md +0 -123
- mockstack-0.12.0/examples/filefixtures-with-templates/templates/servicename-api-v1-items.j2 +0 -19
- mockstack-0.12.0/examples/proxyrules-with-rules-file/README.md +0 -15
- mockstack-0.12.0/mockstack/constants.py +0 -38
- mockstack-0.12.0/mockstack/display.py +0 -30
- mockstack-0.12.0/mockstack/routers/catchall.py +0 -16
- mockstack-0.12.0/mockstack/rules.py +0 -117
- mockstack-0.12.0/mockstack/strategies/proxyrules.py +0 -274
- mockstack-0.12.0/mockstack/tests/conftest.py +0 -78
- mockstack-0.12.0/mockstack/tests/routers/test_catchall.py +0 -46
- mockstack-0.12.0/mockstack/tests/routers/test_homepage.py +0 -27
- mockstack-0.12.0/mockstack/tests/strategies/test_filefixtures.py +0 -275
- mockstack-0.12.0/mockstack/tests/strategies/test_proxyrules.py +0 -397
- mockstack-0.12.0/mockstack/tests/test_intent.py +0 -160
- mockstack-0.12.0/mockstack/tests/test_middleware.py +0 -37
- mockstack-0.12.0/mockstack/tests/test_rules.py +0 -168
- mockstack-0.12.0/mockstack.egg-info/requires.txt +0 -15
- mockstack-0.12.0/pyproject.toml +0 -69
- mockstack-0.12.0/uv.lock +0 -1535
- {mockstack-0.12.0 → mockstack-0.13.0}/.dockerignore +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/CODE_OF_CONDUCT.md +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/LICENSE +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/SECURITY.md +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/docker-entrypoint.sh +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/docs/assets/favicon.ico +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/docs/assets/logo.png +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/docs/assets/mockstack.png +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/examples/filefixtures-with-templates/templates/servicename2-api-v2-user.533ec889-7c68-45c8-b21e-4a7e455d1234.j2 +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/examples/llm/README.md +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/examples/llm/templates/ollama-openai-v1-chat-completions.j2 +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/examples/llm/templates/openai-v1-chat-completions.j2 +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/examples/llm/templates/openai-v1-deployments-gpt-4o-chat-completions.j2 +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/examples/proxyrules-with-rules-file/rules.yml +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/__init__.py +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/intent.py +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/llm/__init__.py +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/routers/__init__.py +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/tests/__init__.py +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/tests/fixtures/__init__.py +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/tests/fixtures/proxyrules.yml +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/tests/fixtures/templates/__init__.py +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/tests/fixtures/templates/example-template.j2 +0 -0
- {mockstack-0.12.0/mockstack/tests/llm → mockstack-0.13.0/mockstack/tests/live}/__init__.py +0 -0
- {mockstack-0.12.0/mockstack/tests/routers → mockstack-0.13.0/mockstack/tests/llm}/__init__.py +0 -0
- {mockstack-0.12.0/mockstack/tests/strategies → mockstack-0.13.0/mockstack/tests/routers}/__init__.py +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/tests/test_display.py +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack.egg-info/dependency_links.txt +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack.egg-info/entry_points.txt +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/mockstack.egg-info/top_level.txt +0 -0
- {mockstack-0.12.0 → mockstack-0.13.0}/setup.cfg +0 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Example environment variables for mockstack.
|
|
2
|
+
# Copy this file to `.env` in the directory you run mockstack from and adjust the values.
|
|
3
|
+
# Every setting, with its default, is listed at https://promptromp.github.io/mockstack/configuration/
|
|
4
|
+
|
|
5
|
+
# the strategy to use: filefixtures (the default) or proxyrules
|
|
6
|
+
MOCKSTACK__STRATEGY = filefixtures
|
|
7
|
+
|
|
8
|
+
# strategy-specific settings
|
|
9
|
+
|
|
10
|
+
# base dir for all templates. Required by the 'filefixtures' strategy
|
|
11
|
+
# (and other hybrid strategies utilizing templates); the directory must exist.
|
|
12
|
+
MOCKSTACK__TEMPLATES_DIR = "/some/path/templates/"
|
|
13
|
+
|
|
14
|
+
# When using filefixtures strategy, controls whether POST requests first try a
|
|
15
|
+
# matching template (default: true). With templates off, or when no template
|
|
16
|
+
# matches, a POST is handled as a search, a command or a simulated create.
|
|
17
|
+
MOCKSTACK__FILEFIXTURES_ENABLE_TEMPLATES_FOR_POST = false
|
|
18
|
+
|
|
19
|
+
# When using filefixtures strategy, controls whether a POST with no matching template
|
|
20
|
+
# simulates creating the resource (default: true) or gets the same 404 as a GET.
|
|
21
|
+
MOCKSTACK__FILEFIXTURES_SIMULATE_CREATE_ON_MISSING = true
|
|
22
|
+
|
|
23
|
+
# rules file for the proxyrules strategy. Required by the 'proxyrules' strategy; the
|
|
24
|
+
# file must exist. See examples/proxyrules-with-rules-file/ and
|
|
25
|
+
# mockstack/tests/fixtures/proxyrules.yml for examples.
|
|
26
|
+
MOCKSTACK__PROXYRULES_RULES_FILENAME="/some/path/proxyrules.yml"
|
|
27
|
+
|
|
28
|
+
# reverse_proxy (default), http_307_temporary or http_301_permanent
|
|
29
|
+
MOCKSTACK__PROXYRULES_REDIRECT_VIA = reverse_proxy
|
|
30
|
+
# timeout in seconds for reverse-proxied requests (default: 10.0)
|
|
31
|
+
MOCKSTACK__PROXYRULES_REVERSE_PROXY_TIMEOUT = 10.0
|
|
32
|
+
MOCKSTACK__PROXYRULES_SIMULATE_CREATE_ON_MISSING = true
|
|
33
|
+
MOCKSTACK__PROXYRULES_VERIFY_SSL_CERTIFICATES = true
|
|
34
|
+
|
|
35
|
+
# OpenTelemetry settings
|
|
36
|
+
MOCKSTACK__OPENTELEMETRY__ENABLED = true
|
|
37
|
+
MOCKSTACK__OPENTELEMETRY__ENDPOINT = "http://localhost:4317/"
|
|
38
|
+
MOCKSTACK__OPENTELEMETRY__CAPTURE_RESPONSE_BODY = true
|
|
@@ -20,32 +20,48 @@ jobs:
|
|
|
20
20
|
strategy:
|
|
21
21
|
fail-fast: false
|
|
22
22
|
matrix:
|
|
23
|
-
python-version: ["3.
|
|
23
|
+
python-version: ["3.13", "3.14"]
|
|
24
24
|
|
|
25
25
|
steps:
|
|
26
|
-
- uses: actions/checkout@
|
|
26
|
+
- uses: actions/checkout@v6
|
|
27
27
|
- name: Install uv and set the python version
|
|
28
|
-
uses: astral-sh/setup-uv@
|
|
28
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
29
29
|
with:
|
|
30
30
|
python-version: ${{ matrix.python-version }}
|
|
31
31
|
version: "latest"
|
|
32
32
|
- name: Install ruff
|
|
33
|
-
uses: astral-sh/ruff-action@
|
|
33
|
+
uses: astral-sh/ruff-action@v4.0.0
|
|
34
34
|
with:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
with:
|
|
39
|
-
python-version: ${{ matrix.python-version }}
|
|
35
|
+
# Pinned to the ruff-pre-commit rev: new ruff releases add rules to the selected
|
|
36
|
+
# families, which should arrive through a deliberate bump, not a random CI run.
|
|
37
|
+
version: "0.16.7"
|
|
40
38
|
- name: Install the project
|
|
41
39
|
run: uv sync --all-extras --dev
|
|
42
40
|
- name: Run ruff lint and formatting checks
|
|
43
41
|
run: |
|
|
44
42
|
ruff check
|
|
45
|
-
ruff format
|
|
43
|
+
ruff format --check
|
|
46
44
|
- name: Run mypy
|
|
47
45
|
run: |
|
|
48
46
|
uv pip install types-pyyaml
|
|
49
47
|
uv run mypy mockstack
|
|
50
48
|
- name: Run tests
|
|
51
49
|
run: uv run pytest
|
|
50
|
+
- name: Run live tests
|
|
51
|
+
run: uv run pytest -m slow mockstack/tests/live -v
|
|
52
|
+
|
|
53
|
+
docs:
|
|
54
|
+
|
|
55
|
+
runs-on: ubuntu-latest
|
|
56
|
+
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@v6
|
|
59
|
+
- name: Install uv and set the python version
|
|
60
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
61
|
+
with:
|
|
62
|
+
python-version: "3.13"
|
|
63
|
+
version: "latest"
|
|
64
|
+
- name: Build docs strictly
|
|
65
|
+
run: |
|
|
66
|
+
cp README.md docs/
|
|
67
|
+
uvx --with mkdocs-material mkdocs build --strict
|
|
@@ -13,16 +13,16 @@ jobs:
|
|
|
13
13
|
deploy:
|
|
14
14
|
runs-on: ubuntu-latest
|
|
15
15
|
steps:
|
|
16
|
-
- uses: actions/checkout@
|
|
16
|
+
- uses: actions/checkout@v6
|
|
17
17
|
- name: Configure Git Credentials
|
|
18
18
|
run: |
|
|
19
19
|
git config --global user.name "${GITHUB_ACTOR}"
|
|
20
20
|
git config --global user.email "${GITHUB_ACTOR_ID}+${GITHUB_ACTOR}@users.noreply.github.com"
|
|
21
|
-
- uses: actions/setup-python@
|
|
21
|
+
- uses: actions/setup-python@v6
|
|
22
22
|
with:
|
|
23
23
|
python-version: 3.x
|
|
24
24
|
- run: echo "cache_id=$(date --utc '+%V')" >> "${GITHUB_ENV}"
|
|
25
|
-
- uses: actions/cache@
|
|
25
|
+
- uses: actions/cache@v5
|
|
26
26
|
with:
|
|
27
27
|
key: mkdocs-material-${{ env.cache_id }}
|
|
28
28
|
path: .cache
|
|
@@ -10,11 +10,11 @@ jobs:
|
|
|
10
10
|
build:
|
|
11
11
|
runs-on: ubuntu-latest
|
|
12
12
|
steps:
|
|
13
|
-
- uses: actions/checkout@
|
|
13
|
+
- uses: actions/checkout@v6
|
|
14
14
|
with:
|
|
15
15
|
persist-credentials: false
|
|
16
16
|
- name: Set up Python
|
|
17
|
-
uses: actions/setup-python@
|
|
17
|
+
uses: actions/setup-python@v6
|
|
18
18
|
with:
|
|
19
19
|
python-version: "3.x"
|
|
20
20
|
- name: Install pypa/build
|
|
@@ -23,7 +23,7 @@ jobs:
|
|
|
23
23
|
- name: Build a binary wheel and a source tarball
|
|
24
24
|
run: python3 -m build
|
|
25
25
|
- name: Store the distribution packages
|
|
26
|
-
uses: actions/upload-artifact@
|
|
26
|
+
uses: actions/upload-artifact@v7
|
|
27
27
|
with:
|
|
28
28
|
name: python-package-distributions
|
|
29
29
|
path: dist/
|
|
@@ -42,7 +42,7 @@ jobs:
|
|
|
42
42
|
id-token: write # IMPORTANT: mandatory for trusted publishing
|
|
43
43
|
steps:
|
|
44
44
|
- name: Download all the dists
|
|
45
|
-
uses: actions/download-artifact@
|
|
45
|
+
uses: actions/download-artifact@v8
|
|
46
46
|
with:
|
|
47
47
|
name: python-package-distributions
|
|
48
48
|
path: dist/
|
|
@@ -62,7 +62,7 @@ jobs:
|
|
|
62
62
|
id-token: write # IMPORTANT: mandatory for sigstore
|
|
63
63
|
steps:
|
|
64
64
|
- name: Download all the dists
|
|
65
|
-
uses: actions/download-artifact@
|
|
65
|
+
uses: actions/download-artifact@v8
|
|
66
66
|
with:
|
|
67
67
|
name: python-package-distributions
|
|
68
68
|
path: dist/
|
|
@@ -98,7 +98,7 @@ jobs:
|
|
|
98
98
|
id-token: write # IMPORTANT: mandatory for trusted publishing
|
|
99
99
|
steps:
|
|
100
100
|
- name: Download all the dists
|
|
101
|
-
uses: actions/download-artifact@
|
|
101
|
+
uses: actions/download-artifact@v8
|
|
102
102
|
with:
|
|
103
103
|
name: python-package-distributions
|
|
104
104
|
path: dist/
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Weekly smoke test that installs mockstack from PyPI without the lock file
|
|
2
|
+
# and boots the server. Catches breaking changes in transitive dependencies
|
|
3
|
+
# (fastapi, starlette, uvicorn, pydantic, ...) that would otherwise only
|
|
4
|
+
# surface when end-users run `uvx mockstack`.
|
|
5
|
+
|
|
6
|
+
name: Smoke Test (PyPI, latest deps)
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
on:
|
|
12
|
+
schedule:
|
|
13
|
+
# Mondays at 06:17 UTC. Off-the-hour to avoid GH-Actions cron congestion.
|
|
14
|
+
- cron: '17 6 * * 1'
|
|
15
|
+
workflow_dispatch:
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
smoke-test:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
strategy:
|
|
21
|
+
fail-fast: false
|
|
22
|
+
matrix:
|
|
23
|
+
python-version: ["3.13", "3.14"]
|
|
24
|
+
steps:
|
|
25
|
+
- name: Install uv with Python ${{ matrix.python-version }}
|
|
26
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
27
|
+
with:
|
|
28
|
+
python-version: ${{ matrix.python-version }}
|
|
29
|
+
version: "latest"
|
|
30
|
+
# We don't `actions/checkout` (the smoke test installs from PyPI),
|
|
31
|
+
# so silence setup-uv's empty-workdir + no-lock-to-cache warnings.
|
|
32
|
+
ignore-empty-workdir: true
|
|
33
|
+
enable-cache: false
|
|
34
|
+
|
|
35
|
+
- name: Show resolved dependency versions
|
|
36
|
+
run: |
|
|
37
|
+
set -euo pipefail
|
|
38
|
+
uvx --from mockstack python -c \
|
|
39
|
+
"import fastapi, starlette, uvicorn, pydantic; \
|
|
40
|
+
print(f'fastapi={fastapi.__version__}'); \
|
|
41
|
+
print(f'starlette={starlette.__version__}'); \
|
|
42
|
+
print(f'uvicorn={uvicorn.__version__}'); \
|
|
43
|
+
print(f'pydantic={pydantic.__version__}')"
|
|
44
|
+
|
|
45
|
+
- name: Boot mockstack and probe HTTP
|
|
46
|
+
run: |
|
|
47
|
+
set -euo pipefail
|
|
48
|
+
mkdir -p /tmp/mockstack-templates
|
|
49
|
+
uvx mockstack --templates-dir /tmp/mockstack-templates --port 18999 \
|
|
50
|
+
> server.log 2>&1 &
|
|
51
|
+
SERVER_PID=$!
|
|
52
|
+
echo "started mockstack pid=$SERVER_PID"
|
|
53
|
+
|
|
54
|
+
# Poll for readiness for up to 30s. /openapi.json is served by FastAPI
|
|
55
|
+
# itself, so a 200 here proves import + app construction + uvicorn boot
|
|
56
|
+
# + HTTP serving are all healthy end-to-end.
|
|
57
|
+
for i in $(seq 1 30); do
|
|
58
|
+
if curl -fsS http://127.0.0.1:18999/openapi.json -o /dev/null; then
|
|
59
|
+
echo "ready after ${i}s"
|
|
60
|
+
READY=1
|
|
61
|
+
break
|
|
62
|
+
fi
|
|
63
|
+
if ! kill -0 "$SERVER_PID" 2>/dev/null; then
|
|
64
|
+
echo "server died before becoming ready; logs:"
|
|
65
|
+
cat server.log
|
|
66
|
+
exit 1
|
|
67
|
+
fi
|
|
68
|
+
sleep 1
|
|
69
|
+
done
|
|
70
|
+
|
|
71
|
+
if [ "${READY:-0}" != "1" ]; then
|
|
72
|
+
echo "server never became ready; logs:"
|
|
73
|
+
cat server.log
|
|
74
|
+
kill "$SERVER_PID" 2>/dev/null || true
|
|
75
|
+
exit 1
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
# Final assertion + capture status
|
|
79
|
+
curl -fsS http://127.0.0.1:18999/openapi.json -o /dev/null
|
|
80
|
+
echo "smoke test passed"
|
|
81
|
+
|
|
82
|
+
kill "$SERVER_PID" 2>/dev/null || true
|
|
83
|
+
wait "$SERVER_PID" 2>/dev/null || true
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
fail_fast: true
|
|
2
|
+
default_language_version:
|
|
3
|
+
python: python3
|
|
2
4
|
repos:
|
|
3
5
|
- repo: local
|
|
4
6
|
hooks:
|
|
@@ -10,7 +12,7 @@ repos:
|
|
|
10
12
|
language: pygrep
|
|
11
13
|
types: [text]
|
|
12
14
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
13
|
-
rev:
|
|
15
|
+
rev: v6.0.0
|
|
14
16
|
hooks:
|
|
15
17
|
- id: check-added-large-files
|
|
16
18
|
- id: fix-byte-order-marker
|
|
@@ -24,23 +26,27 @@ repos:
|
|
|
24
26
|
- id: end-of-file-fixer
|
|
25
27
|
- id: trailing-whitespace
|
|
26
28
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
27
|
-
rev: v0.
|
|
29
|
+
rev: v0.16.7
|
|
28
30
|
hooks:
|
|
29
31
|
# Run the linter.
|
|
30
32
|
- id: ruff
|
|
31
33
|
args: [ --fix ]
|
|
32
34
|
# Run the formatter.
|
|
33
35
|
- id: ruff-format
|
|
34
|
-
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
35
|
-
rev: v1.15.0
|
|
36
|
-
hooks:
|
|
37
|
-
- id: mypy
|
|
38
|
-
additional_dependencies: [types-pyyaml>=6.0.12]
|
|
39
36
|
- repo: local
|
|
40
37
|
hooks:
|
|
38
|
+
# mypy runs from the dev dependency group in uv.lock, exactly as CI runs it: the
|
|
39
|
+
# stricter checks need the real third-party packages installed, which an isolated
|
|
40
|
+
# hook environment would have to duplicate in `additional_dependencies`.
|
|
41
|
+
- id: mypy
|
|
42
|
+
name: mypy
|
|
43
|
+
entry: bash -ec "uv run mypy mockstack"
|
|
44
|
+
language: system
|
|
45
|
+
types: [python]
|
|
46
|
+
pass_filenames: false
|
|
41
47
|
- id: pytest
|
|
42
48
|
name: pytest [with coverage, without slow]
|
|
43
|
-
entry: bash -ec "uv run python -m pytest --cov=mockstack mockstack/tests --cov-fail-under='
|
|
49
|
+
entry: bash -ec "uv run python -m pytest --cov=mockstack mockstack/tests --cov-fail-under='90' -m 'not slow'"
|
|
44
50
|
language: system
|
|
45
51
|
types: [python]
|
|
46
52
|
pass_filenames: false
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
Guidance for AI coding agents and contributors working in this repository.
|
|
4
|
+
|
|
5
|
+
## What mockstack is
|
|
6
|
+
|
|
7
|
+
mockstack is an API-mocking service built on FastAPI. A catch-all route hands every
|
|
8
|
+
request to one *strategy*, selected with the `strategy` setting:
|
|
9
|
+
|
|
10
|
+
- `filefixtures`: renders Jinja templates from `templates_dir`, chosen from the request
|
|
11
|
+
path, and simulates resource creation, searches and commands for POSTs.
|
|
12
|
+
- `proxyrules`: evaluates an ordered YAML rules file. The first matching rule serves a
|
|
13
|
+
`file:///` fixture, reverse-proxies to a real service, or redirects. Rules match on
|
|
14
|
+
path, method and `headers`/`query`/`body`/`json` predicates, and every response is
|
|
15
|
+
stamped with `X-Mockstack-Result` and `X-Mockstack-Rule`.
|
|
16
|
+
|
|
17
|
+
Both share `CreateMixin` for simulated creates. Settings come from pydantic-settings:
|
|
18
|
+
CLI flags, `MOCKSTACK__*` environment variables, or a `.env` file.
|
|
19
|
+
|
|
20
|
+
## Layout
|
|
21
|
+
|
|
22
|
+
- `mockstack/main.py`: app factory (`create_app`) and the `mockstack` CLI entry point
|
|
23
|
+
- `mockstack/config.py`: `Settings`; `mockstack/constants.py`: enums, header names
|
|
24
|
+
- `mockstack/strategies/`: `base.py`, `filefixtures.py`, `proxyrules.py`,
|
|
25
|
+
`create_mixin.py`, `factory.py`
|
|
26
|
+
- `mockstack/rules.py`: the proxyrules `Rule`: predicates, load-time validation,
|
|
27
|
+
template context
|
|
28
|
+
- `mockstack/templating.py`: Jinja environment and path-to-template-name resolution
|
|
29
|
+
- `mockstack/intent.py`, `mockstack/identifiers.py`: POST intent (search, command,
|
|
30
|
+
create) and path-identifier heuristics
|
|
31
|
+
- `mockstack/routers/`: catch-all and homepage routes
|
|
32
|
+
- `mockstack/tests/`: unit tests; `conftest.py` holds fixtures shared by unit and live
|
|
33
|
+
tests (`make_settings`, `make_request`, `write_rules`, `write_template`, `span`), and
|
|
34
|
+
`strategies/conftest.py` adds strategy helpers (`traced_request`,
|
|
35
|
+
`proxyrules_strategy`, `apply_rule`). Build settings with `make_settings`: it ignores
|
|
36
|
+
`MOCKSTACK__*` environment variables and `.env` files
|
|
37
|
+
- `mockstack/tests/live/`: live tests against real uvicorn servers on loopback sockets;
|
|
38
|
+
`conftest.py` provides the session-scoped recording echo `upstream`, the module-scoped
|
|
39
|
+
`mockstack_server`, `proxyrules_settings` and `render_rules`
|
|
40
|
+
- `examples/`: runnable examples; `examples/proxyrules-cookbook/` holds the files
|
|
41
|
+
embedded in the cookbook docs page
|
|
42
|
+
- `docs/`: the MkDocs Material site (`mkdocs.yml`); the home page is `README.md`
|
|
43
|
+
|
|
44
|
+
## Commands
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
uv sync # install
|
|
48
|
+
uv run pytest -q --cov=mockstack # unit tests; live tests are deselected
|
|
49
|
+
uv run pytest -m slow mockstack/tests/live -v # live socket tests
|
|
50
|
+
uv run mypy mockstack
|
|
51
|
+
uvx ruff check && uvx ruff format --check
|
|
52
|
+
cp README.md docs/ && uvx --with mkdocs-material mkdocs build --strict
|
|
53
|
+
uvx pre-commit run --all-files # ruff, mypy, unit tests with coverage >= 90%
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
mockstack requires Python 3.13 or later; CI tests 3.13 and 3.14. Ruff (line length 120)
|
|
57
|
+
and mypy are configured in `pyproject.toml`: production code must be fully annotated,
|
|
58
|
+
and a `# noqa` names its rule code with the reason on the line above. CI and pre-commit
|
|
59
|
+
pin ruff (0.16.7); if a newer `uvx ruff` reports findings they do not, run
|
|
60
|
+
`uvx ruff@0.16.7`. Coverage measures production code only, and the pre-commit pytest
|
|
61
|
+
hook fails below 90%.
|
|
62
|
+
|
|
63
|
+
If `VIRTUAL_ENV` points at another checkout, `unset VIRTUAL_ENV` first so `uv` uses
|
|
64
|
+
this project's `.venv`.
|
|
65
|
+
|
|
66
|
+
The docs build copies `README.md` to `docs/README.md` as the site's home page. That
|
|
67
|
+
copy is a build artifact and is gitignored: never commit it. The build is strict and
|
|
68
|
+
`pymdownx.snippets` has `check_paths: true`, so a broken link or a missing embedded
|
|
69
|
+
file fails it. CI runs the unit tests, the live tests, mypy, ruff and the docs build.
|
|
70
|
+
|
|
71
|
+
## Conventions
|
|
72
|
+
|
|
73
|
+
- **TDD.** Write the failing test first. Unit-test logic; add a live test when the
|
|
74
|
+
behaviour depends on real sockets, HTTP framing or a real upstream.
|
|
75
|
+
- **Live tests are marked `slow`** (`pytestmark = pytest.mark.slow`). `pyproject.toml`
|
|
76
|
+
deselects them by default, so run them explicitly.
|
|
77
|
+
- **Docs examples must be backed by tests.** Cookbook recipes are real files under
|
|
78
|
+
`examples/proxyrules-cookbook/`, embedded in `docs/guides/proxyrules-cookbook.md` with
|
|
79
|
+
`--8<--`. `mockstack/tests/live/test_cookbook.py` runs every `curl` command on that
|
|
80
|
+
page, and checks the page embeds every recipe file and that the README's
|
|
81
|
+
proxyrules example matches recipe 1. Change a recipe's files, page section and test
|
|
82
|
+
together. `mockstack/tests/test_docs_yaml.py` checks that every YAML block in the
|
|
83
|
+
docs parses, and `test_docs_settings.py` that every `MOCKSTACK__*` variable in the
|
|
84
|
+
docs, examples and `.env.example` files names a real setting.
|
|
85
|
+
- **Regexes in YAML** go in plain or single-quoted scalars: a double-quoted `"\1"`
|
|
86
|
+
does not parse.
|
|
87
|
+
- **Fail at load, not per request.** Rules are validated and compiled when the
|
|
88
|
+
strategy is constructed. Every `proxyrules` response, including errors, carries the
|
|
89
|
+
result headers.
|
|
90
|
+
- **Keep names generic** in code, tests, docs and examples: projects service,
|
|
91
|
+
analytics SQL gateway, `sales_facts`, orders, users. Never use company, product or
|
|
92
|
+
internal service names.
|
|
93
|
+
- **Branches.** Pushes to `main` and to any `docs/**` branch deploy the docs site
|
|
94
|
+
(`.github/workflows/publish-docs.yml`), so never push a `docs/...` branch casually.
|
|
95
|
+
Use `feat/`, `fix/` and similar prefixes for working branches.
|
|
96
|
+
- **Commits.** Conventional prefixes (`feat:`, `fix:`, `docs:`, `test:`, `chore:`,
|
|
97
|
+
`ci:`). Stage files by explicit path. Commit trailers are added by the tool, so do
|
|
98
|
+
not type them by hand.
|
|
@@ -6,7 +6,8 @@ Thank you for your interest in contributing to mockstack! We welcome contributio
|
|
|
6
6
|
|
|
7
7
|
### Prerequisites
|
|
8
8
|
|
|
9
|
-
- Python 3.
|
|
9
|
+
- Python 3.13 or higher (CI tests 3.13 and 3.14)
|
|
10
|
+
- [uv](https://docs.astral.sh/uv/)
|
|
10
11
|
- Git
|
|
11
12
|
- A GitHub account
|
|
12
13
|
|
|
@@ -18,15 +19,16 @@ Thank you for your interest in contributing to mockstack! We welcome contributio
|
|
|
18
19
|
git clone https://github.com/YOUR_USERNAME/mockstack.git
|
|
19
20
|
cd mockstack
|
|
20
21
|
```
|
|
21
|
-
3.
|
|
22
|
+
3. Install the package in development mode, with its development dependencies. `uv sync`
|
|
23
|
+
creates the `.venv` virtual environment itself:
|
|
22
24
|
```bash
|
|
23
|
-
uv
|
|
24
|
-
source .venv/bin/activate # On Windows: venv\Scripts\activate
|
|
25
|
+
uv sync
|
|
25
26
|
```
|
|
26
|
-
|
|
27
|
+
Run commands in it with `uv run` (e.g. `uv run pytest`), or activate it with
|
|
28
|
+
`source .venv/bin/activate` (on Windows: `.venv\Scripts\activate`).
|
|
29
|
+
4. Install the pre-commit hooks, which run ruff, mypy and the unit tests on every commit:
|
|
27
30
|
```bash
|
|
28
|
-
|
|
29
|
-
pip install -e .
|
|
31
|
+
uvx pre-commit install
|
|
30
32
|
```
|
|
31
33
|
|
|
32
34
|
## How to Contribute
|
|
@@ -38,7 +40,7 @@ If you find a bug, please create an issue using our **Bug Report** template. Thi
|
|
|
38
40
|
- A clear description of the problem
|
|
39
41
|
- Steps to reproduce the issue
|
|
40
42
|
- Expected vs actual behavior
|
|
41
|
-
- Your environment details (
|
|
43
|
+
- Your environment details (mockstack version, Python version, OS, etc.)
|
|
42
44
|
- Any relevant code snippets or error messages
|
|
43
45
|
|
|
44
46
|
### Suggesting Features
|
|
@@ -70,7 +72,9 @@ We welcome feature suggestions! Please use our **Feature Request** template when
|
|
|
70
72
|
|
|
71
73
|
4. **Run the test suite**:
|
|
72
74
|
```bash
|
|
73
|
-
pytest
|
|
75
|
+
uv run pytest # unit tests
|
|
76
|
+
uv run pytest -m slow mockstack/tests/live # live tests against real servers on loopback sockets
|
|
77
|
+
uvx pre-commit run --all-files # ruff, mypy, and the unit tests with a 90% coverage threshold
|
|
74
78
|
```
|
|
75
79
|
|
|
76
80
|
5. **Commit your changes**:
|
|
@@ -91,11 +95,11 @@ We welcome feature suggestions! Please use our **Feature Request** template when
|
|
|
91
95
|
|
|
92
96
|
### Code Style
|
|
93
97
|
|
|
94
|
-
- Follow PEP 8 Python style guidelines. We use `ruff` for linting and formatting.
|
|
98
|
+
- Follow PEP 8 Python style guidelines. We use `ruff` for linting and formatting, with the rule families configured in `pyproject.toml` (bugbear, bandit, pyupgrade, simplify and more). CI and the pre-commit hooks pin the ruff version.
|
|
95
99
|
- Use meaningful variable and function names
|
|
96
|
-
- Write clear commit messages
|
|
97
|
-
- Keep
|
|
98
|
-
-
|
|
100
|
+
- Write clear commit messages, with a conventional prefix (`feat:`, `fix:`, `docs:`, `test:`, `chore:`, `ci:`)
|
|
101
|
+
- Keep lines within 120 characters (enforced by `ruff`)
|
|
102
|
+
- Fully annotate production code: mypy runs with `disallow_untyped_defs`. Tests may leave fixture and parametrized arguments unannotated
|
|
99
103
|
|
|
100
104
|
### Testing
|
|
101
105
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mockstack
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.0
|
|
4
4
|
Summary: An API mocking workhorse
|
|
5
5
|
Author-email: Adam Ever-Hadani <mockstack.contact@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -9,26 +9,27 @@ Project-URL: Issues, https://github.com/promptromp/mockstack/issues
|
|
|
9
9
|
Keywords: mocking,integration-testing,testing,microservices,api
|
|
10
10
|
Classifier: Development Status :: 5 - Production/Stable
|
|
11
11
|
Classifier: Programming Language :: Python
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
14
12
|
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
-
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
16
|
+
Requires-Python: >=3.13
|
|
16
17
|
Description-Content-Type: text/markdown
|
|
17
18
|
License-File: LICENSE
|
|
18
19
|
Requires-Dist: colorama>=0.4.6
|
|
19
|
-
Requires-Dist: fastapi[standard]>=0.
|
|
20
|
+
Requires-Dist: fastapi[standard]>=0.141.1
|
|
20
21
|
Requires-Dist: httpx>=0.28.1
|
|
21
22
|
Requires-Dist: jinja2>=3.1.6
|
|
22
|
-
Requires-Dist: opentelemetry-distro[otlp]>=0.
|
|
23
|
-
Requires-Dist: opentelemetry-instrumentation-fastapi>=0.
|
|
24
|
-
Requires-Dist: pydantic>=2.
|
|
25
|
-
Requires-Dist: pydantic-settings>=2.
|
|
26
|
-
Requires-Dist: pyyaml>=6.0.
|
|
27
|
-
Requires-Dist: rich>=
|
|
28
|
-
Requires-Dist: typer>=0.
|
|
29
|
-
Requires-Dist: uvicorn>=0.
|
|
23
|
+
Requires-Dist: opentelemetry-distro[otlp]>=0.65b0
|
|
24
|
+
Requires-Dist: opentelemetry-instrumentation-fastapi>=0.65b0
|
|
25
|
+
Requires-Dist: pydantic>=2.13.5
|
|
26
|
+
Requires-Dist: pydantic-settings>=2.15.0
|
|
27
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
28
|
+
Requires-Dist: rich>=15.0.0
|
|
29
|
+
Requires-Dist: typer>=0.27.2
|
|
30
|
+
Requires-Dist: uvicorn>=0.53.0
|
|
30
31
|
Provides-Extra: llm
|
|
31
|
-
Requires-Dist: ollama>=0.
|
|
32
|
+
Requires-Dist: ollama>=0.6.2; extra == "llm"
|
|
32
33
|
Dynamic: license-file
|
|
33
34
|
|
|
34
35
|

|
|
@@ -59,6 +60,9 @@ Use mockstack for:
|
|
|
59
60
|
Highlights include:
|
|
60
61
|
|
|
61
62
|
* Multiple strategies for handling requests such as [Jinja](https://jinja.palletsprojects.com/en/stable/) template files with intelligent URL request-to-template routing, proxy strategy, and mixed strategies. :game_die:
|
|
63
|
+
* Rule predicates for the `proxyrules` strategy: match requests on path, method, headers, query parameters and JSON body fields, then serve a fixture, reverse-proxy to a real service, or redirect. :dart:
|
|
64
|
+
* Dynamic replacements: a rule's replacement can be a Jinja template, so a request header can pick the fixture scenario to serve. :twisted_rightwards_arrows:
|
|
65
|
+
* Result headers: every `proxyrules` response is stamped with `X-Mockstack-Result` and `X-Mockstack-Rule`, so a test can assert it got a fixture and not the real service. :label:
|
|
62
66
|
* Observability via [OpenTelemetry](https://opentelemetry.io/) integration. Get detailed traces of your sessions instantly reported to backends such as [Grafana](https://grafana.com/), [Jaeger](https://www.jaegertracing.io/), [Zipkin](https://zipkin.io/), etc. :eyes:
|
|
63
67
|
* Configurability via [pydantic-settings](https://docs.pydantic.dev/latest/concepts/pydantic_settings/) supports customizing behaviour via environment variables and a `.env` file. :flags:
|
|
64
68
|
* Comprehensive unit-tests, linting and formatting coverage as well as vulnerabilities and security scanning with full CI automation to ensure stability and a high-quality codebase for production-grade use. :+1:
|
|
@@ -74,12 +78,13 @@ or install into a persistent environment and add it to the PATH with:
|
|
|
74
78
|
|
|
75
79
|
uv tool install mockstack
|
|
76
80
|
|
|
81
|
+
mockstack requires Python 3.13 or later; `uvx` and `uv tool install` pick a compatible interpreter, downloading one if needed.
|
|
77
82
|
|
|
78
83
|
## Usage
|
|
79
84
|
|
|
80
85
|
See the [examples](https://github.com/promptromp/mockstack/blob/main/examples/) directory for complete examples with documentation.
|
|
81
86
|
|
|
82
|
-
Available configuration options are [here](https://github.com/promptromp/mockstack/blob/main/mockstack/config.py).
|
|
87
|
+
Available configuration options are described in the [Configuration](https://promptromp.github.io/mockstack/configuration/) reference, and defined [here](https://github.com/promptromp/mockstack/blob/main/mockstack/config.py).
|
|
83
88
|
|
|
84
89
|
Setting individual options can be done either through an `.env` file, individual environment variables, or command-line arguments.
|
|
85
90
|
|
|
@@ -103,37 +108,73 @@ See also the included [.env.example](https://github.com/promptromp/mockstack/blo
|
|
|
103
108
|
Out of the box, you get the following behavior when using the default `filefixtures` strategy:
|
|
104
109
|
|
|
105
110
|
- The HTTP request `GET /someservice/api/v1/user/c27f5b2b-6e81-420d-a4e4-6426e1c32db8` will try to find `<templates_dir>/someservice-api-v1-user.c27f5b2b-6e81-420d-a4e4-6426e1c32db8.j2`,
|
|
106
|
-
and will fallback to `<templates_dir>/someservice-api-v1-user.j2` (and finally to `index.j2` if exists). These are j2 files that have access to request body
|
|
107
|
-
- The HTTP request `POST /someservice/api/v2/item` with a JSON body will attempt to intelligently simulate the creation of a resource, returning the appropriate status code and will echo back the provided request resource, after injecting additional metadata fields based on strategy configuration. This is useful for services that expect fields such as `id` and `created_at` on returned created resources.
|
|
108
|
-
- HTTP requests for `DELETE` / `PUT` / `PATCH` are a no-op by default, simply returning the appropriate status code.
|
|
109
|
-
- The HTTP request `POST /someservice/api/v2/embedding_search` will be handled as a search request rather than a resource creation
|
|
111
|
+
and will fallback to `<templates_dir>/someservice-api-v1-user.j2` (and finally to `index.j2` if exists). These are j2 files that have access to request context variables (query parameters, headers, the JSON body and the identifiers in the path).
|
|
112
|
+
- The HTTP request `POST /someservice/api/v2/item` with a JSON body will attempt to intelligently simulate the creation of a resource, returning the appropriate status code and will echo back the provided request resource, after injecting additional metadata fields based on strategy configuration. This is useful for services that expect fields such as `id` and `created_at` on returned created resources. Templates are tried first, so a template matching the path (`index.j2` included) answers the POST instead, unless `filefixtures_enable_templates_for_post=false`. This fallback can be turned off with `filefixtures_simulate_create_on_missing=false`, in which case a create-looking POST with no matching template gets a 404 instead.
|
|
113
|
+
- HTTP requests for `DELETE` / `PUT` / `PATCH` are a no-op by default, simply returning the appropriate status code (204). `HEAD` and `OPTIONS` requests are answered 405.
|
|
114
|
+
- The HTTP request `POST /someservice/api/v2/embedding_search` will be handled as a search request rather than a resource creation: it is answered from its template (`someservice-api-v2-embedding_search.j2`), or with a 404 when there is none.
|
|
110
115
|
|
|
111
116
|
Overall, the design philosophy is that things "just work". The framework attempts to intelligently deduce the intent of the request as much as possible and act accordingly,
|
|
112
117
|
while leaving room for advanced users to go in and customize behavior using the configuration options.
|
|
113
118
|
|
|
119
|
+
### Mix fixtures and real services
|
|
120
|
+
|
|
121
|
+
With the `proxyrules` strategy, one mockstack instance can serve fixtures to test traffic and pass everything else through to the real service. Rules are tried in order and the first match wins:
|
|
122
|
+
|
|
123
|
+
```yaml
|
|
124
|
+
rules:
|
|
125
|
+
- name: projects-fixture
|
|
126
|
+
method: GET
|
|
127
|
+
pattern: ^/projects/api/v1/project/(?P<id>[a-z0-9-]+)$
|
|
128
|
+
headers:
|
|
129
|
+
x-test-run: ".+"
|
|
130
|
+
replacement: file://${FIXTURES_DIR}/projects/project.json.j2
|
|
131
|
+
|
|
132
|
+
- name: projects-passthrough
|
|
133
|
+
pattern: ^/projects/(.*)
|
|
134
|
+
replacement: ${UPSTREAM_URL}/\1
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Fill in the `${FIXTURES_DIR}` and `${UPSTREAM_URL}` placeholders (for example with `envsubst`), then start mockstack with `MOCKSTACK__STRATEGY=proxyrules` and `MOCKSTACK__PROXYRULES_RULES_FILENAME` pointing at the result. A request tagged with `X-Test-Run` is served from the fixture (`X-Mockstack-Result: template`); the untagged one is reverse-proxied to the real service (`X-Mockstack-Result: proxy`):
|
|
138
|
+
|
|
139
|
+
```shell
|
|
140
|
+
curl -i -H "X-Test-Run: ci-42" http://127.0.0.1:8000/projects/api/v1/project/proj-123
|
|
141
|
+
curl -i http://127.0.0.1:8000/projects/api/v1/project/proj-123
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The [ProxyRules cookbook](https://promptromp.github.io/mockstack/guides/proxyrules-cookbook/) walks through this recipe and more (per-scenario fixtures, matching on request bodies and query parameters, asserting in tests), each backed by a live test.
|
|
145
|
+
|
|
114
146
|
|
|
115
147
|
## Testing
|
|
116
148
|
|
|
117
149
|
Invoke unit-tests with:
|
|
118
150
|
|
|
119
|
-
uv run
|
|
151
|
+
uv run pytest
|
|
152
|
+
|
|
153
|
+
Live tests start real mockstack and upstream servers on loopback sockets, including one that runs every example on the ProxyRules cookbook page. They are marked `slow` and deselected by default; run them with:
|
|
154
|
+
|
|
155
|
+
uv run pytest -m slow mockstack/tests/live
|
|
120
156
|
|
|
121
|
-
Linting, formatting, static type checks
|
|
157
|
+
Linting, formatting, static type checks and the unit tests (with a 90% coverage threshold) are all managed via [pre-commit](https://pre-commit.com/) hooks. Install them once and they will run automatically on every commit:
|
|
122
158
|
|
|
123
|
-
pre-commit
|
|
159
|
+
uvx pre-commit install
|
|
160
|
+
|
|
161
|
+
You can invoke these manually on all files with:
|
|
162
|
+
|
|
163
|
+
uvx pre-commit run --all-files
|
|
124
164
|
|
|
125
165
|
|
|
126
166
|
## Contributing
|
|
127
167
|
|
|
128
|
-
If you are contributing to development, you will want to clone this project, and can then install it locally with:
|
|
168
|
+
If you are contributing to development, you will want to clone this project, and can then install it locally (`uv sync` installs the project in editable mode, together with its development dependencies) with:
|
|
129
169
|
|
|
130
170
|
gh repo clone promptromp/mockstack
|
|
131
171
|
cd mockstack/
|
|
132
172
|
uv sync
|
|
133
|
-
uv pip install -e .
|
|
134
173
|
|
|
135
174
|
Run in development mode (for live-reload of changes when developing):
|
|
136
175
|
|
|
137
176
|
uv run uvicorn --factory mockstack.main:create_app --reload
|
|
138
177
|
|
|
139
178
|
Note that when you run using the uvicorn CLI, you will need to set any configuration via `.env` file or environment variables.
|
|
179
|
+
|
|
180
|
+
See [CONTRIBUTING.md](https://github.com/promptromp/mockstack/blob/main/CONTRIBUTING.md) for the full development workflow.
|