test_service_control.py 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. """Real loopback service control without starting the business API."""
  2. import json
  3. from pathlib import Path
  4. from types import SimpleNamespace
  5. import socket
  6. import tempfile
  7. import threading
  8. import time
  9. import unittest
  10. from unittest.mock import patch
  11. from step4_web import service_control as control
  12. class ControlTests(unittest.TestCase):
  13. def setUp(self):
  14. self.tmp=tempfile.TemporaryDirectory()
  15. self.addCleanup(self.tmp.cleanup)
  16. self.root=Path(self.tmp.name)
  17. self.patch=patch.object(control,'ROOT',self.root)
  18. self.patch.start(); self.addCleanup(self.patch.stop)
  19. def test_stop_waits_for_exit_and_allows_new_instance(self):
  20. server=SimpleNamespace(should_exit=False)
  21. ready=threading.Event()
  22. def worker():
  23. with control.managed_service(server):
  24. ready.set()
  25. while not server.should_exit: time.sleep(.01)
  26. thread=threading.Thread(target=worker)
  27. thread.start()
  28. try:
  29. self.assertTrue(ready.wait(3))
  30. self.assertTrue(control.running())
  31. control.stop_for_restart(timeout=3)
  32. thread.join(3)
  33. self.assertFalse(thread.is_alive())
  34. with control.managed_service(SimpleNamespace(should_exit=False)):
  35. self.assertTrue(control.running())
  36. self.assertFalse((self.root/'.runtime/service-control.json').exists())
  37. finally:
  38. server.should_exit=True
  39. thread.join(3)
  40. def test_wrong_token_cannot_stop_service(self):
  41. server=SimpleNamespace(should_exit=False)
  42. with control.managed_service(server):
  43. data=json.loads((self.root/'.runtime/service-control.json').read_text())
  44. with socket.create_connection(('127.0.0.1',data['port'])) as conn:
  45. conn.sendall(b'wrong-token\n')
  46. self.assertEqual(conn.recv(32).strip(),b'DENIED')
  47. self.assertFalse(server.should_exit)
  48. with self.assertRaises(RuntimeError):
  49. with control.managed_service(server): pass
  50. def test_timeout_does_not_start_or_force_kill(self):
  51. server=SimpleNamespace(should_exit=False)
  52. with control.managed_service(server):
  53. with self.assertRaisesRegex(RuntimeError,'未启动新实例'):
  54. control.stop_for_restart(timeout=.01)
  55. self.assertTrue(server.should_exit)
  56. def test_stale_state_does_not_signal_any_process(self):
  57. folder=self.root/'.runtime'; folder.mkdir()
  58. (folder/'service-control.json').write_text('{}')
  59. control.stop_for_restart()
  60. self.assertFalse(control.running())
  61. if __name__=='__main__': unittest.main()