7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
691 lines
24 KiB
Python
691 lines
24 KiB
Python
"""
|
|
Extended tests for news/folder_manager.py
|
|
|
|
Tests cover:
|
|
- FolderManager initialization
|
|
- get_user_folders() - folder retrieval
|
|
- create_folder() - folder creation
|
|
- update_folder() - folder updates
|
|
- delete_folder() - folder deletion with subscription handling
|
|
- get_subscriptions_by_folder() - folder organization
|
|
- update_subscription() - subscription updates with interval recalculation
|
|
- delete_subscription() - subscription deletion
|
|
- get_subscription_stats() - statistics calculation
|
|
- _sub_to_dict() - serialization
|
|
- Edge cases and error handling
|
|
"""
|
|
|
|
from unittest.mock import MagicMock
|
|
from datetime import datetime, timezone, timedelta
|
|
|
|
|
|
class TestFolderManagerInit:
|
|
"""Tests for FolderManager initialization."""
|
|
|
|
def test_init_stores_session(self):
|
|
"""Initialization stores the session."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
manager = FolderManager(mock_session)
|
|
|
|
assert manager.session is mock_session
|
|
|
|
def test_init_accepts_any_session(self):
|
|
"""Accepts any truthy session object."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
# SQLAlchemy session mock
|
|
mock_session = MagicMock()
|
|
mock_session.query = MagicMock()
|
|
|
|
manager = FolderManager(mock_session)
|
|
assert manager.session is mock_session
|
|
|
|
|
|
class TestGetUserFolders:
|
|
"""Tests for get_user_folders method."""
|
|
|
|
def test_returns_all_folders(self):
|
|
"""Returns all folders for user."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
mock_folders = [MagicMock(name="A"), MagicMock(name="B")]
|
|
mock_session.query.return_value.order_by.return_value.all.return_value = mock_folders
|
|
|
|
manager = FolderManager(mock_session)
|
|
result = manager.get_user_folders("user-123")
|
|
|
|
assert result == mock_folders
|
|
|
|
def test_returns_empty_list_when_no_folders(self):
|
|
"""Returns empty list when no folders exist."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
mock_session.query.return_value.order_by.return_value.all.return_value = []
|
|
|
|
manager = FolderManager(mock_session)
|
|
result = manager.get_user_folders("user-123")
|
|
|
|
assert result == []
|
|
|
|
def test_calls_query(self):
|
|
"""Calls query to get folders."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
mock_session.query.return_value.order_by.return_value.all.return_value = []
|
|
|
|
manager = FolderManager(mock_session)
|
|
manager.get_user_folders("user-123")
|
|
|
|
# Verify query was called
|
|
mock_session.query.assert_called_once()
|
|
|
|
|
|
class TestCreateFolder:
|
|
"""Tests for create_folder method."""
|
|
|
|
def test_creates_folder_with_name(self):
|
|
"""Creates folder with the given name."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
manager = FolderManager(mock_session)
|
|
|
|
result = manager.create_folder("My Folder")
|
|
|
|
mock_session.add.assert_called_once()
|
|
mock_session.commit.assert_called_once()
|
|
assert result.name == "My Folder"
|
|
|
|
def test_creates_folder_with_description(self):
|
|
"""Creates folder with name and description."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
manager = FolderManager(mock_session)
|
|
|
|
result = manager.create_folder(
|
|
"My Folder", description="Test description"
|
|
)
|
|
|
|
assert result.description == "Test description"
|
|
|
|
def test_creates_folder_with_generated_uuid(self):
|
|
"""Folder gets a generated UUID."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
manager = FolderManager(mock_session)
|
|
|
|
result = manager.create_folder("Test")
|
|
|
|
# UUID should be 36 characters (UUID format)
|
|
assert len(result.id) == 36
|
|
|
|
|
|
class TestUpdateFolder:
|
|
"""Tests for update_folder method."""
|
|
|
|
def test_update_existing_folder(self):
|
|
"""Updates existing folder."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
mock_folder = MagicMock()
|
|
mock_folder.id = 1
|
|
mock_folder.name = "Old Name"
|
|
mock_folder.updated_at = None
|
|
mock_session.query.return_value.get.return_value = mock_folder
|
|
|
|
manager = FolderManager(mock_session)
|
|
result = manager.update_folder(1, name="New Name")
|
|
|
|
assert result is mock_folder
|
|
assert mock_folder.name == "New Name"
|
|
assert mock_folder.updated_at is not None
|
|
mock_session.commit.assert_called_once()
|
|
|
|
def test_update_nonexistent_folder(self):
|
|
"""Returns None for nonexistent folder."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
mock_session.query.return_value.get.return_value = None
|
|
|
|
manager = FolderManager(mock_session)
|
|
result = manager.update_folder(999, name="New Name")
|
|
|
|
assert result is None
|
|
|
|
def test_update_does_not_modify_id(self):
|
|
"""Does not modify folder id."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
mock_folder = MagicMock()
|
|
mock_folder.id = 1
|
|
mock_session.query.return_value.get.return_value = mock_folder
|
|
|
|
manager = FolderManager(mock_session)
|
|
manager.update_folder(1, id=999)
|
|
|
|
assert mock_folder.id == 1
|
|
|
|
def test_update_does_not_modify_created_at(self):
|
|
"""Does not modify created_at."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
original_created = datetime(2024, 1, 1, tzinfo=timezone.utc)
|
|
mock_folder = MagicMock()
|
|
mock_folder.created_at = original_created
|
|
mock_session.query.return_value.get.return_value = mock_folder
|
|
|
|
manager = FolderManager(mock_session)
|
|
new_created = datetime(2024, 6, 1, tzinfo=timezone.utc)
|
|
manager.update_folder(1, created_at=new_created)
|
|
|
|
assert mock_folder.created_at == original_created
|
|
|
|
def test_update_multiple_fields(self):
|
|
"""Updates multiple fields at once."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
mock_folder = MagicMock()
|
|
mock_folder.name = "Old"
|
|
mock_folder.description = "Old desc"
|
|
mock_session.query.return_value.get.return_value = mock_folder
|
|
|
|
manager = FolderManager(mock_session)
|
|
manager.update_folder(1, name="New", description="New desc")
|
|
|
|
assert mock_folder.name == "New"
|
|
assert mock_folder.description == "New desc"
|
|
|
|
|
|
class TestDeleteFolder:
|
|
"""Tests for delete_folder method."""
|
|
|
|
def test_delete_existing_folder(self):
|
|
"""Deletes existing folder."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
mock_folder = MagicMock()
|
|
mock_folder.name = "Test Folder"
|
|
mock_session.query.return_value.get.return_value = mock_folder
|
|
|
|
manager = FolderManager(mock_session)
|
|
result = manager.delete_folder(1)
|
|
|
|
assert result is True
|
|
mock_session.delete.assert_called_once_with(mock_folder)
|
|
mock_session.commit.assert_called_once()
|
|
|
|
def test_delete_nonexistent_folder(self):
|
|
"""Returns False for nonexistent folder."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
mock_session.query.return_value.get.return_value = None
|
|
|
|
manager = FolderManager(mock_session)
|
|
result = manager.delete_folder(999)
|
|
|
|
assert result is False
|
|
|
|
def test_delete_moves_subscriptions_to_target(self):
|
|
"""Moves subscriptions to target folder when specified."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
mock_folder = MagicMock()
|
|
mock_folder.name = "Old Folder"
|
|
mock_session.query.return_value.get.return_value = mock_folder
|
|
|
|
manager = FolderManager(mock_session)
|
|
manager.delete_folder(1, move_to="New Folder")
|
|
|
|
# Should update subscriptions with the old folder name
|
|
mock_session.query.return_value.filter_by.return_value.update.assert_called_once_with(
|
|
{"folder": "New Folder"}
|
|
)
|
|
|
|
def test_delete_sets_subscriptions_to_none(self):
|
|
"""Sets subscription folder to None when no target."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
mock_folder = MagicMock()
|
|
mock_folder.name = "Old Folder"
|
|
mock_session.query.return_value.get.return_value = mock_folder
|
|
|
|
manager = FolderManager(mock_session)
|
|
manager.delete_folder(1, move_to=None)
|
|
|
|
mock_session.query.return_value.filter_by.return_value.update.assert_called_once_with(
|
|
{"folder": None}
|
|
)
|
|
|
|
|
|
class TestGetSubscriptionsByFolder:
|
|
"""Tests for get_subscriptions_by_folder method."""
|
|
|
|
def test_returns_organized_structure(self):
|
|
"""Returns proper folder structure."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
|
|
# Mock folders
|
|
mock_folder = MagicMock()
|
|
mock_folder.name = "Tech"
|
|
mock_folder.to_dict.return_value = {"id": 1, "name": "Tech"}
|
|
|
|
# Mock subscriptions
|
|
mock_sub = MagicMock()
|
|
mock_sub.id = "sub-1"
|
|
mock_sub.subscription_type = "search"
|
|
mock_sub.query_or_topic = "AI"
|
|
mock_sub.created_at = datetime.now(timezone.utc)
|
|
mock_sub.last_refresh = None
|
|
mock_sub.next_refresh = None
|
|
mock_sub.refresh_interval_minutes = 60
|
|
mock_sub.status = "active"
|
|
|
|
# Setup query chain
|
|
mock_session.query.return_value.order_by.return_value.all.return_value = [
|
|
mock_folder
|
|
]
|
|
mock_session.query.return_value.filter_by.return_value.all.return_value = [
|
|
mock_sub
|
|
]
|
|
|
|
manager = FolderManager(mock_session)
|
|
result = manager.get_subscriptions_by_folder("user-123")
|
|
|
|
assert "folders" in result
|
|
assert "uncategorized" in result
|
|
|
|
def test_includes_uncategorized_subscriptions(self):
|
|
"""Includes subscriptions without folders."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
|
|
# No folders
|
|
mock_session.query.return_value.order_by.return_value.all.return_value = []
|
|
|
|
# Uncategorized subscription
|
|
mock_sub = MagicMock()
|
|
mock_sub.id = "sub-1"
|
|
mock_sub.subscription_type = "search"
|
|
mock_sub.query_or_topic = "Test"
|
|
mock_sub.created_at = datetime.now(timezone.utc)
|
|
mock_sub.last_refresh = None
|
|
mock_sub.next_refresh = None
|
|
mock_sub.refresh_interval_minutes = 60
|
|
mock_sub.status = "active"
|
|
|
|
# Return uncategorized for filter_by(folder=None, status="active")
|
|
mock_session.query.return_value.filter_by.return_value.all.return_value = [
|
|
mock_sub
|
|
]
|
|
|
|
manager = FolderManager(mock_session)
|
|
result = manager.get_subscriptions_by_folder("user-123")
|
|
|
|
assert len(result["uncategorized"]) > 0
|
|
|
|
|
|
class TestUpdateSubscriptionExtended:
|
|
"""Extended tests for update_subscription method."""
|
|
|
|
def test_update_changes_query_or_topic(self):
|
|
"""Updates query_or_topic field."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
mock_sub = MagicMock()
|
|
mock_sub.id = "sub-123"
|
|
mock_sub.query_or_topic = "Old Query"
|
|
mock_sub.updated_at = None
|
|
mock_session.query.return_value.filter_by.return_value.first.return_value = mock_sub
|
|
|
|
manager = FolderManager(mock_session)
|
|
result = manager.update_subscription(
|
|
"sub-123", query_or_topic="New Query"
|
|
)
|
|
|
|
assert mock_sub.query_or_topic == "New Query"
|
|
assert result == mock_sub
|
|
|
|
def test_update_changes_folder(self):
|
|
"""Updates folder field."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
mock_sub = MagicMock()
|
|
mock_sub.id = "sub-123"
|
|
mock_sub.folder = "Old Folder"
|
|
mock_sub.updated_at = None
|
|
mock_session.query.return_value.filter_by.return_value.first.return_value = mock_sub
|
|
|
|
manager = FolderManager(mock_session)
|
|
manager.update_subscription("sub-123", folder="New Folder")
|
|
|
|
assert mock_sub.folder == "New Folder"
|
|
|
|
def test_update_refresh_interval_with_existing_last_refresh(self):
|
|
"""Recalculates next_refresh based on last_refresh."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
mock_sub = MagicMock()
|
|
mock_sub.id = "sub-123"
|
|
mock_sub.last_refresh = datetime(
|
|
2024, 1, 15, 10, 0, tzinfo=timezone.utc
|
|
)
|
|
mock_sub.next_refresh = datetime(
|
|
2024, 1, 15, 11, 0, tzinfo=timezone.utc
|
|
)
|
|
mock_sub.updated_at = None
|
|
mock_session.query.return_value.filter_by.return_value.first.return_value = mock_sub
|
|
|
|
manager = FolderManager(mock_session)
|
|
manager.update_subscription("sub-123", refresh_interval_minutes=180)
|
|
|
|
# next_refresh = last_refresh + 180 minutes
|
|
expected = datetime(2024, 1, 15, 13, 0, tzinfo=timezone.utc)
|
|
assert mock_sub.next_refresh == expected
|
|
|
|
def test_update_refresh_interval_without_last_refresh(self):
|
|
"""Calculates next_refresh from now when no last_refresh."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
mock_sub = MagicMock()
|
|
mock_sub.id = "sub-123"
|
|
mock_sub.last_refresh = None
|
|
mock_sub.next_refresh = None
|
|
mock_sub.updated_at = None
|
|
mock_session.query.return_value.filter_by.return_value.first.return_value = mock_sub
|
|
|
|
manager = FolderManager(mock_session)
|
|
before = datetime.now(timezone.utc)
|
|
manager.update_subscription("sub-123", refresh_interval_minutes=30)
|
|
after = datetime.now(timezone.utc)
|
|
|
|
expected_min = before + timedelta(minutes=30)
|
|
expected_max = after + timedelta(minutes=30)
|
|
assert expected_min <= mock_sub.next_refresh <= expected_max
|
|
|
|
def test_update_commits_transaction(self):
|
|
"""Commits the transaction after update."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
mock_sub = MagicMock()
|
|
mock_sub.updated_at = None
|
|
mock_session.query.return_value.filter_by.return_value.first.return_value = mock_sub
|
|
|
|
manager = FolderManager(mock_session)
|
|
manager.update_subscription("sub-123", status="paused")
|
|
|
|
mock_session.commit.assert_called_once()
|
|
|
|
|
|
class TestDeleteSubscription:
|
|
"""Tests for delete_subscription method."""
|
|
|
|
def test_delete_existing_subscription(self):
|
|
"""Deletes existing subscription."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
mock_sub = MagicMock()
|
|
mock_session.query.return_value.filter_by.return_value.first.return_value = mock_sub
|
|
|
|
manager = FolderManager(mock_session)
|
|
result = manager.delete_subscription("sub-123")
|
|
|
|
assert result is True
|
|
mock_session.delete.assert_called_once_with(mock_sub)
|
|
mock_session.commit.assert_called_once()
|
|
|
|
def test_delete_nonexistent_subscription(self):
|
|
"""Returns False for nonexistent subscription."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
mock_session.query.return_value.filter_by.return_value.first.return_value = None
|
|
|
|
manager = FolderManager(mock_session)
|
|
result = manager.delete_subscription("nonexistent")
|
|
|
|
assert result is False
|
|
mock_session.delete.assert_not_called()
|
|
|
|
|
|
class TestGetSubscriptionStats:
|
|
"""Tests for get_subscription_stats method."""
|
|
|
|
def test_stats_structure(self):
|
|
"""Returns correct stats structure."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
mock_session.query.return_value.count.return_value = 10
|
|
mock_session.query.return_value.filter_by.return_value.count.return_value = 5
|
|
mock_session.query.return_value.order_by.return_value.all.return_value = []
|
|
|
|
manager = FolderManager(mock_session)
|
|
result = manager.get_subscription_stats("user-123")
|
|
|
|
assert "total" in result
|
|
assert "active" in result
|
|
assert "by_type" in result
|
|
assert "folders" in result
|
|
|
|
def test_stats_counts_by_type(self):
|
|
"""Counts subscriptions by type correctly."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
|
|
# Setup different counts for different filter combinations
|
|
def mock_filter_by(**kwargs):
|
|
result = MagicMock()
|
|
if (
|
|
kwargs.get("subscription_type") == "search"
|
|
and kwargs.get("status") == "active"
|
|
):
|
|
result.count.return_value = 3
|
|
elif (
|
|
kwargs.get("subscription_type") == "topic"
|
|
and kwargs.get("status") == "active"
|
|
):
|
|
result.count.return_value = 2
|
|
elif kwargs.get("status") == "active":
|
|
result.count.return_value = 5
|
|
else:
|
|
result.count.return_value = 0
|
|
return result
|
|
|
|
mock_session.query.return_value.count.return_value = 10
|
|
mock_session.query.return_value.filter_by = mock_filter_by
|
|
mock_session.query.return_value.order_by.return_value.all.return_value = []
|
|
|
|
manager = FolderManager(mock_session)
|
|
result = manager.get_subscription_stats("user-123")
|
|
|
|
assert result["by_type"]["search"] == 3
|
|
assert result["by_type"]["topic"] == 2
|
|
|
|
|
|
class TestSubToDict:
|
|
"""Tests for _sub_to_dict method."""
|
|
|
|
def test_includes_all_fields(self):
|
|
"""Includes all required fields."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
manager = FolderManager(mock_session)
|
|
|
|
mock_sub = MagicMock()
|
|
mock_sub.id = "sub-123"
|
|
mock_sub.subscription_type = "search"
|
|
mock_sub.query_or_topic = "AI news"
|
|
mock_sub.created_at = datetime(2024, 1, 15, 10, 0, tzinfo=timezone.utc)
|
|
mock_sub.last_refresh = datetime(
|
|
2024, 1, 16, 10, 0, tzinfo=timezone.utc
|
|
)
|
|
mock_sub.next_refresh = datetime(
|
|
2024, 1, 16, 11, 0, tzinfo=timezone.utc
|
|
)
|
|
mock_sub.refresh_interval_minutes = 60
|
|
mock_sub.status = "active"
|
|
|
|
result = manager._sub_to_dict(mock_sub)
|
|
|
|
assert result["id"] == "sub-123"
|
|
assert result["type"] == "search"
|
|
assert result["query_or_topic"] == "AI news"
|
|
assert result["refresh_interval_minutes"] == 60
|
|
assert result["status"] == "active"
|
|
|
|
def test_handles_none_timestamps(self):
|
|
"""Handles None timestamps gracefully."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
manager = FolderManager(mock_session)
|
|
|
|
mock_sub = MagicMock()
|
|
mock_sub.id = "sub-123"
|
|
mock_sub.subscription_type = "topic"
|
|
mock_sub.query_or_topic = "Tech"
|
|
mock_sub.created_at = None
|
|
mock_sub.last_refresh = None
|
|
mock_sub.next_refresh = None
|
|
mock_sub.refresh_interval_minutes = 60
|
|
mock_sub.status = "active"
|
|
|
|
result = manager._sub_to_dict(mock_sub)
|
|
|
|
assert result["created_at"] is None
|
|
assert result["last_refresh"] is None
|
|
assert result["next_refresh"] is None
|
|
|
|
def test_formats_dates_as_iso(self):
|
|
"""Formats dates as ISO strings."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
manager = FolderManager(mock_session)
|
|
|
|
mock_sub = MagicMock()
|
|
mock_sub.id = "sub-123"
|
|
mock_sub.subscription_type = "search"
|
|
mock_sub.query_or_topic = "Test"
|
|
mock_sub.created_at = datetime(
|
|
2024, 1, 15, 10, 30, 0, tzinfo=timezone.utc
|
|
)
|
|
mock_sub.last_refresh = None
|
|
mock_sub.next_refresh = None
|
|
mock_sub.refresh_interval_minutes = 60
|
|
mock_sub.status = "active"
|
|
|
|
result = manager._sub_to_dict(mock_sub)
|
|
|
|
assert result["created_at"] == "2024-01-15T10:30:00+00:00"
|
|
|
|
|
|
class TestFolderManagerEdgeCases:
|
|
"""Edge cases for FolderManager."""
|
|
|
|
def test_empty_folder_name(self):
|
|
"""Handles empty folder name."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
manager = FolderManager(mock_session)
|
|
|
|
result = manager.create_folder("")
|
|
assert result.name == ""
|
|
|
|
def test_unicode_folder_name(self):
|
|
"""Handles unicode folder name."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
manager = FolderManager(mock_session)
|
|
|
|
result = manager.create_folder("技术新闻 📁")
|
|
assert result.name == "技术新闻 📁"
|
|
|
|
def test_very_long_folder_name(self):
|
|
"""Handles very long folder name."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
manager = FolderManager(mock_session)
|
|
|
|
long_name = "A" * 1000
|
|
|
|
result = manager.create_folder(long_name)
|
|
assert result.name == long_name
|
|
|
|
def test_update_with_no_changes(self):
|
|
"""Handles update with no changes."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
mock_folder = MagicMock()
|
|
mock_folder.name = "Test"
|
|
mock_session.query.return_value.get.return_value = mock_folder
|
|
|
|
manager = FolderManager(mock_session)
|
|
result = manager.update_folder(1) # No kwargs
|
|
|
|
assert result is mock_folder
|
|
mock_session.commit.assert_called_once()
|
|
|
|
def test_subscription_with_zero_interval(self):
|
|
"""Handles subscription with zero refresh interval."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
mock_sub = MagicMock()
|
|
mock_sub.last_refresh = datetime.now(timezone.utc)
|
|
mock_sub.updated_at = None
|
|
mock_session.query.return_value.filter_by.return_value.first.return_value = mock_sub
|
|
|
|
manager = FolderManager(mock_session)
|
|
manager.update_subscription("sub-123", refresh_interval_minutes=0)
|
|
|
|
# next_refresh should be same as last_refresh (0 minutes later)
|
|
assert mock_sub.next_refresh is not None
|
|
|
|
def test_stats_with_no_subscriptions(self):
|
|
"""Returns zeros when no subscriptions exist."""
|
|
from local_deep_research.news.folder_manager import FolderManager
|
|
|
|
mock_session = MagicMock()
|
|
mock_session.query.return_value.count.return_value = 0
|
|
mock_session.query.return_value.filter_by.return_value.count.return_value = 0
|
|
mock_session.query.return_value.order_by.return_value.all.return_value = []
|
|
|
|
manager = FolderManager(mock_session)
|
|
result = manager.get_subscription_stats("user-123")
|
|
|
|
assert result["total"] == 0
|
|
assert result["active"] == 0
|
|
assert result["folders"] == 0
|