mirror of
https://gitlab.com/itsulu-odoo/itsulu-blog-publisher.git
synced 2026-05-30 23:41:23 +00:00
port(14.0): fix web_ribbon view attr + convert tests to setUp/self.env
- views/blog_schedule_views.xml: web_ribbon invisible="active" (Odoo 17
bare-expr syntax) -> attrs="{'invisible': [('active','=',True)]}".
This was the view-validation error blocking module install on Odoo 14.
- tests: Odoo 14 TransactionCase exposes self.env in setUp(), not cls.env
in setUpClass() (that pattern is Odoo 15+). Converted all 13 setUpClass
blocks across 6 test files to setUp(self) + self.env/self.factory.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
d0e974a25b
commit
6635313f58
8 changed files with 71 additions and 83 deletions
14
Dockerfile
14
Dockerfile
|
|
@ -1,12 +1,14 @@
|
|||
FROM odoo:14.0
|
||||
|
||||
# Install Python testing dependencies using the system Python
|
||||
# Install Python testing dependencies. odoo:14.0 ships Python 3.7, so pin to
|
||||
# the last releases that still support 3.7 (pytest 8 / pytest-bdd 7 / pytest-html 4
|
||||
# all require 3.8+).
|
||||
RUN python3 -m pip install --no-cache-dir \
|
||||
pytest \
|
||||
pytest-odoo \
|
||||
pytest-bdd \
|
||||
pytest-cov \
|
||||
pytest-html \
|
||||
"pytest>=7,<8" \
|
||||
"pytest-odoo<2" \
|
||||
"pytest-bdd>=6,<7" \
|
||||
"pytest-cov<5" \
|
||||
"pytest-html<4" \
|
||||
requests
|
||||
|
||||
# Copy addon to Odoo addons path
|
||||
|
|
|
|||
|
|
@ -14,11 +14,10 @@ from .factories import BlogPublisherFactory
|
|||
class TestBlogGenerationLogCreation(TransactionCase):
|
||||
"""Verify that generation log records capture the correct metadata."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.factory = BlogPublisherFactory(cls.env)
|
||||
cls.blog = cls.factory.blog(name='ITSulu Insights')
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.factory = BlogPublisherFactory(self.env)
|
||||
self.blog = self.factory.blog(name='ITSulu Insights')
|
||||
|
||||
def test_successful_log_record_is_created_with_correct_fields(self):
|
||||
"""
|
||||
|
|
@ -105,10 +104,9 @@ class TestBlogGenerationLogCreation(TransactionCase):
|
|||
class TestBlogGenerationLogRetry(TransactionCase):
|
||||
"""Verify that failed logs expose a working Retry action."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.factory = BlogPublisherFactory(cls.env)
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.factory = BlogPublisherFactory(self.env)
|
||||
|
||||
def test_error_log_action_retry_returns_wizard_action(self):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -16,11 +16,10 @@ from .factories import BlogPublisherFactory
|
|||
class TestSEOPopulation(TransactionCase):
|
||||
"""Verify that all SEO fields are correctly populated after blog post generation."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.factory = BlogPublisherFactory(cls.env)
|
||||
cls.blog = cls.factory.blog(name='ITSulu Insights')
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.factory = BlogPublisherFactory(self.env)
|
||||
self.blog = self.factory.blog(name='ITSulu Insights')
|
||||
|
||||
def test_generated_post_has_non_empty_meta_title(self):
|
||||
"""
|
||||
|
|
@ -125,11 +124,10 @@ class TestSEOPopulation(TransactionCase):
|
|||
class TestBlogPostSocialModel(TransactionCase):
|
||||
"""Verify the itsulu.blog.post.social model stores all platform copy correctly."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.factory = BlogPublisherFactory(cls.env)
|
||||
cls.blog = cls.factory.blog(name='ITSulu Insights')
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.factory = BlogPublisherFactory(self.env)
|
||||
self.blog = self.factory.blog(name='ITSulu Insights')
|
||||
|
||||
def test_social_record_is_linked_one_to_one_with_blog_post(self):
|
||||
"""Each blog post has at most one social record."""
|
||||
|
|
@ -200,12 +198,11 @@ class TestNotificationEmail(TransactionCase):
|
|||
structure matching the [ITSulu Insights] template in the uploaded .eml.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.factory = BlogPublisherFactory(cls.env)
|
||||
cls.blog = cls.factory.blog(name='ITSulu Insights')
|
||||
cls.env['ir.config_parameter'].sudo().set_param(
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.factory = BlogPublisherFactory(self.env)
|
||||
self.blog = self.factory.blog(name='ITSulu Insights')
|
||||
self.env['ir.config_parameter'].sudo().set_param(
|
||||
'itsulu_blog_publisher.notification_emails',
|
||||
'nicholasr@itsulu.com',
|
||||
)
|
||||
|
|
|
|||
|
|
@ -15,11 +15,10 @@ from .factories import BlogPublisherFactory
|
|||
class TestBlogScheduleConfiguration(TransactionCase):
|
||||
"""Verify that schedule slot records are configured correctly."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.factory = BlogPublisherFactory(cls.env)
|
||||
cls.blog = cls.factory.blog(name='ITSulu Insights')
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.factory = BlogPublisherFactory(self.env)
|
||||
self.blog = self.factory.blog(name='ITSulu Insights')
|
||||
|
||||
def test_schedule_slot_is_created_with_correct_defaults(self):
|
||||
"""
|
||||
|
|
@ -69,12 +68,11 @@ class TestBlogScheduleExecution(TransactionCase):
|
|||
LLM calls are mocked — we are testing orchestration, not the LLM.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.factory = BlogPublisherFactory(cls.env)
|
||||
cls.blog = cls.factory.blog(name='ITSulu Insights')
|
||||
cls.env['ir.config_parameter'].sudo().set_param(
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.factory = BlogPublisherFactory(self.env)
|
||||
self.blog = self.factory.blog(name='ITSulu Insights')
|
||||
self.env['ir.config_parameter'].sudo().set_param(
|
||||
'itsulu_blog_publisher.anthropic_api_key', 'sk-ant-test-key'
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -15,10 +15,9 @@ from .factories import BlogPublisherFactory
|
|||
class TestBlogTopicQueueManagement(TransactionCase):
|
||||
"""Verify that the topic queue picks topics in priority order."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.factory = BlogPublisherFactory(cls.env)
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.factory = BlogPublisherFactory(self.env)
|
||||
|
||||
def test_topic_is_created_with_pending_state(self):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -42,21 +42,20 @@ def _make_mock_llm_response(tokens_used=800):
|
|||
class TestLLMRouterProviderDispatch(TransactionCase):
|
||||
"""Verify that the LLM router dispatches to the correct backend provider."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.factory = BlogPublisherFactory(cls.env)
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.factory = BlogPublisherFactory(self.env)
|
||||
# Store a known-valid stub API key in ir.config_parameter
|
||||
cls.env['ir.config_parameter'].sudo().set_param(
|
||||
self.env['ir.config_parameter'].sudo().set_param(
|
||||
'itsulu_blog_publisher.anthropic_api_key', 'sk-ant-test-key'
|
||||
)
|
||||
cls.env['ir.config_parameter'].sudo().set_param(
|
||||
self.env['ir.config_parameter'].sudo().set_param(
|
||||
'itsulu_blog_publisher.openai_api_key', 'sk-openai-test-key'
|
||||
)
|
||||
cls.env['ir.config_parameter'].sudo().set_param(
|
||||
self.env['ir.config_parameter'].sudo().set_param(
|
||||
'itsulu_blog_publisher.gemini_api_key', 'gemini-test-key'
|
||||
)
|
||||
cls.env['ir.config_parameter'].sudo().set_param(
|
||||
self.env['ir.config_parameter'].sudo().set_param(
|
||||
'itsulu_blog_publisher.ollama_base_url', 'http://localhost:11434'
|
||||
)
|
||||
|
||||
|
|
@ -170,11 +169,10 @@ class TestLLMRouterProviderDispatch(TransactionCase):
|
|||
class TestLLMRouterTokenLogging(TransactionCase):
|
||||
"""Verify that token usage is captured from provider responses."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.factory = BlogPublisherFactory(cls.env)
|
||||
cls.env['ir.config_parameter'].sudo().set_param(
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.factory = BlogPublisherFactory(self.env)
|
||||
self.env['ir.config_parameter'].sudo().set_param(
|
||||
'itsulu_blog_publisher.anthropic_api_key', 'sk-ant-test-key'
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -16,12 +16,11 @@ _logger = logging.getLogger(__name__)
|
|||
class TestGenerationLatency(TransactionCase):
|
||||
"""Measure time from run_generation() call to blog.post creation."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.factory = BlogPublisherFactory(cls.env)
|
||||
cls.blog = cls.factory.blog(name='ITSulu Insights')
|
||||
cls.env['ir.config_parameter'].sudo().set_param(
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.factory = BlogPublisherFactory(self.env)
|
||||
self.blog = self.factory.blog(name='ITSulu Insights')
|
||||
self.env['ir.config_parameter'].sudo().set_param(
|
||||
'itsulu_blog_publisher.anthropic_api_key', 'sk-ant-test-key'
|
||||
)
|
||||
|
||||
|
|
@ -103,12 +102,11 @@ class TestGenerationLatency(TransactionCase):
|
|||
class TestQueryCount(TransactionCase):
|
||||
"""Verify N+1 query patterns don't exist in critical paths."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.factory = BlogPublisherFactory(cls.env)
|
||||
cls.blog = cls.factory.blog(name='ITSulu Insights')
|
||||
cls.env['ir.config_parameter'].sudo().set_param(
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.factory = BlogPublisherFactory(self.env)
|
||||
self.blog = self.factory.blog(name='ITSulu Insights')
|
||||
self.env['ir.config_parameter'].sudo().set_param(
|
||||
'itsulu_blog_publisher.anthropic_api_key', 'sk-ant-test-key'
|
||||
)
|
||||
|
||||
|
|
@ -179,11 +177,10 @@ class TestQueryCount(TransactionCase):
|
|||
class TestTokenUsageBaseline(TransactionCase):
|
||||
"""Establish token usage baseline for cost tracking."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.factory = BlogPublisherFactory(cls.env)
|
||||
cls.blog = cls.factory.blog(name='ITSulu Insights')
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.factory = BlogPublisherFactory(self.env)
|
||||
self.blog = self.factory.blog(name='ITSulu Insights')
|
||||
|
||||
def test_typical_post_uses_800_to_1200_tokens(self):
|
||||
"""
|
||||
|
|
@ -231,12 +228,11 @@ class TestTokenUsageBaseline(TransactionCase):
|
|||
class TestConcurrentGeneration(TransactionCase):
|
||||
"""Test that concurrent post generation handles contention correctly."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.factory = BlogPublisherFactory(cls.env)
|
||||
cls.blog = cls.factory.blog(name='ITSulu Insights')
|
||||
cls.env['ir.config_parameter'].sudo().set_param(
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.factory = BlogPublisherFactory(self.env)
|
||||
self.blog = self.factory.blog(name='ITSulu Insights')
|
||||
self.env['ir.config_parameter'].sudo().set_param(
|
||||
'itsulu_blog_publisher.anthropic_api_key', 'sk-ant-test-key'
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
</header>
|
||||
<sheet>
|
||||
<widget name="web_ribbon" title="Inactive" bg_color="bg-danger"
|
||||
invisible="active"/>
|
||||
attrs="{'invisible': [('active', '=', True)]}"/>
|
||||
<div class="oe_title">
|
||||
<h1><field name="name" placeholder="e.g. Morning Post"/></h1>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue