fixturify 0.1.9__py3-none-any.whl
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.
- fixturify/__init__.py +21 -0
- fixturify/_utils/__init__.py +7 -0
- fixturify/_utils/_constants.py +10 -0
- fixturify/_utils/_fixture_discovery.py +165 -0
- fixturify/_utils/_path_resolver.py +135 -0
- fixturify/http_d/__init__.py +80 -0
- fixturify/http_d/_config.py +214 -0
- fixturify/http_d/_decorator.py +267 -0
- fixturify/http_d/_exceptions.py +153 -0
- fixturify/http_d/_fixture_discovery.py +33 -0
- fixturify/http_d/_matcher.py +372 -0
- fixturify/http_d/_mock_context.py +154 -0
- fixturify/http_d/_models.py +205 -0
- fixturify/http_d/_patcher.py +524 -0
- fixturify/http_d/_player.py +222 -0
- fixturify/http_d/_recorder.py +1350 -0
- fixturify/http_d/_stubs/__init__.py +8 -0
- fixturify/http_d/_stubs/_aiohttp.py +220 -0
- fixturify/http_d/_stubs/_connection.py +478 -0
- fixturify/http_d/_stubs/_httpcore.py +269 -0
- fixturify/http_d/_stubs/_tornado.py +95 -0
- fixturify/http_d/_utils.py +194 -0
- fixturify/json_assert/__init__.py +13 -0
- fixturify/json_assert/_actual_saver.py +67 -0
- fixturify/json_assert/_assert.py +173 -0
- fixturify/json_assert/_comparator.py +183 -0
- fixturify/json_assert/_diff_formatter.py +265 -0
- fixturify/json_assert/_normalizer.py +83 -0
- fixturify/object_mapper/__init__.py +5 -0
- fixturify/object_mapper/_deserializers/__init__.py +19 -0
- fixturify/object_mapper/_deserializers/_base.py +186 -0
- fixturify/object_mapper/_deserializers/_dataclass.py +52 -0
- fixturify/object_mapper/_deserializers/_plain.py +55 -0
- fixturify/object_mapper/_deserializers/_pydantic_v1.py +38 -0
- fixturify/object_mapper/_deserializers/_pydantic_v2.py +41 -0
- fixturify/object_mapper/_deserializers/_sqlalchemy.py +72 -0
- fixturify/object_mapper/_deserializers/_sqlmodel.py +43 -0
- fixturify/object_mapper/_detectors/__init__.py +5 -0
- fixturify/object_mapper/_detectors/_type_detector.py +186 -0
- fixturify/object_mapper/_serializers/__init__.py +19 -0
- fixturify/object_mapper/_serializers/_base.py +260 -0
- fixturify/object_mapper/_serializers/_dataclass.py +55 -0
- fixturify/object_mapper/_serializers/_plain.py +49 -0
- fixturify/object_mapper/_serializers/_pydantic_v1.py +49 -0
- fixturify/object_mapper/_serializers/_pydantic_v2.py +49 -0
- fixturify/object_mapper/_serializers/_sqlalchemy.py +70 -0
- fixturify/object_mapper/_serializers/_sqlmodel.py +54 -0
- fixturify/object_mapper/mapper.py +256 -0
- fixturify/read_d/__init__.py +5 -0
- fixturify/read_d/_decorator.py +193 -0
- fixturify/read_d/_fixture_loader.py +88 -0
- fixturify/sql_d/__init__.py +7 -0
- fixturify/sql_d/_config.py +30 -0
- fixturify/sql_d/_decorator.py +373 -0
- fixturify/sql_d/_driver_registry.py +133 -0
- fixturify/sql_d/_executor.py +82 -0
- fixturify/sql_d/_fixture_discovery.py +55 -0
- fixturify/sql_d/_phase.py +10 -0
- fixturify/sql_d/_strategies/__init__.py +11 -0
- fixturify/sql_d/_strategies/_aiomysql.py +63 -0
- fixturify/sql_d/_strategies/_aiosqlite.py +29 -0
- fixturify/sql_d/_strategies/_asyncpg.py +34 -0
- fixturify/sql_d/_strategies/_base.py +118 -0
- fixturify/sql_d/_strategies/_mysql.py +70 -0
- fixturify/sql_d/_strategies/_psycopg.py +35 -0
- fixturify/sql_d/_strategies/_psycopg2.py +40 -0
- fixturify/sql_d/_strategies/_registry.py +109 -0
- fixturify/sql_d/_strategies/_sqlite.py +33 -0
- fixturify-0.1.9.dist-info/METADATA +122 -0
- fixturify-0.1.9.dist-info/RECORD +71 -0
- fixturify-0.1.9.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""WireMock-compatible data models for HTTP recording and playback."""
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
import json
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import Any, Dict, List, Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class HttpRequest:
|
|
11
|
+
"""
|
|
12
|
+
Represents an HTTP request in WireMock format.
|
|
13
|
+
|
|
14
|
+
Attributes:
|
|
15
|
+
method: HTTP method (GET, POST, PUT, DELETE, etc.)
|
|
16
|
+
url: Full URL including scheme and host
|
|
17
|
+
headers: Request headers as dict
|
|
18
|
+
queryParameters: Query parameters as dict
|
|
19
|
+
body: Request body (string or None)
|
|
20
|
+
bodyEncoding: Encoding for binary bodies ('base64' or None for text)
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
method: str
|
|
24
|
+
url: str
|
|
25
|
+
headers: Dict[str, str] = field(default_factory=dict)
|
|
26
|
+
queryParameters: Dict[str, str] = field(default_factory=dict)
|
|
27
|
+
body: Optional[str] = None
|
|
28
|
+
bodyEncoding: Optional[str] = None
|
|
29
|
+
|
|
30
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
31
|
+
"""Convert to dictionary for JSON serialization."""
|
|
32
|
+
result: Dict[str, Any] = {
|
|
33
|
+
"method": self.method.upper(),
|
|
34
|
+
"url": self.url,
|
|
35
|
+
"headers": self.headers,
|
|
36
|
+
"queryParameters": self.queryParameters,
|
|
37
|
+
}
|
|
38
|
+
if self.body is not None:
|
|
39
|
+
result["body"] = self.body
|
|
40
|
+
if self.bodyEncoding is not None:
|
|
41
|
+
result["bodyEncoding"] = self.bodyEncoding
|
|
42
|
+
return result
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def from_dict(cls, data: Dict[str, Any]) -> "HttpRequest":
|
|
46
|
+
"""Create from dictionary."""
|
|
47
|
+
return cls(
|
|
48
|
+
method=data.get("method", "GET"),
|
|
49
|
+
url=data.get("url", ""),
|
|
50
|
+
headers=data.get("headers", {}),
|
|
51
|
+
queryParameters=data.get("queryParameters", {}),
|
|
52
|
+
body=data.get("body"),
|
|
53
|
+
bodyEncoding=data.get("bodyEncoding"),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class HttpResponse:
|
|
59
|
+
"""
|
|
60
|
+
Represents an HTTP response in WireMock format.
|
|
61
|
+
|
|
62
|
+
Attributes:
|
|
63
|
+
status: HTTP status code
|
|
64
|
+
headers: Response headers as dict
|
|
65
|
+
body: Response body (string or None if using bodyFileName)
|
|
66
|
+
bodyFileName: Optional filename for binary content (stored separately)
|
|
67
|
+
bodyEncoding: Encoding for inline binary bodies ('base64' or None for text)
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
status: int
|
|
71
|
+
headers: Dict[str, str] = field(default_factory=dict)
|
|
72
|
+
body: Optional[str] = ""
|
|
73
|
+
bodyFileName: Optional[str] = None
|
|
74
|
+
bodyEncoding: Optional[str] = None
|
|
75
|
+
# Runtime field: actual bytes loaded from file (not serialized)
|
|
76
|
+
_body_bytes: Optional[bytes] = field(default=None, repr=False)
|
|
77
|
+
|
|
78
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
79
|
+
"""Convert to dictionary for JSON serialization."""
|
|
80
|
+
result: Dict[str, Any] = {
|
|
81
|
+
"status": self.status,
|
|
82
|
+
"headers": self.headers,
|
|
83
|
+
}
|
|
84
|
+
if self.bodyFileName is not None:
|
|
85
|
+
result["bodyFileName"] = self.bodyFileName
|
|
86
|
+
else:
|
|
87
|
+
result["body"] = self.body if self.body is not None else ""
|
|
88
|
+
if self.bodyEncoding is not None:
|
|
89
|
+
result["bodyEncoding"] = self.bodyEncoding
|
|
90
|
+
return result
|
|
91
|
+
|
|
92
|
+
@classmethod
|
|
93
|
+
def from_dict(cls, data: Dict[str, Any]) -> "HttpResponse":
|
|
94
|
+
"""Create from dictionary."""
|
|
95
|
+
return cls(
|
|
96
|
+
status=data.get("status", 200),
|
|
97
|
+
headers=data.get("headers", {}),
|
|
98
|
+
body=data.get("body", ""),
|
|
99
|
+
bodyFileName=data.get("bodyFileName"),
|
|
100
|
+
bodyEncoding=data.get("bodyEncoding"),
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
def get_body_bytes(self) -> bytes:
|
|
104
|
+
"""
|
|
105
|
+
Get response body as bytes, handling base64 encoding and file references.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Response body as bytes
|
|
109
|
+
|
|
110
|
+
Note:
|
|
111
|
+
If bodyFileName is used, _body_bytes must be set by the player
|
|
112
|
+
after loading the file.
|
|
113
|
+
"""
|
|
114
|
+
# If body was loaded from file
|
|
115
|
+
if self._body_bytes is not None:
|
|
116
|
+
return self._body_bytes
|
|
117
|
+
|
|
118
|
+
# If using base64 encoding
|
|
119
|
+
if self.bodyEncoding == "base64" and self.body:
|
|
120
|
+
return base64.b64decode(self.body)
|
|
121
|
+
|
|
122
|
+
# Regular text body
|
|
123
|
+
return (self.body or "").encode("utf-8")
|
|
124
|
+
|
|
125
|
+
def set_body_bytes(self, data: bytes) -> None:
|
|
126
|
+
"""Set body bytes (used when loading from file)."""
|
|
127
|
+
self._body_bytes = data
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@dataclass
|
|
131
|
+
class HttpMapping:
|
|
132
|
+
"""
|
|
133
|
+
Represents a request-response mapping in WireMock format.
|
|
134
|
+
|
|
135
|
+
Attributes:
|
|
136
|
+
request: The HTTP request
|
|
137
|
+
response: The HTTP response
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
request: HttpRequest
|
|
141
|
+
response: HttpResponse
|
|
142
|
+
|
|
143
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
144
|
+
"""Convert to dictionary for JSON serialization."""
|
|
145
|
+
return {
|
|
146
|
+
"request": self.request.to_dict(),
|
|
147
|
+
"response": self.response.to_dict(),
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@classmethod
|
|
151
|
+
def from_dict(cls, data: Dict[str, Any]) -> "HttpMapping":
|
|
152
|
+
"""Create from dictionary."""
|
|
153
|
+
return cls(
|
|
154
|
+
request=HttpRequest.from_dict(data.get("request", {})),
|
|
155
|
+
response=HttpResponse.from_dict(data.get("response", {})),
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@dataclass
|
|
160
|
+
class HttpMappings:
|
|
161
|
+
"""
|
|
162
|
+
Container for multiple HTTP mappings in WireMock format.
|
|
163
|
+
|
|
164
|
+
The JSON structure is:
|
|
165
|
+
{
|
|
166
|
+
"mappings": [
|
|
167
|
+
{ "request": {...}, "response": {...} },
|
|
168
|
+
...
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
mappings: List[HttpMapping] = field(default_factory=list)
|
|
174
|
+
|
|
175
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
176
|
+
"""Convert to dictionary for JSON serialization."""
|
|
177
|
+
return {"mappings": [m.to_dict() for m in self.mappings]}
|
|
178
|
+
|
|
179
|
+
def to_json(self, indent: int = 2) -> str:
|
|
180
|
+
"""Convert to JSON string."""
|
|
181
|
+
return json.dumps(self.to_dict(), indent=indent, ensure_ascii=False)
|
|
182
|
+
|
|
183
|
+
@classmethod
|
|
184
|
+
def from_dict(cls, data: Dict[str, Any]) -> "HttpMappings":
|
|
185
|
+
"""Create from dictionary."""
|
|
186
|
+
mappings_data = data.get("mappings", [])
|
|
187
|
+
return cls(mappings=[HttpMapping.from_dict(m) for m in mappings_data])
|
|
188
|
+
|
|
189
|
+
@classmethod
|
|
190
|
+
def from_json(cls, json_str: str) -> "HttpMappings":
|
|
191
|
+
"""Create from JSON string."""
|
|
192
|
+
data = json.loads(json_str)
|
|
193
|
+
return cls.from_dict(data)
|
|
194
|
+
|
|
195
|
+
def add(self, mapping: HttpMapping) -> None:
|
|
196
|
+
"""Add a mapping."""
|
|
197
|
+
self.mappings.append(mapping)
|
|
198
|
+
|
|
199
|
+
def __len__(self) -> int:
|
|
200
|
+
"""Return number of mappings."""
|
|
201
|
+
return len(self.mappings)
|
|
202
|
+
|
|
203
|
+
def __iter__(self):
|
|
204
|
+
"""Iterate over mappings."""
|
|
205
|
+
return iter(self.mappings)
|