itsulu-blog-publisher/addons/itsulu_blog_publisher/tests/test_blog_topic.py
Nicholas Riegel 0fc4febabf Reorganize codebase into Odoo addon structure per ARCHITECTURE.md
Restructure project files to follow the addon layout:
- Move models to addons/itsulu_blog_publisher/models/
- Move services (LLM providers, routers) to addons/itsulu_blog_publisher/services/
- Move wizards to addons/itsulu_blog_publisher/wizards/
- Move views (XML templates) to addons/itsulu_blog_publisher/views/
- Move data (cron, mail templates) to addons/itsulu_blog_publisher/data/
- Move security (ACL) to addons/itsulu_blog_publisher/security/
- Move tests and factories to addons/itsulu_blog_publisher/tests/
- Move BDD features to addons/itsulu_blog_publisher/features/
- Create __init__.py files for all Python packages

This enables proper Odoo module discovery and import structure.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-29 12:11:42 -04:00

113 lines
4.1 KiB
Python

# addons/itsulu_blog_publisher/tests/test_blog_topic.py
"""
Tests for models/blog_topic.py
Behaviour: topic queue management — create, prioritise, mark used,
pop next topic, fall back gracefully when queue is empty.
RED PHASE — all tests FAIL until itsulu.blog.topic model exists.
"""
from odoo.tests import TransactionCase, tagged
from odoo.exceptions import ValidationError
from .factories import BlogPublisherFactory
@tagged('post_install', '-at_install', 'itsulu_blog_publisher', 'blog_topic')
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 test_topic_is_created_with_pending_state(self):
"""
1. Behaviour: newly created topic starts in state='pending'
2. ARRANGE: nothing — default factory values
3. ACT: create a topic record
4. ASSERT: state == 'pending'
5. FAIL reason: model itsulu.blog.topic does not exist
"""
# ARRANGE + ACT
topic = self.factory.blog_topic(name='AI Ethics in Healthcare')
# ASSERT
self.assertEqual(topic.state, 'pending')
def test_get_next_topic_returns_highest_priority_pending_topic(self):
"""
get_next_topic() on the model returns the pending topic
with the highest priority (urgent > high > normal > low).
"""
# ARRANGE
low = self.factory.blog_topic(name='Low priority topic', priority='low')
normal = self.factory.blog_topic(name='Normal priority topic', priority='normal')
urgent = self.factory.blog_topic(name='Urgent topic', priority='urgent')
high = self.factory.blog_topic(name='High priority topic', priority='high')
# ACT
next_topic = self.env['itsulu.blog.topic'].get_next_topic()
# ASSERT
self.assertEqual(next_topic.id, urgent.id,
"get_next_topic() must return the urgent-priority topic first")
def test_get_next_topic_returns_none_when_queue_is_empty(self):
"""get_next_topic() returns False (empty recordset) when no pending topics exist."""
# ARRANGE — mark all pending topics as used to clear the queue
self.env['itsulu.blog.topic'].search([('state', '=', 'pending')]).write(
{'state': 'used'}
)
# ACT
result = self.env['itsulu.blog.topic'].get_next_topic()
# ASSERT
self.assertFalse(result, "get_next_topic() must return empty recordset when queue is empty")
def test_mark_topic_as_used_changes_state(self):
"""Calling topic.mark_used() sets state to 'used'."""
# ARRANGE
topic = self.factory.blog_topic(name='DevOps best practices')
# ACT
topic.mark_used()
# ASSERT
self.assertEqual(topic.state, 'used')
def test_used_topic_is_excluded_from_next_topic_selection(self):
"""A used topic is never returned by get_next_topic()."""
# ARRANGE
used_topic = self.factory.blog_topic(
name='Already published topic', priority='urgent', state='used'
)
pending_topic = self.factory.blog_topic(
name='Not yet published', priority='normal', state='pending'
)
# ACT
next_topic = self.env['itsulu.blog.topic'].get_next_topic()
# ASSERT
self.assertNotEqual(next_topic.id, used_topic.id)
self.assertEqual(next_topic.id, pending_topic.id)
def test_topic_name_cannot_be_empty(self):
"""Creating a topic with an empty name raises ValidationError."""
with self.assertRaises((ValidationError, Exception)):
self.factory.blog_topic(name='')
def test_topic_can_be_linked_to_a_specific_blog(self):
"""A topic can optionally specify which blog it should be published to."""
# ARRANGE
blog = self.factory.blog(name='ITSulu Insights')
# ACT
topic = self.factory.blog_topic(
name='Kubernetes for SMBs',
blog_id=blog.id,
)
# ASSERT
self.assertEqual(topic.blog_id.id, blog.id)