afluakd9's picture
Initial commit without binary files
bbe872c
Raw History Blame Contribute Delete
8.76 kB
"""Tests for dashboard/db.py"""
import asyncio
import os
import tempfile
import time
# Must set DB_PATH before importing db
_tmpdir = tempfile.mkdtemp()
os.environ["DB_PATH"] = os.path.join(_tmpdir, "test_reclip.db")
import db # noqa: E402
_loop = asyncio.new_event_loop()
def run(coro):
return _loop.run_until_complete(coro)
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def _job(suffix=""):
return f"job-{suffix or int(time.time() * 1000)}"
async def _seed_download(job_id, platform="youtube", status="done", user_id=1,
username="alice", chat_id=100, url="https://example.com/v"):
await db.insert_download_start(
job_id=job_id, user_id=user_id, username=username,
chat_id=chat_id, url=url, platform=platform,
)
if status == "done":
await db.update_download_done(
job_id=job_id, title="Test Video", format="mp4",
quality="720p", file_size_bytes=1_000_000,
download_duration_sec=3.5,
)
elif status == "error":
await db.update_download_error(job_id=job_id, error_message="network timeout")
# ---------------------------------------------------------------------------
# Task 1 tests: CRUD
# ---------------------------------------------------------------------------
def test_insert_download_start():
job_id = _job("start1")
run(db.insert_download_start(
job_id=job_id, user_id=42, username="bob",
chat_id=999, url="https://youtube.com/watch?v=abc",
platform="youtube",
))
row = run(db.get_download_by_job_id(job_id))
assert row is not None
assert row["job_id"] == job_id
assert row["status"] == "started"
assert row["user_id"] == 42
assert row["username"] == "bob"
assert row["platform"] == "youtube"
assert row["url"] == "https://youtube.com/watch?v=abc"
def test_update_download_done():
job_id = _job("done1")
run(db.insert_download_start(
job_id=job_id, user_id=1, username="alice",
chat_id=10, url="https://example.com",
platform="tiktok",
))
run(db.update_download_done(
job_id=job_id, title="Cool Clip", format="mp4",
quality="1080p", file_size_bytes=5_000_000,
download_duration_sec=10.2,
))
row = run(db.get_download_by_job_id(job_id))
assert row["status"] == "done"
assert row["title"] == "Cool Clip"
assert row["format"] == "mp4"
assert row["quality"] == "1080p"
assert row["file_size_bytes"] == 5_000_000
assert abs(row["download_duration_sec"] - 10.2) < 0.01
assert row["completed_at"] is not None
def test_update_download_error():
job_id = _job("err1")
run(db.insert_download_start(
job_id=job_id, user_id=7, username="charlie",
chat_id=77, url="https://example.com",
platform="instagram",
))
run(db.update_download_error(job_id=job_id, error_message="403 Forbidden"))
row = run(db.get_download_by_job_id(job_id))
assert row["status"] == "error"
assert row["error_message"] == "403 Forbidden"
assert row["completed_at"] is not None
def test_get_download_by_job_id_missing():
row = run(db.get_download_by_job_id("nonexistent-job-id"))
assert row is None
# ---------------------------------------------------------------------------
# Task 2 tests: stats queries
# ---------------------------------------------------------------------------
def test_get_dashboard_stats_empty():
"""Stats on a fresh DB (after inserts above, but we want structure check)."""
stats = run(db.get_dashboard_stats())
assert "downloads_today" in stats
assert "downloads_yesterday" in stats
assert "active_users_24h" in stats
assert "error_rate" in stats
assert "error_rate_yesterday" in stats
assert "errors_today" in stats
assert "total_today" in stats
def test_get_dashboard_stats_with_data():
job_done = _job("stats-done")
job_err = _job("stats-err")
run(_seed_download(job_done, platform="youtube", status="done"))
run(_seed_download(job_err, platform="youtube", status="error"))
stats = run(db.get_dashboard_stats())
# At least the downloads we inserted are counted
assert stats["downloads_today"] >= 1
assert stats["errors_today"] >= 1
assert stats["total_today"] >= 2
def test_get_chart_data_ranges():
for rng in ("1D", "7D", "1M", "1Y"):
data = run(db.get_chart_data(rng))
assert "labels" in data
assert "values" in data
assert "platforms" in data
assert "top_users" in data
assert isinstance(data["labels"], list)
assert isinstance(data["values"], list)
assert isinstance(data["platforms"], list)
assert isinstance(data["top_users"], list)
assert len(data["labels"]) == len(data["values"])
def test_get_chart_data_platform_top5():
data = run(db.get_chart_data("7D"))
assert len(data["platforms"]) <= 5
for p in data["platforms"]:
assert "platform" in p
assert "count" in p
def test_get_chart_data_top_users():
data = run(db.get_chart_data("7D"))
assert len(data["top_users"]) <= 5
for u in data["top_users"]:
assert "username" in u
assert "count" in u
def test_get_downloads_page_basic():
result = run(db.get_downloads_page(page=1, per_page=10))
assert "rows" in result
assert "total" in result
assert "pages" in result
assert "page" in result
assert result["page"] == 1
assert isinstance(result["rows"], list)
def test_get_downloads_page_pagination():
# Seed 5 more downloads
for i in range(5):
run(_seed_download(_job(f"page-{i}"), platform="youtube", status="done",
user_id=10 + i, username=f"user{i}"))
result_p1 = run(db.get_downloads_page(page=1, per_page=3))
assert len(result_p1["rows"]) == 3
assert result_p1["total"] >= 5
result_p2 = run(db.get_downloads_page(page=2, per_page=3))
assert len(result_p2["rows"]) >= 1
# No overlap between pages
ids_p1 = {r["id"] for r in result_p1["rows"]}
ids_p2 = {r["id"] for r in result_p2["rows"]}
assert ids_p1.isdisjoint(ids_p2)
def test_get_downloads_page_filters():
run(_seed_download(_job("filter-tw"), platform="twitter", status="done",
user_id=999, username="twitteruser"))
result = run(db.get_downloads_page(page=1, per_page=50, platform="twitter"))
assert all(r["platform"] == "twitter" for r in result["rows"])
result_err = run(db.get_downloads_page(page=1, per_page=50, status="error"))
assert all(r["status"] == "error" for r in result_err["rows"])
result_user = run(db.get_downloads_page(page=1, per_page=50, user="twitteruser"))
assert any(r["username"] == "twitteruser" for r in result_user["rows"])
def test_get_error_downloads():
errors = run(db.get_error_downloads())
assert isinstance(errors, list)
for e in errors:
assert e["status"] == "error"
def test_get_error_downloads_date_filter():
from datetime import datetime, timezone, timedelta
yesterday = (datetime.now(timezone.utc) - timedelta(days=1)).strftime("%Y-%m-%d")
tomorrow = (datetime.now(timezone.utc) + timedelta(days=1)).strftime("%Y-%m-%d")
errors = run(db.get_error_downloads(date_from=yesterday, date_to=tomorrow))
assert isinstance(errors, list)
# All errors we seeded today should appear
for e in errors:
assert e["status"] == "error"
def test_insert_and_get_disk_snapshot():
run(db.insert_disk_snapshot(total_bytes=1_000_000_000, file_count=42))
snap = run(db.get_latest_disk_snapshot())
assert snap is not None
assert snap["total_bytes"] == 1_000_000_000
assert snap["file_count"] == 42
assert snap["snapshot_at"] is not None
def test_get_latest_disk_snapshot_empty():
# Use a separate temp DB to test empty state
with tempfile.TemporaryDirectory() as d:
old_path = os.environ["DB_PATH"]
empty_path = os.path.join(d, "empty.db")
os.environ["DB_PATH"] = empty_path
# init_db will create schema on empty_path
run(db.init_db())
snap = run(db.get_latest_disk_snapshot())
assert snap is None
os.environ["DB_PATH"] = old_path
def test_disk_snapshot_latest_is_newest():
run(db.insert_disk_snapshot(total_bytes=100, file_count=1))
run(db.insert_disk_snapshot(total_bytes=200, file_count=2))
run(db.insert_disk_snapshot(total_bytes=300, file_count=3))
snap = run(db.get_latest_disk_snapshot())
assert snap["total_bytes"] == 300
assert snap["file_count"] == 3