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:
2025-12-16 17:22:22 -07:00
parent fd92a1d1eb
commit 07f351fef7
9 changed files with 1114 additions and 67 deletions

View File

@@ -430,6 +430,126 @@ class TestMediaSecurityEscaping:
assert '&lt;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"""