# Phase 2: TDD Implementation Roadmap **Status:** In Progress **Start:** 2026-05-29 **Target:** All 64 tests passing ✅ ## TDD Workflow (Golden Rules) 1. **RED**: Write failing test (shows missing implementation) 2. **GREEN**: Write minimum code to make it pass 3. **REFACTOR**: Clean up while test stays green 4. **REPEAT**: Next test, next feature ## Implementation Order (Bottom-Up) ### Layer 1: Models (Core Data) - [ ] `itsulu.blog.topic` — topic queue management - [ ] `test_topic_is_created_with_pending_state` - [ ] `test_get_next_topic_returns_highest_priority_pending_topic` - [ ] `test_get_next_topic_returns_none_when_queue_is_empty` - [ ] `test_mark_topic_as_used_changes_state` - [ ] `test_topic_can_be_linked_to_a_specific_blog` - [ ] `test_topic_name_cannot_be_empty` - [ ] `test_used_topic_is_excluded_from_next_topic_selection` - [ ] `itsulu.blog.generation.log` — execution audit trail - [ ] `test_successful_log_record_is_created_with_correct_fields` - [ ] `test_error_log_has_no_linked_blog_post` - [ ] `test_error_log_record_stores_human_readable_error_message` - [ ] `test_log_trigger_source_can_be_scheduled` - [ ] `test_log_trigger_source_records_slot_name_for_scheduled_runs` - [ ] `test_tokens_used_defaults_to_zero_for_error_logs` - [ ] `test_error_log_action_retry_returns_wizard_action` - [ ] `test_success_log_does_not_expose_retry_button` - [ ] `itsulu.blog.post.social` — social media copy - [ ] `test_social_record_is_linked_one_to_one_with_blog_post` - [ ] `test_twitter_posts_stored_and_retrievable` - [ ] `test_twitter_posts_are_within_character_limit` - [ ] `test_linkedin_post_is_at_least_150_characters` - [ ] `test_mastodon_post_is_within_character_limit` - [ ] `itsulu.blog.schedule` — cron slot configuration - [ ] `test_schedule_slot_is_created_with_correct_defaults` - [ ] `test_disabled_schedule_slot_has_active_false` - [ ] `test_three_distinct_slot_values_are_valid` - [ ] `test_active_slot_run_creates_blog_post` - [ ] `test_active_slot_run_creates_generation_log_with_success_state` - [ ] `test_auto_publish_true_publishes_the_created_blog_post` - [ ] `test_auto_publish_false_leaves_the_created_blog_post_as_draft` - [ ] `test_inactive_slot_run_does_not_create_blog_post` - [ ] `test_slot_picks_next_topic_from_queue_when_available` - [ ] `test_slot_falls_back_to_llm_chosen_topic_when_queue_is_empty` ### Layer 2: Services (LLM Integration) - [ ] `llm_router.py` — dispatcher to provider APIs - [ ] `test_router_response_includes_tokens_used` - [ ] `test_anthropic_provider_calls_anthropic_endpoint` - [ ] `test_openai_provider_calls_openai_endpoint` - [ ] `test_gemini_provider_calls_gemini_endpoint` - [ ] `test_ollama_provider_calls_local_api_endpoint` - [ ] `test_unknown_provider_raises_user_error` - [ ] `test_missing_api_key_raises_user_error` ### Layer 3: Integration (E2E Flows) - [ ] Generation orchestration - [ ] `test_generate_and_autopublish_a_blog_post_from_the_backend` - [ ] `test_generate_a_blog_post_and_leave_it_as_draft` - [ ] `test_llm_api_call_fails_gracefully` - [ ] `test_active_morning_slot_creates_a_blog_post_when_run` - [ ] SEO population - [ ] `test_generated_post_has_non_empty_meta_title` - [ ] `test_generated_post_has_non_empty_meta_description` - [ ] `test_generated_post_has_meta_keywords` - [ ] `test_generated_post_has_at_least_two_tags` - [ ] `test_new_tags_from_llm_are_created_automatically` - [ ] `test_seo_title_is_not_identical_to_post_title` - [ ] Notification email - [ ] `test_notification_email_is_sent_after_auto_publish` - [ ] `test_notification_email_is_not_sent_for_draft_posts` - [ ] `test_notification_email_subject_matches_expected_format` - [ ] `test_notification_email_body_contains_post_url` - [ ] `test_notification_email_body_contains_all_social_platforms` - [ ] Provider-specific tests - [ ] `test_anthropic_provider_generates_blog_content` - [ ] `test_openai_provider_generates_blog_content` - [ ] `test_gemini_provider_generates_blog_content` - [ ] `test_ollama_provider_generates_blog_content_using_local_model` ## Daily TDD Rhythm ``` Morning (RED): - Pick next failing test - Read test specification - Understand what model/method is missing Midday (GREEN): - Write minimum code to pass the test - Run test locally (K8s job): pytest addons/itsulu_blog_publisher/tests -k -v - If fails, iterate Afternoon (REFACTOR): - Clean up code style - Run full test suite: pytest addons/itsulu_blog_publisher/tests/ -v - Pre-commit: black, isort, pylint-odoo - Commit with message: "feat: implement (test: )" ``` ## Success Criteria ✅ **Phase 2 Complete when:** - [ ] All 64 tests passing - [ ] Coverage ≥ 80% on new code - [ ] Pre-commit hooks pass - [ ] CI pipeline green (lint + test + build) - [ ] README updated with feature list - [ ] Ready for Phase 3 (Runboat E2E, performance testing) ## Current Progress - Infrastructure: ✅ Complete (K8s, Docker, CI/CD) - Template DB: 🔄 Priming in progress - Model implementations: 0/64 tests passing - Code coverage: 0% --- **Next Step:** Verify template DB has models installed, then start with `test_blog_topic.py` (simplest model).