|
|
|
|
|
""" |
|
|
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() |