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.
Files changed (158) hide show
  1. mockstack-0.13.0/.env.example +38 -0
  2. {mockstack-0.12.0 → mockstack-0.13.0}/.github/workflows/ci.yml +26 -10
  3. {mockstack-0.12.0 → mockstack-0.13.0}/.github/workflows/publish-docs.yml +3 -3
  4. {mockstack-0.12.0 → mockstack-0.13.0}/.github/workflows/publish-to-pypi.yml +6 -6
  5. mockstack-0.13.0/.github/workflows/smoke-test-pypi.yml +83 -0
  6. {mockstack-0.12.0 → mockstack-0.13.0}/.gitignore +3 -0
  7. {mockstack-0.12.0 → mockstack-0.13.0}/.pre-commit-config.yaml +14 -8
  8. mockstack-0.13.0/CLAUDE.md +98 -0
  9. {mockstack-0.12.0 → mockstack-0.13.0}/CONTRIBUTING.md +17 -13
  10. {mockstack-0.12.0 → mockstack-0.13.0}/Dockerfile +1 -1
  11. {mockstack-0.12.0/mockstack.egg-info → mockstack-0.13.0}/PKG-INFO +65 -24
  12. mockstack-0.12.0/PKG-INFO → mockstack-0.13.0/README.md +50 -43
  13. mockstack-0.13.0/docs/configuration.md +127 -0
  14. mockstack-0.13.0/docs/guides/proxyrules-cookbook.md +418 -0
  15. {mockstack-0.12.0 → mockstack-0.13.0}/docs/ollama.md +5 -1
  16. {mockstack-0.12.0 → mockstack-0.13.0}/docs/strategies/base.md +1 -0
  17. mockstack-0.13.0/docs/strategies/filefixtures.md +136 -0
  18. mockstack-0.13.0/docs/strategies/proxyrules.md +447 -0
  19. {mockstack-0.12.0 → mockstack-0.13.0}/examples/filefixtures-with-templates/.env.example +1 -1
  20. {mockstack-0.12.0 → mockstack-0.13.0}/examples/filefixtures-with-templates/README.md +2 -2
  21. mockstack-0.13.0/examples/filefixtures-with-templates/templates/servicename-api-v1-items.j2 +20 -0
  22. {mockstack-0.12.0 → mockstack-0.13.0}/examples/llm/mockstack-langchain-example.ipynb +15 -12
  23. mockstack-0.13.0/examples/proxyrules-cookbook/01-tagged-traffic/fixtures/projects/project.json.j2 +1 -0
  24. mockstack-0.13.0/examples/proxyrules-cookbook/01-tagged-traffic/rules.yml +11 -0
  25. mockstack-0.13.0/examples/proxyrules-cookbook/02-scenario-directories/fixtures/archived/projects/project.json.j2 +1 -0
  26. mockstack-0.13.0/examples/proxyrules-cookbook/02-scenario-directories/fixtures/healthy/projects/project.json.j2 +1 -0
  27. mockstack-0.13.0/examples/proxyrules-cookbook/02-scenario-directories/rules.yml +11 -0
  28. mockstack-0.13.0/examples/proxyrules-cookbook/03-sql-gateway/fixtures/analytics/sales_facts.json.j2 +1 -0
  29. mockstack-0.13.0/examples/proxyrules-cookbook/03-sql-gateway/rules.yml +11 -0
  30. mockstack-0.13.0/examples/proxyrules-cookbook/04-json-literals/fixtures/orders/search.json.j2 +1 -0
  31. mockstack-0.13.0/examples/proxyrules-cookbook/04-json-literals/rules.yml +32 -0
  32. mockstack-0.13.0/examples/proxyrules-cookbook/05-query-parameters/fixtures/users/archived.json.j2 +1 -0
  33. mockstack-0.13.0/examples/proxyrules-cookbook/05-query-parameters/fixtures/users/page.json.j2 +1 -0
  34. mockstack-0.13.0/examples/proxyrules-cookbook/05-query-parameters/rules.yml +18 -0
  35. mockstack-0.13.0/examples/proxyrules-cookbook/06-asserting-in-tests/fixture_assertions.py +40 -0
  36. mockstack-0.13.0/examples/proxyrules-cookbook/06-asserting-in-tests/fixtures/orders/echo.json.j2 +1 -0
  37. mockstack-0.13.0/examples/proxyrules-cookbook/06-asserting-in-tests/fixtures/projects/project.json.j2 +1 -0
  38. mockstack-0.13.0/examples/proxyrules-cookbook/06-asserting-in-tests/fixtures/users/user-1.json.j2 +1 -0
  39. mockstack-0.13.0/examples/proxyrules-cookbook/06-asserting-in-tests/rules.yml +44 -0
  40. mockstack-0.13.0/examples/proxyrules-cookbook/07-redirect-mode/fixtures/users/user-1.json.j2 +1 -0
  41. mockstack-0.13.0/examples/proxyrules-cookbook/07-redirect-mode/rules.yml +9 -0
  42. mockstack-0.13.0/examples/proxyrules-cookbook/README.md +54 -0
  43. mockstack-0.13.0/examples/proxyrules-cookbook/upstream.py +40 -0
  44. mockstack-0.13.0/examples/proxyrules-eval-isolation/.env.example +11 -0
  45. mockstack-0.13.0/examples/proxyrules-eval-isolation/README.md +78 -0
  46. mockstack-0.13.0/examples/proxyrules-eval-isolation/fixtures/healthy/analytics/sales_facts.json.j2 +1 -0
  47. mockstack-0.13.0/examples/proxyrules-eval-isolation/fixtures/healthy/projects/project.json.j2 +1 -0
  48. mockstack-0.13.0/examples/proxyrules-eval-isolation/rules.yml +26 -0
  49. mockstack-0.13.0/examples/proxyrules-eval-isolation/upstream.py +27 -0
  50. {mockstack-0.12.0 → mockstack-0.13.0}/examples/proxyrules-with-rules-file/.env.example +2 -2
  51. mockstack-0.13.0/examples/proxyrules-with-rules-file/README.md +16 -0
  52. {mockstack-0.12.0 → mockstack-0.13.0}/mkdocs.yml +5 -2
  53. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/config.py +21 -21
  54. mockstack-0.13.0/mockstack/constants.py +54 -0
  55. mockstack-0.13.0/mockstack/display.py +37 -0
  56. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/exceptions.py +3 -1
  57. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/identifiers.py +9 -10
  58. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/lifespan.py +3 -3
  59. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/llm/ollama.py +9 -8
  60. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/main.py +10 -10
  61. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/middleware.py +11 -10
  62. mockstack-0.13.0/mockstack/routers/catchall.py +23 -0
  63. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/routers/homepage.py +3 -2
  64. mockstack-0.13.0/mockstack/rules.py +319 -0
  65. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/strategies/__init__.py +1 -0
  66. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/strategies/base.py +4 -4
  67. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/strategies/create_mixin.py +12 -19
  68. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/strategies/factory.py +1 -3
  69. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/strategies/filefixtures.py +51 -21
  70. mockstack-0.13.0/mockstack/strategies/proxyrules.py +535 -0
  71. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/telemetry.py +9 -20
  72. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/templating.py +19 -21
  73. mockstack-0.13.0/mockstack/tests/conftest.py +189 -0
  74. mockstack-0.13.0/mockstack/tests/live/conftest.py +253 -0
  75. mockstack-0.13.0/mockstack/tests/live/test_cookbook.py +523 -0
  76. mockstack-0.13.0/mockstack/tests/live/test_example_eval_isolation.py +89 -0
  77. mockstack-0.13.0/mockstack/tests/live/test_predicates_live.py +58 -0
  78. mockstack-0.13.0/mockstack/tests/live/test_reverse_proxy_live.py +116 -0
  79. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/tests/llm/test_ollama.py +32 -36
  80. mockstack-0.13.0/mockstack/tests/routers/test_catchall.py +60 -0
  81. mockstack-0.13.0/mockstack/tests/routers/test_homepage.py +15 -0
  82. mockstack-0.13.0/mockstack/tests/strategies/__init__.py +0 -0
  83. mockstack-0.13.0/mockstack/tests/strategies/conftest.py +52 -0
  84. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/tests/strategies/test_create_mixin.py +19 -75
  85. mockstack-0.13.0/mockstack/tests/strategies/test_filefixtures.py +294 -0
  86. mockstack-0.13.0/mockstack/tests/strategies/test_filefixtures_example.py +41 -0
  87. mockstack-0.13.0/mockstack/tests/strategies/test_proxyrules.py +1110 -0
  88. mockstack-0.13.0/mockstack/tests/test_config.py +58 -0
  89. mockstack-0.13.0/mockstack/tests/test_docs_settings.py +73 -0
  90. mockstack-0.13.0/mockstack/tests/test_docs_yaml.py +54 -0
  91. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/tests/test_identifiers.py +29 -40
  92. mockstack-0.13.0/mockstack/tests/test_intent.py +76 -0
  93. mockstack-0.13.0/mockstack/tests/test_middleware.py +23 -0
  94. mockstack-0.13.0/mockstack/tests/test_rules.py +750 -0
  95. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/tests/test_telemetry.py +40 -73
  96. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/tests/test_templating.py +75 -123
  97. mockstack-0.12.0/README.md → mockstack-0.13.0/mockstack.egg-info/PKG-INFO +84 -10
  98. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack.egg-info/SOURCES.txt +43 -0
  99. mockstack-0.13.0/mockstack.egg-info/requires.txt +15 -0
  100. mockstack-0.13.0/mockstack.egg-info/scm_file_list.json +130 -0
  101. mockstack-0.13.0/mockstack.egg-info/scm_version.json +8 -0
  102. mockstack-0.13.0/pyproject.toml +167 -0
  103. mockstack-0.13.0/uv.lock +1936 -0
  104. mockstack-0.12.0/.env.example +0 -25
  105. mockstack-0.12.0/docs/configuration.md +0 -103
  106. mockstack-0.12.0/docs/strategies/filefixtures.md +0 -109
  107. mockstack-0.12.0/docs/strategies/proxyrules.md +0 -123
  108. mockstack-0.12.0/examples/filefixtures-with-templates/templates/servicename-api-v1-items.j2 +0 -19
  109. mockstack-0.12.0/examples/proxyrules-with-rules-file/README.md +0 -15
  110. mockstack-0.12.0/mockstack/constants.py +0 -38
  111. mockstack-0.12.0/mockstack/display.py +0 -30
  112. mockstack-0.12.0/mockstack/routers/catchall.py +0 -16
  113. mockstack-0.12.0/mockstack/rules.py +0 -117
  114. mockstack-0.12.0/mockstack/strategies/proxyrules.py +0 -274
  115. mockstack-0.12.0/mockstack/tests/conftest.py +0 -78
  116. mockstack-0.12.0/mockstack/tests/routers/test_catchall.py +0 -46
  117. mockstack-0.12.0/mockstack/tests/routers/test_homepage.py +0 -27
  118. mockstack-0.12.0/mockstack/tests/strategies/test_filefixtures.py +0 -275
  119. mockstack-0.12.0/mockstack/tests/strategies/test_proxyrules.py +0 -397
  120. mockstack-0.12.0/mockstack/tests/test_intent.py +0 -160
  121. mockstack-0.12.0/mockstack/tests/test_middleware.py +0 -37
  122. mockstack-0.12.0/mockstack/tests/test_rules.py +0 -168
  123. mockstack-0.12.0/mockstack.egg-info/requires.txt +0 -15
  124. mockstack-0.12.0/pyproject.toml +0 -69
  125. mockstack-0.12.0/uv.lock +0 -1535
  126. {mockstack-0.12.0 → mockstack-0.13.0}/.dockerignore +0 -0
  127. {mockstack-0.12.0 → mockstack-0.13.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  128. {mockstack-0.12.0 → mockstack-0.13.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  129. {mockstack-0.12.0 → mockstack-0.13.0}/CODE_OF_CONDUCT.md +0 -0
  130. {mockstack-0.12.0 → mockstack-0.13.0}/LICENSE +0 -0
  131. {mockstack-0.12.0 → mockstack-0.13.0}/SECURITY.md +0 -0
  132. {mockstack-0.12.0 → mockstack-0.13.0}/docker-entrypoint.sh +0 -0
  133. {mockstack-0.12.0 → mockstack-0.13.0}/docs/assets/favicon.ico +0 -0
  134. {mockstack-0.12.0 → mockstack-0.13.0}/docs/assets/logo.png +0 -0
  135. {mockstack-0.12.0 → mockstack-0.13.0}/docs/assets/mockstack.png +0 -0
  136. {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
  137. {mockstack-0.12.0 → mockstack-0.13.0}/examples/llm/README.md +0 -0
  138. {mockstack-0.12.0 → mockstack-0.13.0}/examples/llm/templates/ollama-openai-v1-chat-completions.j2 +0 -0
  139. {mockstack-0.12.0 → mockstack-0.13.0}/examples/llm/templates/openai-v1-chat-completions.j2 +0 -0
  140. {mockstack-0.12.0 → mockstack-0.13.0}/examples/llm/templates/openai-v1-deployments-gpt-4o-chat-completions.j2 +0 -0
  141. {mockstack-0.12.0 → mockstack-0.13.0}/examples/proxyrules-with-rules-file/rules.yml +0 -0
  142. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/__init__.py +0 -0
  143. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/intent.py +0 -0
  144. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/llm/__init__.py +0 -0
  145. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/routers/__init__.py +0 -0
  146. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/tests/__init__.py +0 -0
  147. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/tests/fixtures/__init__.py +0 -0
  148. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/tests/fixtures/proxyrules.yml +0 -0
  149. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/tests/fixtures/templates/__init__.py +0 -0
  150. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/tests/fixtures/templates/example-template.j2 +0 -0
  151. {mockstack-0.12.0/mockstack/tests/llm → mockstack-0.13.0/mockstack/tests/live}/__init__.py +0 -0
  152. {mockstack-0.12.0/mockstack/tests/routers → mockstack-0.13.0/mockstack/tests/llm}/__init__.py +0 -0
  153. {mockstack-0.12.0/mockstack/tests/strategies → mockstack-0.13.0/mockstack/tests/routers}/__init__.py +0 -0
  154. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack/tests/test_display.py +0 -0
  155. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack.egg-info/dependency_links.txt +0 -0
  156. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack.egg-info/entry_points.txt +0 -0
  157. {mockstack-0.12.0 → mockstack-0.13.0}/mockstack.egg-info/top_level.txt +0 -0
  158. {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.11", "3.12", "3.13"]
23
+ python-version: ["3.13", "3.14"]
24
24
 
25
25
  steps:
26
- - uses: actions/checkout@v4
26
+ - uses: actions/checkout@v6
27
27
  - name: Install uv and set the python version
28
- uses: astral-sh/setup-uv@v5
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@v3
33
+ uses: astral-sh/ruff-action@v4.0.0
34
34
  with:
35
- version: "latest"
36
- - name: Set up Python ${{ matrix.python-version }}
37
- uses: actions/setup-python@v3
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@v4
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@v5
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@v4
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@v4
13
+ - uses: actions/checkout@v6
14
14
  with:
15
15
  persist-credentials: false
16
16
  - name: Set up Python
17
- uses: actions/setup-python@v5
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@v4
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@v4
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@v4
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@v4
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
@@ -173,3 +173,6 @@ cython_debug/
173
173
 
174
174
  # do not check in any user templates
175
175
  templates/*.j2
176
+
177
+ # rules files rendered from the examples' ${VAR} placeholders
178
+ examples/**/rules.local.yml
@@ -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: v5.0.0
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.11.9
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='85' -m 'not slow'"
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.11 or higher
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. Create a virtual environment:
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 venv
24
- source .venv/bin/activate # On Windows: venv\Scripts\activate
25
+ uv sync
25
26
  ```
26
- 4. Install the package in development mode:
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
- uv sync
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 (Python version, pytest version, etc.)
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 line length reasonable (ideally under 120 characters)
98
- - Use type hints where appropriate
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
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Define build argument for version
4
4
  ARG PYTHON_IMAGE_VERSION=3.13-slim
5
- ARG MOCKSTACK_VERSION=0.8.0
5
+ ARG MOCKSTACK_VERSION=0.13.0
6
6
 
7
7
  # ---------------------------- Base Image --------------------------------
8
8
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mockstack
3
- Version: 0.12.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
- Requires-Python: >=3.11
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.115.12
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.53b1
23
- Requires-Dist: opentelemetry-instrumentation-fastapi>=0.53b1
24
- Requires-Dist: pydantic>=2.11.3
25
- Requires-Dist: pydantic-settings>=2.9.1
26
- Requires-Dist: pyyaml>=6.0.2
27
- Requires-Dist: rich>=14.0.0
28
- Requires-Dist: typer>=0.15.2
29
- Requires-Dist: uvicorn>=0.34.2
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.4.8; extra == "llm"
32
+ Requires-Dist: ollama>=0.6.2; extra == "llm"
32
33
  Dynamic: license-file
33
34
 
34
35
  ![mockstack logo](https://github.com/promptromp/mockstack/raw/main/docs/assets/mockstack.png)
@@ -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 context variables.
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, returning an appropriate http status code and mock results based on user-configurable formatting.
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 python -m pytest
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 etc. are all managed via [pre-commit](https://pre-commit.com/) hooks. These will run automatically on every commit. You can invoke these manually on all files with:
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 run --all-files
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.