4a93bd1c44
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
213 lines
8.3 KiB
Python
213 lines
8.3 KiB
Python
import unittest
|
|
from datetime import datetime, timezone
|
|
|
|
from fastapi import HTTPException
|
|
|
|
from app.services.template_service import TemplateService
|
|
|
|
|
|
class StubRepository:
|
|
def __init__(self):
|
|
self.items = {}
|
|
|
|
def create(self, contract_template):
|
|
if contract_template.created_at is None:
|
|
contract_template.created_at = datetime.now(timezone.utc)
|
|
if contract_template.updated_at is None:
|
|
contract_template.updated_at = datetime.now(timezone.utc)
|
|
self.items[contract_template.id] = contract_template
|
|
return contract_template
|
|
|
|
def update(self, contract_template):
|
|
contract_template.updated_at = datetime.now(timezone.utc)
|
|
self.items[contract_template.id] = contract_template
|
|
return contract_template
|
|
|
|
def delete(self, contract_template):
|
|
self.items.pop(contract_template.id, None)
|
|
|
|
def get_by_id(self, template_id):
|
|
return self.items.get(template_id)
|
|
|
|
def list_paginated(self, tenant_id, page, page_size, contract_type=None):
|
|
items = [item for item in self.items.values() if item.tenant_id == tenant_id]
|
|
if contract_type:
|
|
items = [item for item in items if item.contract_type == contract_type]
|
|
items.sort(key=lambda item: (item.updated_at, item.created_at), reverse=True)
|
|
start = (page - 1) * page_size
|
|
return items[start : start + page_size]
|
|
|
|
def count(self, tenant_id, contract_type=None):
|
|
return len(self.list_paginated(tenant_id, 1, 10000, contract_type))
|
|
|
|
|
|
class TemplateServiceTestCase(unittest.TestCase):
|
|
def setUp(self):
|
|
self.service = TemplateService(db=None)
|
|
self.service.repository = StubRepository()
|
|
self.current_user = {"id": "user-admin", "tenant_id": "tenant-demo-001", "role": "admin"}
|
|
self.other_user = {"id": "user-other", "tenant_id": "tenant-other", "role": "admin"}
|
|
self.member_user = {"id": "user-member", "tenant_id": "tenant-demo-001", "role": "member"}
|
|
|
|
def test_admin_can_create_and_list_templates(self):
|
|
created = self.service.create_template(
|
|
self.current_user,
|
|
name=" 劳动合同模板 ",
|
|
contract_type=" labor ",
|
|
description=" 标准劳动合同 ",
|
|
content=" 第一条 工作内容 ",
|
|
variables_json='[{"name": "employee_name", "label": "员工姓名"}]',
|
|
enabled=True,
|
|
)
|
|
|
|
listed = self.service.list_templates(self.current_user, page=0, page_size=200, contract_type=" labor ")
|
|
|
|
self.assertEqual(created["name"], "劳动合同模板")
|
|
self.assertEqual(created["contract_type"], "labor")
|
|
self.assertEqual(created["description"], "标准劳动合同")
|
|
self.assertEqual(created["content"], "第一条 工作内容")
|
|
self.assertEqual(created["variables"][0]["name"], "employee_name")
|
|
self.assertEqual(listed["page"], 1)
|
|
self.assertEqual(listed["page_size"], 100)
|
|
self.assertEqual(listed["total"], 1)
|
|
self.assertEqual(listed["items"][0]["id"], created["id"])
|
|
|
|
def test_update_template_requires_same_tenant(self):
|
|
created = self.service.create_template(
|
|
self.current_user,
|
|
name="采购合同模板",
|
|
contract_type="procurement",
|
|
description=None,
|
|
content="付款条款",
|
|
variables_json=None,
|
|
enabled=True,
|
|
)
|
|
|
|
with self.assertRaises(HTTPException) as context:
|
|
self.service.update_template(
|
|
self.other_user,
|
|
created["id"],
|
|
name="采购合同模板-更新",
|
|
contract_type="procurement",
|
|
description="跨租户",
|
|
content="付款条款更新",
|
|
variables_json=None,
|
|
enabled=False,
|
|
)
|
|
|
|
self.assertEqual(context.exception.status_code, 404)
|
|
self.assertEqual(context.exception.detail, "template not found")
|
|
|
|
def test_delete_template_removes_item(self):
|
|
created = self.service.create_template(
|
|
self.current_user,
|
|
name="服务合同模板",
|
|
contract_type="service",
|
|
description=None,
|
|
content="服务范围",
|
|
variables_json=None,
|
|
enabled=True,
|
|
)
|
|
|
|
self.service.delete_template(self.current_user, created["id"])
|
|
listed = self.service.list_templates(self.current_user)
|
|
|
|
self.assertEqual(listed["total"], 0)
|
|
self.assertEqual(listed["items"], [])
|
|
|
|
def test_member_cannot_manage_templates(self):
|
|
with self.assertRaises(HTTPException) as context:
|
|
self.service.list_templates(self.member_user)
|
|
|
|
self.assertEqual(context.exception.status_code, 403)
|
|
self.assertEqual(context.exception.detail, "forbidden")
|
|
|
|
def test_admin_can_update_template_with_multiple_variables(self):
|
|
created = self.service.create_template(
|
|
self.current_user,
|
|
name="采购合同模板",
|
|
contract_type="procurement",
|
|
description="原始说明",
|
|
content="原始内容",
|
|
variables_json='[{"name": "supplier_name", "label": "供应商名称"}]',
|
|
enabled=True,
|
|
)
|
|
|
|
updated = self.service.update_template(
|
|
self.current_user,
|
|
created["id"],
|
|
name="采购合同模板-更新",
|
|
contract_type="procurement",
|
|
description="更新说明",
|
|
content="更新后的内容",
|
|
variables_json='[{"name": "supplier_name", "label": "供应商名称"}, {"name": "contract_amount", "label": "合同金额"}]',
|
|
enabled=False,
|
|
)
|
|
|
|
self.assertEqual(updated["name"], "采购合同模板-更新")
|
|
self.assertFalse(updated["enabled"])
|
|
self.assertEqual(
|
|
updated["variables"],
|
|
[
|
|
{"name": "supplier_name", "label": "供应商名称"},
|
|
{"name": "contract_amount", "label": "合同金额"},
|
|
],
|
|
)
|
|
|
|
def test_variables_json_must_be_valid_object_array(self):
|
|
with self.assertRaises(HTTPException) as invalid_json_context:
|
|
self.service.create_template(
|
|
self.current_user,
|
|
name="模板",
|
|
contract_type=None,
|
|
description=None,
|
|
content="内容",
|
|
variables_json="not-json",
|
|
enabled=True,
|
|
)
|
|
self.assertEqual(invalid_json_context.exception.status_code, 400)
|
|
self.assertEqual(invalid_json_context.exception.detail, "variables_json must be valid JSON")
|
|
|
|
with self.assertRaises(HTTPException) as invalid_type_context:
|
|
self.service.create_template(
|
|
self.current_user,
|
|
name="模板",
|
|
contract_type=None,
|
|
description=None,
|
|
content="内容",
|
|
variables_json='{"name": "employee_name"}',
|
|
enabled=True,
|
|
)
|
|
self.assertEqual(invalid_type_context.exception.status_code, 400)
|
|
self.assertEqual(invalid_type_context.exception.detail, "variables_json must be a JSON array")
|
|
|
|
with self.assertRaises(HTTPException) as invalid_item_context:
|
|
self.service.create_template(
|
|
self.current_user,
|
|
name="模板",
|
|
contract_type=None,
|
|
description=None,
|
|
content="内容",
|
|
variables_json='["employee_name"]',
|
|
enabled=True,
|
|
)
|
|
self.assertEqual(invalid_item_context.exception.status_code, 400)
|
|
self.assertEqual(invalid_item_context.exception.detail, "variables_json items must be objects")
|
|
|
|
with self.assertRaises(HTTPException) as invalid_nested_item_context:
|
|
self.service.create_template(
|
|
self.current_user,
|
|
name="模板",
|
|
contract_type=None,
|
|
description=None,
|
|
content="内容",
|
|
variables_json='[{"name": "employee_name"}, ["invalid"]]',
|
|
enabled=True,
|
|
)
|
|
self.assertEqual(invalid_nested_item_context.exception.status_code, 400)
|
|
self.assertEqual(invalid_nested_item_context.exception.detail, "variables_json items must be objects")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|