fishertools 0.2.1__py3-none-any.whl → 0.4.0__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.
- fishertools/__init__.py +16 -5
- fishertools/errors/__init__.py +11 -3
- fishertools/errors/exception_types.py +282 -0
- fishertools/errors/explainer.py +87 -1
- fishertools/errors/models.py +73 -1
- fishertools/errors/patterns.py +40 -0
- fishertools/examples/cli_example.py +156 -0
- fishertools/examples/learn_example.py +65 -0
- fishertools/examples/logger_example.py +176 -0
- fishertools/examples/menu_example.py +101 -0
- fishertools/examples/storage_example.py +175 -0
- fishertools/input_utils.py +185 -0
- fishertools/learn/__init__.py +19 -2
- fishertools/learn/examples.py +88 -1
- fishertools/learn/knowledge_engine.py +321 -0
- fishertools/learn/repl/__init__.py +19 -0
- fishertools/learn/repl/cli.py +31 -0
- fishertools/learn/repl/code_sandbox.py +229 -0
- fishertools/learn/repl/command_handler.py +544 -0
- fishertools/learn/repl/command_parser.py +165 -0
- fishertools/learn/repl/engine.py +479 -0
- fishertools/learn/repl/models.py +121 -0
- fishertools/learn/repl/session_manager.py +284 -0
- fishertools/learn/repl/test_code_sandbox.py +261 -0
- fishertools/learn/repl/test_code_sandbox_pbt.py +148 -0
- fishertools/learn/repl/test_command_handler.py +224 -0
- fishertools/learn/repl/test_command_handler_pbt.py +189 -0
- fishertools/learn/repl/test_command_parser.py +160 -0
- fishertools/learn/repl/test_command_parser_pbt.py +100 -0
- fishertools/learn/repl/test_engine.py +190 -0
- fishertools/learn/repl/test_session_manager.py +310 -0
- fishertools/learn/repl/test_session_manager_pbt.py +182 -0
- fishertools/learn/test_knowledge_engine.py +241 -0
- fishertools/learn/test_knowledge_engine_pbt.py +180 -0
- fishertools/patterns/__init__.py +46 -0
- fishertools/patterns/cli.py +175 -0
- fishertools/patterns/logger.py +140 -0
- fishertools/patterns/menu.py +99 -0
- fishertools/patterns/storage.py +127 -0
- fishertools/readme_transformer.py +631 -0
- fishertools/safe/__init__.py +6 -1
- fishertools/safe/files.py +329 -1
- fishertools/transform_readme.py +105 -0
- fishertools-0.4.0.dist-info/METADATA +104 -0
- fishertools-0.4.0.dist-info/RECORD +131 -0
- {fishertools-0.2.1.dist-info → fishertools-0.4.0.dist-info}/WHEEL +1 -1
- tests/test_documentation_properties.py +329 -0
- tests/test_documentation_structure.py +349 -0
- tests/test_errors/test_exception_types.py +446 -0
- tests/test_errors/test_exception_types_pbt.py +333 -0
- tests/test_errors/test_patterns.py +52 -0
- tests/test_input_utils/__init__.py +1 -0
- tests/test_input_utils/test_input_utils.py +65 -0
- tests/test_learn/test_examples.py +179 -1
- tests/test_learn/test_explain_properties.py +307 -0
- tests/test_patterns_cli.py +611 -0
- tests/test_patterns_docstrings.py +473 -0
- tests/test_patterns_logger.py +465 -0
- tests/test_patterns_menu.py +440 -0
- tests/test_patterns_storage.py +447 -0
- tests/test_readme_enhancements_v0_3_1.py +2036 -0
- tests/test_readme_transformer/__init__.py +1 -0
- tests/test_readme_transformer/test_readme_infrastructure.py +1023 -0
- tests/test_readme_transformer/test_transform_readme_integration.py +431 -0
- tests/test_safe/test_files.py +726 -1
- fishertools-0.2.1.dist-info/METADATA +0 -256
- fishertools-0.2.1.dist-info/RECORD +0 -81
- {fishertools-0.2.1.dist-info → fishertools-0.4.0.dist-info}/licenses/LICENSE +0 -0
- {fishertools-0.2.1.dist-info → fishertools-0.4.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Property-based tests for the SimpleLogger class in fishertools.patterns.
|
|
3
|
+
|
|
4
|
+
Tests the correctness properties of the SimpleLogger class using hypothesis
|
|
5
|
+
for property-based testing.
|
|
6
|
+
|
|
7
|
+
**Validates: Requirements 10.2, 10.3, 10.5**
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
import os
|
|
12
|
+
import tempfile
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
from hypothesis import given, strategies as st, assume
|
|
15
|
+
|
|
16
|
+
from fishertools.patterns.logger import SimpleLogger
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class TestSimpleLoggerWritesMessages:
|
|
20
|
+
"""
|
|
21
|
+
Property 6: SimpleLogger Writes Messages
|
|
22
|
+
|
|
23
|
+
For any message and log level, calling the appropriate logging method should
|
|
24
|
+
result in the message being written to the log file with the correct level
|
|
25
|
+
and a timestamp.
|
|
26
|
+
|
|
27
|
+
**Validates: Requirements 10.2, 10.3**
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def test_info_writes_message(self):
|
|
31
|
+
"""Test that info() writes a message to the log file."""
|
|
32
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
33
|
+
log_path = os.path.join(tmpdir, "test.log")
|
|
34
|
+
logger = SimpleLogger(log_path)
|
|
35
|
+
|
|
36
|
+
logger.info("Test info message")
|
|
37
|
+
|
|
38
|
+
# Read the log file
|
|
39
|
+
with open(log_path, 'r') as f:
|
|
40
|
+
content = f.read()
|
|
41
|
+
|
|
42
|
+
assert "Test info message" in content
|
|
43
|
+
assert "[INFO]" in content
|
|
44
|
+
|
|
45
|
+
def test_warning_writes_message(self):
|
|
46
|
+
"""Test that warning() writes a message to the log file."""
|
|
47
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
48
|
+
log_path = os.path.join(tmpdir, "test.log")
|
|
49
|
+
logger = SimpleLogger(log_path)
|
|
50
|
+
|
|
51
|
+
logger.warning("Test warning message")
|
|
52
|
+
|
|
53
|
+
# Read the log file
|
|
54
|
+
with open(log_path, 'r') as f:
|
|
55
|
+
content = f.read()
|
|
56
|
+
|
|
57
|
+
assert "Test warning message" in content
|
|
58
|
+
assert "[WARNING]" in content
|
|
59
|
+
|
|
60
|
+
def test_error_writes_message(self):
|
|
61
|
+
"""Test that error() writes a message to the log file."""
|
|
62
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
63
|
+
log_path = os.path.join(tmpdir, "test.log")
|
|
64
|
+
logger = SimpleLogger(log_path)
|
|
65
|
+
|
|
66
|
+
logger.error("Test error message")
|
|
67
|
+
|
|
68
|
+
# Read the log file
|
|
69
|
+
with open(log_path, 'r') as f:
|
|
70
|
+
content = f.read()
|
|
71
|
+
|
|
72
|
+
assert "Test error message" in content
|
|
73
|
+
assert "[ERROR]" in content
|
|
74
|
+
|
|
75
|
+
def test_message_includes_timestamp(self):
|
|
76
|
+
"""Test that logged messages include a timestamp."""
|
|
77
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
78
|
+
log_path = os.path.join(tmpdir, "test.log")
|
|
79
|
+
logger = SimpleLogger(log_path)
|
|
80
|
+
|
|
81
|
+
logger.info("Test message")
|
|
82
|
+
|
|
83
|
+
# Read the log file
|
|
84
|
+
with open(log_path, 'r') as f:
|
|
85
|
+
content = f.read()
|
|
86
|
+
|
|
87
|
+
# Check for timestamp format YYYY-MM-DD HH:MM:SS
|
|
88
|
+
import re
|
|
89
|
+
timestamp_pattern = r'\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\]'
|
|
90
|
+
assert re.search(timestamp_pattern, content)
|
|
91
|
+
|
|
92
|
+
def test_message_format_is_correct(self):
|
|
93
|
+
"""Test that the log message format is [TIMESTAMP] [LEVEL] message."""
|
|
94
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
95
|
+
log_path = os.path.join(tmpdir, "test.log")
|
|
96
|
+
logger = SimpleLogger(log_path)
|
|
97
|
+
|
|
98
|
+
logger.info("Test message")
|
|
99
|
+
|
|
100
|
+
# Read the log file
|
|
101
|
+
with open(log_path, 'r') as f:
|
|
102
|
+
content = f.read().strip()
|
|
103
|
+
|
|
104
|
+
# Check format: [TIMESTAMP] [LEVEL] message
|
|
105
|
+
import re
|
|
106
|
+
pattern = r'\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\] \[INFO\] Test message'
|
|
107
|
+
assert re.match(pattern, content)
|
|
108
|
+
|
|
109
|
+
def test_multiple_messages_are_appended(self):
|
|
110
|
+
"""Test that multiple messages are appended to the log file."""
|
|
111
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
112
|
+
log_path = os.path.join(tmpdir, "test.log")
|
|
113
|
+
logger = SimpleLogger(log_path)
|
|
114
|
+
|
|
115
|
+
logger.info("First message")
|
|
116
|
+
logger.warning("Second message")
|
|
117
|
+
logger.error("Third message")
|
|
118
|
+
|
|
119
|
+
# Read the log file
|
|
120
|
+
with open(log_path, 'r') as f:
|
|
121
|
+
content = f.read()
|
|
122
|
+
|
|
123
|
+
assert "First message" in content
|
|
124
|
+
assert "Second message" in content
|
|
125
|
+
assert "Third message" in content
|
|
126
|
+
|
|
127
|
+
# Count lines
|
|
128
|
+
lines = content.strip().split('\n')
|
|
129
|
+
assert len(lines) == 3
|
|
130
|
+
|
|
131
|
+
def test_messages_with_special_characters(self):
|
|
132
|
+
"""Test that messages with special characters are logged correctly."""
|
|
133
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
134
|
+
log_path = os.path.join(tmpdir, "test.log")
|
|
135
|
+
logger = SimpleLogger(log_path)
|
|
136
|
+
|
|
137
|
+
special_message = "Message with special chars: !@#$%^&*()"
|
|
138
|
+
logger.info(special_message)
|
|
139
|
+
|
|
140
|
+
# Read the log file
|
|
141
|
+
with open(log_path, 'r') as f:
|
|
142
|
+
content = f.read()
|
|
143
|
+
|
|
144
|
+
assert special_message in content
|
|
145
|
+
|
|
146
|
+
def test_messages_with_unicode(self):
|
|
147
|
+
"""Test that messages with unicode characters are logged correctly."""
|
|
148
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
149
|
+
log_path = os.path.join(tmpdir, "test.log")
|
|
150
|
+
logger = SimpleLogger(log_path)
|
|
151
|
+
|
|
152
|
+
unicode_message = "Message with unicode: 你好世界 🎉"
|
|
153
|
+
logger.info(unicode_message)
|
|
154
|
+
|
|
155
|
+
# Read the log file with UTF-8 encoding
|
|
156
|
+
with open(log_path, 'r', encoding='utf-8') as f:
|
|
157
|
+
content = f.read()
|
|
158
|
+
|
|
159
|
+
assert unicode_message in content
|
|
160
|
+
|
|
161
|
+
def test_messages_with_newlines(self):
|
|
162
|
+
"""Test that messages with newlines are logged correctly."""
|
|
163
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
164
|
+
log_path = os.path.join(tmpdir, "test.log")
|
|
165
|
+
logger = SimpleLogger(log_path)
|
|
166
|
+
|
|
167
|
+
message_with_newline = "Line 1\nLine 2"
|
|
168
|
+
logger.info(message_with_newline)
|
|
169
|
+
|
|
170
|
+
# Read the log file
|
|
171
|
+
with open(log_path, 'r') as f:
|
|
172
|
+
content = f.read()
|
|
173
|
+
|
|
174
|
+
assert message_with_newline in content
|
|
175
|
+
|
|
176
|
+
def test_empty_message(self):
|
|
177
|
+
"""Test that empty messages are logged correctly."""
|
|
178
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
179
|
+
log_path = os.path.join(tmpdir, "test.log")
|
|
180
|
+
logger = SimpleLogger(log_path)
|
|
181
|
+
|
|
182
|
+
logger.info("")
|
|
183
|
+
|
|
184
|
+
# Read the log file
|
|
185
|
+
with open(log_path, 'r') as f:
|
|
186
|
+
content = f.read()
|
|
187
|
+
|
|
188
|
+
# Should have a log entry with empty message
|
|
189
|
+
assert "[INFO]" in content
|
|
190
|
+
|
|
191
|
+
@given(st.text(min_size=0, max_size=500, alphabet=st.characters(blacklist_categories=('Cc', 'Cs'))))
|
|
192
|
+
def test_any_message_is_logged(self, message):
|
|
193
|
+
"""
|
|
194
|
+
Property: For any message string, calling info() should write it to
|
|
195
|
+
the log file with the correct format.
|
|
196
|
+
"""
|
|
197
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
198
|
+
log_path = os.path.join(tmpdir, "test.log")
|
|
199
|
+
logger = SimpleLogger(log_path)
|
|
200
|
+
|
|
201
|
+
logger.info(message)
|
|
202
|
+
|
|
203
|
+
# Read the log file with UTF-8 encoding
|
|
204
|
+
with open(log_path, 'r', encoding='utf-8') as f:
|
|
205
|
+
content = f.read()
|
|
206
|
+
|
|
207
|
+
# Message should be in the log
|
|
208
|
+
assert message in content
|
|
209
|
+
# Should have the correct level
|
|
210
|
+
assert "[INFO]" in content
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class TestSimpleLoggerCreatesFile:
|
|
214
|
+
"""
|
|
215
|
+
Property 7: SimpleLogger Creates File
|
|
216
|
+
|
|
217
|
+
For any log file path that doesn't exist, SimpleLogger should create the
|
|
218
|
+
file when the first message is logged.
|
|
219
|
+
|
|
220
|
+
**Validates: Requirements 10.5**
|
|
221
|
+
"""
|
|
222
|
+
|
|
223
|
+
def test_creates_file_on_first_write(self):
|
|
224
|
+
"""Test that SimpleLogger creates the log file on first write."""
|
|
225
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
226
|
+
log_path = os.path.join(tmpdir, "test.log")
|
|
227
|
+
|
|
228
|
+
# File should not exist yet
|
|
229
|
+
assert not os.path.exists(log_path)
|
|
230
|
+
|
|
231
|
+
logger = SimpleLogger(log_path)
|
|
232
|
+
logger.info("First message")
|
|
233
|
+
|
|
234
|
+
# File should now exist
|
|
235
|
+
assert os.path.exists(log_path)
|
|
236
|
+
assert os.path.isfile(log_path)
|
|
237
|
+
|
|
238
|
+
def test_creates_parent_directories(self):
|
|
239
|
+
"""Test that SimpleLogger creates parent directories if needed."""
|
|
240
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
241
|
+
log_path = os.path.join(tmpdir, "logs", "app", "test.log")
|
|
242
|
+
|
|
243
|
+
# Parent directories should not exist
|
|
244
|
+
assert not os.path.exists(os.path.dirname(log_path))
|
|
245
|
+
|
|
246
|
+
logger = SimpleLogger(log_path)
|
|
247
|
+
logger.info("Test message")
|
|
248
|
+
|
|
249
|
+
# Parent directories should now exist
|
|
250
|
+
assert os.path.exists(os.path.dirname(log_path))
|
|
251
|
+
assert os.path.isfile(log_path)
|
|
252
|
+
|
|
253
|
+
def test_creates_single_parent_directory(self):
|
|
254
|
+
"""Test that SimpleLogger creates a single parent directory."""
|
|
255
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
256
|
+
log_path = os.path.join(tmpdir, "logs", "test.log")
|
|
257
|
+
|
|
258
|
+
logger = SimpleLogger(log_path)
|
|
259
|
+
logger.info("Test message")
|
|
260
|
+
|
|
261
|
+
assert os.path.exists(log_path)
|
|
262
|
+
|
|
263
|
+
def test_creates_deeply_nested_directories(self):
|
|
264
|
+
"""Test that SimpleLogger creates deeply nested directories."""
|
|
265
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
266
|
+
log_path = os.path.join(tmpdir, "a", "b", "c", "d", "e", "test.log")
|
|
267
|
+
|
|
268
|
+
logger = SimpleLogger(log_path)
|
|
269
|
+
logger.info("Test message")
|
|
270
|
+
|
|
271
|
+
assert os.path.exists(log_path)
|
|
272
|
+
|
|
273
|
+
def test_does_not_fail_if_directory_exists(self):
|
|
274
|
+
"""Test that SimpleLogger doesn't fail if directory already exists."""
|
|
275
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
276
|
+
log_dir = os.path.join(tmpdir, "logs")
|
|
277
|
+
os.makedirs(log_dir)
|
|
278
|
+
|
|
279
|
+
log_path = os.path.join(log_dir, "test.log")
|
|
280
|
+
|
|
281
|
+
logger = SimpleLogger(log_path)
|
|
282
|
+
logger.info("Test message")
|
|
283
|
+
|
|
284
|
+
assert os.path.exists(log_path)
|
|
285
|
+
|
|
286
|
+
def test_appends_to_existing_file(self):
|
|
287
|
+
"""Test that SimpleLogger appends to an existing log file."""
|
|
288
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
289
|
+
log_path = os.path.join(tmpdir, "test.log")
|
|
290
|
+
|
|
291
|
+
# Create initial log file with content
|
|
292
|
+
with open(log_path, 'w') as f:
|
|
293
|
+
f.write("Initial content\n")
|
|
294
|
+
|
|
295
|
+
logger = SimpleLogger(log_path)
|
|
296
|
+
logger.info("New message")
|
|
297
|
+
|
|
298
|
+
# Read the log file
|
|
299
|
+
with open(log_path, 'r') as f:
|
|
300
|
+
content = f.read()
|
|
301
|
+
|
|
302
|
+
# Both messages should be present
|
|
303
|
+
assert "Initial content" in content
|
|
304
|
+
assert "New message" in content
|
|
305
|
+
|
|
306
|
+
def test_file_path_attribute_is_set(self):
|
|
307
|
+
"""Test that file_path attribute is set correctly."""
|
|
308
|
+
log_path = "test/path/app.log"
|
|
309
|
+
logger = SimpleLogger(log_path)
|
|
310
|
+
|
|
311
|
+
assert logger.file_path == log_path
|
|
312
|
+
|
|
313
|
+
@given(st.lists(st.text(
|
|
314
|
+
alphabet="abcdefghijklmnopqrstuvwxyz0123456789_-",
|
|
315
|
+
min_size=1,
|
|
316
|
+
max_size=20
|
|
317
|
+
), min_size=1, max_size=5))
|
|
318
|
+
def test_creates_file_for_any_path(self, path_parts):
|
|
319
|
+
"""
|
|
320
|
+
Property: For any valid path with non-existent parent directories,
|
|
321
|
+
SimpleLogger should create the file when logging.
|
|
322
|
+
"""
|
|
323
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
324
|
+
# Build a nested path
|
|
325
|
+
nested_path = os.path.join(tmpdir, *path_parts)
|
|
326
|
+
log_path = os.path.join(nested_path, "test.log")
|
|
327
|
+
|
|
328
|
+
logger = SimpleLogger(log_path)
|
|
329
|
+
logger.info("Test message")
|
|
330
|
+
|
|
331
|
+
# File should exist
|
|
332
|
+
assert os.path.exists(log_path)
|
|
333
|
+
assert os.path.isfile(log_path)
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
class TestSimpleLoggerIntegration:
|
|
337
|
+
"""Integration tests for SimpleLogger."""
|
|
338
|
+
|
|
339
|
+
def test_multiple_loggers_same_file(self):
|
|
340
|
+
"""Test that multiple logger instances can write to the same file."""
|
|
341
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
342
|
+
log_path = os.path.join(tmpdir, "shared.log")
|
|
343
|
+
|
|
344
|
+
logger1 = SimpleLogger(log_path)
|
|
345
|
+
logger1.info("Message from logger 1")
|
|
346
|
+
|
|
347
|
+
logger2 = SimpleLogger(log_path)
|
|
348
|
+
logger2.warning("Message from logger 2")
|
|
349
|
+
|
|
350
|
+
# Read the log file
|
|
351
|
+
with open(log_path, 'r') as f:
|
|
352
|
+
content = f.read()
|
|
353
|
+
|
|
354
|
+
assert "Message from logger 1" in content
|
|
355
|
+
assert "Message from logger 2" in content
|
|
356
|
+
|
|
357
|
+
def test_all_log_levels(self):
|
|
358
|
+
"""Test that all log levels work correctly."""
|
|
359
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
360
|
+
log_path = os.path.join(tmpdir, "test.log")
|
|
361
|
+
logger = SimpleLogger(log_path)
|
|
362
|
+
|
|
363
|
+
logger.info("Info message")
|
|
364
|
+
logger.warning("Warning message")
|
|
365
|
+
logger.error("Error message")
|
|
366
|
+
|
|
367
|
+
# Read the log file
|
|
368
|
+
with open(log_path, 'r') as f:
|
|
369
|
+
content = f.read()
|
|
370
|
+
|
|
371
|
+
assert "[INFO]" in content
|
|
372
|
+
assert "[WARNING]" in content
|
|
373
|
+
assert "[ERROR]" in content
|
|
374
|
+
assert "Info message" in content
|
|
375
|
+
assert "Warning message" in content
|
|
376
|
+
assert "Error message" in content
|
|
377
|
+
|
|
378
|
+
def test_logger_with_relative_path(self):
|
|
379
|
+
"""Test that SimpleLogger works with relative paths."""
|
|
380
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
381
|
+
original_cwd = os.getcwd()
|
|
382
|
+
try:
|
|
383
|
+
os.chdir(tmpdir)
|
|
384
|
+
|
|
385
|
+
logger = SimpleLogger("test.log")
|
|
386
|
+
logger.info("Test message")
|
|
387
|
+
|
|
388
|
+
assert os.path.exists("test.log")
|
|
389
|
+
|
|
390
|
+
with open("test.log", 'r') as f:
|
|
391
|
+
content = f.read()
|
|
392
|
+
|
|
393
|
+
assert "Test message" in content
|
|
394
|
+
finally:
|
|
395
|
+
os.chdir(original_cwd)
|
|
396
|
+
|
|
397
|
+
def test_logger_with_absolute_path(self):
|
|
398
|
+
"""Test that SimpleLogger works with absolute paths."""
|
|
399
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
400
|
+
log_path = os.path.abspath(os.path.join(tmpdir, "test.log"))
|
|
401
|
+
logger = SimpleLogger(log_path)
|
|
402
|
+
|
|
403
|
+
logger.info("Test message")
|
|
404
|
+
|
|
405
|
+
assert os.path.exists(log_path)
|
|
406
|
+
|
|
407
|
+
def test_logger_preserves_message_content(self):
|
|
408
|
+
"""Test that SimpleLogger preserves exact message content."""
|
|
409
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
410
|
+
log_path = os.path.join(tmpdir, "test.log")
|
|
411
|
+
logger = SimpleLogger(log_path)
|
|
412
|
+
|
|
413
|
+
messages = [
|
|
414
|
+
"Simple message",
|
|
415
|
+
"Message with numbers 12345",
|
|
416
|
+
"Message with symbols !@#$%",
|
|
417
|
+
"Message with quotes 'single' and \"double\"",
|
|
418
|
+
"Message with tabs\tand\tspaces"
|
|
419
|
+
]
|
|
420
|
+
|
|
421
|
+
for msg in messages:
|
|
422
|
+
logger.info(msg)
|
|
423
|
+
|
|
424
|
+
# Read the log file
|
|
425
|
+
with open(log_path, 'r') as f:
|
|
426
|
+
content = f.read()
|
|
427
|
+
|
|
428
|
+
# All messages should be preserved
|
|
429
|
+
for msg in messages:
|
|
430
|
+
assert msg in content
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
class TestSimpleLoggerErrorHandling:
|
|
434
|
+
"""Test error handling in SimpleLogger."""
|
|
435
|
+
|
|
436
|
+
def test_raises_ioerror_for_invalid_path(self):
|
|
437
|
+
"""Test that SimpleLogger raises an error for invalid paths."""
|
|
438
|
+
# Use a path that will fail during file operations
|
|
439
|
+
# On Windows, we can use a reserved device name
|
|
440
|
+
invalid_path = "CON" # Reserved device name on Windows
|
|
441
|
+
logger = SimpleLogger(invalid_path)
|
|
442
|
+
|
|
443
|
+
with pytest.raises((IOError, OSError, ValueError)):
|
|
444
|
+
logger.info("Test message")
|
|
445
|
+
|
|
446
|
+
def test_raises_ioerror_for_permission_denied(self):
|
|
447
|
+
"""Test that SimpleLogger raises IOError for permission denied."""
|
|
448
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
449
|
+
log_path = os.path.join(tmpdir, "test.log")
|
|
450
|
+
logger = SimpleLogger(log_path)
|
|
451
|
+
|
|
452
|
+
# Create the file
|
|
453
|
+
logger.info("Initial message")
|
|
454
|
+
|
|
455
|
+
# Make the file read-only
|
|
456
|
+
os.chmod(log_path, 0o444)
|
|
457
|
+
|
|
458
|
+
try:
|
|
459
|
+
# Try to write to read-only file
|
|
460
|
+
with pytest.raises(IOError):
|
|
461
|
+
logger.info("Another message")
|
|
462
|
+
finally:
|
|
463
|
+
# Restore permissions for cleanup
|
|
464
|
+
os.chmod(log_path, 0o644)
|
|
465
|
+
|