pdd-cli 0.0.90__py3-none-any.whl → 0.0.121__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 (151) hide show
  1. pdd/__init__.py +38 -6
  2. pdd/agentic_bug.py +323 -0
  3. pdd/agentic_bug_orchestrator.py +506 -0
  4. pdd/agentic_change.py +231 -0
  5. pdd/agentic_change_orchestrator.py +537 -0
  6. pdd/agentic_common.py +533 -770
  7. pdd/agentic_crash.py +2 -1
  8. pdd/agentic_e2e_fix.py +319 -0
  9. pdd/agentic_e2e_fix_orchestrator.py +582 -0
  10. pdd/agentic_fix.py +118 -3
  11. pdd/agentic_update.py +27 -9
  12. pdd/agentic_verify.py +3 -2
  13. pdd/architecture_sync.py +565 -0
  14. pdd/auth_service.py +210 -0
  15. pdd/auto_deps_main.py +63 -53
  16. pdd/auto_include.py +236 -3
  17. pdd/auto_update.py +125 -47
  18. pdd/bug_main.py +195 -23
  19. pdd/cmd_test_main.py +345 -197
  20. pdd/code_generator.py +4 -2
  21. pdd/code_generator_main.py +118 -32
  22. pdd/commands/__init__.py +6 -0
  23. pdd/commands/analysis.py +113 -48
  24. pdd/commands/auth.py +309 -0
  25. pdd/commands/connect.py +358 -0
  26. pdd/commands/fix.py +155 -114
  27. pdd/commands/generate.py +5 -0
  28. pdd/commands/maintenance.py +3 -2
  29. pdd/commands/misc.py +8 -0
  30. pdd/commands/modify.py +225 -163
  31. pdd/commands/sessions.py +284 -0
  32. pdd/commands/utility.py +12 -7
  33. pdd/construct_paths.py +334 -32
  34. pdd/context_generator_main.py +167 -170
  35. pdd/continue_generation.py +6 -3
  36. pdd/core/__init__.py +33 -0
  37. pdd/core/cli.py +44 -7
  38. pdd/core/cloud.py +237 -0
  39. pdd/core/dump.py +68 -20
  40. pdd/core/errors.py +4 -0
  41. pdd/core/remote_session.py +61 -0
  42. pdd/crash_main.py +219 -23
  43. pdd/data/llm_model.csv +4 -4
  44. pdd/docs/prompting_guide.md +864 -0
  45. pdd/docs/whitepaper_with_benchmarks/data_and_functions/benchmark_analysis.py +495 -0
  46. pdd/docs/whitepaper_with_benchmarks/data_and_functions/creation_compare.py +528 -0
  47. pdd/fix_code_loop.py +208 -34
  48. pdd/fix_code_module_errors.py +6 -2
  49. pdd/fix_error_loop.py +291 -38
  50. pdd/fix_main.py +208 -6
  51. pdd/fix_verification_errors_loop.py +235 -26
  52. pdd/fix_verification_main.py +269 -83
  53. pdd/frontend/dist/assets/index-B5DZHykP.css +1 -0
  54. pdd/frontend/dist/assets/index-CUWd8al1.js +450 -0
  55. pdd/frontend/dist/index.html +376 -0
  56. pdd/frontend/dist/logo.svg +33 -0
  57. pdd/generate_output_paths.py +46 -5
  58. pdd/generate_test.py +212 -151
  59. pdd/get_comment.py +19 -44
  60. pdd/get_extension.py +8 -9
  61. pdd/get_jwt_token.py +309 -20
  62. pdd/get_language.py +8 -7
  63. pdd/get_run_command.py +7 -5
  64. pdd/insert_includes.py +2 -1
  65. pdd/llm_invoke.py +531 -97
  66. pdd/load_prompt_template.py +15 -34
  67. pdd/operation_log.py +342 -0
  68. pdd/path_resolution.py +140 -0
  69. pdd/postprocess.py +122 -97
  70. pdd/preprocess.py +68 -12
  71. pdd/preprocess_main.py +33 -1
  72. pdd/prompts/agentic_bug_step10_pr_LLM.prompt +182 -0
  73. pdd/prompts/agentic_bug_step1_duplicate_LLM.prompt +73 -0
  74. pdd/prompts/agentic_bug_step2_docs_LLM.prompt +129 -0
  75. pdd/prompts/agentic_bug_step3_triage_LLM.prompt +95 -0
  76. pdd/prompts/agentic_bug_step4_reproduce_LLM.prompt +97 -0
  77. pdd/prompts/agentic_bug_step5_root_cause_LLM.prompt +123 -0
  78. pdd/prompts/agentic_bug_step6_test_plan_LLM.prompt +107 -0
  79. pdd/prompts/agentic_bug_step7_generate_LLM.prompt +172 -0
  80. pdd/prompts/agentic_bug_step8_verify_LLM.prompt +119 -0
  81. pdd/prompts/agentic_bug_step9_e2e_test_LLM.prompt +289 -0
  82. pdd/prompts/agentic_change_step10_identify_issues_LLM.prompt +1006 -0
  83. pdd/prompts/agentic_change_step11_fix_issues_LLM.prompt +984 -0
  84. pdd/prompts/agentic_change_step12_create_pr_LLM.prompt +140 -0
  85. pdd/prompts/agentic_change_step1_duplicate_LLM.prompt +73 -0
  86. pdd/prompts/agentic_change_step2_docs_LLM.prompt +101 -0
  87. pdd/prompts/agentic_change_step3_research_LLM.prompt +126 -0
  88. pdd/prompts/agentic_change_step4_clarify_LLM.prompt +164 -0
  89. pdd/prompts/agentic_change_step5_docs_change_LLM.prompt +981 -0
  90. pdd/prompts/agentic_change_step6_devunits_LLM.prompt +1005 -0
  91. pdd/prompts/agentic_change_step7_architecture_LLM.prompt +1044 -0
  92. pdd/prompts/agentic_change_step8_analyze_LLM.prompt +1027 -0
  93. pdd/prompts/agentic_change_step9_implement_LLM.prompt +1077 -0
  94. pdd/prompts/agentic_e2e_fix_step1_unit_tests_LLM.prompt +90 -0
  95. pdd/prompts/agentic_e2e_fix_step2_e2e_tests_LLM.prompt +91 -0
  96. pdd/prompts/agentic_e2e_fix_step3_root_cause_LLM.prompt +89 -0
  97. pdd/prompts/agentic_e2e_fix_step4_fix_e2e_tests_LLM.prompt +96 -0
  98. pdd/prompts/agentic_e2e_fix_step5_identify_devunits_LLM.prompt +91 -0
  99. pdd/prompts/agentic_e2e_fix_step6_create_unit_tests_LLM.prompt +106 -0
  100. pdd/prompts/agentic_e2e_fix_step7_verify_tests_LLM.prompt +116 -0
  101. pdd/prompts/agentic_e2e_fix_step8_run_pdd_fix_LLM.prompt +120 -0
  102. pdd/prompts/agentic_e2e_fix_step9_verify_all_LLM.prompt +146 -0
  103. pdd/prompts/agentic_fix_primary_LLM.prompt +2 -2
  104. pdd/prompts/agentic_update_LLM.prompt +192 -338
  105. pdd/prompts/auto_include_LLM.prompt +22 -0
  106. pdd/prompts/change_LLM.prompt +3093 -1
  107. pdd/prompts/detect_change_LLM.prompt +571 -14
  108. pdd/prompts/fix_code_module_errors_LLM.prompt +8 -0
  109. pdd/prompts/fix_errors_from_unit_tests_LLM.prompt +1 -0
  110. pdd/prompts/generate_test_LLM.prompt +19 -1
  111. pdd/prompts/generate_test_from_example_LLM.prompt +366 -0
  112. pdd/prompts/insert_includes_LLM.prompt +262 -252
  113. pdd/prompts/prompt_code_diff_LLM.prompt +123 -0
  114. pdd/prompts/prompt_diff_LLM.prompt +82 -0
  115. pdd/remote_session.py +876 -0
  116. pdd/server/__init__.py +52 -0
  117. pdd/server/app.py +335 -0
  118. pdd/server/click_executor.py +587 -0
  119. pdd/server/executor.py +338 -0
  120. pdd/server/jobs.py +661 -0
  121. pdd/server/models.py +241 -0
  122. pdd/server/routes/__init__.py +31 -0
  123. pdd/server/routes/architecture.py +451 -0
  124. pdd/server/routes/auth.py +364 -0
  125. pdd/server/routes/commands.py +929 -0
  126. pdd/server/routes/config.py +42 -0
  127. pdd/server/routes/files.py +603 -0
  128. pdd/server/routes/prompts.py +1347 -0
  129. pdd/server/routes/websocket.py +473 -0
  130. pdd/server/security.py +243 -0
  131. pdd/server/terminal_spawner.py +217 -0
  132. pdd/server/token_counter.py +222 -0
  133. pdd/summarize_directory.py +236 -237
  134. pdd/sync_animation.py +8 -4
  135. pdd/sync_determine_operation.py +329 -47
  136. pdd/sync_main.py +272 -28
  137. pdd/sync_orchestration.py +289 -211
  138. pdd/sync_order.py +304 -0
  139. pdd/template_expander.py +161 -0
  140. pdd/templates/architecture/architecture_json.prompt +41 -46
  141. pdd/trace.py +1 -1
  142. pdd/track_cost.py +0 -13
  143. pdd/unfinished_prompt.py +2 -1
  144. pdd/update_main.py +68 -26
  145. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/METADATA +15 -10
  146. pdd_cli-0.0.121.dist-info/RECORD +229 -0
  147. pdd_cli-0.0.90.dist-info/RECORD +0 -153
  148. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/WHEEL +0 -0
  149. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/entry_points.txt +0 -0
  150. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/licenses/LICENSE +0 -0
  151. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/top_level.txt +0 -0
@@ -9,6 +9,16 @@
9
9
  - The example file will be saved at: <test_file_path>{test_file_path}</test_file_path>
10
10
  - The module name (without extension) is: <module_name>{module_name}</module_name>
11
11
 
12
+ % EXISTING TESTS (if provided - your output will be APPENDED to this file):
13
+ <existing_tests>{existing_tests}</existing_tests>
14
+
15
+ % If existing tests are provided above:
16
+ - Generate ONLY NEW test functions (your output will be appended to the existing file)
17
+ - Do NOT include import statements (they already exist in the file)
18
+ - Do NOT duplicate any existing test function names
19
+ - Maintain consistent style with existing tests (fixtures, naming conventions)
20
+ - Focus on testing functionality NOT already covered by existing tests
21
+
12
22
  % Follow these rules:
13
23
  - CRITICAL: You MUST analyze the actual code provided in code_under_test and generate tests for the EXACT functions defined in that code
14
24
  - CRITICAL: Import statements must use the ACTUAL module name from the code file path, not generic names. Include the necessary code/path info to import the code under test.
@@ -22,7 +32,15 @@
22
32
  - Setup and teardown methods should only use public APIs and environment variables, never reset internal module state directly.
23
33
  - Design tests to be independent of implementation details that might change when code is regenerated.
24
34
  - For test isolation, use fixtures and mocking of external dependencies rather than manipulating internal module state. In general minimize the amount of mocking needed so that the tests are more robust to changes in the code under test and more code is tested.
25
- <include>./context/test.prompt</include>
35
+
36
+ % TEST ISOLATION PRINCIPLE:
37
+ % CRITICAL: Each test MUST be isolated and not pollute state for other tests.
38
+ - Tests must clean up any state they modify (environment variables, global state, files, mocks)
39
+ - Use the testing framework's built-in isolation mechanisms (fixtures, setup/teardown, context managers)
40
+ - Avoid modifying global or module-level state directly; if unavoidable, always restore original state
41
+ - Prefer function-scoped test resources over shared/module-scoped ones to ensure isolation
42
+
43
+ <include>context/test.prompt</include>
26
44
 
27
45
  <instructions>
28
46
  1. FIRST: Carefully analyze the ACTUAL code provided in code_under_test:
@@ -0,0 +1,366 @@
1
+ % You are an expert Software Test Engineer. Your goal is to generate tests based on the intended behavior described in a prompt and demonstrated in an example file.
2
+
3
+ % Here a description of what the code is supposed to do and was the prompt that generated the code: <prompt_that_generated_code>{prompt_that_generated_code}</prompt_that_generated_code>
4
+
5
+ % Here is an example showing how the module should be used: <example_usage>{example}</example_usage>
6
+
7
+ % File path information:
8
+ - The example file is located at: <example_file_path>{source_file_path}</example_file_path>
9
+ - The test file will be saved at: <test_file_path>{test_file_path}</test_file_path>
10
+ - The module name (without extension) is: <module_name>{module_name}</module_name>
11
+
12
+ % EXISTING TESTS (if provided - your output will be APPENDED to this file):
13
+ <existing_tests>{existing_tests}</existing_tests>
14
+
15
+ % If existing tests are provided above:
16
+ - Generate ONLY NEW test functions (your output will be appended to the existing file)
17
+ - Do NOT include import statements (they already exist in the file)
18
+ - Do NOT duplicate any existing test function names
19
+ - Maintain consistent style with existing tests (fixtures, naming conventions)
20
+ - Focus on testing functionality NOT already covered by existing tests
21
+
22
+ % Follow these rules:
23
+ - CRITICAL: Analyze the EXAMPLE to understand the API (function names, parameters, return values)
24
+ - CRITICAL: Import statements must match the module structure shown in the example
25
+ - CRITICAL: Test the intended function names and behavior based on the prompt
26
+ - The module name for the code under test will have the same name as the function name
27
+ - The unit test should be in {language}. If Python, use pytest.
28
+ - Use individual test functions for each case to make it easier to identify which specific cases pass or fail.
29
+ - Use the description of the functionality in the prompt to generate tests with useful tests with good code coverage.
30
+ - Focus on testing the INTENDED FUNCTIONALITY, not implementation details.
31
+ - NEVER access internal implementation details (variables/functions starting with underscore) in your tests.
32
+ - Setup and teardown methods should only use public APIs and environment variables, never reset internal module state directly.
33
+ - Design tests to be independent of implementation details.
34
+ - For test isolation, use fixtures and mocking of external dependencies rather than manipulating internal module state. In general minimize the amount of mocking needed so that the tests are more robust to changes in the code under test and more code is tested.
35
+ - Know that the generated test will be in a different directory (`tests`) than the module (in directory `pdd`) it is calling and will need an absolute reference. The module file name will be same as the function name.
36
+ - Created files should be in the `output` directory.
37
+ - Data files (language_format.csv and llm_model.csv) already exist in the PDD_PATH/`data` directory. Do not write over them. It already contains data for popular languages and LLM models and can be used for tests.
38
+ - The PDD_PATH environment variable is already set.
39
+
40
+ % PYTEST TEST ISOLATION AND ANTI-POLLUTION RULES:
41
+ % CRITICAL: Generated tests MUST be isolated and not pollute state for other tests. Follow these rules strictly:
42
+
43
+ % 1. ENVIRONMENT VARIABLES:
44
+ - ALWAYS use monkeypatch.setenv() or monkeypatch.delenv() instead of os.environ["VAR"] = "value"
45
+ - NEVER use direct os.environ manipulation - it persists beyond the test and pollutes other tests
46
+ - BAD: os.environ["API_KEY"] = "test_key" # POLLUTION: persists after test ends
47
+ - GOOD: monkeypatch.setenv("API_KEY", "test_key") # Auto-cleaned by pytest
48
+
49
+ % 2. MOCKING EXTERNAL DEPENDENCIES:
50
+ - Use context managers or monkeypatch for mocks - they auto-cleanup after the test
51
+ - Prefer monkeypatch.setattr() over unittest.mock.patch() decorators at module level
52
+ - BAD: @patch('module.func') at module/class level # Can leak if exception occurs
53
+ - GOOD: monkeypatch.setattr('module.func', mock_func) # Always cleaned up
54
+ - GOOD: with patch('module.func') as mock: # Context manager ensures cleanup
55
+
56
+ % 3. FIXTURE CLEANUP WITH YIELD:
57
+ - Use yield-based fixtures with cleanup code after yield for any resources
58
+ - Prefer function-scoped fixtures over module or session scope to ensure isolation
59
+ - BAD: @pytest.fixture(scope="module") without cleanup # State leaks between tests
60
+ - GOOD: @pytest.fixture with yield and cleanup after yield # Always cleans up
61
+ - Example of proper fixture:
62
+ @pytest.fixture
63
+ def temp_resource():
64
+ resource = setup_resource()
65
+ yield resource
66
+ resource.cleanup() # Always runs after test, even on failure
67
+
68
+ % 4. SYS.MODULES MANIPULATION:
69
+ - AVOID manipulating sys.modules directly whenever possible
70
+ - If unavoidable, ALWAYS save and restore in try/finally or fixture with yield
71
+ - BAD: sys.modules["module"] = mock_module # Pollutes all subsequent tests
72
+ - GOOD: Use a fixture that saves, mocks, and restores:
73
+ @pytest.fixture
74
+ def mock_module():
75
+ saved = sys.modules.get("module")
76
+ sys.modules["module"] = MagicMock()
77
+ yield
78
+ if saved is not None:
79
+ sys.modules["module"] = saved
80
+ elif "module" in sys.modules:
81
+ del sys.modules["module"]
82
+
83
+ % 5. FILE SYSTEM OPERATIONS:
84
+ - ALWAYS use the tmp_path fixture for creating temporary files and directories
85
+ - NEVER create files in the working directory or fixed paths
86
+ - BAD: with open("test_output.txt", "w") as f: ... # Leaves file behind
87
+ - GOOD: def test_file(tmp_path): (tmp_path / "test_output.txt").write_text(...)
88
+
89
+ % 6. GLOBAL/MODULE STATE:
90
+ - Never modify global variables or module-level state directly in tests
91
+ - Use monkeypatch.setattr() for any module-level variables that need changing
92
+ - Reset any singleton instances using fixtures with proper teardown
93
+
94
+ % SUMMARY OF GOOD PATTERNS:
95
+ - Use tmp_path fixture for file operations
96
+ - Use monkeypatch fixture for environment variables and attributes
97
+ - Use pytest.raises() as context manager for exception testing
98
+ - Prefer function-scoped fixtures over module or session scope
99
+ - Use yield in fixtures to ensure cleanup runs even on test failure
100
+
101
+ % 7. MODULE-LEVEL SYS.MODULES FOR IMPORT-TIME DEPENDENCIES:
102
+ - Sometimes you must mock modules BEFORE importing the code under test
103
+ (e.g., when decorators or top-level imports need mocking)
104
+ - ALWAYS save original values, apply mocks, load module, then RESTORE immediately
105
+ - BAD: sys.modules.update(mocks); exec_module(...) # No cleanup - pollutes all tests!
106
+ - GOOD: See PATTERN 7 in pytest_isolation_example.py for the full save/restore pattern
107
+
108
+ <isolation_example>
109
+ """
110
+ Example code patterns demonstrating proper test isolation to prevent test pollution.
111
+
112
+ This file provides reference implementations of CORRECT patterns that should be used
113
+ in generated tests. These patterns prevent test pollution and ensure tests are independent.
114
+
115
+ IMPORTANT: This is a context file for the LLM, not a runnable test file.
116
+ """
117
+
118
+ import os
119
+ import sys
120
+ from pathlib import Path
121
+ from unittest.mock import MagicMock, patch
122
+
123
+ import pytest
124
+
125
+
126
+ # =============================================================================
127
+ # PATTERN 1: Environment Variable Handling with monkeypatch
128
+ # =============================================================================
129
+
130
+ def test_set_env_var_with_monkeypatch(monkeypatch):
131
+ """GOOD: Use monkeypatch.setenv() for setting env vars.
132
+
133
+ monkeypatch automatically restores the original value after the test,
134
+ preventing pollution of subsequent tests.
135
+ """
136
+ monkeypatch.setenv("TEST_API_KEY", "test_key_123")
137
+ assert os.environ["TEST_API_KEY"] == "test_key_123"
138
+ # Automatically cleaned up after test
139
+
140
+
141
+ def test_delete_env_var_with_monkeypatch(monkeypatch):
142
+ """GOOD: Use monkeypatch.delenv() for removing env vars."""
143
+ monkeypatch.setenv("TEMP_VAR_TO_DELETE", "value")
144
+ monkeypatch.delenv("TEMP_VAR_TO_DELETE")
145
+ assert "TEMP_VAR_TO_DELETE" not in os.environ
146
+
147
+
148
+ def test_multiple_env_vars(monkeypatch):
149
+ """GOOD: Set multiple env vars safely with monkeypatch."""
150
+ monkeypatch.setenv("VAR_ONE", "value1")
151
+ monkeypatch.setenv("VAR_TWO", "value2")
152
+ monkeypatch.setenv("VAR_THREE", "value3")
153
+ # All automatically cleaned up
154
+
155
+
156
+ # =============================================================================
157
+ # PATTERN 2: Mocking with monkeypatch and context managers
158
+ # =============================================================================
159
+
160
+ def test_mock_function_with_monkeypatch(monkeypatch):
161
+ """GOOD: Use monkeypatch.setattr() for mocking functions."""
162
+ def mock_getcwd():
163
+ return "/mock/path"
164
+
165
+ monkeypatch.setattr(os, "getcwd", mock_getcwd)
166
+ assert os.getcwd() == "/mock/path"
167
+ # Original function automatically restored after test
168
+
169
+
170
+ def test_mock_with_context_manager():
171
+ """GOOD: Use patch as context manager for scoped mocking."""
172
+ with patch("os.path.exists") as mock_exists:
173
+ mock_exists.return_value = True
174
+ assert os.path.exists("/fake/nonexistent/path") is True
175
+ # Mock is automatically removed when context exits
176
+
177
+
178
+ # =============================================================================
179
+ # PATTERN 3: File System Operations with tmp_path
180
+ # =============================================================================
181
+
182
+ def test_create_temp_file(tmp_path):
183
+ """GOOD: Use tmp_path fixture for temporary files."""
184
+ test_file = tmp_path / "test_output.txt"
185
+ test_file.write_text("test content")
186
+ assert test_file.exists()
187
+ assert test_file.read_text() == "test content"
188
+ # tmp_path is automatically cleaned up by pytest
189
+
190
+
191
+ def test_create_temp_directory_structure(tmp_path):
192
+ """GOOD: Create directory structures in tmp_path."""
193
+ subdir = tmp_path / "subdir" / "nested"
194
+ subdir.mkdir(parents=True)
195
+ config_file = subdir / "config.json"
196
+ config_file.write_text('{{"key": "value"}}')
197
+ assert config_file.exists()
198
+
199
+
200
+ # =============================================================================
201
+ # PATTERN 4: Fixtures with Proper Cleanup
202
+ # =============================================================================
203
+
204
+ @pytest.fixture
205
+ def resource_with_cleanup():
206
+ """GOOD: Fixture with proper cleanup using yield.
207
+
208
+ The cleanup code after yield always runs, even if the test fails.
209
+ """
210
+ # Setup
211
+ resource = {{"initialized": True, "data": []}}
212
+ yield resource
213
+ # Cleanup - always runs
214
+ resource["initialized"] = False
215
+ resource["data"].clear()
216
+
217
+
218
+ @pytest.fixture
219
+ def mock_module_with_cleanup():
220
+ """GOOD: Fixture for sys.modules with save/restore.
221
+
222
+ This pattern ensures sys.modules is always restored to its original
223
+ state after the test, preventing pollution.
224
+ """
225
+ module_name = "test_mock_module"
226
+ saved = sys.modules.get(module_name)
227
+
228
+ mock_module = MagicMock()
229
+ sys.modules[module_name] = mock_module
230
+
231
+ yield mock_module
232
+
233
+ # Cleanup - restore original state
234
+ if saved is not None:
235
+ sys.modules[module_name] = saved
236
+ elif module_name in sys.modules:
237
+ del sys.modules[module_name]
238
+
239
+
240
+ def test_with_resource_cleanup(resource_with_cleanup):
241
+ """Test using fixture with automatic cleanup."""
242
+ assert resource_with_cleanup["initialized"] is True
243
+ resource_with_cleanup["data"].append("test_item")
244
+
245
+
246
+ def test_with_mock_module_cleanup(mock_module_with_cleanup):
247
+ """Test using sys.modules fixture with cleanup."""
248
+ assert "test_mock_module" in sys.modules
249
+
250
+
251
+ # =============================================================================
252
+ # PATTERN 5: Exception Testing with Context Manager
253
+ # =============================================================================
254
+
255
+ def test_exception_with_context_manager():
256
+ """GOOD: Use pytest.raises() as context manager."""
257
+ with pytest.raises(ValueError) as exc_info:
258
+ raise ValueError("expected error message")
259
+ assert "expected error message" in str(exc_info.value)
260
+
261
+
262
+ def test_exception_with_match():
263
+ """GOOD: Use match parameter for regex matching."""
264
+ with pytest.raises(ValueError, match=r"invalid.*value"):
265
+ raise ValueError("invalid input value provided")
266
+
267
+
268
+ # =============================================================================
269
+ # PATTERN 6: Combining Multiple Isolation Techniques
270
+ # =============================================================================
271
+
272
+ def test_combined_env_and_file(monkeypatch, tmp_path):
273
+ """GOOD: Combine monkeypatch and tmp_path for full isolation."""
274
+ config_path = tmp_path / "config"
275
+ config_path.mkdir()
276
+ monkeypatch.setenv("CONFIG_DIR", str(config_path))
277
+
278
+ config_file = config_path / "settings.json"
279
+ config_file.write_text('{{"debug": true}}')
280
+
281
+ assert os.environ["CONFIG_DIR"] == str(config_path)
282
+ assert config_file.exists()
283
+ # Both automatically cleaned up
284
+
285
+
286
+ def test_combined_mock_and_env(monkeypatch):
287
+ """GOOD: Combine function mocking with environment variables."""
288
+ monkeypatch.setattr(os.path, "isfile", lambda x: True)
289
+ monkeypatch.setenv("TEST_MODE", "true")
290
+
291
+ assert os.path.isfile("/any/path") is True
292
+ assert os.environ["TEST_MODE"] == "true"
293
+ # Both automatically cleaned up
294
+
295
+
296
+ # =============================================================================
297
+ # PATTERN 7: Module-Level sys.modules for Import-Time Dependencies
298
+ # =============================================================================
299
+ #
300
+ # When you need to mock modules BEFORE importing code under test
301
+ # (e.g., for decorators or top-level imports), use this pattern.
302
+ #
303
+ # This is necessary when the code under test has decorators or module-level
304
+ # imports that you need to mock. Unlike fixture-based mocking, this happens
305
+ # at test file load time, before any test functions run.
306
+ #
307
+ # CRITICAL: You MUST restore original modules after loading, or you will
308
+ # pollute sys.modules for all other test files during collection!
309
+ #
310
+ # Example usage (place at module level, outside any test function):
311
+ #
312
+ # import importlib.util
313
+ # from unittest.mock import MagicMock
314
+ #
315
+ # # Step 1: Define mocks for dependencies that need mocking at import time
316
+ # _mock_decorator = lambda f: f # Pass-through decorator
317
+ # _mock_dependency = MagicMock(some_decorator=_mock_decorator)
318
+ # _module_mocks = {{
319
+ # "some.dependency": _mock_dependency,
320
+ # }}
321
+ #
322
+ # # Step 2: Save originals BEFORE patching
323
+ # _original_modules = {{key: sys.modules.get(key) for key in _module_mocks}}
324
+ #
325
+ # # Step 3: Apply mocks to sys.modules
326
+ # sys.modules.update(_module_mocks)
327
+ #
328
+ # # Step 4: Load the module under test using importlib
329
+ # _module_path = os.path.join(os.path.dirname(__file__), "..", "src", "module.py")
330
+ # _module_path = os.path.abspath(_module_path)
331
+ # _spec = importlib.util.spec_from_file_location("my_module", _module_path)
332
+ # _module = importlib.util.module_from_spec(_spec)
333
+ # sys.modules["my_module"] = _module
334
+ # _spec.loader.exec_module(_module)
335
+ # function_to_test = _module.function_to_test
336
+ #
337
+ # # Step 5: RESTORE originals immediately after load
338
+ # # This is CRITICAL to avoid polluting other test files!
339
+ # for key, original in _original_modules.items():
340
+ # if original is None:
341
+ # sys.modules.pop(key, None)
342
+ # else:
343
+ # sys.modules[key] = original
344
+ #
345
+ # # Now you can write normal test functions using function_to_test
346
+ # def test_something():
347
+ # result = function_to_test()
348
+ # assert result == expected
349
+
350
+ </isolation_example>
351
+
352
+
353
+ <instructions>
354
+ 1. FIRST: Carefully analyze the EXAMPLE to understand:
355
+ - How to import the module (exact import statements)
356
+ - What functions/classes are exposed
357
+ - How they are called (parameters, return values)
358
+ 2. SECOND: Analyze the prompt that describes the intended functionality and edge cases.
359
+ 3. THIRD: For each edge case explain whether it is better to do the test using Z3 formal verification or unit tests.
360
+ 4. FOURTH: Develop a detailed test plan that will ensure the intended functionality is correct. This should involve both Z3 formal verification and unit tests.
361
+ 5. FIFTH: Write the test file with:
362
+ a) The first part of the test file should be the detailed test plan from step 4 above in comments.
363
+ b) Import statements matching the module structure from the example
364
+ c) Tests for the intended function names and behavior from the prompt
365
+ d) Z3 formal verification tests that are runnable as unit tests.
366
+ </instructions>