Focus: Cleanup, test coverage, and quality of life improvements Goals: - 90% test coverage target - MPO format test coverage (High backlog item) - Debug file storage cleanup (Medium backlog item) - Filename sanitization in debug path (Medium backlog item) - N+1 query pattern fix (Medium backlog item) - Atomic variant generation (Medium backlog item) - Default slug change to timestamp format (Medium backlog item) Backlog items marked as scheduled for v1.5.0. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
7.3 KiB
StarPunk v1.5.0 Release
Status: Planning Codename: "Trigger" Focus: Cleanup, Test Coverage, and Quality of Life Improvements
Overview
This minor release focuses on technical debt reduction, improving test coverage to 90%, and addressing quality-of-life issues identified in previous release reviews. No new user-facing features are planned.
Goals
- 90% Test Coverage - Increase overall test coverage from current baseline to 90%
- Address Backlog Technical Debt - Resolve 6 specific backlog items
- Improve Code Quality - Better error handling, atomicity, and performance
Features
1. Test Coverage Target: 90%
Increase overall test coverage to 90% minimum across all modules.
Scope
- Identify modules with coverage below 90%
- Write missing unit tests for uncovered code paths
- Add integration tests for critical workflows
- Ensure edge cases and error paths are tested
Acceptance Criteria
uv run pytest --cov=starpunkreports ≥90% overall coverage- No module below 85% coverage
- All new code in this release has 100% coverage
2. MPO Format Test Coverage
Source: Backlog - High Priority (Developer Review M1)
Add test coverage for MPO (Multi-Picture Object) format handling.
Current State
- MPO conversion code exists at
starpunk/media.pylines 163-173 - Advertised in CHANGELOG but untested
Implementation
- Add
test_mpo_detection_and_conversion()toTestHEICSupportclass - Create MPO test image using Pillow's MPO support
- Test primary image extraction
- Test conversion to JPEG
Acceptance Criteria
- MPO detection tested
- MPO to JPEG conversion tested
- Edge cases (corrupted MPO, single-frame MPO) tested
3. Debug File Storage Cleanup
Source: Backlog - Medium Priority (Developer Review M2, Architect Review 1.2.2)
Implement cleanup mechanism for failed upload debug files.
Current State
- Failed uploads saved to
data/debug/directory - No cleanup mechanism exists
- Potential disk space exhaustion
Implementation
- Add
DEBUG_SAVE_FAILED_UPLOADSconfig option (default:falsein production) - Implement automatic cleanup for files older than 7 days
- Add disk space check or size limit (100MB max for debug folder)
- Cleanup runs on application startup and periodically
Configuration
DEBUG_SAVE_FAILED_UPLOADS = False # Enable only for debugging
DEBUG_FILE_MAX_AGE_DAYS = 7 # Auto-delete after 7 days
DEBUG_FILE_MAX_SIZE_MB = 100 # Maximum debug folder size
Acceptance Criteria
- Debug files disabled by default in production
- Old files automatically cleaned up when enabled
- Disk space protected with size limit
- Tests cover all cleanup scenarios
4. Filename Sanitization in Debug Path
Source: Backlog - Medium Priority (Architect Review 1.2.3)
Sanitize filenames before use in debug file paths.
Current State
- Original filename used directly at
starpunk/media.pyline 135 - Path traversal or special character issues possible
Implementation
safe_filename = "".join(c for c in filename if c.isalnum() or c in "._-")[:50]
Acceptance Criteria
- Filenames sanitized before debug path construction
- Path traversal attempts neutralized
- Special characters removed
- Filename length limited
- Tests cover malicious filename patterns
5. N+1 Query Pattern Fix
Source: Backlog - Medium Priority (Architect Review 2.2.9)
Eliminate N+1 query pattern in feed generation.
Current State
_get_cached_notes()loads media and tags per-note- For 50 notes: 100 additional database queries
- Performance degrades with note count
Implementation
- Create
get_media_for_notes(note_ids: List[int])batch function - Create
get_tags_for_notes(note_ids: List[int])batch function - Use single query with
WHERE note_id IN (...) - Update
_get_cached_notes()to use batch loading
Example
def get_media_for_notes(note_ids: List[int]) -> Dict[int, List[dict]]:
"""Batch load media for multiple notes."""
# Single query: SELECT * FROM note_media WHERE note_id IN (?)
# Returns: {note_id: [media_list]}
Acceptance Criteria
- Feed generation uses batch queries
- Query count reduced from O(n) to O(1) for media/tags
- Performance improvement measurable in tests
- No change to API behavior
6. Atomic Variant Generation
Source: Backlog - Medium Priority (Architect Review 2.2.6)
Make variant file generation atomic with database commits.
Current State
- Files written to disk before database commit
- Database commit failure leaves orphaned files
Implementation
- Write variant files to temporary location first
- Database insert in transaction
- Move files to final location after successful commit
- Cleanup temp files on any failure
Flow
1. Generate variants to temp directory
2. BEGIN TRANSACTION
3. INSERT media record
4. INSERT variant records
5. COMMIT
6. Move files from temp to final location
7. On failure: rollback DB, delete temp files
Acceptance Criteria
- No orphaned files on database failures
- No orphaned database records on file failures
- Atomic operation for all media saves
- Tests simulate failure scenarios
7. Default Slug Format Change
Source: Backlog - Medium Priority
Change default slug format from content-based to timestamp-based.
Current State
- Slugs generated from note content
- Can produce unwanted slugs from private content
New Format
- Default:
YYYYMMDDHHMMSS(e.g.,20251216143052) - Duplicate handling: append
-1,-2, etc. - Custom slugs still supported via
mp-slug
Implementation
- Update
generate_slug()function instarpunk/slug_utils.py - Generate timestamp-based slug by default
- Check for duplicates and append suffix if needed
- Preserve custom slug functionality
Example
def generate_slug(content: str = None, custom_slug: str = None) -> str:
if custom_slug:
return sanitize_slug(custom_slug)
# Default: timestamp-based
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
return ensure_unique_slug(timestamp)
Acceptance Criteria
- New notes use timestamp slugs by default
- Duplicate timestamps handled with suffix
- Custom
mp-slugstill works - Existing notes unchanged
- Tests cover all slug generation scenarios
Out of Scope
- New user-facing features
- UI changes
- New feed formats
- Micropub extensions
- Database schema changes (except for bug fixes)
Dependencies
- v1.4.x complete
- No new external dependencies expected
Success Criteria
- ✅ Test coverage ≥90% overall
- ✅ MPO format has test coverage
- ✅ Debug file cleanup implemented and tested
- ✅ Filename sanitization implemented and tested
- ✅ N+1 query pattern eliminated
- ✅ Variant generation is atomic
- ✅ Default slugs are timestamp-based
- ✅ All existing tests continue to pass
- ✅ No regressions in functionality
Related Backlog Items
After completion, the following backlog items should be marked complete or moved to "Recently Completed":
- MPO Format Test Coverage (High)
- Debug File Storage Without Cleanup Mechanism (Medium)
- Filename Not Sanitized in Debug Path (Medium)
- N+1 Query Pattern in Feed Generation (Medium)
- Transaction Not Atomic in Variant Generation (Medium)
- Default Slug Change (Medium)