dubizzle project
This commit is contained in:
81
tests/test_self_heal.py
Normal file
81
tests/test_self_heal.py
Normal file
@@ -0,0 +1,81 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from dubizzle_scraper.worker import self_heal
|
||||
|
||||
|
||||
class TestSelfHeal(unittest.TestCase):
|
||||
def test_read_last_progress_ts_uses_global_key(self) -> None:
|
||||
redis_client = MagicMock()
|
||||
redis_client.get.return_value = "1776800000"
|
||||
|
||||
ts = self_heal._read_last_progress_ts(redis_client)
|
||||
|
||||
self.assertEqual(ts, 1776800000)
|
||||
redis_client.scan_iter.assert_not_called()
|
||||
|
||||
def test_read_last_progress_ts_fallbacks_to_task_progress_keys(self) -> None:
|
||||
redis_client = MagicMock()
|
||||
redis_client.get.side_effect = lambda key: {
|
||||
self_heal.GLOBAL_PROGRESS_TS_KEY: None,
|
||||
"dubizzle:state:task_progress:a": '{"ts": 100}',
|
||||
"dubizzle:state:task_progress:b": '{"ts": 250}',
|
||||
"dubizzle:state:task_progress:c": '{"ts": 150}',
|
||||
}.get(key)
|
||||
redis_client.scan_iter.return_value = [
|
||||
"dubizzle:state:task_progress:a",
|
||||
"dubizzle:state:task_progress:b",
|
||||
"dubizzle:state:task_progress:c",
|
||||
]
|
||||
|
||||
ts = self_heal._read_last_progress_ts(redis_client)
|
||||
|
||||
self.assertEqual(ts, 250)
|
||||
|
||||
def test_read_last_progress_ts_ignores_broken_payloads(self) -> None:
|
||||
redis_client = MagicMock()
|
||||
redis_client.get.side_effect = lambda key: {
|
||||
self_heal.GLOBAL_PROGRESS_TS_KEY: None,
|
||||
"dubizzle:state:task_progress:a": "{bad-json}",
|
||||
"dubizzle:state:task_progress:b": '{"foo": "bar"}',
|
||||
}.get(key)
|
||||
redis_client.scan_iter.return_value = [
|
||||
"dubizzle:state:task_progress:a",
|
||||
"dubizzle:state:task_progress:b",
|
||||
]
|
||||
|
||||
ts = self_heal._read_last_progress_ts(redis_client)
|
||||
|
||||
self.assertIsNone(ts)
|
||||
|
||||
@patch("dubizzle_scraper.worker.self_heal.time.sleep", return_value=None)
|
||||
@patch("dubizzle_scraper.worker.self_heal.os.kill")
|
||||
@patch("builtins.open")
|
||||
def test_kill_worker_process_sends_term_and_kill(self, open_mock, kill_mock, _sleep_mock) -> None:
|
||||
open_mock.return_value.__enter__.return_value.read.return_value = "123"
|
||||
# SIGTERM -> process alive check (pid,0) -> SIGKILL
|
||||
kill_mock.side_effect = [None, None, None]
|
||||
|
||||
self_heal._kill_worker_process()
|
||||
|
||||
self.assertEqual(kill_mock.call_args_list[0].args[0], 123)
|
||||
self.assertEqual(kill_mock.call_args_list[1].args, (123, 0))
|
||||
self.assertEqual(kill_mock.call_args_list[2].args[0], 123)
|
||||
|
||||
@patch("dubizzle_scraper.worker.self_heal.time.sleep", return_value=None)
|
||||
@patch("dubizzle_scraper.worker.self_heal.os.kill")
|
||||
@patch("builtins.open")
|
||||
def test_kill_worker_process_skips_sigkill_when_already_exited(self, open_mock, kill_mock, _sleep_mock) -> None:
|
||||
open_mock.return_value.__enter__.return_value.read.return_value = "123"
|
||||
kill_mock.side_effect = [None, ProcessLookupError()]
|
||||
|
||||
self_heal._kill_worker_process()
|
||||
|
||||
# Только SIGTERM и проверка существования процесса.
|
||||
self.assertEqual(len(kill_mock.call_args_list), 2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user