Files
7z-encrypt/tests/test_state.py
T

119 lines
4.1 KiB
Python

"""state 模块行为测试 (SQLite 真实读写, 非 mock)
运行: cd /home/lou/文档/7z-encrypt && .venv/bin/python -m unittest discover -s tests -v
"""
import os
import sys
import tempfile
import unittest
from datetime import datetime, timedelta
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from state import (
CHUNK_FAILED,
CHUNK_UPLOADED,
STATUS_DONE,
STATUS_FAILED,
STATUS_UPLOADING,
TaskStore,
)
class TaskStoreBase(unittest.TestCase):
def setUp(self):
self._td = tempfile.TemporaryDirectory(prefix='hermes-test-')
self.addCleanup(self._td.cleanup)
self.store = TaskStore(os.path.join(self._td.name, 'tasks.db'))
self.store.create_task('T1', 'a.mp4', 100, 3)
self.store.create_task('T2', 'b.mp4', 200, 2)
class TestCreate(TaskStoreBase):
def test_create_and_get(self):
t = self.store.get_task('T1')
self.assertEqual(t['file_name'], 'a.mp4')
self.assertEqual(t['file_size'], 100)
self.assertEqual(t['chunk_count'], 3)
self.assertEqual(t['status'], STATUS_UPLOADING)
self.assertIsNone(self.store.get_task('NOPE'))
def test_db_file_created(self):
self.assertTrue(os.path.exists(os.path.join(self._td.name, 'tasks.db')))
def test_persist_across_reopen(self):
store2 = TaskStore(os.path.join(self._td.name, 'tasks.db'))
t = store2.get_task('T1')
assert t is not None
self.assertEqual(t['file_name'], 'a.mp4')
class TestChunkEvents(TaskStoreBase):
def test_mark_uploaded(self):
self.store.mark_chunk_uploaded('T1', 1)
self.assertEqual(self.store.get_chunk_status('T1'), {1: CHUNK_UPLOADED})
def test_mark_failed(self):
self.store.mark_chunk_failed('T1', 2)
self.assertEqual(self.store.get_chunk_status('T1'), {2: CHUNK_FAILED})
def test_upsert_overwrite(self):
self.store.mark_chunk_uploaded('T1', 1)
self.store.mark_chunk_failed('T1', 1) # 覆盖为 failed
self.assertEqual(self.store.get_chunk_status('T1'), {1: CHUNK_FAILED})
class TestPending(TaskStoreBase):
def test_pending_contains_uploading(self):
pending = {t['task_id'] for t in self.store.get_pending_tasks()}
self.assertEqual(pending, {'T1', 'T2'})
def test_uploaded_chunks_listed(self):
self.store.mark_chunk_uploaded('T1', 1)
self.store.mark_chunk_uploaded('T1', 3)
self.store.mark_chunk_failed('T1', 2)
t1 = next(t for t in self.store.get_pending_tasks() if t['task_id'] == 'T1')
self.assertEqual(t1['uploaded_chunks'], [1, 3]) # failed 卷不算已传
def test_done_not_pending(self):
self.store.mark_done('T1')
pending = {t['task_id'] for t in self.store.get_pending_tasks()}
self.assertEqual(pending, {'T2'})
def test_failed_not_pending(self):
self.store.mark_failed('T1')
pending = {t['task_id'] for t in self.store.get_pending_tasks()}
self.assertEqual(pending, {'T2'})
class TestCleanup(TaskStoreBase):
def _age_task(self, task_id: str, days: int) -> None:
"""把任务 updated_at 改老, 模拟过期"""
old = (datetime.now() - timedelta(days=days)).isoformat(timespec='seconds')
with self.store._connect() as conn:
conn.execute("UPDATE tasks SET updated_at = ? WHERE task_id = ?", (old, task_id))
def test_cleanup_removes_old_done(self):
self.store.mark_done('T1')
self._age_task('T1', days=30)
n = self.store.cleanup(days=7)
self.assertEqual(n, 1)
self.assertIsNone(self.store.get_task('T1'))
def test_cleanup_keeps_uploading(self):
self._age_task('T1', days=30) # uploading 状态不受清理
n = self.store.cleanup(days=7)
self.assertEqual(n, 0)
self.assertIsNotNone(self.store.get_task('T1'))
def test_cleanup_removes_chunks_too(self):
self.store.mark_chunk_uploaded('T1', 1)
self.store.mark_done('T1')
self._age_task('T1', days=30)
self.store.cleanup(days=7)
self.assertEqual(self.store.get_chunk_status('T1'), {})
if __name__ == '__main__':
unittest.main()