[Feat] Add the function which can remove existed service from services.json

This commit is contained in:
2025-07-12 14:01:18 +08:00
parent f935f6ad3f
commit bca1965507
4 changed files with 202 additions and 3 deletions

View File

@ -49,4 +49,6 @@
## TODO ## TODO
- [ ] Add the function that can remove services - [x] Add the function that can remove services
- [ ] Package src as a module

13
main.py
View File

@ -23,6 +23,10 @@ def main():
operate_parser = subparsers.add_parser("operate", help="Service operations to carry out") operate_parser = subparsers.add_parser("operate", help="Service operations to carry out")
operate_parser.add_argument("index", type=int, help="Index of the service, which is a integer") operate_parser.add_argument("index", type=int, help="Index of the service, which is a integer")
# 移除服务命令
remove_parser = subparsers.add_parser("remove", help="Remove a service")
remove_parser.add_argument("index", type=int, help="Index of the service to remove")
args = parser.parse_args() args = parser.parse_args()
# 初始化仓库和管理器 # 初始化仓库和管理器
@ -51,6 +55,15 @@ def main():
print("Error: invalid index") print("Error: invalid index")
except Exception as e: except Exception as e:
print(f"Operation failed: {str(e)}") print(f"Operation failed: {str(e)}")
elif args.command == "remove":
try:
manager.remove_service(args.index)
print(f"Service at index {args.index} removed successfully")
except IndexError:
print("Error: invalid index")
except Exception as e:
print(f"Remove failed: {str(e)}")
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -63,7 +63,7 @@ class ServiceRepository:
def __init__(self, file_path: str = 'data/services.json'): def __init__(self, file_path: str = 'data/services.json'):
self.file_path = file_path self.file_path = file_path
# 确保目录存在 # Make sure the path has existed
dir_path = os.path.dirname(file_path) dir_path = os.path.dirname(file_path)
if dir_path: if dir_path:
os.makedirs(dir_path, exist_ok=True) os.makedirs(dir_path, exist_ok=True)
@ -100,6 +100,47 @@ class ServiceRepository:
return [] return []
logger.info("Service repository file not found, starting fresh") logger.info("Service repository file not found, starting fresh")
return [] return []
def remove(self, service_name: str) -> None:
"""Remove a service by name.
Args:
service_name: Name of the service to remove
Raises:
ValueError: If service not found
"""
try:
# Load all servives
services = self.load_all()
# Search for matching services (case insensitive)
original_count = len(services)
services = [s for s in services
if s['name'].strip().lower() != service_name.strip().lower()]
# Check and remove
if len(services) == original_count:
raise ValueError(f"Service '{service_name}' not found")
# Update services,json
with open(self.file_path, 'w') as file:
json.dump(services, file, indent=4)
logger.info(f"Removed service: {service_name}")
except FileNotFoundError:
logger.warning("Services file not found, nothing to remove")
raise ValueError("Services file does not exist")
except json.JSONDecodeError:
logger.error("Invalid JSON format in services file")
raise ValueError("Invalid services data format")
except PermissionError:
logger.error("Permission denied when writing services file")
raise
except Exception as e:
logger.error(f"Error removing service: {str(e)}")
raise
class Manager: class Manager:
@ -151,7 +192,16 @@ class Manager:
logger.error(f"Service registration failed: {str(e)}") logger.error(f"Service registration failed: {str(e)}")
raise raise
# TODO: Add the function that can remove services def remove_service(self, service_name: str) -> None:
"""Remove a service by name.
Args:
service_name: Name of the service to remove
Raises:
ValueError: If service not found
"""
self.repository.remove(service_name)
def list_services(self) -> List[Service]: def list_services(self) -> List[Service]:
"""List all registered services. """List all registered services.
@ -192,3 +242,24 @@ class Manager:
service = services[index] service = services[index]
service.service_operation() service.service_operation()
if __name__ == "__main__":
# 测试服务移除功能
manager = Manager()
try:
# 注册测试服务
test_service = manager.register_service("sys", "test-service")
print("Registered test service")
# 移除测试服务
manager.remove_service("test-service")
print("Successfully removed test service")
# 尝试移除不存在的服务
try:
manager.remove_service("non-existent")
except ValueError as e:
print(f"Expected error: {str(e)}")
except Exception as e:
print(f"Test failed: {str(e)}")

View File

@ -2,6 +2,7 @@ import unittest
from unittest.mock import patch, MagicMock from unittest.mock import patch, MagicMock
from src.manager import ServiceFactory, ServiceRepository, Manager from src.manager import ServiceFactory, ServiceRepository, Manager
import os import os
import json
import tempfile import tempfile
class TestServiceFactory(unittest.TestCase): class TestServiceFactory(unittest.TestCase):
@ -59,6 +60,65 @@ class TestServiceRepository(unittest.TestCase):
services = self.repo.load_all() services = self.repo.load_all()
self.assertEqual(len(services), 0) self.assertEqual(len(services), 0)
def test_remove_service_success(self):
"""测试正常移除服务"""
# 添加测试服务
mock_service = MagicMock()
mock_service.to_dict.return_value = {
"tag": "sys",
"name": "nginx",
"path": None
}
self.repo.save(mock_service)
# 移除服务
self.repo.remove("nginx")
# 验证服务已被移除
services = self.repo.load_all()
self.assertEqual(len(services), 0)
def test_remove_service_case_insensitive(self):
"""测试大小写不敏感移除"""
# 添加测试服务
mock_service = MagicMock()
mock_service.to_dict.return_value = {
"tag": "sys",
"name": "Nginx",
"path": None
}
self.repo.save(mock_service)
# 使用小写名称移除
self.repo.remove("nginx")
# 验证服务已被移除
services = self.repo.load_all()
self.assertEqual(len(services), 0)
def test_remove_service_whitespace(self):
"""测试移除带空格的服务名"""
# 添加测试服务
mock_service = MagicMock()
mock_service.to_dict.return_value = {
"tag": "sys",
"name": " nginx ",
"path": None
}
self.repo.save(mock_service)
# 使用带空格名称移除
self.repo.remove(" nginx ")
# 验证服务已被移除
services = self.repo.load_all()
self.assertEqual(len(services), 0)
def test_remove_nonexistent_service(self):
"""测试移除不存在的服务"""
with self.assertRaises(ValueError):
self.repo.remove("nonexistent")
class TestManager(unittest.TestCase): class TestManager(unittest.TestCase):
@patch("src.manager.ServiceRepository") @patch("src.manager.ServiceRepository")
@ -130,6 +190,59 @@ class TestManager(unittest.TestCase):
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
manager.register_service("invalid", "invalid_service") manager.register_service("invalid", "invalid_service")
@patch("src.manager.ServiceRepository")
def test_remove_service_success(self, mock_repo):
"""测试正常移除服务"""
manager = Manager(mock_repo.return_value)
service_name = "nginx"
# 调用remove_service
manager.remove_service(service_name)
# 验证ServiceRepository的remove方法被正确调用
mock_repo.return_value.remove.assert_called_once_with(service_name)
@patch("src.manager.ServiceRepository")
def test_remove_service_case_insensitive(self, mock_repo):
"""测试大小写不敏感移除"""
manager = Manager(mock_repo.return_value)
# 使用混合大小写
manager.remove_service("NgInX")
# 验证调用时使用原始大小写
mock_repo.return_value.remove.assert_called_once_with("NgInX")
@patch("src.manager.ServiceRepository")
def test_remove_service_nonexistent(self, mock_repo):
"""测试移除不存在的服务"""
# 设置remove方法抛出ValueError
mock_repo.return_value.remove.side_effect = ValueError("Service not found")
manager = Manager(mock_repo.return_value)
with self.assertRaises(ValueError):
manager.remove_service("nonexistent")
@patch("src.manager.ServiceRepository")
def test_remove_service_file_not_found(self, mock_repo):
"""测试文件不存在时的错误处理"""
# 设置remove方法抛出FileNotFoundError
mock_repo.return_value.remove.side_effect = FileNotFoundError
manager = Manager(mock_repo.return_value)
with self.assertRaises(FileNotFoundError):
manager.remove_service("nginx")
@patch("src.manager.ServiceRepository")
def test_remove_service_invalid_json(self, mock_repo):
"""测试无效JSON文件时的异常处理"""
# 设置remove方法抛出JSONDecodeError
mock_repo.return_value.remove.side_effect = json.JSONDecodeError("Expecting value", "", 0)
manager = Manager(mock_repo.return_value)
with self.assertRaises(json.JSONDecodeError):
manager.remove_service("nginx")
@patch("src.manager.ServiceRepository") @patch("src.manager.ServiceRepository")
@patch("src.manager.logger") @patch("src.manager.logger")
def test_execute_service_operation_invalid_index(self, mock_logger, mock_repo): def test_execute_service_operation_invalid_index(self, mock_logger, mock_repo):