provide-foundation 0.0.0.dev0__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 +41 -23
- provide/foundation/archive/__init__.py +23 -0
- provide/foundation/archive/base.py +70 -0
- provide/foundation/archive/bzip2.py +157 -0
- provide/foundation/archive/gzip.py +159 -0
- provide/foundation/archive/operations.py +334 -0
- provide/foundation/archive/tar.py +164 -0
- provide/foundation/archive/zip.py +203 -0
- 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/base.py +2 -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/config/sync.py +19 -4
- 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/core.py +1 -2
- provide/foundation/crypto/__init__.py +2 -0
- provide/foundation/crypto/certificates/__init__.py +34 -0
- provide/foundation/crypto/certificates/base.py +173 -0
- provide/foundation/crypto/certificates/certificate.py +290 -0
- provide/foundation/crypto/certificates/factory.py +213 -0
- provide/foundation/crypto/certificates/generator.py +138 -0
- provide/foundation/crypto/certificates/loader.py +130 -0
- provide/foundation/crypto/certificates/operations.py +198 -0
- provide/foundation/crypto/certificates/trust.py +107 -0
- provide/foundation/errors/__init__.py +2 -3
- provide/foundation/errors/decorators.py +0 -231
- provide/foundation/errors/types.py +0 -97
- provide/foundation/eventsets/__init__.py +0 -0
- provide/foundation/eventsets/display.py +84 -0
- provide/foundation/eventsets/registry.py +160 -0
- provide/foundation/eventsets/resolver.py +192 -0
- provide/foundation/eventsets/sets/das.py +128 -0
- provide/foundation/eventsets/sets/database.py +125 -0
- provide/foundation/eventsets/sets/http.py +153 -0
- provide/foundation/eventsets/sets/llm.py +139 -0
- provide/foundation/eventsets/sets/task_queue.py +107 -0
- provide/foundation/eventsets/types.py +70 -0
- provide/foundation/file/directory.py +13 -22
- provide/foundation/file/lock.py +3 -1
- provide/foundation/hub/components.py +77 -515
- 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/__init__.py +3 -10
- provide/foundation/logger/config/logging.py +68 -298
- provide/foundation/logger/config/telemetry.py +41 -121
- provide/foundation/logger/core.py +0 -2
- provide/foundation/logger/custom_processors.py +1 -0
- provide/foundation/logger/factories.py +11 -2
- provide/foundation/logger/processors/main.py +20 -84
- provide/foundation/logger/setup/__init__.py +5 -1
- provide/foundation/logger/setup/coordinator.py +76 -24
- provide/foundation/logger/setup/processors.py +2 -9
- provide/foundation/logger/trace.py +27 -0
- provide/foundation/metrics/otel.py +10 -10
- 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 +115 -59
- 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 +84 -2
- provide/foundation/testing/archive/__init__.py +24 -0
- provide/foundation/testing/archive/fixtures.py +217 -0
- provide/foundation/testing/cli.py +30 -17
- provide/foundation/testing/common/__init__.py +32 -0
- provide/foundation/testing/common/fixtures.py +236 -0
- provide/foundation/testing/file/__init__.py +40 -0
- provide/foundation/testing/file/content_fixtures.py +316 -0
- provide/foundation/testing/file/directory_fixtures.py +107 -0
- provide/foundation/testing/file/fixtures.py +52 -0
- provide/foundation/testing/file/special_fixtures.py +153 -0
- provide/foundation/testing/logger.py +117 -11
- provide/foundation/testing/mocking/__init__.py +46 -0
- provide/foundation/testing/mocking/fixtures.py +331 -0
- provide/foundation/testing/process/__init__.py +48 -0
- provide/foundation/testing/process/async_fixtures.py +405 -0
- provide/foundation/testing/process/fixtures.py +56 -0
- provide/foundation/testing/process/subprocess_fixtures.py +209 -0
- provide/foundation/testing/threading/__init__.py +38 -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 +54 -0
- provide/foundation/testing/threading/sync_fixtures.py +97 -0
- provide/foundation/testing/time/__init__.py +32 -0
- provide/foundation/testing/time/fixtures.py +409 -0
- provide/foundation/testing/transport/__init__.py +30 -0
- provide/foundation/testing/transport/fixtures.py +280 -0
- provide/foundation/tools/__init__.py +58 -0
- provide/foundation/tools/base.py +348 -0
- provide/foundation/tools/cache.py +268 -0
- provide/foundation/tools/downloader.py +224 -0
- provide/foundation/tools/installer.py +254 -0
- provide/foundation/tools/registry.py +223 -0
- provide/foundation/tools/resolver.py +321 -0
- provide/foundation/tools/verifier.py +186 -0
- provide/foundation/tracer/otel.py +7 -11
- provide/foundation/tracer/spans.py +2 -2
- provide/foundation/transport/__init__.py +155 -0
- provide/foundation/transport/base.py +171 -0
- provide/foundation/transport/client.py +266 -0
- provide/foundation/transport/config.py +140 -0
- provide/foundation/transport/errors.py +79 -0
- provide/foundation/transport/http.py +232 -0
- provide/foundation/transport/middleware.py +360 -0
- provide/foundation/transport/registry.py +167 -0
- provide/foundation/transport/types.py +45 -0
- provide/foundation/utils/deps.py +14 -12
- provide/foundation/utils/parsing.py +49 -4
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/METADATA +5 -28
- provide_foundation-0.0.0.dev2.dist-info/RECORD +225 -0
- provide/foundation/cli/commands/logs/generate_old.py +0 -569
- provide/foundation/crypto/certificates.py +0 -896
- provide/foundation/logger/emoji/__init__.py +0 -44
- provide/foundation/logger/emoji/matrix.py +0 -209
- provide/foundation/logger/emoji/sets.py +0 -458
- provide/foundation/logger/emoji/types.py +0 -56
- provide/foundation/logger/setup/emoji_resolver.py +0 -64
- provide_foundation-0.0.0.dev0.dist-info/RECORD +0 -149
- /provide/foundation/{observability → integrations}/openobserve/exceptions.py +0 -0
- /provide/foundation/{observability → integrations}/openobserve/models.py +0 -0
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/WHEEL +0 -0
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/entry_points.txt +0 -0
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/licenses/LICENSE +0 -0
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,217 @@
|
|
1
|
+
"""
|
2
|
+
Archive Testing Fixtures.
|
3
|
+
|
4
|
+
Fixtures specific to testing archive operations like tar, zip, gzip, bzip2.
|
5
|
+
Builds on top of file fixtures for archive-specific test scenarios.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from pathlib import Path
|
9
|
+
from collections.abc import Generator
|
10
|
+
|
11
|
+
import pytest
|
12
|
+
|
13
|
+
from provide.foundation.testing.file.fixtures import temp_directory
|
14
|
+
|
15
|
+
|
16
|
+
@pytest.fixture
|
17
|
+
def archive_test_content() -> Generator[tuple[Path, dict[str, str]], None, None]:
|
18
|
+
"""
|
19
|
+
Create a standard set of files for archive testing.
|
20
|
+
|
21
|
+
Creates multiple files with different types of content to ensure
|
22
|
+
proper compression and extraction testing.
|
23
|
+
|
24
|
+
Yields:
|
25
|
+
Tuple of (source_dir, content_map) where content_map maps
|
26
|
+
relative paths to their expected content.
|
27
|
+
"""
|
28
|
+
with temp_directory() as temp_dir:
|
29
|
+
source = temp_dir / "archive_source"
|
30
|
+
source.mkdir()
|
31
|
+
|
32
|
+
content_map = {
|
33
|
+
"text_file.txt": "This is a text file for archive testing.\n" * 10,
|
34
|
+
"data.json": '{"test": "data", "array": [1, 2, 3]}',
|
35
|
+
"script.py": "#!/usr/bin/env python\nprint('Hello from archive')\n",
|
36
|
+
"nested/dir/file.md": "# Nested File\nContent in nested directory",
|
37
|
+
"binary.dat": "Binary\x00\x01\x02\x03\xFF\xFE data",
|
38
|
+
"empty.txt": "",
|
39
|
+
}
|
40
|
+
|
41
|
+
# Create all files
|
42
|
+
for rel_path, content in content_map.items():
|
43
|
+
file_path = source / rel_path
|
44
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
45
|
+
|
46
|
+
if isinstance(content, str):
|
47
|
+
file_path.write_text(content)
|
48
|
+
else:
|
49
|
+
file_path.write_bytes(content.encode() if isinstance(content, str) else content)
|
50
|
+
|
51
|
+
yield source, content_map
|
52
|
+
|
53
|
+
|
54
|
+
@pytest.fixture
|
55
|
+
def large_file_for_compression() -> Generator[Path, None, None]:
|
56
|
+
"""
|
57
|
+
Create a large file suitable for compression testing.
|
58
|
+
|
59
|
+
The file contains repetitive content that compresses well.
|
60
|
+
|
61
|
+
Yields:
|
62
|
+
Path to a large file with compressible content.
|
63
|
+
"""
|
64
|
+
with temp_directory() as temp_dir:
|
65
|
+
large_file = temp_dir / "large_compressible.txt"
|
66
|
+
|
67
|
+
# Create 10MB of highly compressible content
|
68
|
+
content = "This is a line of text that will be repeated many times.\n" * 100
|
69
|
+
large_content = content * 1000 # ~6MB of repetitive text
|
70
|
+
|
71
|
+
large_file.write_text(large_content)
|
72
|
+
yield large_file
|
73
|
+
|
74
|
+
|
75
|
+
@pytest.fixture
|
76
|
+
def multi_format_archives() -> Generator[dict[str, Path], None, None]:
|
77
|
+
"""
|
78
|
+
Create sample archives in different formats for format detection testing.
|
79
|
+
|
80
|
+
Yields:
|
81
|
+
Dict mapping format names to paths of sample archives.
|
82
|
+
"""
|
83
|
+
with temp_directory() as temp_dir:
|
84
|
+
archives = {}
|
85
|
+
|
86
|
+
# Create minimal valid archives in different formats
|
87
|
+
# Note: These are minimal headers, not full valid archives
|
88
|
+
|
89
|
+
# GZIP file (magic: 1f 8b)
|
90
|
+
gzip_file = temp_dir / "sample.gz"
|
91
|
+
gzip_file.write_bytes(b'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03' + b'compressed data')
|
92
|
+
archives["gzip"] = gzip_file
|
93
|
+
|
94
|
+
# BZIP2 file (magic: BZh)
|
95
|
+
bzip2_file = temp_dir / "sample.bz2"
|
96
|
+
bzip2_file.write_bytes(b'BZh91AY&SY' + b'compressed data')
|
97
|
+
archives["bzip2"] = bzip2_file
|
98
|
+
|
99
|
+
# ZIP file (magic: PK\x03\x04)
|
100
|
+
zip_file = temp_dir / "sample.zip"
|
101
|
+
zip_file.write_bytes(b'PK\x03\x04' + b'\x00' * 16 + b'zipfile')
|
102
|
+
archives["zip"] = zip_file
|
103
|
+
|
104
|
+
# TAR file (has specific header structure)
|
105
|
+
tar_file = temp_dir / "sample.tar"
|
106
|
+
# Minimal tar header (512 bytes)
|
107
|
+
tar_header = b'testfile.txt' + b'\x00' * 88 # name
|
108
|
+
tar_header += b'0000644\x00' # mode
|
109
|
+
tar_header += b'0000000\x00' # uid
|
110
|
+
tar_header += b'0000000\x00' # gid
|
111
|
+
tar_header += b'00000000000\x00' # size
|
112
|
+
tar_header += b'00000000000\x00' # mtime
|
113
|
+
tar_header += b' ' # checksum placeholder
|
114
|
+
tar_header += b'0' # typeflag
|
115
|
+
tar_header += b'\x00' * 355 # padding to 512 bytes
|
116
|
+
tar_file.write_bytes(tar_header[:512])
|
117
|
+
archives["tar"] = tar_file
|
118
|
+
|
119
|
+
yield archives
|
120
|
+
|
121
|
+
|
122
|
+
@pytest.fixture
|
123
|
+
def archive_with_permissions() -> Generator[Path, None, None]:
|
124
|
+
"""
|
125
|
+
Create files with specific permissions for archive permission testing.
|
126
|
+
|
127
|
+
Yields:
|
128
|
+
Path to directory containing files with various permission modes.
|
129
|
+
"""
|
130
|
+
with temp_directory() as temp_dir:
|
131
|
+
source = temp_dir / "permissions_test"
|
132
|
+
source.mkdir()
|
133
|
+
|
134
|
+
# Regular file
|
135
|
+
regular = source / "regular.txt"
|
136
|
+
regular.write_text("Regular file")
|
137
|
+
regular.chmod(0o644)
|
138
|
+
|
139
|
+
# Executable file
|
140
|
+
executable = source / "script.sh"
|
141
|
+
executable.write_text("#!/bin/bash\necho 'Hello'")
|
142
|
+
executable.chmod(0o755)
|
143
|
+
|
144
|
+
# Read-only file
|
145
|
+
readonly = source / "readonly.txt"
|
146
|
+
readonly.write_text("Read only content")
|
147
|
+
readonly.chmod(0o444)
|
148
|
+
|
149
|
+
# Directory with specific permissions
|
150
|
+
special_dir = source / "special"
|
151
|
+
special_dir.mkdir()
|
152
|
+
special_dir.chmod(0o700)
|
153
|
+
|
154
|
+
yield source
|
155
|
+
|
156
|
+
|
157
|
+
@pytest.fixture
|
158
|
+
def corrupted_archives() -> Generator[dict[str, Path], None, None]:
|
159
|
+
"""
|
160
|
+
Create corrupted archive files for error handling testing.
|
161
|
+
|
162
|
+
Yields:
|
163
|
+
Dict mapping format names to paths of corrupted archives.
|
164
|
+
"""
|
165
|
+
with temp_directory() as temp_dir:
|
166
|
+
corrupted = {}
|
167
|
+
|
168
|
+
# Corrupted GZIP (invalid header)
|
169
|
+
bad_gzip = temp_dir / "corrupted.gz"
|
170
|
+
bad_gzip.write_bytes(b'\x1f\x8c' + b'not really gzip data')
|
171
|
+
corrupted["gzip"] = bad_gzip
|
172
|
+
|
173
|
+
# Corrupted ZIP (incomplete header)
|
174
|
+
bad_zip = temp_dir / "corrupted.zip"
|
175
|
+
bad_zip.write_bytes(b'PK\x03') # Incomplete magic
|
176
|
+
corrupted["zip"] = bad_zip
|
177
|
+
|
178
|
+
# Corrupted BZIP2 (wrong magic)
|
179
|
+
bad_bzip2 = temp_dir / "corrupted.bz2"
|
180
|
+
bad_bzip2.write_bytes(b'BZX' + b'not bzip2')
|
181
|
+
corrupted["bzip2"] = bad_bzip2
|
182
|
+
|
183
|
+
# Empty file claiming to be archive
|
184
|
+
empty_archive = temp_dir / "empty.tar.gz"
|
185
|
+
empty_archive.write_bytes(b'')
|
186
|
+
corrupted["empty"] = empty_archive
|
187
|
+
|
188
|
+
yield corrupted
|
189
|
+
|
190
|
+
|
191
|
+
@pytest.fixture
|
192
|
+
def archive_stress_test_files() -> Generator[Path, None, None]:
|
193
|
+
"""
|
194
|
+
Create a large number of files for stress testing archive operations.
|
195
|
+
|
196
|
+
Yields:
|
197
|
+
Path to directory with many files for stress testing.
|
198
|
+
"""
|
199
|
+
with temp_directory() as temp_dir:
|
200
|
+
stress_dir = temp_dir / "stress_test"
|
201
|
+
stress_dir.mkdir()
|
202
|
+
|
203
|
+
# Create 100 files in various subdirectories
|
204
|
+
for i in range(10):
|
205
|
+
subdir = stress_dir / f"subdir_{i}"
|
206
|
+
subdir.mkdir()
|
207
|
+
|
208
|
+
for j in range(10):
|
209
|
+
file_path = subdir / f"file_{j}.txt"
|
210
|
+
file_path.write_text(f"Content of file {i}_{j}\n" * 10)
|
211
|
+
|
212
|
+
# Add some binary files
|
213
|
+
for i in range(5):
|
214
|
+
bin_file = stress_dir / f"binary_{i}.dat"
|
215
|
+
bin_file.write_bytes(bytes(range(256)) * 10)
|
216
|
+
|
217
|
+
yield stress_dir
|
@@ -15,14 +15,15 @@ from unittest.mock import MagicMock
|
|
15
15
|
|
16
16
|
import click
|
17
17
|
from click.testing import CliRunner
|
18
|
+
import pytest
|
18
19
|
|
19
|
-
from provide.foundation.context import
|
20
|
+
from provide.foundation.context import CLIContext
|
20
21
|
from provide.foundation.logger import get_logger
|
21
22
|
|
22
23
|
log = get_logger(__name__)
|
23
24
|
|
24
25
|
|
25
|
-
class MockContext(
|
26
|
+
class MockContext(CLIContext):
|
26
27
|
"""Mock context for testing that tracks method calls."""
|
27
28
|
|
28
29
|
def __init__(self, **kwargs) -> None:
|
@@ -157,7 +158,7 @@ def create_test_cli(
|
|
157
158
|
@click.pass_context
|
158
159
|
def cli(ctx, **kwargs) -> None:
|
159
160
|
"""Test CLI for testing."""
|
160
|
-
ctx.obj =
|
161
|
+
ctx.obj = CLIContext(**{k: v for k, v in kwargs.items() if v is not None})
|
161
162
|
|
162
163
|
if commands:
|
163
164
|
for cmd in commands:
|
@@ -166,20 +167,6 @@ def create_test_cli(
|
|
166
167
|
return cli
|
167
168
|
|
168
169
|
|
169
|
-
def mock_logger():
|
170
|
-
"""
|
171
|
-
Create a mock logger for testing.
|
172
|
-
|
173
|
-
Returns:
|
174
|
-
MagicMock with common logger methods
|
175
|
-
"""
|
176
|
-
mock = MagicMock()
|
177
|
-
mock.debug = MagicMock()
|
178
|
-
mock.info = MagicMock()
|
179
|
-
mock.warning = MagicMock()
|
180
|
-
mock.error = MagicMock()
|
181
|
-
mock.critical = MagicMock()
|
182
|
-
return mock
|
183
170
|
|
184
171
|
|
185
172
|
class CliTestCase:
|
@@ -225,3 +212,29 @@ class CliTestCase:
|
|
225
212
|
assert output[key] == value, (
|
226
213
|
f"Value mismatch for '{key}': {output[key]} != {value}"
|
227
214
|
)
|
215
|
+
|
216
|
+
|
217
|
+
@pytest.fixture
|
218
|
+
def click_testing_mode():
|
219
|
+
"""
|
220
|
+
Pytest fixture to enable Click testing mode.
|
221
|
+
|
222
|
+
Sets CLICK_TESTING=1 environment variable for the duration of the test,
|
223
|
+
then restores the original value. This fixture makes it easy to enable
|
224
|
+
Click testing mode without manual environment variable management.
|
225
|
+
|
226
|
+
Usage:
|
227
|
+
def test_my_cli(click_testing_mode):
|
228
|
+
# Test CLI code here - CLICK_TESTING is automatically set
|
229
|
+
pass
|
230
|
+
"""
|
231
|
+
original_value = os.environ.get("CLICK_TESTING")
|
232
|
+
os.environ["CLICK_TESTING"] = "1"
|
233
|
+
|
234
|
+
try:
|
235
|
+
yield
|
236
|
+
finally:
|
237
|
+
if original_value is None:
|
238
|
+
os.environ.pop("CLICK_TESTING", None)
|
239
|
+
else:
|
240
|
+
os.environ["CLICK_TESTING"] = original_value
|
@@ -0,0 +1,32 @@
|
|
1
|
+
"""
|
2
|
+
Common testing fixtures for the provide-io ecosystem.
|
3
|
+
|
4
|
+
Standard mock objects and fixtures that are used across multiple modules
|
5
|
+
in any project that depends on provide.foundation.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from provide.foundation.testing.common.fixtures import (
|
9
|
+
mock_http_config,
|
10
|
+
mock_telemetry_config,
|
11
|
+
mock_config_source,
|
12
|
+
mock_event_emitter,
|
13
|
+
mock_transport,
|
14
|
+
mock_metrics_collector,
|
15
|
+
mock_cache,
|
16
|
+
mock_database,
|
17
|
+
mock_file_system,
|
18
|
+
mock_subprocess,
|
19
|
+
)
|
20
|
+
|
21
|
+
__all__ = [
|
22
|
+
"mock_http_config",
|
23
|
+
"mock_telemetry_config",
|
24
|
+
"mock_config_source",
|
25
|
+
"mock_event_emitter",
|
26
|
+
"mock_transport",
|
27
|
+
"mock_metrics_collector",
|
28
|
+
"mock_cache",
|
29
|
+
"mock_database",
|
30
|
+
"mock_file_system",
|
31
|
+
"mock_subprocess",
|
32
|
+
]
|
@@ -0,0 +1,236 @@
|
|
1
|
+
"""
|
2
|
+
Common Mock Objects and Fixtures.
|
3
|
+
|
4
|
+
Reusable mock objects for configuration, logging, and other common
|
5
|
+
testing scenarios across the provide-io ecosystem.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from unittest.mock import Mock, MagicMock, PropertyMock
|
9
|
+
from typing import Any
|
10
|
+
|
11
|
+
import pytest
|
12
|
+
|
13
|
+
from provide.foundation import TelemetryConfig
|
14
|
+
from provide.foundation.logger.config.logging import LoggingConfig
|
15
|
+
|
16
|
+
|
17
|
+
@pytest.fixture
|
18
|
+
def mock_http_config():
|
19
|
+
"""
|
20
|
+
Standard HTTP configuration for testing.
|
21
|
+
|
22
|
+
Returns:
|
23
|
+
HTTPConfig with common test settings.
|
24
|
+
"""
|
25
|
+
from provide.foundation.transport.config import HTTPConfig
|
26
|
+
|
27
|
+
return HTTPConfig(
|
28
|
+
timeout=30.0,
|
29
|
+
max_retries=3,
|
30
|
+
retry_backoff_factor=0.5,
|
31
|
+
verify_ssl=True,
|
32
|
+
pool_connections=10,
|
33
|
+
pool_maxsize=100,
|
34
|
+
follow_redirects=True,
|
35
|
+
http2=True,
|
36
|
+
max_redirects=5,
|
37
|
+
)
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
@pytest.fixture
|
43
|
+
def mock_telemetry_config():
|
44
|
+
"""
|
45
|
+
Standard telemetry configuration for testing.
|
46
|
+
|
47
|
+
Returns:
|
48
|
+
TelemetryConfig with debug logging enabled.
|
49
|
+
"""
|
50
|
+
return TelemetryConfig(
|
51
|
+
logging=LoggingConfig(default_level="DEBUG"),
|
52
|
+
globally_disabled=False,
|
53
|
+
service_name="test_service",
|
54
|
+
service_version="1.0.0",
|
55
|
+
)
|
56
|
+
|
57
|
+
|
58
|
+
@pytest.fixture
|
59
|
+
def mock_config_source():
|
60
|
+
"""
|
61
|
+
Mock configuration source for testing config loading.
|
62
|
+
|
63
|
+
Returns:
|
64
|
+
Mock that simulates a configuration source.
|
65
|
+
"""
|
66
|
+
source = Mock()
|
67
|
+
source.load = Mock(return_value={"key": "value", "nested": {"key": "value"}})
|
68
|
+
source.exists = Mock(return_value=True)
|
69
|
+
source.reload = Mock()
|
70
|
+
source.watch = Mock()
|
71
|
+
source.priority = 100
|
72
|
+
source.name = "mock_source"
|
73
|
+
|
74
|
+
return source
|
75
|
+
|
76
|
+
|
77
|
+
@pytest.fixture
|
78
|
+
def mock_event_emitter():
|
79
|
+
"""
|
80
|
+
Mock event emitter for testing event-driven components.
|
81
|
+
|
82
|
+
Returns:
|
83
|
+
Mock with emit, on, off methods.
|
84
|
+
"""
|
85
|
+
emitter = Mock()
|
86
|
+
emitter.emit = Mock()
|
87
|
+
emitter.on = Mock()
|
88
|
+
emitter.off = Mock()
|
89
|
+
emitter.once = Mock()
|
90
|
+
emitter.listeners = Mock(return_value=[])
|
91
|
+
emitter.remove_all_listeners = Mock()
|
92
|
+
|
93
|
+
return emitter
|
94
|
+
|
95
|
+
|
96
|
+
@pytest.fixture
|
97
|
+
def mock_transport():
|
98
|
+
"""
|
99
|
+
Mock transport for testing network operations.
|
100
|
+
|
101
|
+
Returns:
|
102
|
+
Mock transport with request/response methods.
|
103
|
+
"""
|
104
|
+
transport = Mock()
|
105
|
+
transport.request = Mock(return_value={"status": 200, "data": {}})
|
106
|
+
transport.get = Mock(return_value={"status": 200, "data": {}})
|
107
|
+
transport.post = Mock(return_value={"status": 200, "data": {}})
|
108
|
+
transport.put = Mock(return_value={"status": 200, "data": {}})
|
109
|
+
transport.delete = Mock(return_value={"status": 204})
|
110
|
+
transport.close = Mock()
|
111
|
+
|
112
|
+
return transport
|
113
|
+
|
114
|
+
|
115
|
+
@pytest.fixture
|
116
|
+
def mock_metrics_collector():
|
117
|
+
"""
|
118
|
+
Mock metrics collector for testing instrumentation.
|
119
|
+
|
120
|
+
Returns:
|
121
|
+
Mock with common metrics methods.
|
122
|
+
"""
|
123
|
+
collector = Mock()
|
124
|
+
collector.increment = Mock()
|
125
|
+
collector.decrement = Mock()
|
126
|
+
collector.gauge = Mock()
|
127
|
+
collector.histogram = Mock()
|
128
|
+
collector.timer = Mock()
|
129
|
+
collector.flush = Mock()
|
130
|
+
|
131
|
+
# Add context manager support for timing
|
132
|
+
timer_cm = Mock()
|
133
|
+
timer_cm.__enter__ = Mock(return_value=timer_cm)
|
134
|
+
timer_cm.__exit__ = Mock(return_value=None)
|
135
|
+
collector.timer.return_value = timer_cm
|
136
|
+
|
137
|
+
return collector
|
138
|
+
|
139
|
+
|
140
|
+
@pytest.fixture
|
141
|
+
def mock_cache():
|
142
|
+
"""
|
143
|
+
Mock cache for testing caching behavior.
|
144
|
+
|
145
|
+
Returns:
|
146
|
+
Mock with get, set, delete, clear methods.
|
147
|
+
"""
|
148
|
+
cache_data = {}
|
149
|
+
|
150
|
+
cache = Mock()
|
151
|
+
cache.get = Mock(side_effect=lambda k, default=None: cache_data.get(k, default))
|
152
|
+
cache.set = Mock(side_effect=lambda k, v, ttl=None: cache_data.update({k: v}))
|
153
|
+
cache.delete = Mock(side_effect=lambda k: cache_data.pop(k, None))
|
154
|
+
cache.clear = Mock(side_effect=cache_data.clear)
|
155
|
+
cache.exists = Mock(side_effect=lambda k: k in cache_data)
|
156
|
+
cache.keys = Mock(return_value=list(cache_data.keys()))
|
157
|
+
|
158
|
+
# Store reference to data for test assertions
|
159
|
+
cache._data = cache_data
|
160
|
+
|
161
|
+
return cache
|
162
|
+
|
163
|
+
|
164
|
+
@pytest.fixture
|
165
|
+
def mock_database():
|
166
|
+
"""
|
167
|
+
Mock database connection for testing.
|
168
|
+
|
169
|
+
Returns:
|
170
|
+
Mock with execute, fetch, commit, rollback methods.
|
171
|
+
"""
|
172
|
+
db = Mock()
|
173
|
+
db.execute = Mock(return_value=Mock(rowcount=1))
|
174
|
+
db.fetch = Mock(return_value=[])
|
175
|
+
db.fetchone = Mock(return_value=None)
|
176
|
+
db.fetchall = Mock(return_value=[])
|
177
|
+
db.commit = Mock()
|
178
|
+
db.rollback = Mock()
|
179
|
+
db.close = Mock()
|
180
|
+
db.is_connected = PropertyMock(return_value=True)
|
181
|
+
|
182
|
+
# Add context manager support
|
183
|
+
db.__enter__ = Mock(return_value=db)
|
184
|
+
db.__exit__ = Mock(return_value=None)
|
185
|
+
|
186
|
+
return db
|
187
|
+
|
188
|
+
|
189
|
+
@pytest.fixture
|
190
|
+
def mock_file_system():
|
191
|
+
"""
|
192
|
+
Mock file system operations.
|
193
|
+
|
194
|
+
Returns:
|
195
|
+
Mock with read, write, exists, delete methods.
|
196
|
+
"""
|
197
|
+
fs = Mock()
|
198
|
+
fs.read = Mock(return_value=b"content")
|
199
|
+
fs.write = Mock()
|
200
|
+
fs.exists = Mock(return_value=True)
|
201
|
+
fs.delete = Mock()
|
202
|
+
fs.mkdir = Mock()
|
203
|
+
fs.rmdir = Mock()
|
204
|
+
fs.list = Mock(return_value=[])
|
205
|
+
fs.stat = Mock(return_value=Mock(st_size=1024, st_mtime=0))
|
206
|
+
|
207
|
+
return fs
|
208
|
+
|
209
|
+
|
210
|
+
@pytest.fixture
|
211
|
+
def mock_subprocess():
|
212
|
+
"""
|
213
|
+
Mock subprocess for testing command execution.
|
214
|
+
|
215
|
+
Returns:
|
216
|
+
Mock with run, Popen methods.
|
217
|
+
"""
|
218
|
+
subprocess = Mock()
|
219
|
+
|
220
|
+
# Mock run method
|
221
|
+
result = Mock()
|
222
|
+
result.returncode = 0
|
223
|
+
result.stdout = "output"
|
224
|
+
result.stderr = ""
|
225
|
+
subprocess.run = Mock(return_value=result)
|
226
|
+
|
227
|
+
# Mock Popen
|
228
|
+
process = Mock()
|
229
|
+
process.communicate = Mock(return_value=("output", ""))
|
230
|
+
process.returncode = 0
|
231
|
+
process.pid = 12345
|
232
|
+
process.poll = Mock(return_value=0)
|
233
|
+
process.wait = Mock(return_value=0)
|
234
|
+
subprocess.Popen = Mock(return_value=process)
|
235
|
+
|
236
|
+
return subprocess
|
@@ -0,0 +1,40 @@
|
|
1
|
+
"""
|
2
|
+
File testing fixtures for the provide-io ecosystem.
|
3
|
+
|
4
|
+
Standard fixtures for file and directory operations that can be used
|
5
|
+
across any project that depends on provide.foundation.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from provide.foundation.testing.file.fixtures import (
|
9
|
+
temp_directory,
|
10
|
+
test_files_structure,
|
11
|
+
temp_file,
|
12
|
+
binary_file,
|
13
|
+
nested_directory_structure,
|
14
|
+
empty_directory,
|
15
|
+
readonly_file,
|
16
|
+
temp_named_file,
|
17
|
+
temp_file_with_content,
|
18
|
+
temp_binary_file,
|
19
|
+
temp_csv_file,
|
20
|
+
temp_json_file,
|
21
|
+
temp_symlink,
|
22
|
+
temp_executable_file,
|
23
|
+
)
|
24
|
+
|
25
|
+
__all__ = [
|
26
|
+
"temp_directory",
|
27
|
+
"test_files_structure",
|
28
|
+
"temp_file",
|
29
|
+
"binary_file",
|
30
|
+
"nested_directory_structure",
|
31
|
+
"empty_directory",
|
32
|
+
"readonly_file",
|
33
|
+
"temp_named_file",
|
34
|
+
"temp_file_with_content",
|
35
|
+
"temp_binary_file",
|
36
|
+
"temp_csv_file",
|
37
|
+
"temp_json_file",
|
38
|
+
"temp_symlink",
|
39
|
+
"temp_executable_file",
|
40
|
+
]
|