[Feat] Add the function which can remove existed service from services.json
This commit is contained in:
@ -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
13
main.py
@ -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()
|
||||||
|
@ -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)}")
|
||||||
|
@ -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):
|
||||||
|
Reference in New Issue
Block a user