provide-foundation 0.0.0.dev1__py3-none-any.whl → 0.0.0.dev2__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.
- provide/foundation/__init__.py +29 -3
- provide/foundation/archive/operations.py +4 -6
- provide/foundation/cli/__init__.py +2 -2
- provide/foundation/cli/commands/deps.py +13 -7
- provide/foundation/cli/commands/logs/__init__.py +1 -1
- provide/foundation/cli/commands/logs/query.py +1 -1
- provide/foundation/cli/commands/logs/send.py +1 -1
- provide/foundation/cli/commands/logs/tail.py +1 -1
- provide/foundation/cli/decorators.py +11 -10
- provide/foundation/cli/main.py +1 -1
- provide/foundation/cli/testing.py +2 -35
- provide/foundation/cli/utils.py +21 -17
- provide/foundation/config/__init__.py +35 -2
- provide/foundation/config/converters.py +479 -0
- provide/foundation/config/defaults.py +67 -0
- provide/foundation/config/env.py +4 -19
- provide/foundation/config/loader.py +9 -3
- provide/foundation/console/input.py +5 -5
- provide/foundation/console/output.py +35 -13
- provide/foundation/context/__init__.py +8 -4
- provide/foundation/context/core.py +85 -109
- provide/foundation/crypto/certificates/operations.py +1 -1
- provide/foundation/errors/__init__.py +2 -3
- provide/foundation/errors/decorators.py +0 -231
- provide/foundation/errors/types.py +0 -97
- provide/foundation/file/directory.py +13 -22
- provide/foundation/file/lock.py +3 -1
- provide/foundation/hub/components.py +72 -384
- provide/foundation/hub/config.py +151 -0
- provide/foundation/hub/discovery.py +62 -0
- provide/foundation/hub/handlers.py +81 -0
- provide/foundation/hub/lifecycle.py +194 -0
- provide/foundation/hub/manager.py +4 -4
- provide/foundation/hub/processors.py +44 -0
- provide/foundation/integrations/__init__.py +11 -0
- provide/foundation/{observability → integrations}/openobserve/__init__.py +10 -7
- provide/foundation/{observability → integrations}/openobserve/auth.py +1 -1
- provide/foundation/{observability → integrations}/openobserve/client.py +12 -12
- provide/foundation/{observability → integrations}/openobserve/commands.py +3 -3
- provide/foundation/integrations/openobserve/config.py +37 -0
- provide/foundation/{observability → integrations}/openobserve/formatters.py +1 -1
- provide/foundation/{observability → integrations}/openobserve/otlp.py +1 -1
- provide/foundation/{observability → integrations}/openobserve/search.py +2 -2
- provide/foundation/{observability → integrations}/openobserve/streaming.py +4 -4
- provide/foundation/logger/config/logging.py +68 -298
- provide/foundation/logger/config/telemetry.py +41 -121
- provide/foundation/logger/setup/coordinator.py +1 -1
- provide/foundation/observability/__init__.py +2 -2
- provide/foundation/process/__init__.py +9 -0
- provide/foundation/process/exit.py +47 -0
- provide/foundation/process/lifecycle.py +33 -33
- provide/foundation/resilience/__init__.py +35 -0
- provide/foundation/resilience/circuit.py +164 -0
- provide/foundation/resilience/decorators.py +220 -0
- provide/foundation/resilience/fallback.py +193 -0
- provide/foundation/resilience/retry.py +325 -0
- provide/foundation/streams/config.py +79 -0
- provide/foundation/streams/console.py +7 -8
- provide/foundation/streams/core.py +6 -3
- provide/foundation/streams/file.py +12 -2
- provide/foundation/testing/__init__.py +7 -2
- provide/foundation/testing/cli.py +30 -17
- provide/foundation/testing/common/__init__.py +0 -2
- provide/foundation/testing/common/fixtures.py +0 -27
- provide/foundation/testing/file/content_fixtures.py +316 -0
- provide/foundation/testing/file/directory_fixtures.py +107 -0
- provide/foundation/testing/file/fixtures.py +45 -516
- provide/foundation/testing/file/special_fixtures.py +153 -0
- provide/foundation/testing/logger.py +76 -0
- provide/foundation/testing/process/async_fixtures.py +405 -0
- provide/foundation/testing/process/fixtures.py +50 -571
- provide/foundation/testing/process/subprocess_fixtures.py +209 -0
- provide/foundation/testing/threading/basic_fixtures.py +101 -0
- provide/foundation/testing/threading/data_fixtures.py +99 -0
- provide/foundation/testing/threading/execution_fixtures.py +263 -0
- provide/foundation/testing/threading/fixtures.py +34 -500
- provide/foundation/testing/threading/sync_fixtures.py +97 -0
- provide/foundation/testing/time/fixtures.py +4 -4
- provide/foundation/tools/cache.py +8 -6
- provide/foundation/tools/downloader.py +23 -12
- provide/foundation/tracer/spans.py +2 -2
- provide/foundation/transport/config.py +26 -95
- provide/foundation/transport/middleware.py +30 -36
- provide/foundation/utils/deps.py +14 -12
- provide/foundation/utils/parsing.py +49 -4
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev2.dist-info}/METADATA +1 -1
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev2.dist-info}/RECORD +93 -68
- /provide/foundation/{observability → integrations}/openobserve/exceptions.py +0 -0
- /provide/foundation/{observability → integrations}/openobserve/models.py +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev2.dist-info}/WHEEL +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev2.dist-info}/entry_points.txt +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev2.dist-info}/licenses/LICENSE +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev2.dist-info}/top_level.txt +0 -0
@@ -1,577 +1,56 @@
|
|
1
1
|
"""
|
2
|
-
Process
|
2
|
+
Process Test Fixtures.
|
3
3
|
|
4
|
+
Core process testing fixtures with re-exports from specialized modules.
|
4
5
|
Utilities for testing async code, managing event loops, and handling
|
5
6
|
async subprocess mocking across the provide-io ecosystem.
|
6
7
|
"""
|
7
8
|
|
8
|
-
|
9
|
-
from
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
#
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
""
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
""
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
Returns:
|
57
|
-
Result of the coroutine or raises asyncio.TimeoutError
|
58
|
-
"""
|
59
|
-
return asyncio.wait_for(coro, timeout=seconds)
|
60
|
-
|
61
|
-
return _timeout_wrapper
|
62
|
-
|
63
|
-
|
64
|
-
@pytest.fixture
|
65
|
-
def mock_async_process() -> AsyncMock:
|
66
|
-
"""
|
67
|
-
Mock async subprocess for testing.
|
68
|
-
|
69
|
-
Returns:
|
70
|
-
AsyncMock configured as a subprocess with common attributes.
|
71
|
-
"""
|
72
|
-
mock_process = AsyncMock()
|
73
|
-
mock_process.communicate = AsyncMock(return_value=(b"output", b""))
|
74
|
-
mock_process.returncode = 0
|
75
|
-
mock_process.pid = 12345
|
76
|
-
mock_process.stdin = AsyncMock()
|
77
|
-
mock_process.stdout = AsyncMock()
|
78
|
-
mock_process.stderr = AsyncMock()
|
79
|
-
mock_process.wait = AsyncMock(return_value=0)
|
80
|
-
mock_process.kill = Mock()
|
81
|
-
mock_process.terminate = Mock()
|
82
|
-
|
83
|
-
return mock_process
|
84
|
-
|
85
|
-
|
86
|
-
@pytest.fixture
|
87
|
-
async def async_stream_reader() -> AsyncMock:
|
88
|
-
"""
|
89
|
-
Mock async stream reader for subprocess stdout/stderr.
|
90
|
-
|
91
|
-
Returns:
|
92
|
-
AsyncMock configured as a stream reader.
|
93
|
-
"""
|
94
|
-
reader = AsyncMock()
|
95
|
-
|
96
|
-
# Simulate reading lines
|
97
|
-
async def readline_side_effect():
|
98
|
-
for line in [b"line1\n", b"line2\n", b""]:
|
99
|
-
yield line
|
100
|
-
|
101
|
-
reader.readline = AsyncMock(side_effect=readline_side_effect().__anext__)
|
102
|
-
reader.read = AsyncMock(return_value=b"full content")
|
103
|
-
reader.at_eof = Mock(side_effect=[False, False, True])
|
104
|
-
|
105
|
-
return reader
|
106
|
-
|
107
|
-
|
108
|
-
@pytest.fixture
|
109
|
-
def event_loop_policy():
|
110
|
-
"""
|
111
|
-
Set event loop policy for tests to avoid conflicts.
|
112
|
-
|
113
|
-
Returns:
|
114
|
-
New event loop policy for isolated testing.
|
115
|
-
"""
|
116
|
-
policy = asyncio.get_event_loop_policy()
|
117
|
-
new_policy = asyncio.DefaultEventLoopPolicy()
|
118
|
-
asyncio.set_event_loop_policy(new_policy)
|
119
|
-
|
120
|
-
yield new_policy
|
121
|
-
|
122
|
-
# Restore original policy
|
123
|
-
asyncio.set_event_loop_policy(policy)
|
124
|
-
|
125
|
-
|
126
|
-
@pytest.fixture
|
127
|
-
async def async_context_manager():
|
128
|
-
"""
|
129
|
-
Factory for creating mock async context managers.
|
130
|
-
|
131
|
-
Returns:
|
132
|
-
Function that creates configured async context manager mocks.
|
133
|
-
"""
|
134
|
-
def _create_async_cm(enter_value=None, exit_value=None):
|
135
|
-
"""
|
136
|
-
Create a mock async context manager.
|
137
|
-
|
138
|
-
Args:
|
139
|
-
enter_value: Value to return from __aenter__
|
140
|
-
exit_value: Value to return from __aexit__
|
141
|
-
|
142
|
-
Returns:
|
143
|
-
AsyncMock configured as context manager
|
144
|
-
"""
|
145
|
-
mock_cm = AsyncMock()
|
146
|
-
mock_cm.__aenter__ = AsyncMock(return_value=enter_value)
|
147
|
-
mock_cm.__aexit__ = AsyncMock(return_value=exit_value)
|
148
|
-
return mock_cm
|
149
|
-
|
150
|
-
return _create_async_cm
|
151
|
-
|
152
|
-
|
153
|
-
@pytest.fixture
|
154
|
-
async def async_iterator():
|
155
|
-
"""
|
156
|
-
Factory for creating mock async iterators.
|
157
|
-
|
158
|
-
Returns:
|
159
|
-
Function that creates async iterator mocks with specified values.
|
160
|
-
"""
|
161
|
-
def _create_async_iter(values):
|
162
|
-
"""
|
163
|
-
Create a mock async iterator.
|
164
|
-
|
165
|
-
Args:
|
166
|
-
values: List of values to yield
|
167
|
-
|
168
|
-
Returns:
|
169
|
-
Async iterator that yields the specified values
|
170
|
-
"""
|
171
|
-
class AsyncIterator:
|
172
|
-
def __init__(self, vals):
|
173
|
-
self.vals = vals
|
174
|
-
self.index = 0
|
175
|
-
|
176
|
-
def __aiter__(self):
|
177
|
-
return self
|
178
|
-
|
179
|
-
async def __anext__(self):
|
180
|
-
if self.index >= len(self.vals):
|
181
|
-
raise StopAsyncIteration
|
182
|
-
value = self.vals[self.index]
|
183
|
-
self.index += 1
|
184
|
-
return value
|
185
|
-
|
186
|
-
return AsyncIterator(values)
|
187
|
-
|
188
|
-
return _create_async_iter
|
189
|
-
|
190
|
-
|
191
|
-
@pytest.fixture
|
192
|
-
def async_queue():
|
193
|
-
"""
|
194
|
-
Create an async queue for testing producer/consumer patterns.
|
195
|
-
|
196
|
-
Returns:
|
197
|
-
asyncio.Queue instance for testing.
|
198
|
-
"""
|
199
|
-
return asyncio.Queue()
|
200
|
-
|
201
|
-
|
202
|
-
@pytest.fixture
|
203
|
-
async def async_lock():
|
204
|
-
"""
|
205
|
-
Create an async lock for testing synchronization.
|
206
|
-
|
207
|
-
Returns:
|
208
|
-
asyncio.Lock instance for testing.
|
209
|
-
"""
|
210
|
-
return asyncio.Lock()
|
211
|
-
|
212
|
-
|
213
|
-
@pytest.fixture
|
214
|
-
def mock_async_sleep():
|
215
|
-
"""
|
216
|
-
Mock asyncio.sleep to speed up tests.
|
217
|
-
|
218
|
-
Returns:
|
219
|
-
Mock that replaces asyncio.sleep with instant return.
|
220
|
-
"""
|
221
|
-
original_sleep = asyncio.sleep
|
222
|
-
|
223
|
-
async def instant_sleep(seconds):
|
224
|
-
"""Sleep replacement that returns immediately."""
|
225
|
-
return None
|
226
|
-
|
227
|
-
asyncio.sleep = instant_sleep
|
228
|
-
|
229
|
-
yield instant_sleep
|
230
|
-
|
231
|
-
# Restore original
|
232
|
-
asyncio.sleep = original_sleep
|
233
|
-
|
234
|
-
|
235
|
-
@pytest.fixture
|
236
|
-
def async_subprocess():
|
237
|
-
"""
|
238
|
-
Create mock async subprocess for testing.
|
239
|
-
|
240
|
-
Returns:
|
241
|
-
Function that creates mock subprocess with configurable behavior.
|
242
|
-
"""
|
243
|
-
def _create_subprocess(
|
244
|
-
returncode: int = 0,
|
245
|
-
stdout: bytes = b"",
|
246
|
-
stderr: bytes = b"",
|
247
|
-
pid: int = 12345
|
248
|
-
) -> AsyncMock:
|
249
|
-
"""
|
250
|
-
Create a mock async subprocess.
|
251
|
-
|
252
|
-
Args:
|
253
|
-
returncode: Process return code
|
254
|
-
stdout: Process stdout output
|
255
|
-
stderr: Process stderr output
|
256
|
-
pid: Process ID
|
257
|
-
|
258
|
-
Returns:
|
259
|
-
AsyncMock configured as subprocess
|
260
|
-
"""
|
261
|
-
process = AsyncMock()
|
262
|
-
process.returncode = returncode
|
263
|
-
process.pid = pid
|
264
|
-
process.communicate = AsyncMock(return_value=(stdout, stderr))
|
265
|
-
process.wait = AsyncMock(return_value=returncode)
|
266
|
-
process.kill = Mock()
|
267
|
-
process.terminate = Mock()
|
268
|
-
process.send_signal = Mock()
|
269
|
-
|
270
|
-
# Add stdout/stderr as async stream readers
|
271
|
-
process.stdout = AsyncMock()
|
272
|
-
process.stdout.read = AsyncMock(return_value=stdout)
|
273
|
-
process.stdout.readline = AsyncMock(side_effect=[stdout, b""])
|
274
|
-
process.stdout.at_eof = Mock(side_effect=[False, True])
|
275
|
-
|
276
|
-
process.stderr = AsyncMock()
|
277
|
-
process.stderr.read = AsyncMock(return_value=stderr)
|
278
|
-
process.stderr.readline = AsyncMock(side_effect=[stderr, b""])
|
279
|
-
process.stderr.at_eof = Mock(side_effect=[False, True])
|
280
|
-
|
281
|
-
process.stdin = AsyncMock()
|
282
|
-
process.stdin.write = AsyncMock()
|
283
|
-
process.stdin.drain = AsyncMock()
|
284
|
-
process.stdin.close = Mock()
|
285
|
-
|
286
|
-
return process
|
287
|
-
|
288
|
-
return _create_subprocess
|
289
|
-
|
290
|
-
|
291
|
-
@pytest.fixture
|
292
|
-
def async_gather_helper():
|
293
|
-
"""
|
294
|
-
Helper for testing asyncio.gather operations.
|
295
|
-
|
296
|
-
Returns:
|
297
|
-
Function to gather async results with error handling.
|
298
|
-
"""
|
299
|
-
async def _gather(*coroutines, return_exceptions: bool = False):
|
300
|
-
"""
|
301
|
-
Gather results from multiple coroutines.
|
302
|
-
|
303
|
-
Args:
|
304
|
-
*coroutines: Coroutines to gather
|
305
|
-
return_exceptions: Whether to return exceptions as results
|
306
|
-
|
307
|
-
Returns:
|
308
|
-
List of results from coroutines
|
309
|
-
"""
|
310
|
-
return await asyncio.gather(*coroutines, return_exceptions=return_exceptions)
|
311
|
-
|
312
|
-
return _gather
|
313
|
-
|
314
|
-
|
315
|
-
@pytest.fixture
|
316
|
-
def async_task_group():
|
317
|
-
"""
|
318
|
-
Manage a group of async tasks with cleanup.
|
319
|
-
|
320
|
-
Returns:
|
321
|
-
AsyncTaskGroup instance for managing tasks.
|
322
|
-
"""
|
323
|
-
class AsyncTaskGroup:
|
324
|
-
def __init__(self):
|
325
|
-
self.tasks = []
|
326
|
-
|
327
|
-
def create_task(self, coro):
|
328
|
-
"""Create and track a task."""
|
329
|
-
task = asyncio.create_task(coro)
|
330
|
-
self.tasks.append(task)
|
331
|
-
return task
|
332
|
-
|
333
|
-
async def wait_all(self, timeout: float = None):
|
334
|
-
"""Wait for all tasks to complete."""
|
335
|
-
if not self.tasks:
|
336
|
-
return []
|
337
|
-
|
338
|
-
done, pending = await asyncio.wait(
|
339
|
-
self.tasks,
|
340
|
-
timeout=timeout,
|
341
|
-
return_when=asyncio.ALL_COMPLETED
|
342
|
-
)
|
343
|
-
|
344
|
-
if pending:
|
345
|
-
for task in pending:
|
346
|
-
task.cancel()
|
347
|
-
|
348
|
-
results = []
|
349
|
-
for task in done:
|
350
|
-
try:
|
351
|
-
results.append(task.result())
|
352
|
-
except Exception as e:
|
353
|
-
results.append(e)
|
354
|
-
|
355
|
-
return results
|
356
|
-
|
357
|
-
async def cancel_all(self):
|
358
|
-
"""Cancel all tasks."""
|
359
|
-
for task in self.tasks:
|
360
|
-
if not task.done():
|
361
|
-
task.cancel()
|
362
|
-
|
363
|
-
if self.tasks:
|
364
|
-
await asyncio.gather(*self.tasks, return_exceptions=True)
|
365
|
-
|
366
|
-
async def __aenter__(self):
|
367
|
-
return self
|
368
|
-
|
369
|
-
async def __aexit__(self, *args):
|
370
|
-
await self.cancel_all()
|
371
|
-
|
372
|
-
return AsyncTaskGroup()
|
373
|
-
|
374
|
-
|
375
|
-
@pytest.fixture
|
376
|
-
def async_condition_waiter():
|
377
|
-
"""
|
378
|
-
Helper for waiting on async conditions in tests.
|
379
|
-
|
380
|
-
Returns:
|
381
|
-
Function to wait for conditions with timeout.
|
382
|
-
"""
|
383
|
-
async def _wait_for(condition: Callable[[], bool], timeout: float = 5.0, interval: float = 0.1):
|
384
|
-
"""
|
385
|
-
Wait for a condition to become true.
|
386
|
-
|
387
|
-
Args:
|
388
|
-
condition: Function that returns True when condition is met
|
389
|
-
timeout: Maximum wait time
|
390
|
-
interval: Check interval
|
391
|
-
|
392
|
-
Returns:
|
393
|
-
True if condition met, False if timeout
|
394
|
-
"""
|
395
|
-
start = asyncio.get_event_loop().time()
|
396
|
-
|
397
|
-
while asyncio.get_event_loop().time() - start < timeout:
|
398
|
-
if condition():
|
399
|
-
return True
|
400
|
-
await asyncio.sleep(interval)
|
401
|
-
|
402
|
-
return False
|
403
|
-
|
404
|
-
return _wait_for
|
405
|
-
|
406
|
-
|
407
|
-
@pytest.fixture
|
408
|
-
def async_mock_server():
|
409
|
-
"""
|
410
|
-
Create a mock async server for testing.
|
411
|
-
|
412
|
-
Returns:
|
413
|
-
Mock server with async methods.
|
414
|
-
"""
|
415
|
-
class AsyncMockServer:
|
416
|
-
def __init__(self):
|
417
|
-
self.started = False
|
418
|
-
self.connections = []
|
419
|
-
self.requests = []
|
420
|
-
|
421
|
-
async def start(self, host: str = "localhost", port: int = 8080):
|
422
|
-
"""Start the mock server."""
|
423
|
-
self.started = True
|
424
|
-
self.host = host
|
425
|
-
self.port = port
|
426
|
-
|
427
|
-
async def stop(self):
|
428
|
-
"""Stop the mock server."""
|
429
|
-
self.started = False
|
430
|
-
for conn in self.connections:
|
431
|
-
await conn.close()
|
432
|
-
|
433
|
-
async def handle_connection(self, reader, writer):
|
434
|
-
"""Mock connection handler."""
|
435
|
-
conn = {"reader": reader, "writer": writer}
|
436
|
-
self.connections.append(conn)
|
437
|
-
|
438
|
-
# Mock reading request
|
439
|
-
data = await reader.read(1024)
|
440
|
-
self.requests.append(data)
|
441
|
-
|
442
|
-
# Mock sending response
|
443
|
-
writer.write(b"HTTP/1.1 200 OK\r\n\r\nOK")
|
444
|
-
await writer.drain()
|
445
|
-
|
446
|
-
writer.close()
|
447
|
-
await writer.wait_closed()
|
448
|
-
|
449
|
-
def get_url(self) -> str:
|
450
|
-
"""Get server URL."""
|
451
|
-
return f"http://{self.host}:{self.port}"
|
452
|
-
|
453
|
-
return AsyncMockServer()
|
454
|
-
|
455
|
-
|
456
|
-
@pytest.fixture
|
457
|
-
def async_pipeline():
|
458
|
-
"""
|
459
|
-
Create an async pipeline for testing data flow.
|
460
|
-
|
461
|
-
Returns:
|
462
|
-
AsyncPipeline instance for chaining async operations.
|
463
|
-
"""
|
464
|
-
class AsyncPipeline:
|
465
|
-
def __init__(self):
|
466
|
-
self.stages = []
|
467
|
-
self.results = []
|
468
|
-
|
469
|
-
def add_stage(self, func: Callable):
|
470
|
-
"""Add a processing stage."""
|
471
|
-
self.stages.append(func)
|
472
|
-
return self
|
473
|
-
|
474
|
-
async def process(self, data):
|
475
|
-
"""Process data through all stages."""
|
476
|
-
result = data
|
477
|
-
for stage in self.stages:
|
478
|
-
if asyncio.iscoroutinefunction(stage):
|
479
|
-
result = await stage(result)
|
480
|
-
else:
|
481
|
-
result = stage(result)
|
482
|
-
self.results.append(result)
|
483
|
-
return result
|
484
|
-
|
485
|
-
async def process_batch(self, items: list):
|
486
|
-
"""Process multiple items."""
|
487
|
-
tasks = [self.process(item) for item in items]
|
488
|
-
return await asyncio.gather(*tasks)
|
489
|
-
|
490
|
-
def clear(self):
|
491
|
-
"""Clear stages and results."""
|
492
|
-
self.stages.clear()
|
493
|
-
self.results.clear()
|
494
|
-
|
495
|
-
return AsyncPipeline()
|
496
|
-
|
497
|
-
|
498
|
-
@pytest.fixture
|
499
|
-
def async_rate_limiter():
|
500
|
-
"""
|
501
|
-
Create an async rate limiter for testing.
|
502
|
-
|
503
|
-
Returns:
|
504
|
-
AsyncRateLimiter instance for controlling request rates.
|
505
|
-
"""
|
506
|
-
class AsyncRateLimiter:
|
507
|
-
def __init__(self, rate: int = 10, per: float = 1.0):
|
508
|
-
self.rate = rate
|
509
|
-
self.per = per
|
510
|
-
self.allowance = rate
|
511
|
-
self.last_check = asyncio.get_event_loop().time()
|
512
|
-
|
513
|
-
async def acquire(self):
|
514
|
-
"""Acquire permission to proceed."""
|
515
|
-
current = asyncio.get_event_loop().time()
|
516
|
-
time_passed = current - self.last_check
|
517
|
-
self.last_check = current
|
518
|
-
|
519
|
-
self.allowance += time_passed * (self.rate / self.per)
|
520
|
-
if self.allowance > self.rate:
|
521
|
-
self.allowance = self.rate
|
522
|
-
|
523
|
-
if self.allowance < 1.0:
|
524
|
-
sleep_time = (1.0 - self.allowance) * (self.per / self.rate)
|
525
|
-
await asyncio.sleep(sleep_time)
|
526
|
-
self.allowance = 0.0
|
527
|
-
else:
|
528
|
-
self.allowance -= 1.0
|
529
|
-
|
530
|
-
async def __aenter__(self):
|
531
|
-
await self.acquire()
|
532
|
-
return self
|
533
|
-
|
534
|
-
async def __aexit__(self, *args):
|
535
|
-
pass
|
536
|
-
|
537
|
-
return AsyncRateLimiter()
|
538
|
-
|
539
|
-
|
540
|
-
@pytest.fixture
|
541
|
-
def async_test_client():
|
542
|
-
"""
|
543
|
-
Create an async HTTP test client.
|
544
|
-
|
545
|
-
Returns:
|
546
|
-
Mock async HTTP client for testing.
|
547
|
-
"""
|
548
|
-
class AsyncTestClient:
|
549
|
-
def __init__(self):
|
550
|
-
self.responses = {}
|
551
|
-
self.requests = []
|
552
|
-
|
553
|
-
def set_response(self, url: str, response: dict):
|
554
|
-
"""Set a mock response for a URL."""
|
555
|
-
self.responses[url] = response
|
556
|
-
|
557
|
-
async def get(self, url: str, **kwargs) -> dict:
|
558
|
-
"""Mock GET request."""
|
559
|
-
self.requests.append({"method": "GET", "url": url, "kwargs": kwargs})
|
560
|
-
return self.responses.get(url, {"status": 404, "body": "Not Found"})
|
561
|
-
|
562
|
-
async def post(self, url: str, data=None, **kwargs) -> dict:
|
563
|
-
"""Mock POST request."""
|
564
|
-
self.requests.append({"method": "POST", "url": url, "data": data, "kwargs": kwargs})
|
565
|
-
return self.responses.get(url, {"status": 200, "body": "OK"})
|
566
|
-
|
567
|
-
async def close(self):
|
568
|
-
"""Close the client."""
|
569
|
-
pass
|
570
|
-
|
571
|
-
async def __aenter__(self):
|
572
|
-
return self
|
573
|
-
|
574
|
-
async def __aexit__(self, *args):
|
575
|
-
await self.close()
|
576
|
-
|
577
|
-
return AsyncTestClient()
|
9
|
+
# Re-export all fixtures from specialized modules
|
10
|
+
from provide.foundation.testing.process.async_fixtures import (
|
11
|
+
clean_event_loop,
|
12
|
+
async_timeout,
|
13
|
+
event_loop_policy,
|
14
|
+
async_context_manager,
|
15
|
+
async_iterator,
|
16
|
+
async_queue,
|
17
|
+
async_lock,
|
18
|
+
mock_async_sleep,
|
19
|
+
async_gather_helper,
|
20
|
+
async_task_group,
|
21
|
+
async_condition_waiter,
|
22
|
+
async_pipeline,
|
23
|
+
async_rate_limiter,
|
24
|
+
)
|
25
|
+
|
26
|
+
from provide.foundation.testing.process.subprocess_fixtures import (
|
27
|
+
mock_async_process,
|
28
|
+
async_stream_reader,
|
29
|
+
async_subprocess,
|
30
|
+
async_mock_server,
|
31
|
+
async_test_client,
|
32
|
+
)
|
33
|
+
|
34
|
+
|
35
|
+
__all__ = [
|
36
|
+
# Async fixtures
|
37
|
+
"clean_event_loop",
|
38
|
+
"async_timeout",
|
39
|
+
"event_loop_policy",
|
40
|
+
"async_context_manager",
|
41
|
+
"async_iterator",
|
42
|
+
"async_queue",
|
43
|
+
"async_lock",
|
44
|
+
"mock_async_sleep",
|
45
|
+
"async_gather_helper",
|
46
|
+
"async_task_group",
|
47
|
+
"async_condition_waiter",
|
48
|
+
"async_pipeline",
|
49
|
+
"async_rate_limiter",
|
50
|
+
# Subprocess fixtures
|
51
|
+
"mock_async_process",
|
52
|
+
"async_stream_reader",
|
53
|
+
"async_subprocess",
|
54
|
+
"async_mock_server",
|
55
|
+
"async_test_client",
|
56
|
+
]
|