| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566 |
- from pathlib import Path
- from tempfile import TemporaryDirectory
- import unittest
- from unittest.mock import Mock, patch
- from scripts.memory_service import render, unit_name
- from scripts.deploy import Deployment, MODULE
- class MemoryServiceTests(unittest.TestCase):
- def test_render_limits_children_swap_and_restart_storms(self):
- content = render('/opt/business-assistant', 'assistant', 4096)
- for expected in ('MemoryMax=4096M', 'MemoryHigh=3584M', 'MemorySwapMax=0',
- 'OOMPolicy=kill', 'KillMode=control-group', 'Restart=on-failure',
- 'StartLimitBurst=3', 'API_REQUIRE_MEMORY_CGROUP=1',
- 'API_MEMORY_HARD_LIMIT_MB=4096', 'API_MEMORY_BUDGET_MB=3072',
- 'ExecStart=/bin/sh /opt/business-assistant/deploy.sh serve'):
- self.assertIn(expected, content)
- self.assertNotIn('EnvironmentFile', content)
- self.assertEqual(unit_name('/opt/a'), unit_name('/opt/a'))
- self.assertNotEqual(unit_name('/opt/a'), unit_name('/opt/b'))
- def test_render_rejects_unsafe_parameters(self):
- for args in [('/opt/a\nMemoryMax=infinity', 'assistant', 4096),
- ('/opt/%a', 'assistant', 4096), ('/opt/a', 'root', 4096),
- ('/opt/a', 'assistant', 100), ('/opt/a', 'a\nUser=root', 4096)]:
- with self.assertRaises(ValueError):
- render(*args)
- def test_render_uses_offline_entry_for_extracted_release(self):
- content = render('/opt/business-assistant', 'assistant', 4096, offline=True)
- self.assertIn('ExecStart=/bin/sh /opt/business-assistant/deploy-offline.sh serve', content)
- def test_installed_unit_routes_start_stop_without_background_fallback(self):
- with TemporaryDirectory() as directory:
- deployment = Deployment(Path(directory))
- deployment.systemd_managed = lambda: True
- deployment.systemd_pid = lambda: 1
- deployment.settings = Mock(return_value=Mock(host='127.0.0.1', port=8000))
- deployment.processes = Mock(return_value=[{'pid': 1}])
- deployment.healthy = Mock(return_value=True)
- deployment.run = Mock()
- with patch('subprocess.Popen') as popen:
- deployment.start()
- popen.assert_not_called()
- deployment.run.assert_called_once_with('systemctl', 'start', unit_name(deployment.root))
- deployment.run.reset_mock()
- deployment.processes.return_value = []
- deployment.stop()
- deployment.run.assert_called_once_with('systemctl', 'stop', unit_name(deployment.root))
- deployment.run.side_effect = RuntimeError('synthetic permission denied')
- with patch('subprocess.Popen') as popen:
- with self.assertRaises(RuntimeError):
- deployment.start()
- popen.assert_not_called()
- def test_foreground_resolves_selected_runtime_and_requires_guard(self):
- with TemporaryDirectory() as directory:
- deployment = Deployment(Path(directory))
- deployment.processes = Mock(return_value=[])
- with patch.dict('os.environ', {}, clear=True):
- with self.assertRaises(RuntimeError):
- deployment.serve()
- with patch.dict('os.environ', {'API_REQUIRE_MEMORY_CGROUP': '1'}), patch('os.execv') as execute:
- deployment.serve()
- execute.assert_called_once_with(str(deployment.python), [str(deployment.python), '-m', MODULE])
|