224 lines
8.1 KiB
Python
224 lines
8.1 KiB
Python
"""Tests for QueueStore - JSON persistence, corruption recovery, and CRUD operations."""
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
|
|
from models import QueueItem
|
|
from models.queue_store import QueueStore
|
|
|
|
|
|
def make_item(item_id="test1", video_id="abc123", title="Test Video", status="pending", **kwargs):
|
|
return QueueItem(
|
|
id=item_id,
|
|
video_id=video_id,
|
|
title=title,
|
|
url=f"https://youtube.com/watch?v={video_id}",
|
|
thumbnail="https://img.youtube.com/vi/" + video_id + "/hqdefault.jpg",
|
|
status=status,
|
|
progress=kwargs.get("progress", 0.0),
|
|
category=kwargs.get("category", "General"),
|
|
added_at=kwargs.get("added_at", "2026-01-01T00:00:00+00:00"),
|
|
quality=kwargs.get("quality", "1080"),
|
|
)
|
|
|
|
|
|
def test_create_empty_store():
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
path = os.path.join(tmpdir, "test_queue.json")
|
|
store = QueueStore(store_path=path)
|
|
assert store.get_all() == []
|
|
print("PASS: test_create_empty_store")
|
|
|
|
|
|
def test_add_and_get_item():
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
path = os.path.join(tmpdir, "test_queue.json")
|
|
store = QueueStore(store_path=path)
|
|
item = make_item()
|
|
store.add_item(item)
|
|
items = store.get_all()
|
|
assert len(items) == 1
|
|
assert items[0].id == "test1"
|
|
assert items[0].title == "Test Video"
|
|
print("PASS: test_add_and_get_item")
|
|
|
|
|
|
def test_update_status():
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
path = os.path.join(tmpdir, "test_queue.json")
|
|
store = QueueStore(store_path=path)
|
|
store.add_item(make_item())
|
|
store.update_status("test1", "completed", download_path="/foo/bar.mp4", file_size="100MB")
|
|
item = store.get_item("test1")
|
|
assert item.status == "completed"
|
|
assert item.download_path == "/foo/bar.mp4"
|
|
assert item.file_size == "100MB"
|
|
assert item.progress == 100.0
|
|
print("PASS: test_update_status")
|
|
|
|
|
|
def test_update_progress():
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
path = os.path.join(tmpdir, "test_queue.json")
|
|
store = QueueStore(store_path=path)
|
|
store.add_item(make_item())
|
|
store.update_progress("test1", 45.5, speed="1.2MB/s", eta="5m")
|
|
item = store.get_item("test1")
|
|
assert item.progress == 45.5
|
|
assert item.speed == "1.2MB/s"
|
|
assert item.eta == "5m"
|
|
print("PASS: test_update_progress")
|
|
|
|
|
|
def test_remove_item():
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
path = os.path.join(tmpdir, "test_queue.json")
|
|
store = QueueStore(store_path=path)
|
|
store.add_item(make_item())
|
|
assert store.remove_item("test1") is True
|
|
assert store.get_all() == []
|
|
assert store.remove_item("nonexistent") is False
|
|
print("PASS: test_remove_item")
|
|
|
|
|
|
def test_clear_completed():
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
path = os.path.join(tmpdir, "test_queue.json")
|
|
store = QueueStore(store_path=path)
|
|
store.add_item(make_item(item_id="done1", status="completed"))
|
|
store.add_item(make_item(item_id="done2", status="completed"))
|
|
store.add_item(make_item(item_id="pend1", status="pending"))
|
|
removed = store.clear_completed()
|
|
assert removed == 2
|
|
assert len(store.get_all()) == 1
|
|
assert store.get_item("pend1").status == "pending"
|
|
print("PASS: test_clear_completed")
|
|
|
|
|
|
def test_clear_failed():
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
path = os.path.join(tmpdir, "test_queue.json")
|
|
store = QueueStore(store_path=path)
|
|
store.add_item(make_item(item_id="fail1", status="failed"))
|
|
store.add_item(make_item(item_id="pend1", status="pending"))
|
|
removed = store.clear_failed()
|
|
assert removed == 1
|
|
assert len(store.get_all()) == 1
|
|
print("PASS: test_clear_failed")
|
|
|
|
|
|
def test_clear_all():
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
path = os.path.join(tmpdir, "test_queue.json")
|
|
store = QueueStore(store_path=path)
|
|
store.add_item(make_item(item_id="a"))
|
|
store.add_item(make_item(item_id="b"))
|
|
removed = store.clear_all()
|
|
assert removed == 2
|
|
assert len(store.get_all()) == 0
|
|
print("PASS: test_clear_all")
|
|
|
|
|
|
def test_get_stats():
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
path = os.path.join(tmpdir, "test_queue.json")
|
|
store = QueueStore(store_path=path)
|
|
store.add_item(make_item(item_id="p1", status="pending"))
|
|
store.add_item(make_item(item_id="d1", status="downloading"))
|
|
store.add_item(make_item(item_id="c1", status="completed"))
|
|
store.add_item(make_item(item_id="f1", status="failed"))
|
|
stats = store.get_stats()
|
|
assert stats["total"] == 4
|
|
assert stats["pending"] == 1
|
|
assert stats["downloading"] == 1
|
|
assert stats["completed"] == 1
|
|
assert stats["failed"] == 1
|
|
print("PASS: test_get_stats")
|
|
|
|
|
|
def test_corrupted_json_recovery():
|
|
"""Test that corrupted JSON file is handled gracefully."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
path = os.path.join(tmpdir, "test_queue.json")
|
|
with open(path, "w") as f:
|
|
f.write('{"id": "test1", "title": "Video with invalid char: \x01\x02\x03"}')
|
|
store = QueueStore(store_path=path)
|
|
items = store.get_all()
|
|
assert items == []
|
|
assert store.get_item("test1") is None
|
|
print("PASS: test_corrupted_json_recovery")
|
|
|
|
|
|
def test_corrupted_json_with_control_chars():
|
|
"""Test recovery from control character corruption (the actual bug)."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
path = os.path.join(tmpdir, "test_queue.json")
|
|
with open(path, "w") as f:
|
|
f.write('{"test1": {"id": "test1", "title": "Error: Some long message\nwith\ncontrol\x00chars"}}')
|
|
store = QueueStore(store_path=path)
|
|
items = store.get_all()
|
|
assert items == []
|
|
print("PASS: test_corrupted_json_with_control_chars")
|
|
|
|
|
|
def test_empty_file_recovery():
|
|
"""Test recovery from empty file."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
path = os.path.join(tmpdir, "test_queue.json")
|
|
with open(path, "w") as f:
|
|
f.write("")
|
|
store = QueueStore(store_path=path)
|
|
items = store.get_all()
|
|
assert items == []
|
|
print("PASS: test_empty_file_recovery")
|
|
|
|
|
|
def test_persistence_across_instances():
|
|
"""Test that data persists when creating new QueueStore instance."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
path = os.path.join(tmpdir, "test_queue.json")
|
|
store1 = QueueStore(store_path=path)
|
|
store1.add_item(make_item())
|
|
del store1
|
|
store2 = QueueStore(store_path=path)
|
|
items = store2.get_all()
|
|
assert len(items) == 1
|
|
assert items[0].id == "test1"
|
|
print("PASS: test_persistence_across_instances")
|
|
|
|
|
|
def test_error_message_with_special_chars():
|
|
"""Test that error messages with special characters don't corrupt the file."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
path = os.path.join(tmpdir, "test_queue.json")
|
|
store = QueueStore(store_path=path)
|
|
store.add_item(make_item())
|
|
special_msg = "Error: Connection timeout\nRetrying...\nFailed after 3 attempts"
|
|
store.update_status("test1", "failed", error_message=special_msg)
|
|
item = store.get_item("test1")
|
|
assert item.error_message == special_msg
|
|
assert item.status == "failed"
|
|
print("PASS: test_error_message_with_special_chars")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
test_create_empty_store()
|
|
test_add_and_get_item()
|
|
test_update_status()
|
|
test_update_progress()
|
|
test_remove_item()
|
|
test_clear_completed()
|
|
test_clear_failed()
|
|
test_clear_all()
|
|
test_get_stats()
|
|
test_corrupted_json_recovery()
|
|
test_corrupted_json_with_control_chars()
|
|
test_empty_file_recovery()
|
|
test_persistence_across_instances()
|
|
test_error_message_with_special_chars()
|
|
print("\nAll tests passed!") |