Backeup configs & cleanup of files and db
This commit is contained in:
@@ -1,2 +0,0 @@
|
||||
# Optimization tests
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
"""
|
||||
Tests for ContentAnalyzer
|
||||
"""
|
||||
from django.test import TestCase
|
||||
from igny8_core.business.content.models import Content
|
||||
from igny8_core.business.optimization.services.analyzer import ContentAnalyzer
|
||||
from igny8_core.api.tests.test_integration_base import IntegrationTestBase
|
||||
|
||||
|
||||
class ContentAnalyzerTests(IntegrationTestBase):
|
||||
"""Tests for ContentAnalyzer"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.analyzer = ContentAnalyzer()
|
||||
|
||||
def test_analyze_returns_all_scores(self):
|
||||
"""Test that analyze returns all required scores"""
|
||||
content = Content.objects.create(
|
||||
account=self.account,
|
||||
site=self.site,
|
||||
sector=self.sector,
|
||||
title="Test Content",
|
||||
html_content="<p>This is test content.</p>",
|
||||
meta_title="Test Title",
|
||||
meta_description="Test description",
|
||||
primary_keyword="test keyword",
|
||||
word_count=1500,
|
||||
status='draft'
|
||||
)
|
||||
|
||||
scores = self.analyzer.analyze(content)
|
||||
|
||||
self.assertIn('seo_score', scores)
|
||||
self.assertIn('readability_score', scores)
|
||||
self.assertIn('engagement_score', scores)
|
||||
self.assertIn('overall_score', scores)
|
||||
self.assertIn('word_count', scores)
|
||||
self.assertIn('has_meta_title', scores)
|
||||
self.assertIn('has_meta_description', scores)
|
||||
self.assertIn('has_primary_keyword', scores)
|
||||
self.assertIn('internal_links_count', scores)
|
||||
|
||||
def test_analyze_returns_zero_scores_for_empty_content(self):
|
||||
"""Test that empty content returns zero scores"""
|
||||
content = Content.objects.create(
|
||||
account=self.account,
|
||||
site=self.site,
|
||||
sector=self.sector,
|
||||
title="Empty",
|
||||
html_content="",
|
||||
word_count=0,
|
||||
status='draft'
|
||||
)
|
||||
|
||||
scores = self.analyzer.analyze(content)
|
||||
|
||||
self.assertEqual(scores['seo_score'], 0)
|
||||
self.assertEqual(scores['readability_score'], 0)
|
||||
self.assertEqual(scores['engagement_score'], 0)
|
||||
self.assertEqual(scores['overall_score'], 0)
|
||||
|
||||
def test_calculate_seo_score_with_meta_title(self):
|
||||
"""Test SEO score calculation with meta title"""
|
||||
content = Content.objects.create(
|
||||
account=self.account,
|
||||
site=self.site,
|
||||
sector=self.sector,
|
||||
title="Test",
|
||||
meta_title="Test Title" * 5, # 50 chars - optimal length
|
||||
word_count=1500,
|
||||
status='draft'
|
||||
)
|
||||
|
||||
scores = self.analyzer.analyze(content)
|
||||
|
||||
self.assertGreater(scores['seo_score'], 0)
|
||||
|
||||
def test_calculate_seo_score_with_primary_keyword(self):
|
||||
"""Test SEO score calculation with primary keyword"""
|
||||
content = Content.objects.create(
|
||||
account=self.account,
|
||||
site=self.site,
|
||||
sector=self.sector,
|
||||
title="Test",
|
||||
primary_keyword="test keyword",
|
||||
word_count=1500,
|
||||
status='draft'
|
||||
)
|
||||
|
||||
scores = self.analyzer.analyze(content)
|
||||
|
||||
self.assertGreater(scores['seo_score'], 0)
|
||||
|
||||
def test_calculate_readability_score(self):
|
||||
"""Test readability score calculation"""
|
||||
# Create content with good readability (short sentences, paragraphs)
|
||||
html = "<p>This is a sentence.</p><p>This is another sentence.</p><p>And one more.</p>"
|
||||
content = Content.objects.create(
|
||||
account=self.account,
|
||||
site=self.site,
|
||||
sector=self.sector,
|
||||
title="Test",
|
||||
html_content=html,
|
||||
word_count=20,
|
||||
status='draft'
|
||||
)
|
||||
|
||||
scores = self.analyzer.analyze(content)
|
||||
|
||||
self.assertGreater(scores['readability_score'], 0)
|
||||
|
||||
def test_calculate_engagement_score_with_headings(self):
|
||||
"""Test engagement score calculation with headings"""
|
||||
html = "<h1>Main Heading</h1><h2>Subheading 1</h2><h2>Subheading 2</h2>"
|
||||
content = Content.objects.create(
|
||||
account=self.account,
|
||||
site=self.site,
|
||||
sector=self.sector,
|
||||
title="Test",
|
||||
html_content=html,
|
||||
word_count=100,
|
||||
status='draft'
|
||||
)
|
||||
|
||||
scores = self.analyzer.analyze(content)
|
||||
|
||||
self.assertGreater(scores['engagement_score'], 0)
|
||||
|
||||
def test_calculate_engagement_score_with_internal_links(self):
|
||||
"""Test engagement score calculation with internal links"""
|
||||
content = Content.objects.create(
|
||||
account=self.account,
|
||||
site=self.site,
|
||||
sector=self.sector,
|
||||
title="Test",
|
||||
html_content="<p>Test content.</p>",
|
||||
internal_links=[
|
||||
{'content_id': 1, 'anchor_text': 'link1'},
|
||||
{'content_id': 2, 'anchor_text': 'link2'},
|
||||
{'content_id': 3, 'anchor_text': 'link3'}
|
||||
],
|
||||
word_count=100,
|
||||
status='draft'
|
||||
)
|
||||
|
||||
scores = self.analyzer.analyze(content)
|
||||
|
||||
self.assertGreater(scores['engagement_score'], 0)
|
||||
self.assertEqual(scores['internal_links_count'], 3)
|
||||
|
||||
def test_overall_score_is_weighted_average(self):
|
||||
"""Test that overall score is weighted average"""
|
||||
content = Content.objects.create(
|
||||
account=self.account,
|
||||
site=self.site,
|
||||
sector=self.sector,
|
||||
title="Test",
|
||||
html_content="<p>Test content.</p>",
|
||||
meta_title="Test Title",
|
||||
meta_description="Test description",
|
||||
primary_keyword="test",
|
||||
word_count=1500,
|
||||
status='draft'
|
||||
)
|
||||
|
||||
scores = self.analyzer.analyze(content)
|
||||
|
||||
# Overall should be weighted: SEO (40%) + Readability (30%) + Engagement (30%)
|
||||
expected = (
|
||||
scores['seo_score'] * 0.4 +
|
||||
scores['readability_score'] * 0.3 +
|
||||
scores['engagement_score'] * 0.3
|
||||
)
|
||||
|
||||
self.assertAlmostEqual(scores['overall_score'], expected, places=1)
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
"""
|
||||
Tests for OptimizerService
|
||||
"""
|
||||
from unittest.mock import Mock, patch, MagicMock
|
||||
from django.test import TestCase
|
||||
from igny8_core.business.content.models import Content
|
||||
from igny8_core.business.optimization.models import OptimizationTask
|
||||
from igny8_core.business.optimization.services.optimizer_service import OptimizerService
|
||||
from igny8_core.business.billing.exceptions import InsufficientCreditsError
|
||||
from igny8_core.api.tests.test_integration_base import IntegrationTestBase
|
||||
|
||||
|
||||
class OptimizerServiceTests(IntegrationTestBase):
|
||||
"""Tests for OptimizerService"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.service = OptimizerService()
|
||||
|
||||
# Create test content
|
||||
self.content = Content.objects.create(
|
||||
account=self.account,
|
||||
site=self.site,
|
||||
sector=self.sector,
|
||||
title="Test Content",
|
||||
html_content="<p>This is test content.</p>",
|
||||
meta_title="Test Title",
|
||||
meta_description="Test description",
|
||||
primary_keyword="test keyword",
|
||||
word_count=500,
|
||||
status='draft',
|
||||
source='igny8'
|
||||
)
|
||||
|
||||
@patch('igny8_core.business.optimization.services.optimizer_service.CreditService.check_credits')
|
||||
@patch('igny8_core.business.optimization.services.optimizer_service.ContentAnalyzer.analyze')
|
||||
@patch('igny8_core.business.optimization.services.optimizer_service.OptimizerService._optimize_content')
|
||||
@patch('igny8_core.business.optimization.services.optimizer_service.CreditService.deduct_credits_for_operation')
|
||||
def test_optimize_from_writer(self, mock_deduct, mock_optimize, mock_analyze, mock_check):
|
||||
"""Test optimize_from_writer entry point"""
|
||||
mock_check.return_value = True
|
||||
mock_analyze.return_value = {
|
||||
'seo_score': 50.0,
|
||||
'readability_score': 60.0,
|
||||
'engagement_score': 55.0,
|
||||
'overall_score': 55.0
|
||||
}
|
||||
|
||||
optimized_content = Content.objects.create(
|
||||
account=self.account,
|
||||
site=self.site,
|
||||
sector=self.sector,
|
||||
title="Optimized Content",
|
||||
html_content="<p>Optimized content.</p>",
|
||||
word_count=500,
|
||||
status='draft',
|
||||
source='igny8'
|
||||
)
|
||||
mock_optimize.return_value = optimized_content
|
||||
|
||||
result = self.service.optimize_from_writer(self.content.id)
|
||||
|
||||
self.assertEqual(result.id, self.content.id)
|
||||
mock_check.assert_called_once()
|
||||
mock_deduct.assert_called_once()
|
||||
|
||||
def test_optimize_from_writer_invalid_content(self):
|
||||
"""Test that ValueError is raised for invalid content"""
|
||||
with self.assertRaises(ValueError):
|
||||
self.service.optimize_from_writer(99999)
|
||||
|
||||
def test_optimize_from_writer_wrong_source(self):
|
||||
"""Test that ValueError is raised for wrong source"""
|
||||
content = Content.objects.create(
|
||||
account=self.account,
|
||||
site=self.site,
|
||||
sector=self.sector,
|
||||
title="WordPress Content",
|
||||
word_count=100,
|
||||
source='wordpress'
|
||||
)
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
self.service.optimize_from_writer(content.id)
|
||||
|
||||
@patch('igny8_core.business.optimization.services.optimizer_service.CreditService.check_credits')
|
||||
def test_optimize_insufficient_credits(self, mock_check):
|
||||
"""Test that InsufficientCreditsError is raised when credits are insufficient"""
|
||||
mock_check.side_effect = InsufficientCreditsError("Insufficient credits")
|
||||
|
||||
with self.assertRaises(InsufficientCreditsError):
|
||||
self.service.optimize(self.content)
|
||||
|
||||
@patch('igny8_core.business.optimization.services.optimizer_service.CreditService.check_credits')
|
||||
@patch('igny8_core.business.optimization.services.optimizer_service.ContentAnalyzer.analyze')
|
||||
@patch('igny8_core.business.optimization.services.optimizer_service.OptimizerService._optimize_content')
|
||||
@patch('igny8_core.business.optimization.services.optimizer_service.CreditService.deduct_credits_for_operation')
|
||||
def test_optimize_creates_optimization_task(self, mock_deduct, mock_optimize, mock_analyze, mock_check):
|
||||
"""Test that optimization creates OptimizationTask"""
|
||||
mock_check.return_value = True
|
||||
scores = {
|
||||
'seo_score': 50.0,
|
||||
'readability_score': 60.0,
|
||||
'engagement_score': 55.0,
|
||||
'overall_score': 55.0
|
||||
}
|
||||
mock_analyze.return_value = scores
|
||||
|
||||
optimized_content = Content.objects.create(
|
||||
account=self.account,
|
||||
site=self.site,
|
||||
sector=self.sector,
|
||||
title="Optimized",
|
||||
html_content="<p>Optimized.</p>",
|
||||
word_count=500,
|
||||
status='draft'
|
||||
)
|
||||
mock_optimize.return_value = optimized_content
|
||||
|
||||
result = self.service.optimize(self.content)
|
||||
|
||||
# Check that task was created
|
||||
task = OptimizationTask.objects.filter(content=self.content).first()
|
||||
self.assertIsNotNone(task)
|
||||
self.assertEqual(task.status, 'completed')
|
||||
self.assertEqual(task.scores_before, scores)
|
||||
|
||||
@patch('igny8_core.business.optimization.services.optimizer_service.CreditService.check_credits')
|
||||
@patch('igny8_core.business.optimization.services.optimizer_service.ContentAnalyzer.analyze')
|
||||
def test_analyze_only_returns_scores(self, mock_analyze, mock_check):
|
||||
"""Test analyze_only method returns scores without optimizing"""
|
||||
scores = {
|
||||
'seo_score': 50.0,
|
||||
'readability_score': 60.0,
|
||||
'engagement_score': 55.0,
|
||||
'overall_score': 55.0
|
||||
}
|
||||
mock_analyze.return_value = scores
|
||||
|
||||
result = self.service.analyze_only(self.content.id)
|
||||
|
||||
self.assertEqual(result, scores)
|
||||
mock_analyze.assert_called_once()
|
||||
|
||||
def test_optimize_from_wordpress_sync(self):
|
||||
"""Test optimize_from_wordpress_sync entry point"""
|
||||
content = Content.objects.create(
|
||||
account=self.account,
|
||||
site=self.site,
|
||||
sector=self.sector,
|
||||
title="WordPress Content",
|
||||
word_count=100,
|
||||
source='wordpress'
|
||||
)
|
||||
|
||||
with patch.object(self.service, 'optimize') as mock_optimize:
|
||||
mock_optimize.return_value = content
|
||||
result = self.service.optimize_from_wordpress_sync(content.id)
|
||||
|
||||
self.assertEqual(result.id, content.id)
|
||||
mock_optimize.assert_called_once()
|
||||
|
||||
def test_optimize_from_external_sync(self):
|
||||
"""Test optimize_from_external_sync entry point"""
|
||||
content = Content.objects.create(
|
||||
account=self.account,
|
||||
site=self.site,
|
||||
sector=self.sector,
|
||||
title="Shopify Content",
|
||||
word_count=100,
|
||||
source='shopify'
|
||||
)
|
||||
|
||||
with patch.object(self.service, 'optimize') as mock_optimize:
|
||||
mock_optimize.return_value = content
|
||||
result = self.service.optimize_from_external_sync(content.id)
|
||||
|
||||
self.assertEqual(result.id, content.id)
|
||||
mock_optimize.assert_called_once()
|
||||
|
||||
def test_optimize_manual(self):
|
||||
"""Test optimize_manual entry point"""
|
||||
with patch.object(self.service, 'optimize') as mock_optimize:
|
||||
mock_optimize.return_value = self.content
|
||||
result = self.service.optimize_manual(self.content.id)
|
||||
|
||||
self.assertEqual(result.id, self.content.id)
|
||||
mock_optimize.assert_called_once()
|
||||
|
||||
@@ -1,184 +0,0 @@
|
||||
"""
|
||||
Tests for Universal Content Types Optimization (Phase 8)
|
||||
Tests for product and taxonomy optimization
|
||||
"""
|
||||
from unittest.mock import patch, MagicMock
|
||||
from django.test import TestCase
|
||||
from igny8_core.business.content.models import Content
|
||||
from igny8_core.business.optimization.services.optimizer_service import OptimizerService
|
||||
from igny8_core.business.optimization.models import OptimizationTask
|
||||
from igny8_core.api.tests.test_integration_base import IntegrationTestBase
|
||||
|
||||
|
||||
class UniversalContentOptimizationTests(IntegrationTestBase):
|
||||
"""Tests for Phase 8: Universal Content Types Optimization"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
# Add credits to account for testing
|
||||
self.account.credits = 10000
|
||||
self.account.save()
|
||||
self.optimizer_service = OptimizerService()
|
||||
|
||||
# Create product content
|
||||
self.product_content = Content.objects.create(
|
||||
account=self.account,
|
||||
site=self.site,
|
||||
sector=self.sector,
|
||||
title='Test Product',
|
||||
html_content='<p>Product content that needs optimization.</p>',
|
||||
entity_type='product',
|
||||
json_blocks=[
|
||||
{'type': 'features', 'heading': 'Features', 'items': ['Feature 1']},
|
||||
{'type': 'specifications', 'heading': 'Specs', 'data': {'Spec': 'Value'}}
|
||||
],
|
||||
structure_data={'product_type': 'software', 'price_range': '$99-$199'},
|
||||
word_count=1500,
|
||||
status='draft'
|
||||
)
|
||||
|
||||
# Create taxonomy content
|
||||
self.taxonomy_content = Content.objects.create(
|
||||
account=self.account,
|
||||
site=self.site,
|
||||
sector=self.sector,
|
||||
title='Test Taxonomy',
|
||||
html_content='<p>Taxonomy content that needs optimization.</p>',
|
||||
entity_type='taxonomy',
|
||||
json_blocks=[
|
||||
{'type': 'categories', 'heading': 'Categories', 'items': [{'name': 'Cat 1'}]},
|
||||
{'type': 'tags', 'heading': 'Tags', 'items': ['Tag 1']},
|
||||
{'type': 'hierarchy', 'heading': 'Hierarchy', 'structure': {}}
|
||||
],
|
||||
word_count=1200,
|
||||
status='draft'
|
||||
)
|
||||
|
||||
@patch('igny8_core.business.optimization.services.optimizer_service.OptimizerService._optimize_content')
|
||||
@patch('igny8_core.business.optimization.services.optimizer_service.ContentAnalyzer.analyze')
|
||||
@patch('igny8_core.business.optimization.services.optimizer_service.CreditService.check_credits')
|
||||
@patch('igny8_core.business.optimization.services.optimizer_service.CreditService.get_credit_cost')
|
||||
@patch('igny8_core.business.optimization.services.optimizer_service.CreditService.deduct_credits_for_operation')
|
||||
def test_optimization_works_for_products(self, mock_deduct, mock_get_cost, mock_check_credits, mock_analyze, mock_optimize):
|
||||
"""
|
||||
Test: Optimization works for all content types (products, taxonomies)
|
||||
Task 21: Verify product optimization includes product-specific metrics
|
||||
"""
|
||||
# Mock analyzer
|
||||
mock_analyze.return_value = {
|
||||
'seo_score': 75,
|
||||
'readability_score': 80,
|
||||
'engagement_score': 70,
|
||||
'overall_score': 75
|
||||
}
|
||||
|
||||
# Mock credit cost
|
||||
mock_get_cost.return_value = 10
|
||||
|
||||
# Mock optimization
|
||||
optimized_content = Content.objects.get(id=self.product_content.id)
|
||||
optimized_content.html_content = '<p>Optimized product content.</p>'
|
||||
mock_optimize.return_value = optimized_content
|
||||
|
||||
# Optimize product
|
||||
result = self.optimizer_service.optimize_product(self.product_content.id)
|
||||
|
||||
# Verify result
|
||||
self.assertIsNotNone(result)
|
||||
self.assertEqual(result.entity_type, 'product')
|
||||
self.assertEqual(result.optimizer_version, 1)
|
||||
self.assertIsNotNone(result.optimization_scores)
|
||||
|
||||
# Verify product-specific scores were enhanced
|
||||
scores = result.optimization_scores
|
||||
self.assertIn('product_completeness', scores)
|
||||
self.assertGreaterEqual(scores['product_completeness'], 0)
|
||||
self.assertLessEqual(scores['product_completeness'], 1)
|
||||
|
||||
# Verify optimization task was created
|
||||
task = OptimizationTask.objects.filter(content=result).first()
|
||||
self.assertIsNotNone(task)
|
||||
self.assertEqual(task.status, 'completed')
|
||||
self.assertIn('product_completeness', task.scores_after)
|
||||
|
||||
@patch('igny8_core.business.optimization.services.optimizer_service.OptimizerService._optimize_content')
|
||||
@patch('igny8_core.business.optimization.services.optimizer_service.ContentAnalyzer.analyze')
|
||||
@patch('igny8_core.business.optimization.services.optimizer_service.CreditService.check_credits')
|
||||
@patch('igny8_core.business.optimization.services.optimizer_service.CreditService.get_credit_cost')
|
||||
@patch('igny8_core.business.optimization.services.optimizer_service.CreditService.deduct_credits_for_operation')
|
||||
def test_optimization_works_for_taxonomies(self, mock_deduct, mock_get_cost, mock_check_credits, mock_analyze, mock_optimize):
|
||||
"""
|
||||
Test: Optimization works for all content types (products, taxonomies)
|
||||
Task 21: Verify taxonomy optimization includes taxonomy-specific metrics
|
||||
"""
|
||||
# Mock analyzer
|
||||
mock_analyze.return_value = {
|
||||
'seo_score': 70,
|
||||
'readability_score': 75,
|
||||
'engagement_score': 65,
|
||||
'overall_score': 70
|
||||
}
|
||||
|
||||
# Mock credit cost
|
||||
mock_get_cost.return_value = 8
|
||||
|
||||
# Mock optimization
|
||||
optimized_content = Content.objects.get(id=self.taxonomy_content.id)
|
||||
optimized_content.html_content = '<p>Optimized taxonomy content.</p>'
|
||||
mock_optimize.return_value = optimized_content
|
||||
|
||||
# Optimize taxonomy
|
||||
result = self.optimizer_service.optimize_taxonomy(self.taxonomy_content.id)
|
||||
|
||||
# Verify result
|
||||
self.assertIsNotNone(result)
|
||||
self.assertEqual(result.entity_type, 'taxonomy')
|
||||
self.assertEqual(result.optimizer_version, 1)
|
||||
self.assertIsNotNone(result.optimization_scores)
|
||||
|
||||
# Verify taxonomy-specific scores were enhanced
|
||||
scores = result.optimization_scores
|
||||
self.assertIn('taxonomy_organization', scores)
|
||||
self.assertGreaterEqual(scores['taxonomy_organization'], 0)
|
||||
self.assertLessEqual(scores['taxonomy_organization'], 1)
|
||||
|
||||
# Verify optimization task was created
|
||||
task = OptimizationTask.objects.filter(content=result).first()
|
||||
self.assertIsNotNone(task)
|
||||
self.assertEqual(task.status, 'completed')
|
||||
self.assertIn('taxonomy_organization', task.scores_after)
|
||||
|
||||
def test_enhance_product_scores_includes_completeness(self):
|
||||
"""
|
||||
Test: Optimization works for all content types (products, taxonomies)
|
||||
Task 21: Verify _enhance_product_scores adds product_completeness
|
||||
"""
|
||||
base_scores = {
|
||||
'seo_score': 75,
|
||||
'readability_score': 80,
|
||||
'overall_score': 75
|
||||
}
|
||||
|
||||
enhanced = self.optimizer_service._enhance_product_scores(base_scores, self.product_content)
|
||||
|
||||
self.assertIn('product_completeness', enhanced)
|
||||
self.assertGreaterEqual(enhanced['product_completeness'], 0)
|
||||
self.assertLessEqual(enhanced['product_completeness'], 1)
|
||||
|
||||
def test_enhance_taxonomy_scores_includes_organization(self):
|
||||
"""
|
||||
Test: Optimization works for all content types (products, taxonomies)
|
||||
Task 21: Verify _enhance_taxonomy_scores adds taxonomy_organization
|
||||
"""
|
||||
base_scores = {
|
||||
'seo_score': 70,
|
||||
'readability_score': 75,
|
||||
'overall_score': 70
|
||||
}
|
||||
|
||||
enhanced = self.optimizer_service._enhance_taxonomy_scores(base_scores, self.taxonomy_content)
|
||||
|
||||
self.assertIn('taxonomy_organization', enhanced)
|
||||
self.assertGreaterEqual(enhanced['taxonomy_organization'], 0)
|
||||
self.assertLessEqual(enhanced['taxonomy_organization'], 1)
|
||||
|
||||
Reference in New Issue
Block a user