Backeup configs & cleanup of files and db
This commit is contained in:
@@ -374,100 +374,10 @@ class ContentSyncService:
|
||||
Returns:
|
||||
dict: Sync result with synced_count
|
||||
"""
|
||||
try:
|
||||
from igny8_core.business.site_building.models import SiteBlueprint
|
||||
from igny8_core.business.site_building.services.taxonomy_service import TaxonomyService
|
||||
|
||||
# Get or create site blueprint for this site
|
||||
blueprint = SiteBlueprint.objects.filter(
|
||||
account=integration.account,
|
||||
site=integration.site
|
||||
).first()
|
||||
|
||||
if not blueprint:
|
||||
logger.warning(f"No blueprint found for site {integration.site.id}, skipping taxonomy sync")
|
||||
return {'success': True, 'synced_count': 0}
|
||||
|
||||
taxonomy_service = TaxonomyService()
|
||||
synced_count = 0
|
||||
|
||||
# Sync WordPress categories
|
||||
categories = client.get_categories(per_page=100)
|
||||
category_records = [
|
||||
{
|
||||
'name': cat['name'],
|
||||
'slug': cat['slug'],
|
||||
'description': cat.get('description', ''),
|
||||
'taxonomy_type': 'blog_category',
|
||||
'external_reference': str(cat['id']),
|
||||
'metadata': {'parent': cat.get('parent', 0)}
|
||||
}
|
||||
for cat in categories
|
||||
]
|
||||
if category_records:
|
||||
taxonomy_service.import_from_external(
|
||||
blueprint,
|
||||
category_records,
|
||||
default_type='blog_category'
|
||||
)
|
||||
synced_count += len(category_records)
|
||||
|
||||
# Sync WordPress tags
|
||||
tags = client.get_tags(per_page=100)
|
||||
tag_records = [
|
||||
{
|
||||
'name': tag['name'],
|
||||
'slug': tag['slug'],
|
||||
'description': tag.get('description', ''),
|
||||
'taxonomy_type': 'blog_tag',
|
||||
'external_reference': str(tag['id'])
|
||||
}
|
||||
for tag in tags
|
||||
]
|
||||
if tag_records:
|
||||
taxonomy_service.import_from_external(
|
||||
blueprint,
|
||||
tag_records,
|
||||
default_type='blog_tag'
|
||||
)
|
||||
synced_count += len(tag_records)
|
||||
|
||||
# Sync WooCommerce product categories if available (401 is expected if WooCommerce not installed or credentials missing)
|
||||
try:
|
||||
product_categories = client.get_product_categories(per_page=100)
|
||||
product_category_records = [
|
||||
{
|
||||
'name': cat['name'],
|
||||
'slug': cat['slug'],
|
||||
'description': cat.get('description', ''),
|
||||
'taxonomy_type': 'product_category',
|
||||
'external_reference': f"wc_cat_{cat['id']}",
|
||||
'metadata': {'parent': cat.get('parent', 0)}
|
||||
}
|
||||
for cat in product_categories
|
||||
]
|
||||
if product_category_records:
|
||||
taxonomy_service.import_from_external(
|
||||
blueprint,
|
||||
product_category_records,
|
||||
default_type='product_category'
|
||||
)
|
||||
synced_count += len(product_category_records)
|
||||
except Exception as e:
|
||||
# Silently skip WooCommerce if not available (401 means no consumer key/secret configured or plugin not installed)
|
||||
logger.debug(f"WooCommerce product categories not available: {e}")
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'synced_count': synced_count
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Error syncing taxonomies from WordPress: {e}", exc_info=True)
|
||||
return {
|
||||
'success': False,
|
||||
'error': str(e),
|
||||
'synced_count': 0
|
||||
}
|
||||
# REMOVED: Legacy SiteBlueprint taxonomy sync removed.
|
||||
# Taxonomy management now uses ContentTaxonomy model.
|
||||
logger.info(f"Skipping legacy taxonomy sync for site {integration.site.id}")
|
||||
return {'success': True, 'synced_count': 0}
|
||||
|
||||
def _sync_taxonomies_to_wordpress(
|
||||
self,
|
||||
|
||||
@@ -308,11 +308,14 @@ class SyncHealthService:
|
||||
"""
|
||||
Detect mismatches between IGNY8 and WordPress.
|
||||
|
||||
DEPRECATED: Legacy SiteBlueprint taxonomy mismatch detection removed.
|
||||
Taxonomy management now uses ContentTaxonomy model.
|
||||
|
||||
Args:
|
||||
integration: SiteIntegration instance
|
||||
|
||||
Returns:
|
||||
dict: Mismatch details
|
||||
dict: Mismatch details (empty for now)
|
||||
"""
|
||||
mismatches = {
|
||||
'taxonomies': {
|
||||
@@ -330,116 +333,8 @@ class SyncHealthService:
|
||||
}
|
||||
}
|
||||
|
||||
try:
|
||||
from igny8_core.utils.wordpress import WordPressClient
|
||||
from igny8_core.business.site_building.models import SiteBlueprint, SiteBlueprintTaxonomy
|
||||
from igny8_core.business.content.models import Content
|
||||
|
||||
credentials = integration.get_credentials()
|
||||
client = WordPressClient(
|
||||
site_url=integration.config_json.get('site_url', ''),
|
||||
username=credentials.get('username'),
|
||||
app_password=credentials.get('app_password')
|
||||
)
|
||||
|
||||
# Get site blueprint
|
||||
blueprint = SiteBlueprint.objects.filter(
|
||||
account=integration.account,
|
||||
site=integration.site
|
||||
).first()
|
||||
|
||||
if not blueprint:
|
||||
return mismatches
|
||||
|
||||
# Check taxonomy mismatches
|
||||
# Get IGNY8 taxonomies
|
||||
igny8_taxonomies = SiteBlueprintTaxonomy.objects.filter(
|
||||
site_blueprint=blueprint
|
||||
)
|
||||
|
||||
# Get WordPress categories
|
||||
wp_categories = client.get_categories(per_page=100)
|
||||
wp_category_ids = {str(cat['id']): cat for cat in wp_categories}
|
||||
|
||||
# Get WordPress tags
|
||||
wp_tags = client.get_tags(per_page=100)
|
||||
wp_tag_ids = {str(tag['id']): tag for tag in wp_tags}
|
||||
|
||||
for taxonomy in igny8_taxonomies:
|
||||
if taxonomy.external_reference:
|
||||
# Check if still exists in WordPress
|
||||
if taxonomy.taxonomy_type in ['blog_category', 'product_category']:
|
||||
if taxonomy.external_reference not in wp_category_ids:
|
||||
mismatches['taxonomies']['missing_in_wordpress'].append({
|
||||
'id': taxonomy.id,
|
||||
'name': taxonomy.name,
|
||||
'type': taxonomy.taxonomy_type,
|
||||
'external_reference': taxonomy.external_reference
|
||||
})
|
||||
elif taxonomy.taxonomy_type in ['blog_tag', 'product_tag']:
|
||||
if taxonomy.external_reference not in wp_tag_ids:
|
||||
mismatches['taxonomies']['missing_in_wordpress'].append({
|
||||
'id': taxonomy.id,
|
||||
'name': taxonomy.name,
|
||||
'type': taxonomy.taxonomy_type,
|
||||
'external_reference': taxonomy.external_reference
|
||||
})
|
||||
else:
|
||||
# Taxonomy exists in IGNY8 but not synced to WordPress
|
||||
mismatches['taxonomies']['missing_in_wordpress'].append({
|
||||
'id': taxonomy.id,
|
||||
'name': taxonomy.name,
|
||||
'type': taxonomy.taxonomy_type
|
||||
})
|
||||
|
||||
# Check for WordPress taxonomies not in IGNY8
|
||||
for cat in wp_categories:
|
||||
if not SiteBlueprintTaxonomy.objects.filter(
|
||||
site_blueprint=blueprint,
|
||||
external_reference=str(cat['id'])
|
||||
).exists():
|
||||
mismatches['taxonomies']['missing_in_igny8'].append({
|
||||
'name': cat['name'],
|
||||
'slug': cat['slug'],
|
||||
'type': 'blog_category',
|
||||
'external_reference': str(cat['id'])
|
||||
})
|
||||
|
||||
for tag in wp_tags:
|
||||
if not SiteBlueprintTaxonomy.objects.filter(
|
||||
site_blueprint=blueprint,
|
||||
external_reference=str(tag['id'])
|
||||
).exists():
|
||||
mismatches['taxonomies']['missing_in_igny8'].append({
|
||||
'name': tag['name'],
|
||||
'slug': tag['slug'],
|
||||
'type': 'blog_tag',
|
||||
'external_reference': str(tag['id'])
|
||||
})
|
||||
|
||||
# Check content mismatches (basic check)
|
||||
igny8_content = Content.objects.filter(
|
||||
account=integration.account,
|
||||
site=integration.site,
|
||||
source='igny8',
|
||||
status='publish'
|
||||
)
|
||||
|
||||
for content in igny8_content[:50]: # Limit check
|
||||
if content.metadata and content.metadata.get('wordpress_id'):
|
||||
# Content should exist in WordPress (would need to check)
|
||||
# For now, just note if metadata exists
|
||||
pass
|
||||
else:
|
||||
# Content not synced to WordPress
|
||||
mismatches['posts']['missing_in_wordpress'].append({
|
||||
'id': content.id,
|
||||
'title': content.title,
|
||||
'type': content.content_type
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Error detecting WordPress mismatches: {e}")
|
||||
# Legacy taxonomy detection removed - would need to be reimplemented with ContentTaxonomy
|
||||
logger.info(f"Mismatch detection for integration {integration.id} - legacy code removed")
|
||||
|
||||
return mismatches
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
"""
|
||||
Integration Tests
|
||||
Phase 6: Site Integration & Multi-Destination Publishing
|
||||
"""
|
||||
|
||||
@@ -1,155 +0,0 @@
|
||||
"""
|
||||
Tests for ContentSyncService
|
||||
Phase 6: Site Integration & Multi-Destination Publishing
|
||||
"""
|
||||
from django.test import TestCase
|
||||
from unittest.mock import patch, Mock
|
||||
|
||||
from igny8_core.auth.models import Account, Site, Sector, User, Plan, Industry, IndustrySector
|
||||
from igny8_core.business.integration.models import SiteIntegration
|
||||
from igny8_core.business.integration.services.content_sync_service import ContentSyncService
|
||||
from igny8_core.business.content.models import Content
|
||||
|
||||
|
||||
class ContentSyncServiceTestCase(TestCase):
|
||||
"""Test cases for ContentSyncService"""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test data"""
|
||||
# Create plan first
|
||||
self.plan = Plan.objects.create(
|
||||
name="Test Plan",
|
||||
slug="test-plan",
|
||||
price=0,
|
||||
credits_per_month=1000
|
||||
)
|
||||
|
||||
# Create user first (Account needs owner)
|
||||
self.user = User.objects.create_user(
|
||||
username='testuser',
|
||||
email='test@test.com',
|
||||
password='testpass123',
|
||||
role='owner'
|
||||
)
|
||||
|
||||
# Create account with owner
|
||||
self.account = Account.objects.create(
|
||||
name="Test Account",
|
||||
slug="test-account",
|
||||
plan=self.plan,
|
||||
owner=self.user
|
||||
)
|
||||
|
||||
# Update user to have account
|
||||
self.user.account = self.account
|
||||
self.user.save()
|
||||
|
||||
# Create industry and sector
|
||||
self.industry = Industry.objects.create(
|
||||
name="Test Industry",
|
||||
slug="test-industry"
|
||||
)
|
||||
|
||||
self.industry_sector = IndustrySector.objects.create(
|
||||
industry=self.industry,
|
||||
name="Test Sector",
|
||||
slug="test-sector"
|
||||
)
|
||||
|
||||
self.site = Site.objects.create(
|
||||
account=self.account,
|
||||
name="Test Site",
|
||||
slug="test-site",
|
||||
industry=self.industry
|
||||
)
|
||||
self.sector = Sector.objects.create(
|
||||
account=self.account,
|
||||
site=self.site,
|
||||
industry_sector=self.industry_sector,
|
||||
name="Test Sector",
|
||||
slug="test-sector"
|
||||
)
|
||||
self.integration = SiteIntegration.objects.create(
|
||||
account=self.account,
|
||||
site=self.site,
|
||||
platform='wordpress',
|
||||
platform_type='cms',
|
||||
sync_enabled=True
|
||||
)
|
||||
self.service = ContentSyncService()
|
||||
|
||||
def test_sync_content_from_wordpress_creates_content(self):
|
||||
"""Test: WordPress sync works (when plugin connected)"""
|
||||
mock_posts = [
|
||||
{
|
||||
'id': 1,
|
||||
'title': 'Test Post',
|
||||
'content': '<p>Test content</p>',
|
||||
'status': 'publish',
|
||||
}
|
||||
]
|
||||
|
||||
with patch.object(self.service, '_fetch_wordpress_posts') as mock_fetch:
|
||||
mock_fetch.return_value = mock_posts
|
||||
|
||||
result = self.service.sync_from_wordpress(self.integration)
|
||||
|
||||
self.assertTrue(result.get('success'))
|
||||
self.assertEqual(result.get('synced_count'), 1)
|
||||
|
||||
# Verify content was created
|
||||
content = Content.objects.filter(site=self.site).first()
|
||||
self.assertIsNotNone(content)
|
||||
self.assertEqual(content.title, 'Test Post')
|
||||
self.assertEqual(content.source, 'wordpress')
|
||||
|
||||
def test_sync_content_from_shopify_creates_content(self):
|
||||
"""Test: Content sync works"""
|
||||
mock_products = [
|
||||
{
|
||||
'id': 1,
|
||||
'title': 'Test Product',
|
||||
'body_html': '<p>Product description</p>',
|
||||
}
|
||||
]
|
||||
|
||||
with patch.object(self.service, '_fetch_shopify_products') as mock_fetch:
|
||||
mock_fetch.return_value = mock_products
|
||||
|
||||
result = self.service.sync_from_shopify(self.integration)
|
||||
|
||||
self.assertTrue(result.get('success'))
|
||||
self.assertEqual(result.get('synced_count'), 1)
|
||||
|
||||
def test_sync_handles_duplicate_content(self):
|
||||
"""Test: Content sync works"""
|
||||
# Create existing content
|
||||
Content.objects.create(
|
||||
account=self.account,
|
||||
site=self.site,
|
||||
sector=self.sector,
|
||||
title="Test Post",
|
||||
html_content="<p>Existing</p>",
|
||||
source='wordpress'
|
||||
)
|
||||
|
||||
mock_posts = [
|
||||
{
|
||||
'id': 1,
|
||||
'title': 'Test Post',
|
||||
'content': '<p>Updated content</p>',
|
||||
}
|
||||
]
|
||||
|
||||
with patch.object(self.service, '_fetch_wordpress_posts') as mock_fetch:
|
||||
mock_fetch.return_value = mock_posts
|
||||
|
||||
result = self.service.sync_from_wordpress(self.integration)
|
||||
|
||||
# Should update existing, not create duplicate
|
||||
content_count = Content.objects.filter(
|
||||
site=self.site,
|
||||
title='Test Post'
|
||||
).count()
|
||||
self.assertEqual(content_count, 1)
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
"""
|
||||
Tests for IntegrationService
|
||||
Phase 6: Site Integration & Multi-Destination Publishing
|
||||
"""
|
||||
from django.test import TestCase
|
||||
|
||||
from igny8_core.auth.models import Account, Site, Sector, User, Plan, Industry, IndustrySector
|
||||
from igny8_core.business.integration.models import SiteIntegration
|
||||
from igny8_core.business.integration.services.integration_service import IntegrationService
|
||||
|
||||
|
||||
class IntegrationServiceTestCase(TestCase):
|
||||
"""Test cases for IntegrationService"""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test data"""
|
||||
# Create plan first
|
||||
self.plan = Plan.objects.create(
|
||||
name="Test Plan",
|
||||
slug="test-plan",
|
||||
price=0,
|
||||
credits_per_month=1000
|
||||
)
|
||||
|
||||
# Create user first (Account needs owner)
|
||||
self.user = User.objects.create_user(
|
||||
username='testuser',
|
||||
email='test@test.com',
|
||||
password='testpass123',
|
||||
role='owner'
|
||||
)
|
||||
|
||||
# Create account with owner
|
||||
self.account = Account.objects.create(
|
||||
name="Test Account",
|
||||
slug="test-account",
|
||||
plan=self.plan,
|
||||
owner=self.user
|
||||
)
|
||||
|
||||
# Update user to have account
|
||||
self.user.account = self.account
|
||||
self.user.save()
|
||||
|
||||
# Create industry and sector
|
||||
self.industry = Industry.objects.create(
|
||||
name="Test Industry",
|
||||
slug="test-industry"
|
||||
)
|
||||
|
||||
self.industry_sector = IndustrySector.objects.create(
|
||||
industry=self.industry,
|
||||
name="Test Sector",
|
||||
slug="test-sector"
|
||||
)
|
||||
|
||||
self.site = Site.objects.create(
|
||||
account=self.account,
|
||||
name="Test Site",
|
||||
slug="test-site",
|
||||
industry=self.industry
|
||||
)
|
||||
self.sector = Sector.objects.create(
|
||||
account=self.account,
|
||||
site=self.site,
|
||||
industry_sector=self.industry_sector,
|
||||
name="Test Sector",
|
||||
slug="test-sector"
|
||||
)
|
||||
self.service = IntegrationService()
|
||||
|
||||
def test_create_integration_stores_config(self):
|
||||
"""Test: Site integrations work correctly"""
|
||||
integration = self.service.create_integration(
|
||||
site=self.site,
|
||||
platform='wordpress',
|
||||
config={'url': 'https://example.com'},
|
||||
credentials={'api_key': 'test-key'},
|
||||
platform_type='cms'
|
||||
)
|
||||
|
||||
self.assertIsNotNone(integration)
|
||||
self.assertEqual(integration.platform, 'wordpress')
|
||||
self.assertEqual(integration.platform_type, 'cms')
|
||||
self.assertEqual(integration.config_json.get('url'), 'https://example.com')
|
||||
self.assertTrue(integration.is_active)
|
||||
|
||||
def test_get_integrations_for_site_returns_all(self):
|
||||
"""Test: Site integrations work correctly"""
|
||||
self.service.create_integration(
|
||||
site=self.site,
|
||||
platform='wordpress',
|
||||
config={},
|
||||
credentials={}
|
||||
)
|
||||
self.service.create_integration(
|
||||
site=self.site,
|
||||
platform='shopify',
|
||||
config={},
|
||||
credentials={}
|
||||
)
|
||||
|
||||
integrations = self.service.get_integrations_for_site(self.site)
|
||||
|
||||
self.assertEqual(integrations.count(), 2)
|
||||
platforms = [i.platform for i in integrations]
|
||||
self.assertIn('wordpress', platforms)
|
||||
self.assertIn('shopify', platforms)
|
||||
|
||||
def test_test_connection_validates_credentials(self):
|
||||
"""Test: Site integrations work correctly"""
|
||||
# Test with unsupported platform to verify NotImplementedError is raised
|
||||
integration = self.service.create_integration(
|
||||
site=self.site,
|
||||
platform='unsupported_platform',
|
||||
config={'url': 'https://example.com'},
|
||||
credentials={'api_key': 'test-key'}
|
||||
)
|
||||
|
||||
with self.assertRaises(NotImplementedError):
|
||||
# Connection testing should raise NotImplementedError for unsupported platforms
|
||||
self.service.test_connection(integration)
|
||||
|
||||
def test_update_integration_updates_fields(self):
|
||||
"""Test: Site integrations work correctly"""
|
||||
integration = self.service.create_integration(
|
||||
site=self.site,
|
||||
platform='wordpress',
|
||||
config={'url': 'https://old.com'},
|
||||
credentials={}
|
||||
)
|
||||
|
||||
updated = self.service.update_integration(
|
||||
integration,
|
||||
config={'url': 'https://new.com'},
|
||||
is_active=False
|
||||
)
|
||||
|
||||
self.assertEqual(updated.config_json.get('url'), 'https://new.com')
|
||||
self.assertFalse(updated.is_active)
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
"""
|
||||
Tests for SyncService
|
||||
Phase 6: Site Integration & Multi-Destination Publishing
|
||||
"""
|
||||
from django.test import TestCase
|
||||
from django.utils import timezone
|
||||
from unittest.mock import patch, Mock
|
||||
|
||||
from igny8_core.auth.models import Account, Site, Sector, User, Plan, Industry, IndustrySector
|
||||
from igny8_core.business.integration.models import SiteIntegration
|
||||
from igny8_core.business.integration.services.sync_service import SyncService
|
||||
|
||||
|
||||
class SyncServiceTestCase(TestCase):
|
||||
"""Test cases for SyncService"""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test data"""
|
||||
# Create plan first
|
||||
self.plan = Plan.objects.create(
|
||||
name="Test Plan",
|
||||
slug="test-plan",
|
||||
price=0,
|
||||
credits_per_month=1000
|
||||
)
|
||||
|
||||
# Create user first (Account needs owner)
|
||||
self.user = User.objects.create_user(
|
||||
username='testuser',
|
||||
email='test@test.com',
|
||||
password='testpass123',
|
||||
role='owner'
|
||||
)
|
||||
|
||||
# Create account with owner
|
||||
self.account = Account.objects.create(
|
||||
name="Test Account",
|
||||
slug="test-account",
|
||||
plan=self.plan,
|
||||
owner=self.user
|
||||
)
|
||||
|
||||
# Update user to have account
|
||||
self.user.account = self.account
|
||||
self.user.save()
|
||||
|
||||
# Create industry and sector
|
||||
self.industry = Industry.objects.create(
|
||||
name="Test Industry",
|
||||
slug="test-industry"
|
||||
)
|
||||
|
||||
self.industry_sector = IndustrySector.objects.create(
|
||||
industry=self.industry,
|
||||
name="Test Sector",
|
||||
slug="test-sector"
|
||||
)
|
||||
|
||||
self.site = Site.objects.create(
|
||||
account=self.account,
|
||||
name="Test Site",
|
||||
slug="test-site",
|
||||
industry=self.industry
|
||||
)
|
||||
self.sector = Sector.objects.create(
|
||||
account=self.account,
|
||||
site=self.site,
|
||||
industry_sector=self.industry_sector,
|
||||
name="Test Sector",
|
||||
slug="test-sector"
|
||||
)
|
||||
self.integration = SiteIntegration.objects.create(
|
||||
account=self.account,
|
||||
site=self.site,
|
||||
platform='wordpress',
|
||||
platform_type='cms',
|
||||
sync_enabled=True,
|
||||
sync_status='pending'
|
||||
)
|
||||
self.service = SyncService()
|
||||
|
||||
def test_sync_updates_status(self):
|
||||
"""Test: Two-way sync functions properly"""
|
||||
with patch.object(self.service, '_sync_to_external') as mock_sync_to, \
|
||||
patch.object(self.service, '_sync_from_external') as mock_sync_from:
|
||||
mock_sync_to.return_value = {'success': True, 'synced': 5}
|
||||
mock_sync_from.return_value = {'success': True, 'synced': 3}
|
||||
|
||||
result = self.service.sync(self.integration, direction='both')
|
||||
|
||||
self.assertTrue(result.get('success'))
|
||||
self.integration.refresh_from_db()
|
||||
self.assertEqual(self.integration.sync_status, 'success')
|
||||
self.assertIsNotNone(self.integration.last_sync_at)
|
||||
|
||||
def test_sync_to_external_only(self):
|
||||
"""Test: Two-way sync functions properly"""
|
||||
with patch.object(self.service, '_sync_to_external') as mock_sync_to:
|
||||
mock_sync_to.return_value = {'success': True, 'synced': 5}
|
||||
|
||||
result = self.service.sync(self.integration, direction='to_external')
|
||||
|
||||
self.assertTrue(result.get('success'))
|
||||
mock_sync_to.assert_called_once()
|
||||
|
||||
def test_sync_from_external_only(self):
|
||||
"""Test: WordPress sync works (when plugin connected)"""
|
||||
with patch.object(self.service, '_sync_from_external') as mock_sync_from:
|
||||
mock_sync_from.return_value = {'success': True, 'synced': 3}
|
||||
|
||||
result = self.service.sync(self.integration, direction='from_external')
|
||||
|
||||
self.assertTrue(result.get('success'))
|
||||
mock_sync_from.assert_called_once()
|
||||
|
||||
def test_sync_handles_errors(self):
|
||||
"""Test: Two-way sync functions properly"""
|
||||
with patch.object(self.service, '_sync_to_external') as mock_sync_to:
|
||||
mock_sync_to.side_effect = Exception("Sync failed")
|
||||
|
||||
result = self.service.sync(self.integration, direction='to_external')
|
||||
|
||||
self.assertFalse(result.get('success'))
|
||||
self.integration.refresh_from_db()
|
||||
self.assertEqual(self.integration.sync_status, 'failed')
|
||||
self.assertIsNotNone(self.integration.sync_error)
|
||||
|
||||
Reference in New Issue
Block a user