mockworld 0.2.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.
@@ -0,0 +1,7 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(python3:*)"
5
+ ]
6
+ }
7
+ }
@@ -0,0 +1,101 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ *.manifest
31
+ *.spec
32
+
33
+ # Installer logs
34
+ pip-log.txt
35
+ pip-delete-this-directory.txt
36
+
37
+ # Unit test / coverage reports
38
+ htmlcov/
39
+ .tox/
40
+ .nox/
41
+ .coverage
42
+ .coverage.*
43
+ .cache
44
+ nosetests.xml
45
+ coverage.xml
46
+ *.cover
47
+ *.py,cover
48
+ .hypothesis/
49
+ .pytest_cache/
50
+
51
+ # Translations
52
+ *.mo
53
+ *.pot
54
+
55
+ # Environments
56
+ .env
57
+ .venv
58
+ env/
59
+ venv/
60
+ ENV/
61
+ env.bak/
62
+ venv.bak/
63
+
64
+ # Spyder project settings
65
+ .spyderproject
66
+ .spyproject
67
+
68
+ # Rope project settings
69
+ .ropeproject
70
+
71
+ # mkdocs documentation
72
+ /site
73
+
74
+ # mypy
75
+ .mypy_cache/
76
+ .dmypy.json
77
+ dmypy.json
78
+
79
+ # Pyre type checker
80
+ .pyre/
81
+
82
+ # pytype static type analyzer
83
+ .pytype/
84
+
85
+ # Cython debug symbols
86
+ cython_debug/
87
+
88
+ # IDE
89
+ .idea/
90
+ .vscode/
91
+ *.swp
92
+ *.swo
93
+ *~
94
+
95
+ # OS
96
+ .DS_Store
97
+ Thumbs.db
98
+
99
+ # Project specific
100
+ *.egg-info/
101
+ mockworld/_version.py
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Handled Engineering
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,320 @@
1
+ Metadata-Version: 2.4
2
+ Name: mockworld
3
+ Version: 0.2.0
4
+ Summary: World tests for AI agents - intercept and mock HTTP requests across services
5
+ Project-URL: Homepage, https://github.com/handled-engineering/mockworld-python
6
+ Project-URL: Documentation, https://github.com/handled-engineering/mockworld-python#readme
7
+ Project-URL: Repository, https://github.com/handled-engineering/mockworld-python
8
+ Project-URL: Issues, https://github.com/handled-engineering/mockworld-python/issues
9
+ Author-email: Handled Engineering <engineering@handled.com>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: agent,ai,http,interceptor,mocking,testing
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Topic :: Software Development :: Testing
23
+ Requires-Python: >=3.9
24
+ Requires-Dist: requests>=2.25
25
+ Provides-Extra: dev
26
+ Requires-Dist: httpx; extra == 'dev'
27
+ Requires-Dist: mypy; extra == 'dev'
28
+ Requires-Dist: pytest; extra == 'dev'
29
+ Requires-Dist: pytest-cov; extra == 'dev'
30
+ Requires-Dist: responses; extra == 'dev'
31
+ Requires-Dist: ruff; extra == 'dev'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # mockworld-python
35
+
36
+ > World tests for AI agents - intercept and mock HTTP requests across services
37
+
38
+ [![PyPI version](https://badge.fury.io/py/mockworld.svg)](https://badge.fury.io/py/mockworld)
39
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
40
+
41
+ mockworld intercepts HTTP requests from your AI agent and redirects them to mock endpoints. Configure which services to intercept, and mockworld handles the rest.
42
+
43
+ ## Installation
44
+
45
+ ```bash
46
+ pip install mockworld
47
+ ```
48
+
49
+ ## Quick Start
50
+
51
+ ```python
52
+ import mockworld
53
+
54
+ # Configure connection to MockServer
55
+ mockworld.configure(
56
+ base_url="https://api.mokra.ai/api",
57
+ api_key="your-api-key"
58
+ )
59
+
60
+ # Create a world with your services
61
+ world = mockworld.mockworld("my test", "stripe", "shopify")
62
+
63
+ # Run your agent
64
+ def agent_code(services):
65
+ import requests
66
+ # Your agent code here - HTTP calls are automatically intercepted
67
+ requests.post(
68
+ "https://api.stripe.com/v1/charges",
69
+ json={"amount": 1000, "currency": "usd"}
70
+ )
71
+
72
+ world.run(agent_code)
73
+
74
+ # See what happened
75
+ world.observe()
76
+
77
+ # Assert outcomes
78
+ world.assert_(
79
+ "a charge was created",
80
+ "no errors occurred"
81
+ )
82
+ ```
83
+
84
+ ## How It Works
85
+
86
+ ```
87
+ ┌─────────────────────────────────────────────────────────────┐
88
+ │ Your Agent Code │
89
+ │ ┌─────────────────────────────────────────────────────┐ │
90
+ │ │ world.run(lambda services: │ │
91
+ │ │ requests.post("https://api.stripe...│ │
92
+ │ │ ) │ │
93
+ │ └─────────────────────────────────────────────────────┘ │
94
+ └─────────────────────────────────────────────────────────────┘
95
+
96
+
97
+ ┌─────────────────────────────────────────────────────────────┐
98
+ │ mockworld Interceptor │
99
+ │ │
100
+ │ https://api.stripe.com/v1/charges │
101
+ │ ↓ │
102
+ │ https://api.mokra.ai/api/mock/stripe/v1/charges │
103
+ │ │
104
+ └─────────────────────────────────────────────────────────────┘
105
+
106
+
107
+ ┌─────────────────────────────────────────────────────────────┐
108
+ │ Mock Server (api.mokra.ai) │
109
+ │ │
110
+ │ Records observations, returns mock responses, │
111
+ │ evaluates assertions, manages state │
112
+ └─────────────────────────────────────────────────────────────┘
113
+ ```
114
+
115
+ ## Configuration
116
+
117
+ ### Direct Configuration
118
+
119
+ ```python
120
+ mockworld.configure(
121
+ base_url="https://api.mokra.ai/api",
122
+ api_key="your-api-key"
123
+ )
124
+ ```
125
+
126
+ ### Environment Variables
127
+
128
+ ```bash
129
+ export MOCKWORLD_BASE_URL="https://api.mokra.ai/api"
130
+ export MOCKWORLD_API_KEY="your-api-key"
131
+ ```
132
+
133
+ ### Auth Styles
134
+
135
+ ```python
136
+ # Default: X-API-Key header (for mokra.ai)
137
+ mockworld.configure(
138
+ api_key="your-api-key"
139
+ )
140
+ # Sends: X-API-Key: your-api-key
141
+
142
+ # Bearer token auth
143
+ mockworld.configure(
144
+ api_key="your-token",
145
+ auth_style="bearer"
146
+ )
147
+ # Sends: Authorization: Bearer your-token
148
+
149
+ # Custom header
150
+ mockworld.configure(
151
+ api_key="your-key",
152
+ auth_style="custom",
153
+ auth_header="X-Custom-Auth"
154
+ )
155
+ # Sends: X-Custom-Auth: your-key
156
+ ```
157
+
158
+ ## API Reference
159
+
160
+ ### `mockworld.mockworld(name, *services)`
161
+
162
+ Create a new world with specified services.
163
+
164
+ ```python
165
+ # String shorthand
166
+ world = mockworld.mockworld("my test", "stripe", "shopify", "loop")
167
+
168
+ # Attach existing mock server
169
+ world = mockworld.mockworld("my test",
170
+ "stripe",
171
+ {"service_id": "shopify", "server_id": "existing-ms-id"}
172
+ )
173
+ ```
174
+
175
+ ### `world.run(func)` / `with world.run():`
176
+
177
+ Run your agent. All HTTP calls made during execution are intercepted and recorded by MockServer.
178
+
179
+ ```python
180
+ # Function style
181
+ def agent_code(services):
182
+ # Your agent code
183
+ requests.post("https://api.stripe.com/v1/charges", ...)
184
+
185
+ world.run(agent_code)
186
+
187
+ # Context manager style (recommended for pytest)
188
+ with world.run():
189
+ requests.post("https://api.stripe.com/v1/charges", ...)
190
+ ```
191
+
192
+ ### `world.assert_(*assertions)`
193
+
194
+ Assert conditions using natural language.
195
+
196
+ ```python
197
+ results = world.assert_(
198
+ "customer charged once",
199
+ "no refund issued"
200
+ )
201
+ ```
202
+
203
+ ### `world.observe()`
204
+
205
+ Display observations recorded by MockServer.
206
+
207
+ ```python
208
+ world.observe()
209
+ ```
210
+
211
+ ### `world.seed(func)`
212
+
213
+ Pre-populate state before running the agent.
214
+
215
+ ```python
216
+ def setup(s):
217
+ s.shopify.orders.create({"id": "order-123", "total": 99.99})
218
+
219
+ world.seed(setup)
220
+ ```
221
+
222
+ ### `world.state(mock_server_id=None)`
223
+
224
+ Get current state of a mock server.
225
+
226
+ ```python
227
+ state = world.state()
228
+ state.total # => 5
229
+ state.resource_types # => ["products", "orders"]
230
+ state["products"].count # => 3
231
+ state["products"].records # => [StateRecord, ...]
232
+ state["products"].find_by_id("prod-123")
233
+ ```
234
+
235
+ ## Service Mappings
236
+
237
+ mockworld uses YAML files to map hosts to service slugs. The default mappings for mokra.ai are included.
238
+
239
+ ### Custom Mappings
240
+
241
+ ```python
242
+ from mockworld import ServiceMapper
243
+
244
+ # Add a single mapping
245
+ ServiceMapper.add_mapping("api.myservice.com", "myservice")
246
+
247
+ # Load mappings from a YAML file
248
+ ServiceMapper.load_mappings("path/to/custom.yml")
249
+
250
+ # Load and replace all mappings
251
+ ServiceMapper.load_mappings("path/to/custom.yml", replace=True)
252
+ ```
253
+
254
+ ### Custom YAML Format
255
+
256
+ ```yaml
257
+ # custom_mappings.yml
258
+ api.myservice.com: myservice
259
+ api.another.com: another
260
+ *.internal.mycompany.com: internal
261
+ ```
262
+
263
+ ### Supporting Other Mock Servers
264
+
265
+ Create a YAML file in the mappings directory named after your provider:
266
+
267
+ ```yaml
268
+ # my_provider.yml
269
+ api.stripe.com: stripe
270
+ api.custom.com: custom
271
+ ```
272
+
273
+ Then load it:
274
+
275
+ ```python
276
+ from mockworld import ServiceMapper
277
+
278
+ ServiceMapper.load_provider("my_provider")
279
+ ```
280
+
281
+ ## Pytest Integration
282
+
283
+ mockworld works seamlessly with pytest:
284
+
285
+ ```python
286
+ # conftest.py
287
+ import pytest
288
+ import mockworld
289
+ import os
290
+
291
+ @pytest.fixture(autouse=True)
292
+ def configure_mockworld():
293
+ mockworld.configure(api_key=os.environ["MOCKWORLD_API_KEY"])
294
+ ```
295
+
296
+ ```python
297
+ # test_refund.py
298
+ from mockworld import mockworld
299
+
300
+ def test_refund_service():
301
+ world = mockworld(name="Refund test", services_list=["stripe"])
302
+
303
+ with world.run():
304
+ # Your code that calls Stripe API
305
+ RefundService.process(payment_id="pi_123", amount=5000)
306
+
307
+ world.assert_("a refund was created")
308
+ world.assert_("refund amount is $50")
309
+ ```
310
+
311
+ ## Development
312
+
313
+ ```bash
314
+ pip install -e ".[dev]"
315
+ pytest
316
+ ```
317
+
318
+ ## License
319
+
320
+ MIT