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.
- {mockstack-0.8.0 → mockstack-0.9.1}/Dockerfile +1 -1
- {mockstack-0.8.0 → mockstack-0.9.1}/PKG-INFO +21 -14
- {mockstack-0.8.0 → mockstack-0.9.1}/README.md +18 -11
- {mockstack-0.8.0 → mockstack-0.9.1}/SECURITY.md +1 -1
- {mockstack-0.8.0 → mockstack-0.9.1}/docs/ollama.md +3 -2
- {mockstack-0.8.0 → mockstack-0.9.1}/mkdocs.yml +5 -4
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/intent.py +7 -1
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/strategies/filefixtures.py +1 -8
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/strategies/proxyrules.py +6 -3
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/strategies/test_filefixtures.py +7 -26
- mockstack-0.9.1/mockstack/tests/test_intent.py +162 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack.egg-info/PKG-INFO +21 -14
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack.egg-info/SOURCES.txt +1 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/pyproject.toml +2 -2
- {mockstack-0.8.0 → mockstack-0.9.1}/.dockerignore +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/.env.example +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/.github/workflows/ci.yml +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/.github/workflows/publish-docs.yml +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/.github/workflows/publish-to-dockerhub.yml +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/.github/workflows/publish-to-pypi.yml +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/.gitignore +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/.pre-commit-config.yaml +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/CODE_OF_CONDUCT.md +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/LICENSE +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/docker-entrypoint.sh +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/docs/assets/favicon.ico +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/docs/assets/logo.png +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/docs/assets/mockstack.png +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/docs/configuration.md +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/docs/strategies/base.md +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/docs/strategies/filefixtures.md +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/docs/strategies/proxyrules.md +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/examples/filefixtures-with-templates/.env.example +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/examples/filefixtures-with-templates/README.md +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/examples/filefixtures-with-templates/templates/servicename-api-v1-items.j2 +0 -0
- {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
- {mockstack-0.8.0 → mockstack-0.9.1}/examples/llm/README.md +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/examples/llm/mockstack-langchain-example.ipynb +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/examples/llm/templates/ollama-openai-v1-chat-completions.j2 +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/examples/llm/templates/openai-v1-chat-completions.j2 +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/examples/llm/templates/openai-v1-deployments-gpt-4o-chat-completions.j2 +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/examples/proxyrules-with-rules-file/.env.example +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/examples/proxyrules-with-rules-file/README.md +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/examples/proxyrules-with-rules-file/rules.yml +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/__init__.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/config.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/constants.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/display.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/exceptions.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/identifiers.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/lifespan.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/llm/__init__.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/llm/ollama.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/main.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/middleware.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/routers/__init__.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/routers/catchall.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/routers/homepage.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/strategies/__init__.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/strategies/base.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/strategies/create_mixin.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/strategies/factory.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/telemetry.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/templating.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/__init__.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/conftest.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/fixtures/proxyrules.yml +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/fixtures/templates/__init__.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/fixtures/templates/example-template.j2 +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/llm/__init__.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/llm/test_ollama.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/routers/__init__.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/routers/test_catchall.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/routers/test_homepage.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/strategies/test_create_mixin.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/strategies/test_proxyrules.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/test_display.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/test_identifiers.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/test_middleware.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/test_telemetry.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack/tests/test_templating.py +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack.egg-info/dependency_links.txt +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack.egg-info/entry_points.txt +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack.egg-info/requires.txt +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/mockstack.egg-info/top_level.txt +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/setup.cfg +0 -0
- {mockstack-0.8.0 → mockstack-0.9.1}/uv.lock +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mockstack
|
|
3
|
-
Version: 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/
|
|
8
|
-
Project-URL: Issues, https://github.com/
|
|
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
|
-

|
|
35
35
|
|
|
36
36
|
--------------------------------------------------------------------------------
|
|
37
37
|
|
|
38
38
|
|
|
39
|
-
[](https://github.com/promptromp/mockstack/actions/workflows/ci.yml)
|
|
40
|
+
[](https://github.com/promptromp/mockstack/blob/main/LICENSE)
|
|
41
41
|
[](https://pypi.org/project/mockstack/)
|
|
42
42
|
[](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/
|
|
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/
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-

|
|
2
2
|
|
|
3
3
|
--------------------------------------------------------------------------------
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
[](https://github.com/promptromp/mockstack/actions/workflows/ci.yml)
|
|
7
|
+
[](https://github.com/promptromp/mockstack/blob/main/LICENSE)
|
|
8
8
|
[](https://pypi.org/project/mockstack/)
|
|
9
9
|
[](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/
|
|
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/
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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/
|
|
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
|
|
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": "
|
|
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://
|
|
2
|
+
site_url: https://promptromp.github.io/mockstack
|
|
3
3
|
repo_name: mockstack
|
|
4
|
-
repo_url: https://github.com/
|
|
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
|
-
-
|
|
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/
|
|
65
|
+
link: https://github.com/promptromp/mockstack
|
|
65
66
|
|
|
66
67
|
copyright: Copyright © 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
|
-
|
|
127
|
-
|
|
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
|
|
108
|
-
|
|
109
|
-
await strategy.apply(request)
|
|
87
|
+
# Execute
|
|
88
|
+
response = await strategy.apply(request)
|
|
110
89
|
|
|
111
|
-
|
|
112
|
-
assert
|
|
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.
|
|
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/
|
|
8
|
-
Project-URL: Issues, https://github.com/
|
|
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
|
-

|
|
35
35
|
|
|
36
36
|
--------------------------------------------------------------------------------
|
|
37
37
|
|
|
38
38
|
|
|
39
|
-
[](https://github.com/promptromp/mockstack/actions/workflows/ci.yml)
|
|
40
|
+
[](https://github.com/promptromp/mockstack/blob/main/LICENSE)
|
|
41
41
|
[](https://pypi.org/project/mockstack/)
|
|
42
42
|
[](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/
|
|
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/
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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/
|
|
39
|
-
Issues = "https://github.com/
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mockstack-0.8.0 → mockstack-0.9.1}/examples/llm/templates/ollama-openai-v1-chat-completions.j2
RENAMED
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|