- Split monolithic blog_generation.feature into separate files per feature: * blog_generation.feature: On-demand AI blog generation (3 scenarios) * blog_scheduling.feature: Scheduled posts (2 scenarios) * llm_provider_selection.feature: Multi-provider routing (6 scenarios) * seo_population.feature: SEO field population (1 scenario) * notification_email.feature: Post-generation emails (2 scenarios) Total: 14 BDD scenarios covering all major workflows - Extended test_bdd_steps.py from 363 to 472 lines with new step definitions: * Added no_email_sent() for draft post email suppression verification * Added email_contains_title() for email content validation * Added email_contains_social_copy() for platform copy verification * Added blog_post_has_tags(), blog_post_has_tag() for tag verification * Added blog_post_has_social_copy(), at_least_one_platform_enabled() * Added log_has_correct_provider(), log_has_correct_model() * Added log_trigger_source(), generation_duration_recorded() Follows pytest-bdd best practices: one feature per file, each with dedicated scenarios and step definitions. All 14 scenarios now have complete step coverage. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
46 lines
2 KiB
Gherkin
46 lines
2 KiB
Gherkin
# =================================================================
|
|
# Multi-provider LLM routing
|
|
# =================================================================
|
|
Feature: Multi-provider LLM routing
|
|
As an ITSulu admin
|
|
I want to choose which LLM provider generates each blog post
|
|
So that I can control cost, quality, and availability
|
|
|
|
Background:
|
|
Given the Anthropic API key is configured in Settings
|
|
And the blog "ITSulu Insights" exists in Odoo
|
|
|
|
Scenario: Anthropic provider generates blog content
|
|
Given provider is "anthropic" and model is "claude-sonnet-4-20250514"
|
|
When the LLM router is called with a prompt
|
|
Then the router calls the Anthropic /v1/messages endpoint
|
|
And returns a non-empty string response
|
|
|
|
Scenario: OpenAI provider generates blog content
|
|
Given provider is "openai" and model is "gpt-4o"
|
|
When the LLM router is called with a prompt
|
|
Then the router calls the OpenAI /v1/chat/completions endpoint
|
|
And returns a non-empty string response
|
|
|
|
Scenario: Gemini provider generates blog content
|
|
Given provider is "gemini" and model is "gemini-2.0-flash"
|
|
When the LLM router is called with a prompt
|
|
Then the router calls the Google Gemini API endpoint
|
|
And returns a non-empty string response
|
|
|
|
Scenario: Ollama provider generates blog content using local model
|
|
Given provider is "ollama" and model is "mistral"
|
|
And the Ollama base URL is "http://localhost:11434"
|
|
When the LLM router is called with a prompt
|
|
Then the router calls http://localhost:11434/api/chat
|
|
And returns a non-empty string response
|
|
|
|
Scenario: Unknown provider raises configuration error
|
|
Given provider is "unknown_provider" and model is "some-model"
|
|
When the LLM router is called with a prompt
|
|
Then a UserError is raised with message containing "provider not configured"
|
|
|
|
Scenario: Token usage is recorded in generation log
|
|
Given provider is "anthropic" and model is "claude-sonnet-4-20250514"
|
|
When the LLM router is called with a prompt
|
|
Then the generation log record contains tokens_used > 0
|