ytptube/app/features/tasks/tests/test_tasks_repository.py
2026-06-14 18:35:03 +03:00

267 lines
9.9 KiB
Python

"""Tests for TasksRepository."""
from __future__ import annotations
import pytest
import pytest_asyncio
from app.features.tasks.repository import TasksRepository
from app.library.sqlite_store import SqliteStore
from app.tests.helpers import make_in_memory_db_path
@pytest_asyncio.fixture
async def repo():
"""Provide a fresh repository instance with initialized database for each test."""
TasksRepository._reset_singleton()
SqliteStore._reset_singleton()
store = SqliteStore(db_path=make_in_memory_db_path("tasks-repository"))
await store.get_connection()
repository = TasksRepository.get_instance()
yield repository
await store.close()
TasksRepository._reset_singleton()
SqliteStore._reset_singleton()
class TestTasksRepository:
"""Test suite for TasksRepository database operations."""
@pytest.mark.asyncio
async def test_create_task(self, repo):
"""Create task with valid data."""
data = {
"name": "Daily Download",
"url": "https://example.com/video",
"folder": "/downloads",
"preset": "audio",
"timer": "0 0 * * *",
"template": "%(title)s.%(ext)s",
"cli": "--format best",
"auto_start": True,
"handler_enabled": True,
"enabled": True,
}
model = await repo.create(data)
assert model.id is not None, "Should generate ID for new task"
assert model.name == "Daily Download", "Should store name correctly"
assert model.url == "https://example.com/video", "Should store URL correctly"
assert model.folder == "/downloads", "Should store folder correctly"
assert model.preset == "audio", "Should store preset correctly"
assert model.timer == "0 0 * * *", "Should store timer correctly"
assert model.template == "%(title)s.%(ext)s", "Should store template correctly"
assert model.cli == "--format best", "Should store CLI correctly"
assert model.auto_start is True, "Should store auto_start correctly"
assert model.handler_enabled is True, "Should store handler_enabled correctly"
assert model.enabled is True, "Should store enabled correctly"
assert model.created_at is not None, "Should have created_at timestamp"
assert model.updated_at is not None, "Should have updated_at timestamp"
@pytest.mark.asyncio
async def test_create_with_minimal_data(self, repo):
"""Create task with minimal required data."""
data = {
"name": "Simple Task",
"url": "https://example.com",
}
model = await repo.create(data)
assert model.name == "Simple Task", "Should store name"
assert model.url == "https://example.com", "Should store URL"
assert model.folder == "", "Should default folder to empty string"
assert model.preset == "", "Should default preset to empty string"
assert model.timer == "", "Should default timer to empty string"
assert model.template == "", "Should default template to empty string"
assert model.cli == "", "Should default CLI to empty string"
assert model.auto_start is True, "Should default auto_start to True"
assert model.handler_enabled is True, "Should default handler_enabled to True"
assert model.enabled is True, "Should default enabled to True"
@pytest.mark.asyncio
async def test_get_by_id(self, repo):
"""Get task by integer ID."""
created = await repo.create(
{
"name": "Get Test",
"url": "https://example.com",
}
)
retrieved = await repo.get(created.id)
assert retrieved is not None, "Should retrieve created task"
assert retrieved.id == created.id, "Should retrieve correct task by ID"
assert retrieved.name == "Get Test", "Should match created task name"
@pytest.mark.asyncio
async def test_get_by_string_id(self, repo):
"""Get task by string ID."""
created = await repo.create(
{
"name": "String ID Test",
"url": "https://example.com",
}
)
retrieved = await repo.get(str(created.id))
assert retrieved is not None, "Should retrieve by string ID"
assert retrieved.id == created.id, "Should match created task ID"
@pytest.mark.asyncio
async def test_get_nonexistent(self, repo):
"""Get nonexistent task returns None."""
result = await repo.get(99999)
assert result is None, "Should return None for nonexistent ID"
result = await repo.get("99999")
assert result is None, "Should return None for nonexistent string ID"
@pytest.mark.asyncio
async def test_update_task(self, repo):
"""Update existing task."""
created = await repo.create(
{
"name": "Update Test",
"url": "https://example.com",
"preset": "video",
"enabled": True,
}
)
updated = await repo.update(
created.id,
{
"name": "Updated Name",
"preset": "audio",
"enabled": False,
},
)
assert updated.name == "Updated Name", "Should update name"
assert updated.preset == "audio", "Should update preset"
assert updated.enabled is False, "Should update enabled"
assert updated.url == "https://example.com", "Should preserve unchanged URL"
@pytest.mark.asyncio
async def test_update_nonexistent_raises(self, repo):
"""Update nonexistent task raises KeyError."""
with pytest.raises(KeyError, match="not found"):
await repo.update(99999, {"name": "should_fail"})
@pytest.mark.asyncio
async def test_delete_task(self, repo):
"""Delete existing task."""
created = await repo.create(
{
"name": "Delete Test",
"url": "https://example.com",
}
)
deleted = await repo.delete(created.id)
assert deleted.id == created.id, "Should return deleted task"
result = await repo.get(created.id)
assert result is None, "Deleted task should not be retrievable"
@pytest.mark.asyncio
async def test_delete_nonexistent_raises(self, repo):
"""Delete nonexistent task raises KeyError."""
with pytest.raises(KeyError, match="not found"):
await repo.delete(99999)
@pytest.mark.asyncio
async def test_get_by_name(self, repo):
"""Get task by name."""
await repo.create(
{
"name": "Named Task",
"url": "https://example.com",
}
)
retrieved = await repo.get_by_name("Named Task")
assert retrieved is not None, "Should retrieve by name"
assert retrieved.name == "Named Task", "Should match task name"
@pytest.mark.asyncio
async def test_get_by_name_excludes_id(self, repo):
"""Get by name can exclude specific ID."""
first = await repo.create(
{
"name": "duplicate",
"url": "https://example.com",
}
)
result = await repo.get_by_name("duplicate", exclude_id=first.id)
assert result is None, "Should not find when excluding only match"
result = await repo.get_by_name("duplicate", exclude_id=None)
assert result is not None, "Should find without exclusion"
@pytest.mark.asyncio
async def test_get_all_enabled(self, repo):
"""Get all enabled tasks."""
await repo.create({"name": "Enabled 1", "url": "https://example.com", "enabled": True})
await repo.create({"name": "Disabled", "url": "https://example.com", "enabled": False})
await repo.create({"name": "Enabled 2", "url": "https://example.com", "enabled": True})
enabled = await repo.get_all_enabled()
assert len(enabled) == 2, "Should return only enabled tasks"
assert all(task.enabled for task in enabled), "All tasks should be enabled"
@pytest.mark.asyncio
async def test_get_all_with_timer(self, repo):
"""Get all tasks with timer configured."""
await repo.create({"name": "With Timer", "url": "https://example.com", "timer": "0 0 * * *"})
await repo.create({"name": "No Timer", "url": "https://example.com", "timer": ""})
await repo.create({"name": "Another Timer", "url": "https://example.com", "timer": "0 12 * * *"})
with_timer = await repo.get_all_with_timer()
assert len(with_timer) == 2, "Should return only tasks with timer"
assert all(task.timer for task in with_timer), "All tasks should have timer"
@pytest.mark.asyncio
async def test_list_paginated(self, repo):
"""List paginated returns correct subset."""
for i in range(5):
await repo.create(
{
"name": f"Task {i}",
"url": "https://example.com",
}
)
items, total, page, total_pages = await repo.list_paginated(page=1, per_page=2)
assert len(items) == 2, "Should return 2 items per page"
assert total == 5, "Should report total count of 5"
assert page == 1, "Should be on page 1"
assert total_pages == 3, "Should have 3 pages total"
@pytest.mark.asyncio
async def test_list_ordering(self, repo):
"""List orders by name ascending."""
await repo.create({"name": "Charlie", "url": "https://example.com"})
await repo.create({"name": "Alice", "url": "https://example.com"})
await repo.create({"name": "Bob", "url": "https://example.com"})
items = await repo.all()
assert items[0].name == "Alice", "Should be alphabetically first"
assert items[1].name == "Bob", "Should be alphabetically second"
assert items[2].name == "Charlie", "Should be alphabetically third"