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.
Files changed (161) hide show
  1. provide/foundation/__init__.py +41 -23
  2. provide/foundation/archive/__init__.py +23 -0
  3. provide/foundation/archive/base.py +70 -0
  4. provide/foundation/archive/bzip2.py +157 -0
  5. provide/foundation/archive/gzip.py +159 -0
  6. provide/foundation/archive/operations.py +334 -0
  7. provide/foundation/archive/tar.py +164 -0
  8. provide/foundation/archive/zip.py +203 -0
  9. provide/foundation/cli/__init__.py +2 -2
  10. provide/foundation/cli/commands/deps.py +13 -7
  11. provide/foundation/cli/commands/logs/__init__.py +1 -1
  12. provide/foundation/cli/commands/logs/query.py +1 -1
  13. provide/foundation/cli/commands/logs/send.py +1 -1
  14. provide/foundation/cli/commands/logs/tail.py +1 -1
  15. provide/foundation/cli/decorators.py +11 -10
  16. provide/foundation/cli/main.py +1 -1
  17. provide/foundation/cli/testing.py +2 -35
  18. provide/foundation/cli/utils.py +21 -17
  19. provide/foundation/config/__init__.py +35 -2
  20. provide/foundation/config/base.py +2 -2
  21. provide/foundation/config/converters.py +479 -0
  22. provide/foundation/config/defaults.py +67 -0
  23. provide/foundation/config/env.py +4 -19
  24. provide/foundation/config/loader.py +9 -3
  25. provide/foundation/config/sync.py +19 -4
  26. provide/foundation/console/input.py +5 -5
  27. provide/foundation/console/output.py +35 -13
  28. provide/foundation/context/__init__.py +8 -4
  29. provide/foundation/context/core.py +85 -109
  30. provide/foundation/core.py +1 -2
  31. provide/foundation/crypto/__init__.py +2 -0
  32. provide/foundation/crypto/certificates/__init__.py +34 -0
  33. provide/foundation/crypto/certificates/base.py +173 -0
  34. provide/foundation/crypto/certificates/certificate.py +290 -0
  35. provide/foundation/crypto/certificates/factory.py +213 -0
  36. provide/foundation/crypto/certificates/generator.py +138 -0
  37. provide/foundation/crypto/certificates/loader.py +130 -0
  38. provide/foundation/crypto/certificates/operations.py +198 -0
  39. provide/foundation/crypto/certificates/trust.py +107 -0
  40. provide/foundation/errors/__init__.py +2 -3
  41. provide/foundation/errors/decorators.py +0 -231
  42. provide/foundation/errors/types.py +0 -97
  43. provide/foundation/eventsets/__init__.py +0 -0
  44. provide/foundation/eventsets/display.py +84 -0
  45. provide/foundation/eventsets/registry.py +160 -0
  46. provide/foundation/eventsets/resolver.py +192 -0
  47. provide/foundation/eventsets/sets/das.py +128 -0
  48. provide/foundation/eventsets/sets/database.py +125 -0
  49. provide/foundation/eventsets/sets/http.py +153 -0
  50. provide/foundation/eventsets/sets/llm.py +139 -0
  51. provide/foundation/eventsets/sets/task_queue.py +107 -0
  52. provide/foundation/eventsets/types.py +70 -0
  53. provide/foundation/file/directory.py +13 -22
  54. provide/foundation/file/lock.py +3 -1
  55. provide/foundation/hub/components.py +77 -515
  56. provide/foundation/hub/config.py +151 -0
  57. provide/foundation/hub/discovery.py +62 -0
  58. provide/foundation/hub/handlers.py +81 -0
  59. provide/foundation/hub/lifecycle.py +194 -0
  60. provide/foundation/hub/manager.py +4 -4
  61. provide/foundation/hub/processors.py +44 -0
  62. provide/foundation/integrations/__init__.py +11 -0
  63. provide/foundation/{observability → integrations}/openobserve/__init__.py +10 -7
  64. provide/foundation/{observability → integrations}/openobserve/auth.py +1 -1
  65. provide/foundation/{observability → integrations}/openobserve/client.py +12 -12
  66. provide/foundation/{observability → integrations}/openobserve/commands.py +3 -3
  67. provide/foundation/integrations/openobserve/config.py +37 -0
  68. provide/foundation/{observability → integrations}/openobserve/formatters.py +1 -1
  69. provide/foundation/{observability → integrations}/openobserve/otlp.py +1 -1
  70. provide/foundation/{observability → integrations}/openobserve/search.py +2 -2
  71. provide/foundation/{observability → integrations}/openobserve/streaming.py +4 -4
  72. provide/foundation/logger/__init__.py +3 -10
  73. provide/foundation/logger/config/logging.py +68 -298
  74. provide/foundation/logger/config/telemetry.py +41 -121
  75. provide/foundation/logger/core.py +0 -2
  76. provide/foundation/logger/custom_processors.py +1 -0
  77. provide/foundation/logger/factories.py +11 -2
  78. provide/foundation/logger/processors/main.py +20 -84
  79. provide/foundation/logger/setup/__init__.py +5 -1
  80. provide/foundation/logger/setup/coordinator.py +76 -24
  81. provide/foundation/logger/setup/processors.py +2 -9
  82. provide/foundation/logger/trace.py +27 -0
  83. provide/foundation/metrics/otel.py +10 -10
  84. provide/foundation/observability/__init__.py +2 -2
  85. provide/foundation/process/__init__.py +9 -0
  86. provide/foundation/process/exit.py +47 -0
  87. provide/foundation/process/lifecycle.py +115 -59
  88. provide/foundation/resilience/__init__.py +35 -0
  89. provide/foundation/resilience/circuit.py +164 -0
  90. provide/foundation/resilience/decorators.py +220 -0
  91. provide/foundation/resilience/fallback.py +193 -0
  92. provide/foundation/resilience/retry.py +325 -0
  93. provide/foundation/streams/config.py +79 -0
  94. provide/foundation/streams/console.py +7 -8
  95. provide/foundation/streams/core.py +6 -3
  96. provide/foundation/streams/file.py +12 -2
  97. provide/foundation/testing/__init__.py +84 -2
  98. provide/foundation/testing/archive/__init__.py +24 -0
  99. provide/foundation/testing/archive/fixtures.py +217 -0
  100. provide/foundation/testing/cli.py +30 -17
  101. provide/foundation/testing/common/__init__.py +32 -0
  102. provide/foundation/testing/common/fixtures.py +236 -0
  103. provide/foundation/testing/file/__init__.py +40 -0
  104. provide/foundation/testing/file/content_fixtures.py +316 -0
  105. provide/foundation/testing/file/directory_fixtures.py +107 -0
  106. provide/foundation/testing/file/fixtures.py +52 -0
  107. provide/foundation/testing/file/special_fixtures.py +153 -0
  108. provide/foundation/testing/logger.py +117 -11
  109. provide/foundation/testing/mocking/__init__.py +46 -0
  110. provide/foundation/testing/mocking/fixtures.py +331 -0
  111. provide/foundation/testing/process/__init__.py +48 -0
  112. provide/foundation/testing/process/async_fixtures.py +405 -0
  113. provide/foundation/testing/process/fixtures.py +56 -0
  114. provide/foundation/testing/process/subprocess_fixtures.py +209 -0
  115. provide/foundation/testing/threading/__init__.py +38 -0
  116. provide/foundation/testing/threading/basic_fixtures.py +101 -0
  117. provide/foundation/testing/threading/data_fixtures.py +99 -0
  118. provide/foundation/testing/threading/execution_fixtures.py +263 -0
  119. provide/foundation/testing/threading/fixtures.py +54 -0
  120. provide/foundation/testing/threading/sync_fixtures.py +97 -0
  121. provide/foundation/testing/time/__init__.py +32 -0
  122. provide/foundation/testing/time/fixtures.py +409 -0
  123. provide/foundation/testing/transport/__init__.py +30 -0
  124. provide/foundation/testing/transport/fixtures.py +280 -0
  125. provide/foundation/tools/__init__.py +58 -0
  126. provide/foundation/tools/base.py +348 -0
  127. provide/foundation/tools/cache.py +268 -0
  128. provide/foundation/tools/downloader.py +224 -0
  129. provide/foundation/tools/installer.py +254 -0
  130. provide/foundation/tools/registry.py +223 -0
  131. provide/foundation/tools/resolver.py +321 -0
  132. provide/foundation/tools/verifier.py +186 -0
  133. provide/foundation/tracer/otel.py +7 -11
  134. provide/foundation/tracer/spans.py +2 -2
  135. provide/foundation/transport/__init__.py +155 -0
  136. provide/foundation/transport/base.py +171 -0
  137. provide/foundation/transport/client.py +266 -0
  138. provide/foundation/transport/config.py +140 -0
  139. provide/foundation/transport/errors.py +79 -0
  140. provide/foundation/transport/http.py +232 -0
  141. provide/foundation/transport/middleware.py +360 -0
  142. provide/foundation/transport/registry.py +167 -0
  143. provide/foundation/transport/types.py +45 -0
  144. provide/foundation/utils/deps.py +14 -12
  145. provide/foundation/utils/parsing.py +49 -4
  146. {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/METADATA +5 -28
  147. provide_foundation-0.0.0.dev2.dist-info/RECORD +225 -0
  148. provide/foundation/cli/commands/logs/generate_old.py +0 -569
  149. provide/foundation/crypto/certificates.py +0 -896
  150. provide/foundation/logger/emoji/__init__.py +0 -44
  151. provide/foundation/logger/emoji/matrix.py +0 -209
  152. provide/foundation/logger/emoji/sets.py +0 -458
  153. provide/foundation/logger/emoji/types.py +0 -56
  154. provide/foundation/logger/setup/emoji_resolver.py +0 -64
  155. provide_foundation-0.0.0.dev0.dist-info/RECORD +0 -149
  156. /provide/foundation/{observability → integrations}/openobserve/exceptions.py +0 -0
  157. /provide/foundation/{observability → integrations}/openobserve/models.py +0 -0
  158. {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/WHEEL +0 -0
  159. {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/entry_points.txt +0 -0
  160. {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/licenses/LICENSE +0 -0
  161. {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 Context
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(Context):
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 = Context(**{k: v for k, v in kwargs.items() if v is not None})
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
+ ]