mockstack 0.8.0__tar.gz → 0.9.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. {mockstack-0.8.0 → mockstack-0.9.1}/Dockerfile +1 -1
  2. {mockstack-0.8.0 → mockstack-0.9.1}/PKG-INFO +21 -14
  3. {mockstack-0.8.0 → mockstack-0.9.1}/README.md +18 -11
  4. {mockstack-0.8.0 → mockstack-0.9.1}/SECURITY.md +1 -1
  5. {mockstack-0.8.0 → mockstack-0.9.1}/docs/ollama.md +3 -2
  6. {mockstack-0.8.0 → mockstack-0.9.1}/mkdocs.yml +5 -4
  7. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/intent.py +7 -1
  8. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/strategies/filefixtures.py +1 -8
  9. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/strategies/proxyrules.py +6 -3
  10. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/strategies/test_filefixtures.py +7 -26
  11. mockstack-0.9.1/mockstack/tests/test_intent.py +162 -0
  12. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack.egg-info/PKG-INFO +21 -14
  13. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack.egg-info/SOURCES.txt +1 -0
  14. {mockstack-0.8.0 → mockstack-0.9.1}/pyproject.toml +2 -2
  15. {mockstack-0.8.0 → mockstack-0.9.1}/.dockerignore +0 -0
  16. {mockstack-0.8.0 → mockstack-0.9.1}/.env.example +0 -0
  17. {mockstack-0.8.0 → mockstack-0.9.1}/.github/workflows/ci.yml +0 -0
  18. {mockstack-0.8.0 → mockstack-0.9.1}/.github/workflows/publish-docs.yml +0 -0
  19. {mockstack-0.8.0 → mockstack-0.9.1}/.github/workflows/publish-to-dockerhub.yml +0 -0
  20. {mockstack-0.8.0 → mockstack-0.9.1}/.github/workflows/publish-to-pypi.yml +0 -0
  21. {mockstack-0.8.0 → mockstack-0.9.1}/.gitignore +0 -0
  22. {mockstack-0.8.0 → mockstack-0.9.1}/.pre-commit-config.yaml +0 -0
  23. {mockstack-0.8.0 → mockstack-0.9.1}/CODE_OF_CONDUCT.md +0 -0
  24. {mockstack-0.8.0 → mockstack-0.9.1}/LICENSE +0 -0
  25. {mockstack-0.8.0 → mockstack-0.9.1}/docker-entrypoint.sh +0 -0
  26. {mockstack-0.8.0 → mockstack-0.9.1}/docs/assets/favicon.ico +0 -0
  27. {mockstack-0.8.0 → mockstack-0.9.1}/docs/assets/logo.png +0 -0
  28. {mockstack-0.8.0 → mockstack-0.9.1}/docs/assets/mockstack.png +0 -0
  29. {mockstack-0.8.0 → mockstack-0.9.1}/docs/configuration.md +0 -0
  30. {mockstack-0.8.0 → mockstack-0.9.1}/docs/strategies/base.md +0 -0
  31. {mockstack-0.8.0 → mockstack-0.9.1}/docs/strategies/filefixtures.md +0 -0
  32. {mockstack-0.8.0 → mockstack-0.9.1}/docs/strategies/proxyrules.md +0 -0
  33. {mockstack-0.8.0 → mockstack-0.9.1}/examples/filefixtures-with-templates/.env.example +0 -0
  34. {mockstack-0.8.0 → mockstack-0.9.1}/examples/filefixtures-with-templates/README.md +0 -0
  35. {mockstack-0.8.0 → mockstack-0.9.1}/examples/filefixtures-with-templates/templates/servicename-api-v1-items.j2 +0 -0
  36. {mockstack-0.8.0 → mockstack-0.9.1}/examples/filefixtures-with-templates/templates/servicename2-api-v2-user.533ec889-7c68-45c8-b21e-4a7e455d1234.j2 +0 -0
  37. {mockstack-0.8.0 → mockstack-0.9.1}/examples/llm/README.md +0 -0
  38. {mockstack-0.8.0 → mockstack-0.9.1}/examples/llm/mockstack-langchain-example.ipynb +0 -0
  39. {mockstack-0.8.0 → mockstack-0.9.1}/examples/llm/templates/ollama-openai-v1-chat-completions.j2 +0 -0
  40. {mockstack-0.8.0 → mockstack-0.9.1}/examples/llm/templates/openai-v1-chat-completions.j2 +0 -0
  41. {mockstack-0.8.0 → mockstack-0.9.1}/examples/llm/templates/openai-v1-deployments-gpt-4o-chat-completions.j2 +0 -0
  42. {mockstack-0.8.0 → mockstack-0.9.1}/examples/proxyrules-with-rules-file/.env.example +0 -0
  43. {mockstack-0.8.0 → mockstack-0.9.1}/examples/proxyrules-with-rules-file/README.md +0 -0
  44. {mockstack-0.8.0 → mockstack-0.9.1}/examples/proxyrules-with-rules-file/rules.yml +0 -0
  45. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/__init__.py +0 -0
  46. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/config.py +0 -0
  47. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/constants.py +0 -0
  48. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/display.py +0 -0
  49. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/exceptions.py +0 -0
  50. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/identifiers.py +0 -0
  51. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/lifespan.py +0 -0
  52. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/llm/__init__.py +0 -0
  53. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/llm/ollama.py +0 -0
  54. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/main.py +0 -0
  55. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/middleware.py +0 -0
  56. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/routers/__init__.py +0 -0
  57. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/routers/catchall.py +0 -0
  58. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/routers/homepage.py +0 -0
  59. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/strategies/__init__.py +0 -0
  60. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/strategies/base.py +0 -0
  61. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/strategies/create_mixin.py +0 -0
  62. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/strategies/factory.py +0 -0
  63. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/telemetry.py +0 -0
  64. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/templating.py +0 -0
  65. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/__init__.py +0 -0
  66. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/conftest.py +0 -0
  67. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/fixtures/proxyrules.yml +0 -0
  68. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/fixtures/templates/__init__.py +0 -0
  69. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/fixtures/templates/example-template.j2 +0 -0
  70. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/llm/__init__.py +0 -0
  71. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/llm/test_ollama.py +0 -0
  72. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/routers/__init__.py +0 -0
  73. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/routers/test_catchall.py +0 -0
  74. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/routers/test_homepage.py +0 -0
  75. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/strategies/test_create_mixin.py +0 -0
  76. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/strategies/test_proxyrules.py +0 -0
  77. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/test_display.py +0 -0
  78. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/test_identifiers.py +0 -0
  79. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/test_middleware.py +0 -0
  80. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/test_telemetry.py +0 -0
  81. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/test_templating.py +0 -0
  82. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack.egg-info/dependency_links.txt +0 -0
  83. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack.egg-info/entry_points.txt +0 -0
  84. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack.egg-info/requires.txt +0 -0
  85. {mockstack-0.8.0 → mockstack-0.9.1}/mockstack.egg-info/top_level.txt +0 -0
  86. {mockstack-0.8.0 → mockstack-0.9.1}/setup.cfg +0 -0
  87. {mockstack-0.8.0 → mockstack-0.9.1}/uv.lock +0 -0
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Define build argument for version
4
4
  ARG PYTHON_IMAGE_VERSION=3.13.3-slim
5
- ARG MOCKSTACK_VERSION=0.1.0
5
+ ARG MOCKSTACK_VERSION=0.8.0
6
6
 
7
7
  # ---------------------------- Base Image --------------------------------
8
8
 
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mockstack
3
- Version: 0.8.0
3
+ Version: 0.9.1
4
4
  Summary: An API mocking workhorse
5
5
  Author-email: Adam Ever-Hadani <mockstack.contact@gmail.com>
6
6
  License-Expression: MIT
7
- Project-URL: Homepage, https://github.com/adamhadani/mockstack
8
- Project-URL: Issues, https://github.com/adamhadani/mockstack/issues
7
+ Project-URL: Homepage, https://github.com/promptromp/mockstack
8
+ 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
@@ -31,13 +31,13 @@ Provides-Extra: llm
31
31
  Requires-Dist: ollama>=0.4.8; extra == "llm"
32
32
  Dynamic: license-file
33
33
 
34
- ![mockstack logo](https://github.com/adamhadani/mockstack/raw/main/docs/assets/mockstack.png)
34
+ ![mockstack logo](https://github.com/promptromp/mockstack/raw/main/docs/assets/mockstack.png)
35
35
 
36
36
  --------------------------------------------------------------------------------
37
37
 
38
38
 
39
- [![CI](https://github.com/adamhadani/mockstack/actions/workflows/ci.yml/badge.svg)](https://github.com/adamhadani/mockstack/actions/workflows/ci.yml)
40
- [![GitHub License](https://img.shields.io/github/license/adamhadani/mockstack)](https://github.com/adamhadani/mockstack/blob/main/LICENSE)
39
+ [![CI](https://github.com/promptromp/mockstack/actions/workflows/ci.yml/badge.svg)](https://github.com/promptromp/mockstack/actions/workflows/ci.yml)
40
+ [![GitHub License](https://img.shields.io/github/license/promptromp/mockstack)](https://github.com/promptromp/mockstack/blob/main/LICENSE)
41
41
  [![PyPI - Version](https://img.shields.io/pypi/v/mockstack)](https://pypi.org/project/mockstack/)
42
42
  [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/mockstack)](https://pypi.org/project/mockstack/)
43
43
 
@@ -77,21 +77,28 @@ or install into a persistent environment and add it to the PATH with:
77
77
 
78
78
  ## Usage
79
79
 
80
- See the [examples](https://github.com/adamhadani/mockstack/blob/main/examples/) directory for complete examples with documentation.
80
+ See the [examples](https://github.com/promptromp/mockstack/blob/main/examples/) directory for complete examples with documentation.
81
81
 
82
- Available configuration options are [here](https://github.com/adamhadani/mockstack/blob/main/mockstack/config.py).
82
+ Available configuration options are [here](https://github.com/promptromp/mockstack/blob/main/mockstack/config.py).
83
83
 
84
- Setting individual options can be done either through an `.env` file, individual environment variables, or command-line arguments. For example:
84
+ Setting individual options can be done either through an `.env` file, individual environment variables, or command-line arguments.
85
+
86
+
87
+ Minimal example to get you started:
85
88
 
86
89
  ```shell
87
- export MOCKSTACK__STRATEGY=filefixtures
90
+ mkdir -p ~/mockstack-templates
91
+ echo '{"message": "Hello from mockstack!"}' > ~/mockstack-templates/myservice-api-myresource.j2
92
+
88
93
  export MOCKSTACK__TEMPLATES_DIR=~/mockstack-templates/
89
- export MOCKSTACK__OPENTELEMETRY__ENABLED=true
90
- export MOCKSTACK__OPENTELEMETRY__CAPTURE_RESPONSE_BODY=true
91
94
  uvx mockstack
92
95
  ```
93
96
 
94
- See also the included [.env.example](https://github.com/adamhadani/mockstack/blob/main/.env.example) for more examples. You can copy that file to `.env` and fill in configuration as needed based on the given examples.
97
+ You can then hit `http://localhost:8000/myservice/api/myresource/23faa8cf-5daa-4bcb-8c92-27018b712aa9` (or any other UUID).
98
+
99
+ This is of course just the tip of the iceberg.
100
+
101
+ See also the included [.env.example](https://github.com/promptromp/mockstack/blob/main/.env.example) for more settings you are likely to find useful. You can copy that file to `.env` and fill in configuration as needed based on the given examples.
95
102
 
96
103
  Out of the box, you get the following behavior when using the default `filefixtures` strategy:
97
104
 
@@ -120,7 +127,7 @@ Linting, formatting, static type checks etc. are all managed via [pre-commit](ht
120
127
 
121
128
  If you are contributing to development, you will want to clone this project, and can then install it locally with:
122
129
 
123
- gh repo clone adamhadani/mockstack
130
+ gh repo clone promptromp/mockstack
124
131
  cd mockstack/
125
132
  uv sync
126
133
  uv pip install -e .
@@ -1,10 +1,10 @@
1
- ![mockstack logo](https://github.com/adamhadani/mockstack/raw/main/docs/assets/mockstack.png)
1
+ ![mockstack logo](https://github.com/promptromp/mockstack/raw/main/docs/assets/mockstack.png)
2
2
 
3
3
  --------------------------------------------------------------------------------
4
4
 
5
5
 
6
- [![CI](https://github.com/adamhadani/mockstack/actions/workflows/ci.yml/badge.svg)](https://github.com/adamhadani/mockstack/actions/workflows/ci.yml)
7
- [![GitHub License](https://img.shields.io/github/license/adamhadani/mockstack)](https://github.com/adamhadani/mockstack/blob/main/LICENSE)
6
+ [![CI](https://github.com/promptromp/mockstack/actions/workflows/ci.yml/badge.svg)](https://github.com/promptromp/mockstack/actions/workflows/ci.yml)
7
+ [![GitHub License](https://img.shields.io/github/license/promptromp/mockstack)](https://github.com/promptromp/mockstack/blob/main/LICENSE)
8
8
  [![PyPI - Version](https://img.shields.io/pypi/v/mockstack)](https://pypi.org/project/mockstack/)
9
9
  [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/mockstack)](https://pypi.org/project/mockstack/)
10
10
 
@@ -44,21 +44,28 @@ or install into a persistent environment and add it to the PATH with:
44
44
 
45
45
  ## Usage
46
46
 
47
- See the [examples](https://github.com/adamhadani/mockstack/blob/main/examples/) directory for complete examples with documentation.
47
+ See the [examples](https://github.com/promptromp/mockstack/blob/main/examples/) directory for complete examples with documentation.
48
48
 
49
- Available configuration options are [here](https://github.com/adamhadani/mockstack/blob/main/mockstack/config.py).
49
+ Available configuration options are [here](https://github.com/promptromp/mockstack/blob/main/mockstack/config.py).
50
50
 
51
- Setting individual options can be done either through an `.env` file, individual environment variables, or command-line arguments. For example:
51
+ Setting individual options can be done either through an `.env` file, individual environment variables, or command-line arguments.
52
+
53
+
54
+ Minimal example to get you started:
52
55
 
53
56
  ```shell
54
- export MOCKSTACK__STRATEGY=filefixtures
57
+ mkdir -p ~/mockstack-templates
58
+ echo '{"message": "Hello from mockstack!"}' > ~/mockstack-templates/myservice-api-myresource.j2
59
+
55
60
  export MOCKSTACK__TEMPLATES_DIR=~/mockstack-templates/
56
- export MOCKSTACK__OPENTELEMETRY__ENABLED=true
57
- export MOCKSTACK__OPENTELEMETRY__CAPTURE_RESPONSE_BODY=true
58
61
  uvx mockstack
59
62
  ```
60
63
 
61
- See also the included [.env.example](https://github.com/adamhadani/mockstack/blob/main/.env.example) for more examples. You can copy that file to `.env` and fill in configuration as needed based on the given examples.
64
+ You can then hit `http://localhost:8000/myservice/api/myresource/23faa8cf-5daa-4bcb-8c92-27018b712aa9` (or any other UUID).
65
+
66
+ This is of course just the tip of the iceberg.
67
+
68
+ See also the included [.env.example](https://github.com/promptromp/mockstack/blob/main/.env.example) for more settings you are likely to find useful. You can copy that file to `.env` and fill in configuration as needed based on the given examples.
62
69
 
63
70
  Out of the box, you get the following behavior when using the default `filefixtures` strategy:
64
71
 
@@ -87,7 +94,7 @@ Linting, formatting, static type checks etc. are all managed via [pre-commit](ht
87
94
 
88
95
  If you are contributing to development, you will want to clone this project, and can then install it locally with:
89
96
 
90
- gh repo clone adamhadani/mockstack
97
+ gh repo clone promptromp/mockstack
91
98
  cd mockstack/
92
99
  uv sync
93
100
  uv pip install -e .
@@ -11,4 +11,4 @@ We do not currently run any bug bounty programs.
11
11
  ## Vulnerability disclosures
12
12
 
13
13
  Critical vulnerabilities will be disclosed via GitHub's
14
- [security advisory](https://github.com/adamhadani/mockstack/security) system.
14
+ [security advisory](https://github.com/promptromp/mockstack/security) system.
@@ -43,19 +43,20 @@ The Ollama integration works by routing requests to a template file that uses th
43
43
 
44
44
  ## Integration with Templates
45
45
 
46
- You can also use Ollama responses within your Jinja templates. This allows you to:
46
+ You can use Ollama responses within your Jinja templates via the provided `ollama` template function. This allows you to:
47
47
 
48
48
  1. Mix static and dynamic content
49
49
  2. Apply transformations to the LLM responses
50
50
  3. Create conditional logic based on the responses
51
51
 
52
52
  Example template structure:
53
+
53
54
  ```jinja
54
55
  {
55
56
  "id": "chatcmpl-123",
56
57
  "object": "chat.completion",
57
58
  "created": 1677652288,
58
- "model": "{{ model }}",
59
+ "model": "gpt-4.1",
59
60
  "choices": [{
60
61
  "index": 0,
61
62
  "message": {
@@ -1,7 +1,7 @@
1
1
  site_name: mockstack
2
- site_url: https://adamhadani.github.io/mockstack
2
+ site_url: https://promptromp.github.io/mockstack
3
3
  repo_name: mockstack
4
- repo_url: https://github.com/adamhadani/mockstack
4
+ repo_url: https://github.com/promptromp/mockstack
5
5
 
6
6
  theme:
7
7
  name: material
@@ -53,14 +53,15 @@ nav:
53
53
  - Home: README.md
54
54
  - Configuration: configuration.md
55
55
  - Strategies:
56
- - Overview: strategies/index.md
56
+ - BaseStrategy: strategies/base.md
57
57
  - FileFixtures: strategies/filefixtures.md
58
+ - ProxyRules: strategies/proxyrules.md
58
59
  - LLM Integrations:
59
60
  - Ollama: ollama.md
60
61
 
61
62
  extra:
62
63
  social:
63
64
  - icon: simple/github
64
- link: https://github.com/adamhadani/mockstack
65
+ link: https://github.com/promptromp/mockstack
65
66
 
66
67
  copyright: Copyright &copy; 2025 Adam Ever-Hadani
@@ -62,7 +62,13 @@ def looks_like_a_create(request: Request) -> bool:
62
62
  """
63
63
  return any(
64
64
  (
65
- request.method == "POST",
65
+ request.method == "POST"
66
+ and not any(
67
+ (
68
+ looks_like_a_search(request),
69
+ looks_like_a_command(request),
70
+ )
71
+ ),
66
72
  request.url.path.endswith("/create"),
67
73
  ),
68
74
  )
@@ -6,6 +6,7 @@ from functools import cached_property
6
6
  from pathlib import Path
7
7
 
8
8
  from fastapi import HTTPException, Request, Response, status
9
+ from fastapi.responses import JSONResponse
9
10
  from jinja2 import Environment
10
11
 
11
12
  from mockstack.config import Settings
@@ -18,7 +19,6 @@ from mockstack.strategies.base import BaseStrategy
18
19
  from mockstack.strategies.create_mixin import CreateMixin
19
20
  from mockstack.templating import (
20
21
  iter_possible_template_arguments,
21
- missing_template_detail,
22
22
  templates_env_provider,
23
23
  )
24
24
 
@@ -165,17 +165,10 @@ class FileFixturesStrategy(BaseStrategy, CreateMixin):
165
165
  )
166
166
 
167
167
  # if we get here, we have no template to render.
168
- raise HTTPException(
169
- status_code=status.HTTP_404_NOT_FOUND,
170
- detail=missing_template_detail(request, templates_dir=self.templates_dir),
171
- )
172
- """
173
- # TODO: return custom fields from settings
174
168
  return JSONResponse(
175
169
  content=self.missing_resource_fields,
176
170
  status_code=status.HTTP_404_NOT_FOUND,
177
171
  )
178
- """
179
172
 
180
173
  def update_opentelemetry(self, request: Request, template_args: dict) -> None:
181
174
  """Update the opentelemetry span with the file fixtures details."""
@@ -9,7 +9,7 @@ from urllib.parse import urlparse
9
9
  import httpx
10
10
  import yaml
11
11
  from fastapi import Request, Response, status
12
- from fastapi.responses import RedirectResponse
12
+ from fastapi.responses import JSONResponse, RedirectResponse
13
13
  from jinja2 import Environment
14
14
  from starlette.datastructures import Headers
15
15
 
@@ -73,6 +73,7 @@ class ProxyRulesStrategy(BaseStrategy, CreateMixin):
73
73
  self.reverse_proxy_timeout = settings.proxyrules_reverse_proxy_timeout
74
74
  self.simulate_create_on_missing = settings.proxyrules_simulate_create_on_missing
75
75
  self.created_resource_metadata = settings.created_resource_metadata
76
+ self.missing_resource_fields = settings.missing_resource_fields
76
77
 
77
78
  def __str__(self) -> str:
78
79
  return (
@@ -123,8 +124,10 @@ class ProxyRulesStrategy(BaseStrategy, CreateMixin):
123
124
  created_resource_metadata=self.created_resource_metadata,
124
125
  )
125
126
  else:
126
- # no rule found and we are not simulating resource creation
127
- return Response(status_code=status.HTTP_404_NOT_FOUND)
127
+ return JSONResponse(
128
+ content=self.missing_resource_fields,
129
+ status_code=status.HTTP_404_NOT_FOUND,
130
+ )
128
131
 
129
132
  url = rule.apply(request)
130
133
  self.logger.info(f"Redirecting to: {url}")
@@ -4,6 +4,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
4
4
 
5
5
  import pytest
6
6
  from fastapi import HTTPException, Request, status
7
+ import json
7
8
 
8
9
  from mockstack.strategies.filefixtures import FileFixturesStrategy
9
10
 
@@ -29,27 +30,6 @@ def test_filefixtures_strategy_str(settings):
29
30
  assert str(settings.templates_dir) in str(strategy)
30
31
 
31
32
 
32
- @pytest.mark.asyncio
33
- async def test_filefixtures_strategy_apply(settings, span):
34
- """Test the FileFixturesStrategy apply method."""
35
- strategy = FileFixturesStrategy(settings)
36
- request = Request(
37
- scope={
38
- "type": "http",
39
- "method": "GET",
40
- "path": "/api/v1/projects/1234",
41
- "query_string": b"",
42
- "headers": [],
43
- }
44
- )
45
- request.state.span = span
46
-
47
- with pytest.raises(HTTPException) as exc_info:
48
- await strategy.apply(request)
49
-
50
- assert exc_info.value.status_code == 404
51
-
52
-
53
33
  @pytest.mark.asyncio
54
34
  async def test_file_fixtures_strategy_apply_success(settings, span):
55
35
  """Test the FileFixturesStrategy apply method when template exists."""
@@ -104,12 +84,13 @@ async def test_file_fixtures_strategy_apply_template_not_found(settings, span):
104
84
  )
105
85
  request.state.span = span
106
86
 
107
- # Execute and Assert
108
- with pytest.raises(HTTPException) as exc_info:
109
- await strategy.apply(request)
87
+ # Execute
88
+ response = await strategy.apply(request)
110
89
 
111
- assert exc_info.value.status_code == 404
112
- assert "Template not found" in str(exc_info.value.detail)
90
+ # Assert
91
+ assert response.status_code == 404
92
+ assert response.media_type == "application/json"
93
+ assert json.loads(response.body.decode()) == settings.missing_resource_fields
113
94
 
114
95
 
115
96
  @pytest.mark.asyncio
@@ -0,0 +1,162 @@
1
+ """Unit tests for the intent module."""
2
+
3
+ import pytest
4
+ from fastapi import Request
5
+ from starlette.datastructures import Headers
6
+
7
+ from mockstack.intent import (
8
+ wants_json,
9
+ looks_like_a_search,
10
+ looks_like_a_command,
11
+ looks_like_a_create,
12
+ )
13
+
14
+
15
+ @pytest.fixture
16
+ def mock_request():
17
+ """Create a mock FastAPI request object."""
18
+
19
+ def _create_request(
20
+ method="GET",
21
+ path="/",
22
+ headers=None,
23
+ ):
24
+ return Request(
25
+ {
26
+ "type": "http",
27
+ "method": method,
28
+ "path": path,
29
+ "headers": Headers(headers or {}).raw,
30
+ }
31
+ )
32
+
33
+ return _create_request
34
+
35
+
36
+ @pytest.fixture
37
+ def json_content_types():
38
+ """Return a list of valid JSON content types."""
39
+ return [
40
+ "application/json",
41
+ "text/json",
42
+ ]
43
+
44
+
45
+ @pytest.fixture
46
+ def non_json_content_types():
47
+ """Return a list of non-JSON content types."""
48
+ return [
49
+ "text/plain",
50
+ "application/xml",
51
+ "text/html",
52
+ ]
53
+
54
+
55
+ @pytest.fixture
56
+ def search_paths():
57
+ """Return a list of paths that should be identified as search requests."""
58
+ return [
59
+ "/api/_search",
60
+ "/api/search",
61
+ "/api/_query",
62
+ ]
63
+
64
+
65
+ @pytest.fixture
66
+ def command_paths():
67
+ """Return a list of paths that should be identified as command requests."""
68
+ return [
69
+ "/api/_command",
70
+ "/api/command",
71
+ "/api/_request",
72
+ "/api/request",
73
+ "/api/_run",
74
+ "/api/run",
75
+ "/api/_execute",
76
+ "/api/execute",
77
+ ]
78
+
79
+
80
+ @pytest.fixture
81
+ def create_paths():
82
+ """Return a list of paths that should be identified as create requests."""
83
+ return [
84
+ "/api/create",
85
+ "/api/data", # POST without search/command
86
+ ]
87
+
88
+
89
+ def test_wants_json_with_content_type(
90
+ mock_request, json_content_types, non_json_content_types
91
+ ):
92
+ """Test wants_json with different content types."""
93
+ # Test valid JSON content types
94
+ for content_type in json_content_types:
95
+ request = mock_request(headers={"Content-Type": content_type})
96
+ assert wants_json(request) is True
97
+
98
+ # Test non-JSON content types
99
+ for content_type in non_json_content_types:
100
+ request = mock_request(headers={"Content-Type": content_type})
101
+ assert wants_json(request) is False
102
+
103
+ # Test missing content type
104
+ request = mock_request()
105
+ assert wants_json(request) is False
106
+
107
+
108
+ def test_wants_json_with_path(mock_request):
109
+ """Test wants_json with .json path."""
110
+ # Test .json suffix
111
+ request = mock_request(path="/api/data.json")
112
+ assert wants_json(request) is True
113
+
114
+ # Test non-.json path
115
+ request = mock_request(path="/api/data")
116
+ assert wants_json(request) is False
117
+
118
+
119
+ def test_looks_like_a_search(mock_request, search_paths):
120
+ """Test looks_like_a_search with different paths."""
121
+ # Test search paths
122
+ for path in search_paths:
123
+ request = mock_request(path=path)
124
+ assert looks_like_a_search(request) is True
125
+
126
+ # Test non-search path
127
+ request = mock_request(path="/api/data")
128
+ assert looks_like_a_search(request) is False
129
+
130
+
131
+ def test_looks_like_a_command(mock_request, command_paths):
132
+ """Test looks_like_a_command with different paths."""
133
+ # Test command paths
134
+ for path in command_paths:
135
+ request = mock_request(path=path)
136
+ assert looks_like_a_command(request) is True
137
+
138
+ # Test non-command path
139
+ request = mock_request(path="/api/data")
140
+ assert looks_like_a_command(request) is False
141
+
142
+
143
+ def test_looks_like_a_create(mock_request, create_paths, search_paths, command_paths):
144
+ """Test looks_like_a_create with different scenarios."""
145
+ # Test create paths with POST method
146
+ for path in create_paths:
147
+ request = mock_request(method="POST", path=path)
148
+ assert looks_like_a_create(request) is True
149
+
150
+ # Test POST with search paths (should be False)
151
+ for path in search_paths:
152
+ request = mock_request(method="POST", path=path)
153
+ assert looks_like_a_create(request) is False
154
+
155
+ # Test POST with command paths (should be False)
156
+ for path in command_paths:
157
+ request = mock_request(method="POST", path=path)
158
+ assert looks_like_a_create(request) is False
159
+
160
+ # Test non-POST method
161
+ request = mock_request(method="GET", path="/api/data")
162
+ assert looks_like_a_create(request) is False
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mockstack
3
- Version: 0.8.0
3
+ Version: 0.9.1
4
4
  Summary: An API mocking workhorse
5
5
  Author-email: Adam Ever-Hadani <mockstack.contact@gmail.com>
6
6
  License-Expression: MIT
7
- Project-URL: Homepage, https://github.com/adamhadani/mockstack
8
- Project-URL: Issues, https://github.com/adamhadani/mockstack/issues
7
+ Project-URL: Homepage, https://github.com/promptromp/mockstack
8
+ 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
@@ -31,13 +31,13 @@ Provides-Extra: llm
31
31
  Requires-Dist: ollama>=0.4.8; extra == "llm"
32
32
  Dynamic: license-file
33
33
 
34
- ![mockstack logo](https://github.com/adamhadani/mockstack/raw/main/docs/assets/mockstack.png)
34
+ ![mockstack logo](https://github.com/promptromp/mockstack/raw/main/docs/assets/mockstack.png)
35
35
 
36
36
  --------------------------------------------------------------------------------
37
37
 
38
38
 
39
- [![CI](https://github.com/adamhadani/mockstack/actions/workflows/ci.yml/badge.svg)](https://github.com/adamhadani/mockstack/actions/workflows/ci.yml)
40
- [![GitHub License](https://img.shields.io/github/license/adamhadani/mockstack)](https://github.com/adamhadani/mockstack/blob/main/LICENSE)
39
+ [![CI](https://github.com/promptromp/mockstack/actions/workflows/ci.yml/badge.svg)](https://github.com/promptromp/mockstack/actions/workflows/ci.yml)
40
+ [![GitHub License](https://img.shields.io/github/license/promptromp/mockstack)](https://github.com/promptromp/mockstack/blob/main/LICENSE)
41
41
  [![PyPI - Version](https://img.shields.io/pypi/v/mockstack)](https://pypi.org/project/mockstack/)
42
42
  [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/mockstack)](https://pypi.org/project/mockstack/)
43
43
 
@@ -77,21 +77,28 @@ or install into a persistent environment and add it to the PATH with:
77
77
 
78
78
  ## Usage
79
79
 
80
- See the [examples](https://github.com/adamhadani/mockstack/blob/main/examples/) directory for complete examples with documentation.
80
+ See the [examples](https://github.com/promptromp/mockstack/blob/main/examples/) directory for complete examples with documentation.
81
81
 
82
- Available configuration options are [here](https://github.com/adamhadani/mockstack/blob/main/mockstack/config.py).
82
+ Available configuration options are [here](https://github.com/promptromp/mockstack/blob/main/mockstack/config.py).
83
83
 
84
- Setting individual options can be done either through an `.env` file, individual environment variables, or command-line arguments. For example:
84
+ Setting individual options can be done either through an `.env` file, individual environment variables, or command-line arguments.
85
+
86
+
87
+ Minimal example to get you started:
85
88
 
86
89
  ```shell
87
- export MOCKSTACK__STRATEGY=filefixtures
90
+ mkdir -p ~/mockstack-templates
91
+ echo '{"message": "Hello from mockstack!"}' > ~/mockstack-templates/myservice-api-myresource.j2
92
+
88
93
  export MOCKSTACK__TEMPLATES_DIR=~/mockstack-templates/
89
- export MOCKSTACK__OPENTELEMETRY__ENABLED=true
90
- export MOCKSTACK__OPENTELEMETRY__CAPTURE_RESPONSE_BODY=true
91
94
  uvx mockstack
92
95
  ```
93
96
 
94
- See also the included [.env.example](https://github.com/adamhadani/mockstack/blob/main/.env.example) for more examples. You can copy that file to `.env` and fill in configuration as needed based on the given examples.
97
+ You can then hit `http://localhost:8000/myservice/api/myresource/23faa8cf-5daa-4bcb-8c92-27018b712aa9` (or any other UUID).
98
+
99
+ This is of course just the tip of the iceberg.
100
+
101
+ See also the included [.env.example](https://github.com/promptromp/mockstack/blob/main/.env.example) for more settings you are likely to find useful. You can copy that file to `.env` and fill in configuration as needed based on the given examples.
95
102
 
96
103
  Out of the box, you get the following behavior when using the default `filefixtures` strategy:
97
104
 
@@ -120,7 +127,7 @@ Linting, formatting, static type checks etc. are all managed via [pre-commit](ht
120
127
 
121
128
  If you are contributing to development, you will want to clone this project, and can then install it locally with:
122
129
 
123
- gh repo clone adamhadani/mockstack
130
+ gh repo clone promptromp/mockstack
124
131
  cd mockstack/
125
132
  uv sync
126
133
  uv pip install -e .
@@ -68,6 +68,7 @@ mockstack/tests/__init__.py
68
68
  mockstack/tests/conftest.py
69
69
  mockstack/tests/test_display.py
70
70
  mockstack/tests/test_identifiers.py
71
+ mockstack/tests/test_intent.py
71
72
  mockstack/tests/test_middleware.py
72
73
  mockstack/tests/test_telemetry.py
73
74
  mockstack/tests/test_templating.py
@@ -35,8 +35,8 @@ dependencies = [
35
35
  dynamic = ["version"]
36
36
 
37
37
  [project.urls]
38
- Homepage = "https://github.com/adamhadani/mockstack"
39
- Issues = "https://github.com/adamhadani/mockstack/issues"
38
+ Homepage = "https://github.com/promptromp/mockstack"
39
+ Issues = "https://github.com/promptromp/mockstack/issues"
40
40
 
41
41
  [project.scripts]
42
42
  mockstack = "mockstack.main:run"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes