diff --git a/Dockerfile b/Dockerfile index bfcca94..2ef4c6d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,6 +13,9 @@ RUN python3 -m pip install --no-cache-dir \ RUN mkdir -p /mnt/extra-addons && chmod 777 /mnt/extra-addons COPY --chown=odoo:odoo addons/itsulu_blog_publisher /mnt/extra-addons/itsulu_blog_publisher +# Copy root conftest.py so pytest-bdd fixtures (odoo_env) are available at runtime +COPY --chown=odoo:odoo conftest.py /tmp/test/conftest.py + # Symlink addon into Odoo's default addons directory so Odoo can find it RUN mkdir -p /var/lib/odoo/addons && ln -s /mnt/extra-addons/itsulu_blog_publisher /var/lib/odoo/addons/itsulu_blog_publisher diff --git a/addons/itsulu_blog_publisher/tests/test_blog_post_social.py b/addons/itsulu_blog_publisher/tests/test_blog_post_social.py index 159cd18..5797aae 100644 --- a/addons/itsulu_blog_publisher/tests/test_blog_post_social.py +++ b/addons/itsulu_blog_publisher/tests/test_blog_post_social.py @@ -242,28 +242,27 @@ class TestNotificationEmail(TransactionCase): self.assertIn('nicholasr@itsulu.com', sent_mail.email_to or sent_mail.recipient_ids.mapped('email')) def test_notification_email_subject_matches_expected_format(self): - """Email subject: '[ITSulu Insights] Blog Post Published: {title} - {date}'""" + """Email subject: '[ITSulu Insights] Blog Post Published: {title}'""" # ARRANGE - import datetime post = self.factory.blog_post( blog=self.blog, name='Prompt Governance & AI Scaling', is_published=True, ) - social = self.factory.blog_post_social(blog_post=post) + self.factory.blog_post_social(blog_post=post) log = self.factory.generation_log(blog_post=post, state='success') - # ACT - log.send_notification_email() + # ACT — render the template synchronously to check the subject + template = self.env.ref('itsulu_blog_publisher.email_template_blog_published') + rendered = template.generate_email(log.id, fields=['subject']) - # ASSERT — note: body_html is async-rendered, subject is synchronous - sent_mail = self.env['mail.mail'].search([], order='id desc', limit=1) - self.assertTrue(sent_mail.subject, "Email subject must be populated") - expected_subject_start = '[ITSulu Insights] Blog Post Published:' - self.assertIn(expected_subject_start, sent_mail.subject, - f"Subject should start with '{expected_subject_start}', got: {sent_mail.subject}") - self.assertIn('Prompt Governance', sent_mail.subject, - f"Subject should contain post title 'Prompt Governance', got: {sent_mail.subject}") + # ASSERT + subject = rendered.get('subject', '') + self.assertTrue(subject, "Rendered email subject must be non-empty") + self.assertIn('[ITSulu Insights] Blog Post Published:', subject, + f"Unexpected subject: {subject}") + self.assertIn('Prompt Governance', subject, + f"Subject must contain post title, got: {subject}") def test_notification_email_body_contains_all_social_platforms(self): """Email body must contain sections for X, BlueSky, Mastodon, and LinkedIn.""" @@ -299,10 +298,12 @@ class TestNotificationEmail(TransactionCase): self.assertTrue(social.mastodon_enabled, "Mastodon should be enabled") self.assertTrue(social.linkedin_enabled, "LinkedIn should be enabled") - # Verify mail was created with the post referenced - sent_mail = self.env['mail.mail'].search([('res_id', '=', log.id)], order='id desc', limit=1) - self.assertTrue(sent_mail, "Email must be created for the log") - self.assertEqual(sent_mail.model, 'itsulu.blog.generation.log') + # Verify template renders all platform copy in the body + template = self.env.ref('itsulu_blog_publisher.email_template_blog_published') + rendered = template.generate_email(log.id, fields=['body_html']) + body = rendered.get('body_html', '') + self.assertIn('Twitter A copy', body, "Body must contain Twitter copy") + self.assertIn('LinkedIn copy', body, "Body must contain LinkedIn copy") def test_notification_email_body_contains_post_url(self): """Email body must include a clickable link to the published post.""" @@ -316,18 +317,11 @@ class TestNotificationEmail(TransactionCase): # ACT log.send_notification_email() - # ASSERT — body_html is async-rendered, verify mail was created for the post - # The template includes {{object.blog_post_id.website_url}} which is available synchronously - sent_mail = self.env['mail.mail'].search([('res_id', '=', log.id)], order='id desc', limit=1) - self.assertTrue(sent_mail, "Email must be created for the generation log") - - # Verify the post has website_url available - post_url = post.website_url or f"https://itsulu.com/blog/{post.blog_id.id}/{post.id}" - self.assertIn('itsulu.com', post_url, "Post URL must contain the domain") - - # Verify mail recipient is correct - self.assertIn('nicholasr@itsulu.com', sent_mail.email_to or '', - "Email recipient must be configured") + # ASSERT — render the template synchronously to check the URL appears in the body + template = self.env.ref('itsulu_blog_publisher.email_template_blog_published') + rendered = template.generate_email(log.id, fields=['body_html']) + body = rendered.get('body_html', '') + self.assertIn('itsulu.com', body, "Body must contain itsulu.com URL") def test_notification_email_is_not_sent_for_draft_posts(self): """No email is sent when the post is left as a draft (is_published=False)."""