Backeup configs & cleanup of files and db

This commit is contained in:
IGNY8 VPS (Salman)
2026-01-13 03:31:20 +00:00
parent b5787081e2
commit f46fbe4343
70 changed files with 26362 additions and 6502 deletions

View File

@@ -1,2 +0,0 @@
# Linking tests

View File

@@ -1,139 +0,0 @@
"""
Tests for CandidateEngine
"""
from django.test import TestCase
from igny8_core.business.content.models import Content
from igny8_core.business.linking.services.candidate_engine import CandidateEngine
from igny8_core.api.tests.test_integration_base import IntegrationTestBase
class CandidateEngineTests(IntegrationTestBase):
"""Tests for CandidateEngine"""
def setUp(self):
super().setUp()
self.engine = CandidateEngine()
# Create source content
self.source_content = Content.objects.create(
account=self.account,
site=self.site,
sector=self.sector,
title="Source Content",
html_content="<p>Source content about test keyword.</p>",
primary_keyword="test keyword",
secondary_keywords=["keyword1", "keyword2"],
categories=["category1"],
tags=["tag1", "tag2"],
word_count=100,
status='draft'
)
# Create relevant content (same keyword)
self.relevant_content = Content.objects.create(
account=self.account,
site=self.site,
sector=self.sector,
title="Relevant Content",
html_content="<p>Relevant content about test keyword.</p>",
primary_keyword="test keyword",
secondary_keywords=["keyword1"],
categories=["category1"],
tags=["tag1"],
word_count=150,
status='draft'
)
# Create less relevant content (different keyword)
self.less_relevant = Content.objects.create(
account=self.account,
site=self.site,
sector=self.sector,
title="Less Relevant",
html_content="<p>Different content.</p>",
primary_keyword="different keyword",
word_count=100,
status='draft'
)
def test_find_candidates_returns_relevant_content(self):
"""Test that find_candidates returns relevant content"""
candidates = self.engine.find_candidates(self.source_content, max_candidates=10)
# Should find relevant content
candidate_ids = [c['content_id'] for c in candidates]
self.assertIn(self.relevant_content.id, candidate_ids)
def test_find_candidates_scores_by_relevance(self):
"""Test that candidates are scored by relevance"""
candidates = self.engine.find_candidates(self.source_content, max_candidates=10)
# Relevant content should have higher score
relevant_candidate = next((c for c in candidates if c['content_id'] == self.relevant_content.id), None)
self.assertIsNotNone(relevant_candidate)
self.assertGreater(relevant_candidate['relevance_score'], 0)
def test_find_candidates_excludes_self(self):
"""Test that source content is excluded from candidates"""
candidates = self.engine.find_candidates(self.source_content, max_candidates=10)
candidate_ids = [c['content_id'] for c in candidates]
self.assertNotIn(self.source_content.id, candidate_ids)
def test_find_candidates_respects_account_isolation(self):
"""Test that candidates are only from same account"""
# Create content from different account
from igny8_core.auth.models import Account
other_account = Account.objects.create(
name="Other Account",
slug="other-account",
plan=self.plan,
owner=self.user
)
other_content = Content.objects.create(
account=other_account,
site=self.site,
sector=self.sector,
title="Other Account Content",
primary_keyword="test keyword",
word_count=100,
status='draft'
)
candidates = self.engine.find_candidates(self.source_content, max_candidates=10)
candidate_ids = [c['content_id'] for c in candidates]
self.assertNotIn(other_content.id, candidate_ids)
def test_find_candidates_returns_empty_for_no_content(self):
"""Test that empty list is returned when no content"""
empty_content = Content.objects.create(
account=self.account,
site=self.site,
sector=self.sector,
title="Empty",
html_content="",
word_count=0,
status='draft'
)
candidates = self.engine.find_candidates(empty_content, max_candidates=10)
self.assertEqual(len(candidates), 0)
def test_find_candidates_respects_max_candidates(self):
"""Test that max_candidates limit is respected"""
# Create multiple relevant content items
for i in range(15):
Content.objects.create(
account=self.account,
site=self.site,
sector=self.sector,
title=f"Content {i}",
primary_keyword="test keyword",
word_count=100,
status='draft'
)
candidates = self.engine.find_candidates(self.source_content, max_candidates=5)
self.assertLessEqual(len(candidates), 5)

View File

@@ -1,136 +0,0 @@
"""
Tests for InjectionEngine
"""
from django.test import TestCase
from igny8_core.business.content.models import Content
from igny8_core.business.linking.services.injection_engine import InjectionEngine
from igny8_core.api.tests.test_integration_base import IntegrationTestBase
class InjectionEngineTests(IntegrationTestBase):
"""Tests for InjectionEngine"""
def setUp(self):
super().setUp()
self.engine = InjectionEngine()
# Create content with HTML
self.content = Content.objects.create(
account=self.account,
site=self.site,
sector=self.sector,
title="Test Content",
html_content="<p>This is test content with some keywords and text.</p>",
word_count=100,
status='draft'
)
def test_inject_links_adds_links_to_html(self):
"""Test that links are injected into HTML content"""
candidates = [{
'content_id': 1,
'title': 'Target Content',
'url': '/content/1/',
'relevance_score': 50,
'anchor_text': 'keywords'
}]
result = self.engine.inject_links(self.content, candidates, max_links=5)
# Check that link was added
self.assertIn('<a href="/content/1/" class="internal-link">keywords</a>', result['html_content'])
self.assertEqual(result['links_added'], 1)
self.assertEqual(len(result['links']), 1)
def test_inject_links_respects_max_links(self):
"""Test that max_links limit is respected"""
candidates = [
{'content_id': i, 'title': f'Content {i}', 'url': f'/content/{i}/',
'relevance_score': 50, 'anchor_text': f'keyword{i}'}
for i in range(10)
]
# Update HTML to include all anchor texts
self.content.html_content = "<p>" + " ".join([f'keyword{i}' for i in range(10)]) + "</p>"
self.content.save()
result = self.engine.inject_links(self.content, candidates, max_links=3)
self.assertLessEqual(result['links_added'], 3)
self.assertLessEqual(len(result['links']), 3)
def test_inject_links_returns_unchanged_when_no_candidates(self):
"""Test that content is unchanged when no candidates"""
original_html = self.content.html_content
result = self.engine.inject_links(self.content, [], max_links=5)
self.assertEqual(result['html_content'], original_html)
self.assertEqual(result['links_added'], 0)
self.assertEqual(len(result['links']), 0)
def test_inject_links_returns_unchanged_when_no_html(self):
"""Test that empty HTML returns unchanged"""
self.content.html_content = ""
self.content.save()
candidates = [{
'content_id': 1,
'title': 'Target',
'url': '/content/1/',
'relevance_score': 50,
'anchor_text': 'test'
}]
result = self.engine.inject_links(self.content, candidates, max_links=5)
self.assertEqual(result['html_content'], "")
self.assertEqual(result['links_added'], 0)
def test_inject_links_case_insensitive_matching(self):
"""Test that anchor text matching is case-insensitive"""
self.content.html_content = "<p>This is TEST content.</p>"
self.content.save()
candidates = [{
'content_id': 1,
'title': 'Target',
'url': '/content/1/',
'relevance_score': 50,
'anchor_text': 'test'
}]
result = self.engine.inject_links(self.content, candidates, max_links=5)
# Should find and replace despite case difference
self.assertIn('internal-link', result['html_content'])
self.assertEqual(result['links_added'], 1)
def test_inject_links_prevents_duplicate_links(self):
"""Test that same candidate is not linked twice"""
candidates = [
{
'content_id': 1,
'title': 'Target',
'url': '/content/1/',
'relevance_score': 50,
'anchor_text': 'test'
},
{
'content_id': 1, # Same content_id
'title': 'Target',
'url': '/content/1/',
'relevance_score': 40,
'anchor_text': 'test'
}
]
self.content.html_content = "<p>This is test content with test keywords.</p>"
self.content.save()
result = self.engine.inject_links(self.content, candidates, max_links=5)
# Should only add one link despite two candidates
self.assertEqual(result['links_added'], 1)
self.assertEqual(result['html_content'].count('internal-link'), 1)

View File

@@ -1,141 +0,0 @@
"""
Tests for LinkerService
"""
from unittest.mock import Mock, patch, MagicMock
from django.test import TestCase
from igny8_core.business.content.models import Content
from igny8_core.business.linking.services.linker_service import LinkerService
from igny8_core.business.billing.exceptions import InsufficientCreditsError
from igny8_core.api.tests.test_integration_base import IntegrationTestBase
class LinkerServiceTests(IntegrationTestBase):
"""Tests for LinkerService"""
def setUp(self):
super().setUp()
self.service = LinkerService()
# 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 with some keywords.</p>",
primary_keyword="test keyword",
word_count=100,
status='draft'
)
# Create another content for linking
self.target_content = Content.objects.create(
account=self.account,
site=self.site,
sector=self.sector,
title="Target Content",
html_content="<p>Target content for linking.</p>",
primary_keyword="test keyword",
word_count=150,
status='draft'
)
@patch('igny8_core.business.linking.services.linker_service.CreditService.check_credits')
@patch('igny8_core.business.linking.services.linker_service.CandidateEngine.find_candidates')
@patch('igny8_core.business.linking.services.linker_service.InjectionEngine.inject_links')
@patch('igny8_core.business.linking.services.linker_service.CreditService.deduct_credits_for_operation')
def test_process_single_content(self, mock_deduct, mock_inject, mock_find, mock_check):
"""Test processing single content for linking"""
# Setup mocks
mock_check.return_value = True
mock_find.return_value = [{
'content_id': self.target_content.id,
'title': 'Target Content',
'url': '/content/2/',
'relevance_score': 50,
'anchor_text': 'test keyword'
}]
mock_inject.return_value = {
'html_content': '<p>This is test content with <a href="/content/2/">test keyword</a>.</p>',
'links': [{
'content_id': self.target_content.id,
'anchor_text': 'test keyword',
'url': '/content/2/'
}],
'links_added': 1
}
# Execute
result = self.service.process(self.content.id)
# Assertions
self.assertEqual(result.id, self.content.id)
self.assertEqual(result.linker_version, 1)
self.assertEqual(len(result.internal_links), 1)
mock_check.assert_called_once_with(self.account, 'linking')
mock_deduct.assert_called_once()
@patch('igny8_core.business.linking.services.linker_service.CreditService.check_credits')
def test_process_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.process(self.content.id)
def test_process_content_not_found(self):
"""Test that ValueError is raised when content doesn't exist"""
with self.assertRaises(ValueError):
self.service.process(99999)
@patch('igny8_core.business.linking.services.linker_service.LinkerService.process')
def test_batch_process_multiple_content(self, mock_process):
"""Test batch processing multiple content items"""
# Create additional content
content2 = Content.objects.create(
account=self.account,
site=self.site,
sector=self.sector,
title="Content 2",
html_content="<p>Content 2</p>",
word_count=100,
status='draft'
)
# Setup mock
mock_process.side_effect = [self.content, content2]
# Execute
results = self.service.batch_process([self.content.id, content2.id])
# Assertions
self.assertEqual(len(results), 2)
self.assertEqual(mock_process.call_count, 2)
@patch('igny8_core.business.linking.services.linker_service.LinkerService.process')
def test_batch_process_handles_partial_failure(self, mock_process):
"""Test batch processing handles partial failures"""
# Setup mock to fail on second item
mock_process.side_effect = [self.content, Exception("Processing failed")]
# Execute
results = self.service.batch_process([self.content.id, 99999])
# Assertions - should continue processing other items
self.assertEqual(len(results), 1)
self.assertEqual(results[0].id, self.content.id)
@patch('igny8_core.business.linking.services.linker_service.CreditService.check_credits')
@patch('igny8_core.business.linking.services.linker_service.CandidateEngine.find_candidates')
def test_process_no_candidates_found(self, mock_find, mock_check):
"""Test processing when no candidates are found"""
mock_check.return_value = True
mock_find.return_value = []
# Execute
result = self.service.process(self.content.id)
# Assertions - should return content unchanged
self.assertEqual(result.id, self.content.id)
self.assertEqual(result.linker_version, 0) # Not incremented

View File

@@ -1,193 +0,0 @@
"""
Tests for Universal Content Types Linking (Phase 8)
Tests for product and taxonomy linking
"""
from unittest.mock import patch, MagicMock
from django.test import TestCase
from igny8_core.business.content.models import Content
from igny8_core.business.linking.services.linker_service import LinkerService
from igny8_core.api.tests.test_integration_base import IntegrationTestBase
class UniversalContentLinkingTests(IntegrationTestBase):
"""Tests for Phase 8: Universal Content Types Linking"""
def setUp(self):
super().setUp()
# Add credits to account for testing
self.account.credits = 10000
self.account.save()
self.linker_service = LinkerService()
# 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 with features and specifications.</p>',
entity_type='product',
json_blocks=[
{'type': 'features', 'heading': 'Features', 'items': ['Feature 1', 'Feature 2']}
],
structure_data={'product_type': 'software'},
word_count=1500,
status='draft'
)
# Create related product
self.related_product = Content.objects.create(
account=self.account,
site=self.site,
sector=self.sector,
title='Related Product',
html_content='<p>Related product content.</p>',
entity_type='product',
structure_data={'product_type': 'software'},
word_count=1500,
status='draft'
)
# Create service content
self.service_content = Content.objects.create(
account=self.account,
site=self.site,
sector=self.sector,
title='Related Service',
html_content='<p>Service content.</p>',
entity_type='service',
word_count=1800,
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 with categories.</p>',
entity_type='taxonomy',
json_blocks=[
{
'type': 'categories',
'heading': 'Categories',
'items': [
{'name': 'Category 1', 'description': 'Desc 1', 'subcategories': []}
]
}
],
word_count=1200,
status='draft'
)
# Create related taxonomy
self.related_taxonomy = Content.objects.create(
account=self.account,
site=self.site,
sector=self.sector,
title='Related Taxonomy',
html_content='<p>Related taxonomy content.</p>',
entity_type='taxonomy',
word_count=1200,
status='draft'
)
@patch('igny8_core.business.linking.services.linker_service.InjectionEngine.inject_links')
@patch('igny8_core.business.linking.services.linker_service.CreditService.check_credits')
@patch('igny8_core.business.linking.services.linker_service.CreditService.deduct_credits_for_operation')
def test_linking_works_for_products(self, mock_deduct, mock_check_credits, mock_inject_links):
"""
Test: Linking works for all content types (products, taxonomies)
Task 20: Verify product linking finds related products and services
"""
# Mock injection engine
mock_inject_links.return_value = {
'html_content': '<p>Product content with links.</p>',
'links': [
{'content_id': self.related_product.id, 'anchor_text': 'Related Product'},
{'content_id': self.service_content.id, 'anchor_text': 'Related Service'}
],
'links_added': 2
}
# Process product linking
result = self.linker_service.process_product(self.product_content.id)
# Verify result
self.assertIsNotNone(result)
self.assertEqual(result.entity_type, 'product')
self.assertIsNotNone(result.internal_links)
self.assertEqual(len(result.internal_links), 2)
self.assertEqual(result.linker_version, 1)
# Verify injection was called
mock_inject_links.assert_called_once()
candidates = mock_inject_links.call_args[0][1]
self.assertGreater(len(candidates), 0)
# Verify product candidates were found
product_candidates = [c for c in candidates if c.get('content_id') == self.related_product.id]
self.assertGreater(len(product_candidates), 0)
@patch('igny8_core.business.linking.services.linker_service.InjectionEngine.inject_links')
@patch('igny8_core.business.linking.services.linker_service.CreditService.check_credits')
@patch('igny8_core.business.linking.services.linker_service.CreditService.deduct_credits_for_operation')
def test_linking_works_for_taxonomies(self, mock_deduct, mock_check_credits, mock_inject_links):
"""
Test: Linking works for all content types (products, taxonomies)
Task 20: Verify taxonomy linking finds related taxonomies and content
"""
# Mock injection engine
mock_inject_links.return_value = {
'html_content': '<p>Taxonomy content with links.</p>',
'links': [
{'content_id': self.related_taxonomy.id, 'anchor_text': 'Related Taxonomy'}
],
'links_added': 1
}
# Process taxonomy linking
result = self.linker_service.process_taxonomy(self.taxonomy_content.id)
# Verify result
self.assertIsNotNone(result)
self.assertEqual(result.entity_type, 'taxonomy')
self.assertIsNotNone(result.internal_links)
self.assertEqual(len(result.internal_links), 1)
self.assertEqual(result.linker_version, 1)
# Verify injection was called
mock_inject_links.assert_called_once()
candidates = mock_inject_links.call_args[0][1]
self.assertGreater(len(candidates), 0)
# Verify taxonomy candidates were found
taxonomy_candidates = [c for c in candidates if c.get('content_id') == self.related_taxonomy.id]
self.assertGreater(len(taxonomy_candidates), 0)
def test_product_linking_finds_related_products(self):
"""
Test: Linking works for all content types (products, taxonomies)
Task 20: Verify _find_product_candidates finds related products
"""
candidates = self.linker_service._find_product_candidates(self.product_content)
# Should find related product
product_ids = [c['content_id'] for c in candidates]
self.assertIn(self.related_product.id, product_ids)
# Should find related service
self.assertIn(self.service_content.id, product_ids)
def test_taxonomy_linking_finds_related_taxonomies(self):
"""
Test: Linking works for all content types (products, taxonomies)
Task 20: Verify _find_taxonomy_candidates finds related taxonomies
"""
candidates = self.linker_service._find_taxonomy_candidates(self.taxonomy_content)
# Should find related taxonomy
taxonomy_ids = [c['content_id'] for c in candidates]
self.assertIn(self.related_taxonomy.id, taxonomy_ids)