TradingAgents/tests/domains/news/test_migration_sentiment_fi...

156 lines
7.1 KiB
Python

"""
Simplified tests for sentiment fields migration that don't require database connection.
Tests the migration script structure and logic.
"""
import pytest
import ast
from pathlib import Path
class TestSentimentFieldsMigrationScript:
"""Test the sentiment fields migration script structure and content."""
@pytest.fixture
def migration_file_path(self):
"""Path to the migration file."""
return Path(__file__).parent.parent.parent.parent / "alembic" / "versions" / "20250116_1200_0001_add_sentiment_fields.py"
@pytest.fixture
def migration_content(self, migration_file_path):
"""Read migration file content."""
return migration_file_path.read_text()
def test_migration_file_exists(self, migration_file_path):
"""Test that the migration file exists."""
assert migration_file_path.exists(), "Migration file should exist"
def test_migration_has_required_functions(self, migration_content):
"""Test that migration has upgrade and downgrade functions."""
# Parse the Python code
tree = ast.parse(migration_content)
function_names = [node.name for node in ast.walk(tree) if isinstance(node, ast.FunctionDef)]
assert "upgrade" in function_names, "Migration should have upgrade() function"
assert "downgrade" in function_names, "Migration should have downgrade() function"
def test_migration_has_required_metadata(self, migration_content):
"""Test that migration has required revision metadata."""
# Check for required revision identifiers
assert "revision = " in migration_content, "Should have revision identifier"
assert "down_revision = " in migration_content, "Should have down_revision identifier"
assert "upgrade() -> None:" in migration_content, "upgrade function should be typed"
assert "downgrade() -> None:" in migration_content, "downgrade function should be typed"
def test_upgrade_adds_sentiment_confidence_column(self, migration_content):
"""Test that upgrade adds sentiment_confidence column."""
assert "op.add_column('news_articles', sa.Column('sentiment_confidence', sa.Float(), nullable=True))" in migration_content, \
"Should add sentiment_confidence FLOAT column"
def test_upgrade_adds_sentiment_label_column(self, migration_content):
"""Test that upgrade adds sentiment_label column."""
assert "op.add_column('news_articles', sa.Column('sentiment_label', sa.String(20), nullable=True))" in migration_content, \
"Should add sentiment_label VARCHAR(20) column"
def test_upgrade_creates_index(self, migration_content):
"""Test that upgrade creates index on sentiment_label."""
assert "op.create_index('idx_news_sentiment_label', 'news_articles', ['sentiment_label'])" in migration_content, \
"Should create index on sentiment_label"
def test_downgrade_removes_index_first(self, migration_content):
"""Test that downgrade removes index before columns (correct order)."""
lines = migration_content.split('\n')
# Find downgrade function
downgrade_start = None
for i, line in enumerate(lines):
if "def downgrade()" in line:
downgrade_start = i
break
assert downgrade_start is not None, "Should find downgrade function"
# Check that drop_index comes before drop_column
drop_index_line = None
drop_column_line = None
for i in range(downgrade_start, len(lines)):
line = lines[i].strip()
if "op.drop_index" in line:
drop_index_line = i
elif "op.drop_column" in line and "sentiment" in line:
if drop_column_line is None: # Only capture first sentiment column drop
drop_column_line = i
assert drop_index_line is not None, "Should drop index"
assert drop_column_line is not None, "Should drop columns"
assert drop_index_line < drop_column_line, "Should drop index before columns"
def test_downgrade_removes_sentiment_columns(self, migration_content):
"""Test that downgrade removes both sentiment columns."""
assert "op.drop_column('news_articles', 'sentiment_label')" in migration_content, \
"Should drop sentiment_label column"
assert "op.drop_column('news_articles', 'sentiment_confidence')" in migration_content, \
"Should drop sentiment_confidence column"
def test_migration_follows_naming_convention(self, migration_file_path):
"""Test that migration follows naming convention."""
filename = migration_file_path.name
# Should follow pattern: YYYYMMDD_HHMM_XXXX_descriptive_name.py
assert filename.startswith("20250116_"), "Should start with date"
assert "_add_sentiment_fields.py" in filename, "Should have descriptive name"
def test_migration_has_proper_imports(self, migration_content):
"""Test that migration has proper imports."""
assert "from alembic import op" in migration_content, "Should import op from alembic"
assert "import sqlalchemy as sa" in migration_content, "Should import sqlalchemy"
def test_revision_format(self, migration_content):
"""Test that revision follows expected format."""
lines = migration_content.split('\n')
# Find revision line
revision_line = None
for line in lines:
if line.strip().startswith("revision = "):
revision_line = line.strip()
break
assert revision_line is not None, "Should have revision line"
assert revision_line.startswith("revision = '20250116_1200_0001_add_sentiment_fields'"), \
"Revision should match filename"
class TestMigrationLogic:
"""Test migration logic expectations."""
def test_sentiment_confidence_column_spec(self):
"""Test sentiment_confidence column specification."""
# Should be FLOAT, nullable (for existing data)
# This represents confidence score from 0.0 to 1.0
pass # Column spec tested in migration content test above
def test_sentiment_label_column_spec(self):
"""Test sentiment_label column specification."""
# Should be VARCHAR(20), nullable
# This stores "positive", "negative", "neutral"
pass # Column spec tested in migration content test above
def test_index_specification(self):
"""Test index specification for sentiment filtering."""
# Index on sentiment_label for efficient WHERE clauses
# Name: idx_news_sentiment_label
pass # Index spec tested in migration content test above
def test_backward_compatibility(self):
"""Test that migration maintains backward compatibility."""
# New columns are nullable, so existing code continues to work
# Index doesn't affect existing queries
pass # Tested by nullable=True in column specs
if __name__ == "__main__":
# Run tests directly
pytest.main([__file__, "-v"])