feat(media): Add comprehensive logging for media uploads - v1.4.1
Implements media upload logging per docs/design/v1.4.1/media-logging-design.md Changes: - Add logging to save_media() in starpunk/media.py: * INFO: Successful uploads with file details * WARNING: Validation/optimization/variant failures * ERROR: Unexpected system errors - Remove duplicate logging in Micropub media endpoint - Add 5 comprehensive logging tests in TestMediaLogging class - Bump version to 1.4.1 - Update CHANGELOG.md All media upload operations now logged for debugging and observability. Validation errors, optimization failures, and variant generation issues are tracked at appropriate log levels. Original functionality unchanged. Test results: 28/28 media tests pass, 5 new logging tests pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -430,6 +430,126 @@ class TestMediaSecurityEscaping:
|
||||
assert '<img' in html
|
||||
|
||||
|
||||
class TestMediaLogging:
|
||||
"""Test media upload logging (v1.4.1)"""
|
||||
|
||||
def test_save_media_logs_success(self, app, caplog):
|
||||
"""Test successful upload logs at INFO level"""
|
||||
import logging
|
||||
|
||||
image_data = create_test_image(800, 600, 'PNG')
|
||||
|
||||
with app.app_context():
|
||||
with caplog.at_level(logging.INFO):
|
||||
media_info = save_media(image_data, 'test.png')
|
||||
|
||||
# Check success log exists
|
||||
assert "Media upload successful" in caplog.text
|
||||
assert 'filename="test.png"' in caplog.text
|
||||
assert f'stored="{media_info["stored_filename"]}"' in caplog.text
|
||||
assert f'size={media_info["size"]}b' in caplog.text
|
||||
# optimized flag should be present
|
||||
assert 'optimized=' in caplog.text
|
||||
# variants count should be present
|
||||
assert 'variants=' in caplog.text
|
||||
|
||||
def test_save_media_logs_validation_failure(self, app, caplog):
|
||||
"""Test validation failure logs at WARNING level"""
|
||||
import logging
|
||||
|
||||
# Create invalid data (corrupted image)
|
||||
invalid_data = b'not an image'
|
||||
|
||||
with app.app_context():
|
||||
with caplog.at_level(logging.WARNING):
|
||||
with pytest.raises(ValueError):
|
||||
save_media(invalid_data, 'corrupt.jpg')
|
||||
|
||||
# Check validation failure log
|
||||
assert "Media upload validation failed" in caplog.text
|
||||
assert 'filename="corrupt.jpg"' in caplog.text
|
||||
assert f'size={len(invalid_data)}b' in caplog.text
|
||||
assert 'error=' in caplog.text
|
||||
|
||||
def test_save_media_logs_optimization_failure(self, app, caplog, monkeypatch):
|
||||
"""Test optimization failure logs at WARNING level"""
|
||||
import logging
|
||||
from starpunk import media
|
||||
|
||||
# Mock optimize_image to raise ValueError
|
||||
def mock_optimize_image(image_data, original_size=None):
|
||||
raise ValueError("Image cannot be optimized to target size. Please use a smaller or lower-resolution image.")
|
||||
|
||||
monkeypatch.setattr(media, 'optimize_image', mock_optimize_image)
|
||||
|
||||
image_data = create_test_image(800, 600, 'PNG')
|
||||
|
||||
with app.app_context():
|
||||
with caplog.at_level(logging.WARNING):
|
||||
with pytest.raises(ValueError):
|
||||
save_media(image_data, 'test.png')
|
||||
|
||||
# Check optimization failure log
|
||||
assert "Media upload optimization failed" in caplog.text
|
||||
assert 'filename="test.png"' in caplog.text
|
||||
assert f'size={len(image_data)}b' in caplog.text
|
||||
assert 'error=' in caplog.text
|
||||
|
||||
def test_save_media_logs_variant_failure(self, app, caplog, monkeypatch):
|
||||
"""Test variant generation failure logs at WARNING level but continues"""
|
||||
import logging
|
||||
from starpunk import media
|
||||
|
||||
# Mock generate_all_variants to raise an exception
|
||||
def mock_generate_all_variants(*args, **kwargs):
|
||||
raise RuntimeError("Variant generation failed")
|
||||
|
||||
monkeypatch.setattr(media, 'generate_all_variants', mock_generate_all_variants)
|
||||
|
||||
image_data = create_test_image(800, 600, 'PNG')
|
||||
|
||||
with app.app_context():
|
||||
with caplog.at_level(logging.INFO): # Need INFO level to capture success log
|
||||
# Should succeed despite variant failure
|
||||
media_info = save_media(image_data, 'test.png')
|
||||
|
||||
# Check variant failure log
|
||||
assert "Media upload variant generation failed" in caplog.text
|
||||
assert 'filename="test.png"' in caplog.text
|
||||
assert f'media_id={media_info["id"]}' in caplog.text
|
||||
assert 'error=' in caplog.text
|
||||
|
||||
# But success log should also be present
|
||||
assert "Media upload successful" in caplog.text
|
||||
# And variants should be 0
|
||||
assert 'variants=0' in caplog.text
|
||||
|
||||
def test_save_media_logs_unexpected_error(self, app, caplog, monkeypatch):
|
||||
"""Test unexpected error logs at ERROR level"""
|
||||
import logging
|
||||
from starpunk import media
|
||||
from pathlib import Path as OriginalPath
|
||||
|
||||
# Mock Path.write_bytes to raise OSError (simulating disk full)
|
||||
def mock_write_bytes(self, data):
|
||||
raise OSError("[Errno 28] No space left on device")
|
||||
|
||||
monkeypatch.setattr(Path, 'write_bytes', mock_write_bytes)
|
||||
|
||||
image_data = create_test_image(800, 600, 'PNG')
|
||||
|
||||
with app.app_context():
|
||||
with caplog.at_level(logging.ERROR):
|
||||
with pytest.raises(OSError):
|
||||
save_media(image_data, 'test.png')
|
||||
|
||||
# Check unexpected error log
|
||||
assert "Media upload failed unexpectedly" in caplog.text
|
||||
assert 'filename="test.png"' in caplog.text
|
||||
assert 'error_type="OSError"' in caplog.text
|
||||
assert 'error=' in caplog.text
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_note(app):
|
||||
"""Create a sample note for testing"""
|
||||
|
||||
Reference in New Issue
Block a user