| |
| """ |
| Integration test for scheduling system with image handling. |
| Tests the end-to-end workflow from scheduling through content generation to publishing, |
| specifically for posts with images in both bytes and URL formats. |
| """ |
|
|
| import sys |
| import os |
| import unittest |
| import uuid |
| from unittest.mock import patch, MagicMock |
| from datetime import datetime, timedelta |
|
|
| |
| sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) |
|
|
| from backend.scheduler.apscheduler_service import APSchedulerService |
| from backend.services.content_service import ContentService |
| from backend.utils.image_utils import ensure_bytes_format |
|
|
|
|
| class TestSchedulerImageIntegration(unittest.TestCase): |
| """Test cases for scheduler integration with image handling.""" |
|
|
| def setUp(self): |
| """Set up test fixtures.""" |
| self.user_id = "test_user_123" |
| self.schedule_id = str(uuid.uuid4()) |
| self.social_account_id = "test_social_456" |
| |
| |
| self.mock_app = MagicMock() |
| self.mock_app.config = { |
| 'SUPABASE_URL': 'test_url', |
| 'SUPABASE_KEY': 'test_key' |
| } |
| |
| |
| self.scheduler_service = APSchedulerService(self.mock_app) |
| |
| |
| self.mock_supabase = MagicMock() |
| self.scheduler_service.supabase_client = self.mock_supabase |
| |
| |
| self.mock_scheduler = MagicMock() |
| self.scheduler_service.scheduler = self.mock_scheduler |
| |
| def test_image_bytes_processing_in_content_generation(self): |
| """Test that image bytes are properly processed and stored during content generation.""" |
| |
| test_content = "This is a test post with an image" |
| test_image_bytes = b"fake image bytes data" |
| |
| with patch('backend.scheduler.apscheduler_service.ContentService') as mock_content_service_class: |
| mock_content_service = MagicMock() |
| mock_content_service.generate_post_content.return_value = (test_content, test_image_bytes) |
| mock_content_service_class.return_value = mock_content_service |
| |
| |
| self.mock_supabase.table.return_value.select.return_value.eq.return_value.execute.return_value.data = [ |
| {'id_social': self.social_account_id} |
| ] |
| |
| |
| mock_insert_response = MagicMock() |
| mock_insert_response.data = [{'id': 'test_post_789'}] |
| self.mock_supabase.table.return_value.insert.return_value.execute.return_value = mock_insert_response |
| |
| |
| self.scheduler_service.generate_content_task(self.user_id, self.schedule_id) |
| |
| |
| mock_content_service.generate_post_content.assert_called_once_with(self.user_id) |
| |
| |
| self.mock_supabase.table.assert_called_with("Post_content") |
| self.mock_supabase.table.return_value.insert.assert_called_once() |
| |
| |
| call_args = self.mock_supabase.table.return_value.insert.call_args |
| inserted_data = call_args[0][0] |
| |
| |
| self.assertEqual(inserted_data['Text_content'], test_content) |
| |
| |
| self.assertEqual(inserted_data['image_content_url'], test_image_bytes) |
| self.assertEqual(inserted_data['is_published'], False) |
| self.assertEqual(inserted_data['sched'], self.schedule_id) |
| |
| def test_image_url_processing_in_content_generation(self): |
| """Test that image URLs are properly stored during content generation.""" |
| |
| test_content = "This is a test post with an image URL" |
| test_image_url = "https://example.com/test-image.jpg" |
| |
| with patch('backend.scheduler.apscheduler_service.ContentService') as mock_content_service_class: |
| mock_content_service = MagicMock() |
| mock_content_service.generate_post_content.return_value = (test_content, test_image_url) |
| mock_content_service_class.return_value = mock_content_service |
| |
| |
| self.mock_supabase.table.return_value.select.return_value.eq.return_value.execute.return_value.data = [ |
| {'id_social': self.social_account_id} |
| ] |
| |
| |
| mock_insert_response = MagicMock() |
| mock_insert_response.data = [{'id': 'test_post_789'}] |
| self.mock_supabase.table.return_value.insert.return_value.execute.return_value = mock_insert_response |
| |
| |
| self.scheduler_service.generate_content_task(self.user_id, self.schedule_id) |
| |
| |
| mock_content_service.generate_post_content.assert_called_once_with(self.user_id) |
| |
| |
| self.mock_supabase.table.assert_called_with("Post_content") |
| self.mock_supabase.table.return_value.insert.assert_called_once() |
| |
| |
| call_args = self.mock_supabase.table.return_value.insert.call_args |
| inserted_data = call_args[0][0] |
| |
| |
| self.assertEqual(inserted_data['Text_content'], test_content) |
| |
| |
| self.assertEqual(inserted_data['image_content_url'], test_image_url) |
| self.assertEqual(inserted_data['is_published'], False) |
| self.assertEqual(inserted_data['sched'], self.schedule_id) |
| |
| def test_image_base64_processing_in_content_generation(self): |
| """Test that base64 encoded images are properly converted to bytes during content generation.""" |
| |
| test_content = "This is a test post with a base64 image" |
| test_base64_image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==" |
| expected_bytes = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\rIDATx\xda\x63\xfc\xff\x9f\xa1\x1e\x00\x07\x82\x02\x7f=\xc8H\xef\x00\x00\x00\x00IEND\xaeB`\x82' |
| |
| with patch('backend.scheduler.apscheduler_service.ContentService') as mock_content_service_class: |
| mock_content_service = MagicMock() |
| mock_content_service.generate_post_content.return_value = (test_content, test_base64_image) |
| mock_content_service_class.return_value = mock_content_service |
| |
| |
| self.mock_supabase.table.return_value.select.return_value.eq.return_value.execute.return_value.data = [ |
| {'id_social': self.social_account_id} |
| ] |
| |
| |
| mock_insert_response = MagicMock() |
| mock_insert_response.data = [{'id': 'test_post_789'}] |
| self.mock_supabase.table.return_value.insert.return_value.execute.return_value = mock_insert_response |
| |
| |
| self.scheduler_service.generate_content_task(self.user_id, self.schedule_id) |
| |
| |
| mock_content_service.generate_post_content.assert_called_once_with(self.user_id) |
| |
| |
| self.mock_supabase.table.assert_called_with("Post_content") |
| self.mock_supabase.table.return_value.insert.assert_called_once() |
| |
| |
| call_args = self.mock_supabase.table.return_value.insert.call_args |
| inserted_data = call_args[0][0] |
| |
| |
| self.assertEqual(inserted_data['Text_content'], test_content) |
| |
| |
| self.assertEqual(inserted_data['image_content_url'], expected_bytes) |
| self.assertEqual(inserted_data['is_published'], False) |
| self.assertEqual(inserted_data['sched'], self.schedule_id) |
| |
| def test_publishing_with_image_bytes(self): |
| """Test that posts with image bytes are properly published.""" |
| |
| test_post_id = "test_post_123" |
| test_content = "This is a test post for publishing" |
| test_image_bytes = b"fake image bytes data for publishing" |
| |
| |
| self.mock_supabase.table.return_value.select.return_value.eq.return_value.eq.return_value.order.return_value.limit.return_value.execute.return_value.data = [ |
| { |
| 'id': test_post_id, |
| 'Text_content': test_content, |
| 'image_content_url': test_image_bytes, |
| 'is_published': False |
| } |
| ] |
| |
| |
| self.mock_supabase.table.return_value.select.return_value.eq.return_value.execute.return_value.data = [ |
| { |
| 'Social_network': { |
| 'token': 'test_access_token', |
| 'sub': 'test_user_sub' |
| } |
| } |
| ] |
| |
| |
| with patch('backend.scheduler.apscheduler_service.LinkedInService') as mock_linkedin_service_class: |
| mock_linkedin_service = MagicMock() |
| mock_linkedin_service.publish_post.return_value = {'success': True} |
| mock_linkedin_service_class.return_value = mock_linkedin_service |
| |
| |
| mock_update_response = MagicMock() |
| mock_update_response.data = [{'id': test_post_id}] |
| self.mock_supabase.table.return_value.update.return_value.eq.return_value.execute.return_value = mock_update_response |
| |
| |
| self.scheduler_service.publish_post_task(self.schedule_id) |
| |
| |
| mock_linkedin_service.publish_post.assert_called_once_with( |
| 'test_access_token', |
| 'test_user_sub', |
| test_content, |
| test_image_bytes |
| ) |
| |
| |
| self.mock_supabase.table.return_value.update.assert_called_once_with({"is_published": True}) |
| self.mock_supabase.table.return_value.update.return_value.eq.assert_called_once_with("id", test_post_id) |
| |
| def test_publishing_with_image_url(self): |
| """Test that posts with image URLs are properly published.""" |
| |
| test_post_id = "test_post_456" |
| test_content = "This is a test post with URL for publishing" |
| test_image_url = "https://example.com/publish-test-image.jpg" |
| |
| |
| self.mock_supabase.table.return_value.select.return_value.eq.return_value.eq.return_value.order.return_value.limit.return_value.execute.return_value.data = [ |
| { |
| 'id': test_post_id, |
| 'Text_content': test_content, |
| 'image_content_url': test_image_url, |
| 'is_published': False |
| } |
| ] |
| |
| |
| self.mock_supabase.table.return_value.select.return_value.eq.return_value.execute.return_value.data = [ |
| { |
| 'Social_network': { |
| 'token': 'test_access_token', |
| 'sub': 'test_user_sub' |
| } |
| } |
| ] |
| |
| |
| with patch('backend.scheduler.apscheduler_service.LinkedInService') as mock_linkedin_service_class: |
| mock_linkedin_service = MagicMock() |
| mock_linkedin_service.publish_post.return_value = {'success': True} |
| mock_linkedin_service_class.return_value = mock_linkedin_service |
| |
| |
| mock_update_response = MagicMock() |
| mock_update_response.data = [{'id': test_post_id}] |
| self.mock_supabase.table.return_value.update.return_value.eq.return_value.execute.return_value = mock_update_response |
| |
| |
| self.scheduler_service.publish_post_task(self.schedule_id) |
| |
| |
| mock_linkedin_service.publish_post.assert_called_once_with( |
| 'test_access_token', |
| 'test_user_sub', |
| test_content, |
| test_image_url |
| ) |
| |
| |
| self.mock_supabase.table.return_value.update.assert_called_once_with({"is_published": True}) |
| self.mock_supabase.table.return_value.update.return_value.eq.assert_called_once_with("id", test_post_id) |
| |
| def test_ensure_bytes_format_utility(self): |
| """Test the ensure_bytes_format utility function with different input types.""" |
| |
| test_bytes = b"test bytes data" |
| result = ensure_bytes_format(test_bytes) |
| self.assertEqual(result, test_bytes) |
| |
| |
| test_base64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==" |
| expected_bytes = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\rIDATx\xda\x63\xfc\xff\x9f\xa1\x1e\x00\x07\x82\x02\x7f=\xc8H\xef\x00\x00\x00\x00IEND\xaeB`\x82' |
| result = ensure_bytes_format(test_base64) |
| self.assertEqual(result, expected_bytes) |
| |
| |
| test_url = "https://example.com/image.jpg" |
| result = ensure_bytes_format(test_url) |
| self.assertEqual(result, test_url) |
| |
| |
| result = ensure_bytes_format(None) |
| self.assertIsNone(result) |
|
|
|
|
| if __name__ == '__main__': |
| unittest.main() |