mockstack 0.12.1__tar.gz → 0.14.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.14.0/.env.example +49 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/.github/workflows/ci.yml +23 -3
- {mockstack-0.12.1 → mockstack-0.14.0}/.github/workflows/smoke-test-pypi.yml +1 -1
- {mockstack-0.12.1 → mockstack-0.14.0}/.gitignore +4 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/.pre-commit-config.yaml +14 -8
- mockstack-0.14.0/CLAUDE.md +104 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/CONTRIBUTING.md +17 -13
- {mockstack-0.12.1 → mockstack-0.14.0}/Dockerfile +1 -1
- {mockstack-0.12.1/mockstack.egg-info → mockstack-0.14.0}/PKG-INFO +78 -24
- mockstack-0.12.1/PKG-INFO → mockstack-0.14.0/README.md +63 -43
- mockstack-0.14.0/docs/configuration.md +141 -0
- mockstack-0.14.0/docs/guides/proxyrules-cookbook.md +542 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/docs/ollama.md +5 -1
- {mockstack-0.12.1 → mockstack-0.14.0}/docs/strategies/base.md +1 -0
- mockstack-0.14.0/docs/strategies/filefixtures.md +136 -0
- mockstack-0.14.0/docs/strategies/proxyrules.md +647 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/examples/filefixtures-with-templates/.env.example +1 -1
- {mockstack-0.12.1 → mockstack-0.14.0}/examples/filefixtures-with-templates/README.md +2 -2
- mockstack-0.14.0/examples/filefixtures-with-templates/templates/servicename-api-v1-items.j2 +20 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/examples/llm/mockstack-langchain-example.ipynb +15 -12
- mockstack-0.14.0/examples/proxyrules-cookbook/01-tagged-traffic/fixtures/projects/project.json.j2 +1 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/01-tagged-traffic/rules.yml +11 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/02-scenario-directories/fixtures/archived/projects/project.json.j2 +1 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/02-scenario-directories/fixtures/healthy/projects/project.json.j2 +1 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/02-scenario-directories/rules.yml +11 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/03-sql-gateway/fixtures/analytics/sales_facts.json.j2 +1 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/03-sql-gateway/rules.yml +11 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/04-json-literals/fixtures/orders/search.json.j2 +1 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/04-json-literals/rules.yml +32 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/05-query-parameters/fixtures/users/archived.json.j2 +1 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/05-query-parameters/fixtures/users/page.json.j2 +1 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/05-query-parameters/rules.yml +18 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/06-asserting-in-tests/fixture_assertions.py +40 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/06-asserting-in-tests/fixtures/orders/echo.json.j2 +1 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/06-asserting-in-tests/fixtures/projects/project.json.j2 +1 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/06-asserting-in-tests/fixtures/users/user-1.json.j2 +1 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/06-asserting-in-tests/rules.yml +44 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/07-redirect-mode/fixtures/users/user-1.json.j2 +1 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/07-redirect-mode/rules.yml +9 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/08-status-and-headers/fixtures/errors/rate-limited.json.j2 +1 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/08-status-and-headers/fixtures/errors/unavailable.json.j2 +1 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/08-status-and-headers/fixtures/orders/created.json.j2 +1 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/08-status-and-headers/rules.yml +36 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/09-recording/rules.yml +11 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/09-recording/scrubbers.py +14 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/README.md +60 -0
- mockstack-0.14.0/examples/proxyrules-cookbook/upstream.py +40 -0
- mockstack-0.14.0/examples/proxyrules-eval-isolation/.env.example +16 -0
- mockstack-0.14.0/examples/proxyrules-eval-isolation/README.md +78 -0
- mockstack-0.14.0/examples/proxyrules-eval-isolation/fixtures/healthy/analytics/sales_facts.json.j2 +1 -0
- mockstack-0.14.0/examples/proxyrules-eval-isolation/fixtures/healthy/projects/project.json.j2 +1 -0
- mockstack-0.14.0/examples/proxyrules-eval-isolation/rules.yml +26 -0
- mockstack-0.14.0/examples/proxyrules-eval-isolation/upstream.py +27 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/examples/proxyrules-with-rules-file/.env.example +2 -2
- mockstack-0.14.0/examples/proxyrules-with-rules-file/README.md +16 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/mkdocs.yml +5 -2
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/config.py +45 -22
- mockstack-0.14.0/mockstack/constants.py +80 -0
- mockstack-0.14.0/mockstack/display.py +52 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/exceptions.py +3 -1
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/identifiers.py +9 -10
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/lifespan.py +3 -3
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/llm/ollama.py +9 -8
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/main.py +10 -10
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/middleware.py +11 -10
- mockstack-0.14.0/mockstack/recording.py +77 -0
- mockstack-0.14.0/mockstack/routers/catchall.py +23 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/routers/homepage.py +3 -2
- mockstack-0.14.0/mockstack/rules.py +408 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/strategies/__init__.py +1 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/strategies/base.py +4 -4
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/strategies/create_mixin.py +12 -19
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/strategies/factory.py +1 -3
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/strategies/filefixtures.py +52 -23
- mockstack-0.14.0/mockstack/strategies/proxyrules.py +777 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/telemetry.py +9 -20
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/templating.py +19 -21
- mockstack-0.14.0/mockstack/tests/conftest.py +189 -0
- mockstack-0.14.0/mockstack/tests/live/conftest.py +261 -0
- mockstack-0.14.0/mockstack/tests/live/test_cookbook.py +632 -0
- mockstack-0.14.0/mockstack/tests/live/test_example_eval_isolation.py +89 -0
- mockstack-0.14.0/mockstack/tests/live/test_fixture_status_live.py +73 -0
- mockstack-0.14.0/mockstack/tests/live/test_predicates_live.py +58 -0
- mockstack-0.14.0/mockstack/tests/live/test_record_mode_live.py +142 -0
- mockstack-0.14.0/mockstack/tests/live/test_reverse_proxy_live.py +116 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/tests/llm/test_ollama.py +32 -36
- mockstack-0.14.0/mockstack/tests/routers/test_catchall.py +60 -0
- mockstack-0.14.0/mockstack/tests/routers/test_homepage.py +15 -0
- mockstack-0.14.0/mockstack/tests/strategies/__init__.py +0 -0
- mockstack-0.14.0/mockstack/tests/strategies/conftest.py +74 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/tests/strategies/test_create_mixin.py +19 -75
- mockstack-0.14.0/mockstack/tests/strategies/test_filefixtures.py +294 -0
- mockstack-0.14.0/mockstack/tests/strategies/test_filefixtures_example.py +41 -0
- mockstack-0.14.0/mockstack/tests/strategies/test_proxyrules.py +1427 -0
- mockstack-0.14.0/mockstack/tests/strategies/test_proxyrules_record.py +552 -0
- mockstack-0.14.0/mockstack/tests/test_config.py +168 -0
- mockstack-0.14.0/mockstack/tests/test_display.py +51 -0
- mockstack-0.14.0/mockstack/tests/test_docs_settings.py +73 -0
- mockstack-0.14.0/mockstack/tests/test_docs_yaml.py +54 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/tests/test_identifiers.py +29 -40
- mockstack-0.14.0/mockstack/tests/test_intent.py +76 -0
- mockstack-0.14.0/mockstack/tests/test_middleware.py +23 -0
- mockstack-0.14.0/mockstack/tests/test_recording.py +176 -0
- mockstack-0.14.0/mockstack/tests/test_rules.py +893 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/tests/test_telemetry.py +40 -73
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/tests/test_templating.py +75 -123
- mockstack-0.14.0/mockstack.egg-info/PKG-INFO +193 -0
- mockstack-0.14.0/mockstack.egg-info/SOURCES.txt +145 -0
- mockstack-0.14.0/mockstack.egg-info/requires.txt +15 -0
- mockstack-0.14.0/mockstack.egg-info/scm_file_list.json +141 -0
- mockstack-0.14.0/mockstack.egg-info/scm_version.json +8 -0
- mockstack-0.14.0/pyproject.toml +167 -0
- mockstack-0.14.0/uv.lock +1936 -0
- mockstack-0.12.1/.env.example +0 -25
- mockstack-0.12.1/README.md +0 -106
- mockstack-0.12.1/docs/configuration.md +0 -103
- mockstack-0.12.1/docs/strategies/filefixtures.md +0 -109
- mockstack-0.12.1/docs/strategies/proxyrules.md +0 -123
- mockstack-0.12.1/examples/filefixtures-with-templates/templates/servicename-api-v1-items.j2 +0 -19
- mockstack-0.12.1/examples/proxyrules-with-rules-file/README.md +0 -15
- mockstack-0.12.1/mockstack/constants.py +0 -38
- mockstack-0.12.1/mockstack/display.py +0 -30
- mockstack-0.12.1/mockstack/routers/catchall.py +0 -16
- mockstack-0.12.1/mockstack/rules.py +0 -117
- mockstack-0.12.1/mockstack/strategies/proxyrules.py +0 -274
- mockstack-0.12.1/mockstack/tests/conftest.py +0 -78
- mockstack-0.12.1/mockstack/tests/routers/test_catchall.py +0 -46
- mockstack-0.12.1/mockstack/tests/routers/test_homepage.py +0 -27
- mockstack-0.12.1/mockstack/tests/strategies/test_filefixtures.py +0 -275
- mockstack-0.12.1/mockstack/tests/strategies/test_proxyrules.py +0 -397
- mockstack-0.12.1/mockstack/tests/test_display.py +0 -20
- mockstack-0.12.1/mockstack/tests/test_intent.py +0 -160
- mockstack-0.12.1/mockstack/tests/test_middleware.py +0 -37
- mockstack-0.12.1/mockstack/tests/test_rules.py +0 -168
- mockstack-0.12.1/mockstack.egg-info/SOURCES.txt +0 -92
- mockstack-0.12.1/mockstack.egg-info/requires.txt +0 -15
- mockstack-0.12.1/pyproject.toml +0 -69
- mockstack-0.12.1/uv.lock +0 -2030
- {mockstack-0.12.1 → mockstack-0.14.0}/.dockerignore +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/.github/workflows/publish-docs.yml +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/.github/workflows/publish-to-pypi.yml +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/CODE_OF_CONDUCT.md +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/LICENSE +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/SECURITY.md +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/docker-entrypoint.sh +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/docs/assets/favicon.ico +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/docs/assets/logo.png +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/docs/assets/mockstack.png +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/examples/filefixtures-with-templates/templates/servicename2-api-v2-user.533ec889-7c68-45c8-b21e-4a7e455d1234.j2 +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/examples/llm/README.md +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/examples/llm/templates/ollama-openai-v1-chat-completions.j2 +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/examples/llm/templates/openai-v1-chat-completions.j2 +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/examples/llm/templates/openai-v1-deployments-gpt-4o-chat-completions.j2 +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/examples/proxyrules-with-rules-file/rules.yml +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/__init__.py +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/intent.py +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/llm/__init__.py +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/routers/__init__.py +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/tests/__init__.py +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/tests/fixtures/__init__.py +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/tests/fixtures/proxyrules.yml +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/tests/fixtures/templates/__init__.py +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack/tests/fixtures/templates/example-template.j2 +0 -0
- {mockstack-0.12.1/mockstack/tests/llm → mockstack-0.14.0/mockstack/tests/live}/__init__.py +0 -0
- {mockstack-0.12.1/mockstack/tests/routers → mockstack-0.14.0/mockstack/tests/llm}/__init__.py +0 -0
- {mockstack-0.12.1/mockstack/tests/strategies → mockstack-0.14.0/mockstack/tests/routers}/__init__.py +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack.egg-info/dependency_links.txt +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack.egg-info/entry_points.txt +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/mockstack.egg-info/top_level.txt +0 -0
- {mockstack-0.12.1 → mockstack-0.14.0}/setup.cfg +0 -0
|
@@ -0,0 +1,49 @@
|
|
|
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
|
+
# record mode for the proxyrules strategy: off (default), missing or overwrite.
|
|
36
|
+
# missing records a fixture rule's file from the next matching URL rule when the file
|
|
37
|
+
# does not exist yet; overwrite also re-records files recorded before. Requests really
|
|
38
|
+
# reach the upstream until recorded. Never enable on a shared or exposed instance.
|
|
39
|
+
# See https://promptromp.github.io/mockstack/strategies/proxyrules/#recording-fixtures
|
|
40
|
+
MOCKSTACK__PROXYRULES_RECORD_MODE = off
|
|
41
|
+
# directory every recorded fixture must resolve inside; required when recording.
|
|
42
|
+
# MOCKSTACK__PROXYRULES_RECORD_ROOT = "/some/path/fixtures/"
|
|
43
|
+
# optional module:function called on every body before it is recorded (must be importable).
|
|
44
|
+
# MOCKSTACK__PROXYRULES_RECORD_SCRUBBER = "my_scrubbers:mask_emails"
|
|
45
|
+
|
|
46
|
+
# OpenTelemetry settings
|
|
47
|
+
MOCKSTACK__OPENTELEMETRY__ENABLED = true
|
|
48
|
+
MOCKSTACK__OPENTELEMETRY__ENDPOINT = "http://localhost:4317/"
|
|
49
|
+
MOCKSTACK__OPENTELEMETRY__CAPTURE_RESPONSE_BODY = true
|
|
@@ -20,7 +20,7 @@ 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
26
|
- uses: actions/checkout@v6
|
|
@@ -32,16 +32,36 @@ jobs:
|
|
|
32
32
|
- name: Install ruff
|
|
33
33
|
uses: astral-sh/ruff-action@v4.0.0
|
|
34
34
|
with:
|
|
35
|
-
|
|
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"
|
|
36
38
|
- name: Install the project
|
|
37
39
|
run: uv sync --all-extras --dev
|
|
38
40
|
- name: Run ruff lint and formatting checks
|
|
39
41
|
run: |
|
|
40
42
|
ruff check
|
|
41
|
-
ruff format
|
|
43
|
+
ruff format --check
|
|
42
44
|
- name: Run mypy
|
|
43
45
|
run: |
|
|
44
46
|
uv pip install types-pyyaml
|
|
45
47
|
uv run mypy mockstack
|
|
46
48
|
- name: Run tests
|
|
47
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
|
|
@@ -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,104 @@
|
|
|
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 (with the rule's optional `status` and `response_headers`),
|
|
14
|
+
reverse-proxies to a real service, or redirects. In record mode
|
|
15
|
+
(`proxyrules_record_mode`) a missing fixture is recorded from the next matching URL
|
|
16
|
+
rule. Rules match on path, method and `headers`/`query`/`body`/`json` predicates, and
|
|
17
|
+
every response is stamped with `X-Mockstack-Result` and `X-Mockstack-Rule`.
|
|
18
|
+
|
|
19
|
+
Both share `CreateMixin` for simulated creates. Settings come from pydantic-settings:
|
|
20
|
+
CLI flags, `MOCKSTACK__*` environment variables, or a `.env` file.
|
|
21
|
+
|
|
22
|
+
## Layout
|
|
23
|
+
|
|
24
|
+
- `mockstack/main.py`: app factory (`create_app`) and the `mockstack` CLI entry point
|
|
25
|
+
- `mockstack/config.py`: `Settings`; `mockstack/constants.py`: enums, header names
|
|
26
|
+
- `mockstack/strategies/`: `base.py`, `filefixtures.py`, `proxyrules.py`,
|
|
27
|
+
`create_mixin.py`, `factory.py`
|
|
28
|
+
- `mockstack/rules.py`: the proxyrules `Rule`: predicates, load-time validation,
|
|
29
|
+
template context
|
|
30
|
+
- `mockstack/recording.py`: record-mode building blocks: fixture encoding that renders
|
|
31
|
+
back to the exact body, the recorded marker, root confinement and atomic writes
|
|
32
|
+
- `mockstack/templating.py`: Jinja environment and path-to-template-name resolution
|
|
33
|
+
- `mockstack/intent.py`, `mockstack/identifiers.py`: POST intent (search, command,
|
|
34
|
+
create) and path-identifier heuristics
|
|
35
|
+
- `mockstack/routers/`: catch-all and homepage routes
|
|
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
|
|
38
|
+
`strategies/conftest.py` adds strategy helpers (`traced_request`,
|
|
39
|
+
`proxyrules_strategy`, `apply_rule`, `upstream_send`, `span_attributes`). Build settings with
|
|
40
|
+
`make_settings`: it ignores
|
|
41
|
+
`MOCKSTACK__*` environment variables and `.env` files
|
|
42
|
+
- `mockstack/tests/live/`: live tests against real uvicorn servers on loopback sockets;
|
|
43
|
+
`conftest.py` provides the session-scoped recording echo `upstream`, the module-scoped
|
|
44
|
+
`mockstack_server`, `proxyrules_settings` and `render_rules`; `test_record_mode_live.py`
|
|
45
|
+
records into temporary directories, never the repository
|
|
46
|
+
- `examples/`: runnable examples; `examples/proxyrules-cookbook/` holds the files
|
|
47
|
+
embedded in the cookbook docs page
|
|
48
|
+
- `docs/`: the MkDocs Material site (`mkdocs.yml`); the home page is `README.md`
|
|
49
|
+
|
|
50
|
+
## Commands
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
uv sync # install
|
|
54
|
+
uv run pytest -q --cov=mockstack # unit tests; live tests are deselected
|
|
55
|
+
uv run pytest -m slow mockstack/tests/live -v # live socket tests
|
|
56
|
+
uv run mypy mockstack
|
|
57
|
+
uvx ruff check && uvx ruff format --check
|
|
58
|
+
cp README.md docs/ && uvx --with mkdocs-material mkdocs build --strict
|
|
59
|
+
uvx pre-commit run --all-files # ruff, mypy, unit tests with coverage >= 90%
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
mockstack requires Python 3.13 or later; CI tests 3.13 and 3.14. Ruff (line length 120)
|
|
63
|
+
and mypy are configured in `pyproject.toml`: production code must be fully annotated,
|
|
64
|
+
and a `# noqa` names its rule code with the reason on the line above. CI and pre-commit
|
|
65
|
+
pin ruff (0.16.7); if a newer `uvx ruff` reports findings they do not, run
|
|
66
|
+
`uvx ruff@0.16.7`. Coverage measures production code only, and the pre-commit pytest
|
|
67
|
+
hook fails below 90%.
|
|
68
|
+
|
|
69
|
+
If `VIRTUAL_ENV` points at another checkout, `unset VIRTUAL_ENV` first so `uv` uses
|
|
70
|
+
this project's `.venv`.
|
|
71
|
+
|
|
72
|
+
The docs build copies `README.md` to `docs/README.md` as the site's home page. That
|
|
73
|
+
copy is a build artifact and is gitignored: never commit it. The build is strict and
|
|
74
|
+
`pymdownx.snippets` has `check_paths: true`, so a broken link or a missing embedded
|
|
75
|
+
file fails it. CI runs the unit tests, the live tests, mypy, ruff and the docs build.
|
|
76
|
+
|
|
77
|
+
## Conventions
|
|
78
|
+
|
|
79
|
+
- **TDD.** Write the failing test first. Unit-test logic; add a live test when the
|
|
80
|
+
behaviour depends on real sockets, HTTP framing or a real upstream.
|
|
81
|
+
- **Live tests are marked `slow`** (`pytestmark = pytest.mark.slow`). `pyproject.toml`
|
|
82
|
+
deselects them by default, so run them explicitly.
|
|
83
|
+
- **Docs examples must be backed by tests.** Cookbook recipes are real files under
|
|
84
|
+
`examples/proxyrules-cookbook/`, embedded in `docs/guides/proxyrules-cookbook.md` with
|
|
85
|
+
`--8<--`. `mockstack/tests/live/test_cookbook.py` runs every `curl` command on that
|
|
86
|
+
page, and checks the page embeds every recipe file and that the README's
|
|
87
|
+
proxyrules example matches recipe 1. Change a recipe's files, page section and test
|
|
88
|
+
together. `mockstack/tests/test_docs_yaml.py` checks that every YAML block in the
|
|
89
|
+
docs parses, and `test_docs_settings.py` that every `MOCKSTACK__*` variable in the
|
|
90
|
+
docs, examples and `.env.example` files names a real setting.
|
|
91
|
+
- **Regexes in YAML** go in plain or single-quoted scalars: a double-quoted `"\1"`
|
|
92
|
+
does not parse.
|
|
93
|
+
- **Fail at load, not per request.** Rules are validated and compiled when the
|
|
94
|
+
strategy is constructed. Every `proxyrules` response, including errors, carries the
|
|
95
|
+
result headers.
|
|
96
|
+
- **Keep names generic** in code, tests, docs and examples: projects service,
|
|
97
|
+
analytics SQL gateway, `sales_facts`, orders, users. Never use company, product or
|
|
98
|
+
internal service names.
|
|
99
|
+
- **Branches.** Pushes to `main` and to any `docs/**` branch deploy the docs site
|
|
100
|
+
(`.github/workflows/publish-docs.yml`), so never push a `docs/...` branch casually.
|
|
101
|
+
Use `feat/`, `fix/` and similar prefixes for working branches.
|
|
102
|
+
- **Commits.** Conventional prefixes (`feat:`, `fix:`, `docs:`, `test:`, `chore:`,
|
|
103
|
+
`ci:`). Stage files by explicit path. Commit trailers are added by the tool, so do
|
|
104
|
+
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.14.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,11 @@ 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:
|
|
66
|
+
* Fixture status codes and headers: a `proxyrules` fixture can answer with any status and extra headers, e.g. a 503 with `Retry-After`, to test how a client handles a failing dependency. :vertical_traffic_light:
|
|
67
|
+
* Record mode: `proxyrules` can record real responses into the fixtures its rules serve, with an optional scrubber, then replay them without the real service. :red_circle:
|
|
62
68
|
* 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
69
|
* Configurability via [pydantic-settings](https://docs.pydantic.dev/latest/concepts/pydantic_settings/) supports customizing behaviour via environment variables and a `.env` file. :flags:
|
|
64
70
|
* 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 +80,13 @@ or install into a persistent environment and add it to the PATH with:
|
|
|
74
80
|
|
|
75
81
|
uv tool install mockstack
|
|
76
82
|
|
|
83
|
+
mockstack requires Python 3.13 or later; `uvx` and `uv tool install` pick a compatible interpreter, downloading one if needed.
|
|
77
84
|
|
|
78
85
|
## Usage
|
|
79
86
|
|
|
80
87
|
See the [examples](https://github.com/promptromp/mockstack/blob/main/examples/) directory for complete examples with documentation.
|
|
81
88
|
|
|
82
|
-
Available configuration options are [here](https://github.com/promptromp/mockstack/blob/main/mockstack/config.py).
|
|
89
|
+
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
90
|
|
|
84
91
|
Setting individual options can be done either through an `.env` file, individual environment variables, or command-line arguments.
|
|
85
92
|
|
|
@@ -103,37 +110,84 @@ See also the included [.env.example](https://github.com/promptromp/mockstack/blo
|
|
|
103
110
|
Out of the box, you get the following behavior when using the default `filefixtures` strategy:
|
|
104
111
|
|
|
105
112
|
- 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
|
|
113
|
+
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).
|
|
114
|
+
- 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.
|
|
115
|
+
- 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.
|
|
116
|
+
- 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
117
|
|
|
111
118
|
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
119
|
while leaving room for advanced users to go in and customize behavior using the configuration options.
|
|
113
120
|
|
|
121
|
+
### Mix fixtures and real services
|
|
122
|
+
|
|
123
|
+
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:
|
|
124
|
+
|
|
125
|
+
```yaml
|
|
126
|
+
rules:
|
|
127
|
+
- name: projects-fixture
|
|
128
|
+
method: GET
|
|
129
|
+
pattern: ^/projects/api/v1/project/(?P<id>[a-z0-9-]+)$
|
|
130
|
+
headers:
|
|
131
|
+
x-test-run: ".+"
|
|
132
|
+
replacement: file://${FIXTURES_DIR}/projects/project.json.j2
|
|
133
|
+
|
|
134
|
+
- name: projects-passthrough
|
|
135
|
+
pattern: ^/projects/(.*)
|
|
136
|
+
replacement: ${UPSTREAM_URL}/\1
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
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`):
|
|
140
|
+
|
|
141
|
+
```shell
|
|
142
|
+
curl -i -H "X-Test-Run: ci-42" http://127.0.0.1:8000/projects/api/v1/project/proj-123
|
|
143
|
+
curl -i http://127.0.0.1:8000/projects/api/v1/project/proj-123
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
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, error responses, recording fixtures), each backed by a live test.
|
|
147
|
+
|
|
148
|
+
### Record fixtures from a real service
|
|
149
|
+
|
|
150
|
+
Instead of writing fixtures by hand, record them: with record mode on, a fixture rule whose file does not exist yet sends the request on to the next matching URL rule, saves the response into the fixture file, and serves it from there. Later requests are replayed from the file without calling the service. Record mode is off by default:
|
|
151
|
+
|
|
152
|
+
```shell
|
|
153
|
+
MOCKSTACK__STRATEGY=proxyrules MOCKSTACK__PROXYRULES_RULES_FILENAME=rules.yml \
|
|
154
|
+
MOCKSTACK__PROXYRULES_RECORD_MODE=missing MOCKSTACK__PROXYRULES_RECORD_ROOT=fixtures \
|
|
155
|
+
uv run mockstack
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Requests really reach the service until their response is recorded, so record against a safe environment and review the recorded files before committing them. An optional scrubber can mask sensitive data first; see [Recording fixtures](https://promptromp.github.io/mockstack/strategies/proxyrules/#recording-fixtures) and the cookbook's [Record fixtures from a real service](https://promptromp.github.io/mockstack/guides/proxyrules-cookbook/#9-record-fixtures-from-a-real-service).
|
|
114
159
|
|
|
115
160
|
## Testing
|
|
116
161
|
|
|
117
162
|
Invoke unit-tests with:
|
|
118
163
|
|
|
119
|
-
uv run
|
|
164
|
+
uv run pytest
|
|
120
165
|
|
|
121
|
-
|
|
166
|
+
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:
|
|
122
167
|
|
|
123
|
-
|
|
168
|
+
uv run pytest -m slow mockstack/tests/live
|
|
169
|
+
|
|
170
|
+
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:
|
|
171
|
+
|
|
172
|
+
uvx pre-commit install
|
|
173
|
+
|
|
174
|
+
You can invoke these manually on all files with:
|
|
175
|
+
|
|
176
|
+
uvx pre-commit run --all-files
|
|
124
177
|
|
|
125
178
|
|
|
126
179
|
## Contributing
|
|
127
180
|
|
|
128
|
-
If you are contributing to development, you will want to clone this project, and can then install it locally with:
|
|
181
|
+
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
182
|
|
|
130
183
|
gh repo clone promptromp/mockstack
|
|
131
184
|
cd mockstack/
|
|
132
185
|
uv sync
|
|
133
|
-
uv pip install -e .
|
|
134
186
|
|
|
135
187
|
Run in development mode (for live-reload of changes when developing):
|
|
136
188
|
|
|
137
189
|
uv run uvicorn --factory mockstack.main:create_app --reload
|
|
138
190
|
|
|
139
191
|
Note that when you run using the uvicorn CLI, you will need to set any configuration via `.env` file or environment variables.
|
|
192
|
+
|
|
193
|
+
See [CONTRIBUTING.md](https://github.com/promptromp/mockstack/blob/main/CONTRIBUTING.md) for the full development workflow.
|