forked from erp-dev/erp
feat: stateflow v2 (params required)
This commit is contained in:
471
stateflow/tests/test_business_object_api.py
Normal file
471
stateflow/tests/test_business_object_api.py
Normal file
@@ -0,0 +1,471 @@
|
||||
"""
|
||||
测试 BusinessObject 的完整 API 端点
|
||||
包括 reset, timeline, add_parameters_to_log, get_log_parameters 等
|
||||
"""
|
||||
from django.test import TestCase
|
||||
from rest_framework.test import APIClient
|
||||
from rest_framework import status
|
||||
from django.contrib.auth import get_user_model
|
||||
from stateflow import models, services
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class BusinessObjectAPITestCase(TestCase):
|
||||
"""测试 BusinessObject 的所有 API 端点"""
|
||||
|
||||
def setUp(self):
|
||||
self.client = APIClient()
|
||||
self.user = User.objects.create_user(username='testuser', password='testpass')
|
||||
self.client.force_authenticate(user=self.user)
|
||||
|
||||
# 创建状态
|
||||
self.state1 = models.State.objects.create(name='状态1', description='第一个状态')
|
||||
self.state2 = models.State.objects.create(name='状态2', description='第二个状态')
|
||||
self.state3 = models.State.objects.create(name='状态3', description='第三个状态')
|
||||
|
||||
# 为状态添加参数
|
||||
self.param1 = models.StateParameter.objects.create(
|
||||
key='temperature',
|
||||
value='',
|
||||
is_required=True,
|
||||
description='温度'
|
||||
)
|
||||
self.param2 = models.StateParameter.objects.create(
|
||||
key='humidity',
|
||||
value='',
|
||||
is_required=False,
|
||||
description='湿度'
|
||||
)
|
||||
self.state1.parameters.add(self.param1, self.param2)
|
||||
|
||||
# 创建流程
|
||||
self.process = models.Process.objects.create(name='测试流程')
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state1, order=0)
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state2, order=1)
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state3, order=2)
|
||||
|
||||
# 创建业务对象
|
||||
self.business_object = models.BusinessObject.objects.create(
|
||||
name='测试业务对象',
|
||||
process=self.process
|
||||
)
|
||||
|
||||
def test_reset_api(self):
|
||||
"""测试重置进度 API"""
|
||||
# 先推进两次
|
||||
self.client.post(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/',
|
||||
{'parameters': {'temperature': '25.5'}},
|
||||
format='json'
|
||||
)
|
||||
self.client.post(f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/')
|
||||
|
||||
# 验证已有进度
|
||||
self.business_object.refresh_from_db()
|
||||
self.assertIsNotNone(services.get_business_object_current_state(self.business_object))
|
||||
self.assertEqual(self.business_object.state_logs.count(), 2)
|
||||
|
||||
# 重置进度
|
||||
response = self.client.post(f'/api/v1/stateflow/business-objects/{self.business_object.id}/reset/')
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertTrue(response.data['success'])
|
||||
self.assertIn('重置', response.data['message'])
|
||||
|
||||
# 验证进度已重置
|
||||
self.business_object.refresh_from_db()
|
||||
self.assertIsNone(services.get_business_object_current_state(self.business_object))
|
||||
self.assertEqual(self.business_object.state_logs.filter(is_cancelled=False).count(), 0)
|
||||
|
||||
def test_reset_api_not_started(self):
|
||||
"""测试重置未开始的业务对象"""
|
||||
# 未开始也可以重置(不会报错)
|
||||
response = self.client.post(f'/api/v1/stateflow/business-objects/{self.business_object.id}/reset/')
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertTrue(response.data['success'])
|
||||
|
||||
def test_timeline_api(self):
|
||||
"""测试状态时间线 API"""
|
||||
# 未开始时的时间线
|
||||
response = self.client.get(f'/api/v1/stateflow/business-objects/{self.business_object.id}/timeline/')
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(len(response.data), 3)
|
||||
|
||||
# 验证所有状态都是待执行
|
||||
for item in response.data:
|
||||
self.assertEqual(item['status'], 'not_started')
|
||||
self.assertIsNone(item['completed_at'])
|
||||
self.assertIsNone(item['completed_by'])
|
||||
self.assertFalse(item['is_cancelled'])
|
||||
|
||||
# 推进第一个状态
|
||||
self.client.post(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/',
|
||||
{'parameters': {'temperature': '25.5'}},
|
||||
format='json'
|
||||
)
|
||||
|
||||
# 再次获取时间线
|
||||
response = self.client.get(f'/api/v1/stateflow/business-objects/{self.business_object.id}/timeline/')
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(len(response.data), 3)
|
||||
|
||||
# 第一个状态应该是已完成
|
||||
self.assertEqual(response.data[0]['status'], 'completed')
|
||||
self.assertIsNotNone(response.data[0]['completed_at'])
|
||||
self.assertEqual(response.data[0]['completed_by'], 'testuser')
|
||||
self.assertFalse(response.data[0]['is_cancelled'])
|
||||
|
||||
# 其他状态仍是待执行
|
||||
self.assertEqual(response.data[1]['status'], 'not_started')
|
||||
self.assertEqual(response.data[2]['status'], 'not_started')
|
||||
|
||||
def test_timeline_api_with_step_back(self):
|
||||
"""测试时间线 API 包含回退后的已撤销状态"""
|
||||
# 推进两次
|
||||
self.client.post(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/',
|
||||
{'parameters': {'temperature': '25.5'}},
|
||||
format='json'
|
||||
)
|
||||
self.client.post(f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/')
|
||||
|
||||
# 回退一次
|
||||
self.client.post(f'/api/v1/stateflow/business-objects/{self.business_object.id}/step_back/')
|
||||
|
||||
# 获取时间线
|
||||
response = self.client.get(f'/api/v1/stateflow/business-objects/{self.business_object.id}/timeline/')
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
# 第一个状态:已完成且未撤销
|
||||
self.assertEqual(response.data[0]['status'], 'completed')
|
||||
self.assertFalse(response.data[0]['is_cancelled'])
|
||||
|
||||
# 第二个状态:已撤销
|
||||
self.assertEqual(response.data[1]['status'], 'cancelled')
|
||||
self.assertTrue(response.data[1]['is_cancelled'])
|
||||
self.assertIsNotNone(response.data[1]['cancelled_at'])
|
||||
|
||||
# 第三个状态:待执行
|
||||
self.assertEqual(response.data[2]['status'], 'not_started')
|
||||
|
||||
def test_add_parameters_to_log_api(self):
|
||||
"""测试为状态流转记录补充参数 API"""
|
||||
# 先推进一次
|
||||
response = self.client.post(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/',
|
||||
{'parameters': {'temperature': '25.5'}},
|
||||
format='json'
|
||||
)
|
||||
|
||||
state_log_id = response.data['state_log']['id']
|
||||
|
||||
# 补充参数
|
||||
response = self.client.post(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/state-logs/{state_log_id}/add-parameters/',
|
||||
{
|
||||
'parameters': {
|
||||
'humidity': '60%',
|
||||
'operator': '张三'
|
||||
},
|
||||
'remark': '补充测量数据'
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertTrue(response.data['success'])
|
||||
self.assertIn('parameter_record', response.data)
|
||||
|
||||
# 验证参数记录
|
||||
param_record = response.data['parameter_record']
|
||||
self.assertEqual(param_record['parameters']['humidity'], '60%')
|
||||
self.assertEqual(param_record['parameters']['operator'], '张三')
|
||||
self.assertEqual(param_record['remark'], '补充测量数据')
|
||||
|
||||
def test_add_parameters_to_log_api_empty_parameters(self):
|
||||
"""测试补充空参数应该失败"""
|
||||
# 先推进一次
|
||||
response = self.client.post(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/',
|
||||
{'parameters': {'temperature': '25.5'}},
|
||||
format='json'
|
||||
)
|
||||
|
||||
state_log_id = response.data['state_log']['id']
|
||||
|
||||
# 尝试补充空参数
|
||||
response = self.client.post(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/state-logs/{state_log_id}/add-parameters/',
|
||||
{
|
||||
'parameters': {},
|
||||
'remark': '空参数测试'
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertFalse(response.data['success'])
|
||||
self.assertIn('不能为空', response.data['message'])
|
||||
|
||||
def test_add_parameters_to_log_api_nonexistent_log(self):
|
||||
"""测试为不存在的记录补充参数应该失败"""
|
||||
response = self.client.post(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/state-logs/99999/add-parameters/',
|
||||
{
|
||||
'parameters': {'test': 'value'},
|
||||
'remark': '测试'
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
||||
self.assertFalse(response.data['success'])
|
||||
self.assertIn('不存在', response.data['message'])
|
||||
|
||||
def test_get_log_parameters_api_all_records(self):
|
||||
"""测试获取状态流转记录的所有参数记录 API"""
|
||||
# 推进并提供初始参数
|
||||
response = self.client.post(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/',
|
||||
{'parameters': {'temperature': '25.5', 'humidity': '60%'}},
|
||||
format='json'
|
||||
)
|
||||
|
||||
state_log_id = response.data['state_log']['id']
|
||||
|
||||
# 补充参数两次
|
||||
self.client.post(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/state-logs/{state_log_id}/add-parameters/',
|
||||
{
|
||||
'parameters': {'temperature': '26.0'},
|
||||
'remark': '第一次重测'
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
self.client.post(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/state-logs/{state_log_id}/add-parameters/',
|
||||
{
|
||||
'parameters': {'temperature': '26.5'},
|
||||
'remark': '第二次重测'
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
|
||||
# 获取所有参数记录
|
||||
response = self.client.get(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/state-logs/{state_log_id}/parameters/'
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data['state_log_id'], state_log_id)
|
||||
self.assertFalse(response.data['is_cancelled'])
|
||||
self.assertEqual(response.data['count'], 3)
|
||||
|
||||
# 验证汇总数据
|
||||
summary = response.data['summary']
|
||||
self.assertEqual(summary['temperature'], '26.5') # 最新值
|
||||
self.assertEqual(summary['humidity'], '60%')
|
||||
|
||||
# 验证记录列表
|
||||
self.assertEqual(len(response.data['records']), 3)
|
||||
|
||||
def test_get_log_parameters_api_by_key(self):
|
||||
"""测试获取指定参数的历史记录 API"""
|
||||
# 推进并提供初始参数
|
||||
response = self.client.post(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/',
|
||||
{'parameters': {'temperature': '25.5'}},
|
||||
format='json'
|
||||
)
|
||||
|
||||
state_log_id = response.data['state_log']['id']
|
||||
|
||||
# 补充参数
|
||||
self.client.post(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/state-logs/{state_log_id}/add-parameters/',
|
||||
{
|
||||
'parameters': {'temperature': '26.0', 'humidity': '65%'},
|
||||
'remark': '重测'
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
|
||||
# 获取 temperature 的历史
|
||||
response = self.client.get(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/state-logs/{state_log_id}/parameters/?key=temperature'
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data['key'], 'temperature')
|
||||
self.assertEqual(len(response.data['history']), 2)
|
||||
self.assertEqual(response.data['history'][0]['value'], '25.5')
|
||||
self.assertEqual(response.data['history'][1]['value'], '26.0')
|
||||
self.assertEqual(response.data['history'][1]['remark'], '重测')
|
||||
|
||||
def test_get_log_parameters_api_cancelled_state_excluded(self):
|
||||
"""测试已撤销状态的参数默认不可见"""
|
||||
# 推进
|
||||
response = self.client.post(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/',
|
||||
{'parameters': {'temperature': '25.5'}},
|
||||
format='json'
|
||||
)
|
||||
|
||||
state_log_id = response.data['state_log']['id']
|
||||
|
||||
# 回退(撤销状态)
|
||||
self.client.post(f'/api/v1/stateflow/business-objects/{self.business_object.id}/step_back/')
|
||||
|
||||
# 默认获取参数(应该返回空)
|
||||
response = self.client.get(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/state-logs/{state_log_id}/parameters/'
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertTrue(response.data['is_cancelled'])
|
||||
self.assertEqual(len(response.data['summary']), 0) # 汇总应该为空
|
||||
self.assertEqual(len(response.data['records']), 0) # 记录列表应该为空
|
||||
|
||||
def test_get_log_parameters_api_cancelled_state_with_include_flag(self):
|
||||
"""测试 include_cancelled=true 可以查看已撤销状态的参数"""
|
||||
# 推进
|
||||
response = self.client.post(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/',
|
||||
{'parameters': {'temperature': '25.5', 'humidity': '60%'}},
|
||||
format='json'
|
||||
)
|
||||
|
||||
state_log_id = response.data['state_log']['id']
|
||||
|
||||
# 回退(撤销状态)
|
||||
self.client.post(f'/api/v1/stateflow/business-objects/{self.business_object.id}/step_back/')
|
||||
|
||||
# 使用 include_cancelled=true 获取参数
|
||||
response = self.client.get(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/state-logs/{state_log_id}/parameters/?include_cancelled=true'
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertTrue(response.data['is_cancelled'])
|
||||
self.assertEqual(len(response.data['summary']), 2) # 应该能看到参数
|
||||
self.assertEqual(response.data['summary']['temperature'], '25.5')
|
||||
self.assertEqual(response.data['summary']['humidity'], '60%')
|
||||
|
||||
def test_get_log_parameters_api_key_history_cancelled_excluded(self):
|
||||
"""测试已撤销状态的参数历史默认不可见"""
|
||||
# 推进
|
||||
response = self.client.post(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/',
|
||||
{'parameters': {'temperature': '25.5'}},
|
||||
format='json'
|
||||
)
|
||||
|
||||
state_log_id = response.data['state_log']['id']
|
||||
|
||||
# 补充参数
|
||||
self.client.post(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/state-logs/{state_log_id}/add-parameters/',
|
||||
{'parameters': {'temperature': '26.0'}, 'remark': '重测'},
|
||||
format='json'
|
||||
)
|
||||
|
||||
# 回退
|
||||
self.client.post(f'/api/v1/stateflow/business-objects/{self.business_object.id}/step_back/')
|
||||
|
||||
# 获取参数历史(默认应该为空)
|
||||
response = self.client.get(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/state-logs/{state_log_id}/parameters/?key=temperature'
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(len(response.data['history']), 0)
|
||||
|
||||
# 使用 include_cancelled=true 查看
|
||||
response = self.client.get(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/state-logs/{state_log_id}/parameters/?key=temperature&include_cancelled=true'
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(len(response.data['history']), 2)
|
||||
|
||||
def test_get_log_parameters_api_nonexistent_log(self):
|
||||
"""测试获取不存在的记录的参数应该返回 404"""
|
||||
response = self.client.get(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/state-logs/99999/parameters/'
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
||||
self.assertFalse(response.data['success'])
|
||||
|
||||
def test_advance_api_with_parameters(self):
|
||||
"""测试推进 API 携带参数"""
|
||||
response = self.client.post(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/',
|
||||
{
|
||||
'parameters': {
|
||||
'temperature': '25.5',
|
||||
'humidity': '60%',
|
||||
'operator': '张三'
|
||||
}
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertTrue(response.data['success'])
|
||||
self.assertIn('state_log', response.data)
|
||||
|
||||
# 验证参数摘要(API 返回的是 parameters_summary,不是 parameter_records)
|
||||
state_log = response.data['state_log']
|
||||
self.assertIn('parameters_summary', state_log)
|
||||
|
||||
params = state_log['parameters_summary']
|
||||
self.assertEqual(params['temperature'], '25.5')
|
||||
self.assertEqual(params['humidity'], '60%')
|
||||
self.assertEqual(params['operator'], '张三')
|
||||
|
||||
def test_advance_api_missing_required_parameter(self):
|
||||
"""测试推进 API 缺少必填参数应该失败"""
|
||||
response = self.client.post(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/',
|
||||
{
|
||||
'parameters': {
|
||||
'humidity': '60%' # 只提供可选参数,缺少必填的 temperature
|
||||
}
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertFalse(response.data['success'])
|
||||
self.assertIn('缺失必填参数', response.data['message'])
|
||||
self.assertIn('temperature', response.data['message'])
|
||||
|
||||
def test_timeline_completed_all_states(self):
|
||||
"""测试完成所有状态后的时间线"""
|
||||
# 完成所有状态
|
||||
self.client.post(
|
||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/',
|
||||
{'parameters': {'temperature': '25.5'}},
|
||||
format='json'
|
||||
)
|
||||
self.client.post(f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/')
|
||||
self.client.post(f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/')
|
||||
|
||||
# 获取时间线
|
||||
response = self.client.get(f'/api/v1/stateflow/business-objects/{self.business_object.id}/timeline/')
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
# 所有状态都应该是已完成
|
||||
for item in response.data:
|
||||
self.assertEqual(item['status'], 'completed')
|
||||
self.assertIsNotNone(item['completed_at'])
|
||||
self.assertIsNotNone(item['completed_by'])
|
||||
self.assertFalse(item['is_cancelled'])
|
||||
374
stateflow/tests/test_parameters.py
Normal file
374
stateflow/tests/test_parameters.py
Normal file
@@ -0,0 +1,374 @@
|
||||
"""
|
||||
测试参数管理功能
|
||||
包括必填参数验证、参数记录创建、回退后参数可见性等
|
||||
"""
|
||||
from django.test import TestCase
|
||||
from django.contrib.auth import get_user_model
|
||||
from stateflow import models, services
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class ParameterManagementTestCase(TestCase):
|
||||
"""测试参数管理功能"""
|
||||
|
||||
def setUp(self):
|
||||
"""设置测试数据"""
|
||||
# 创建测试用户
|
||||
self.user = User.objects.create_user(username='testuser', password='testpass')
|
||||
|
||||
# 创建状态
|
||||
self.state1 = models.State.objects.create(name='状态1', description='第一个状态')
|
||||
self.state2 = models.State.objects.create(name='状态2', description='第二个状态')
|
||||
self.state3 = models.State.objects.create(name='状态3', description='第三个状态')
|
||||
|
||||
# 为状态1添加必填参数
|
||||
self.required_param = models.StateParameter.objects.create(
|
||||
key='temperature',
|
||||
value='',
|
||||
is_required=True,
|
||||
description='温度(必填)'
|
||||
)
|
||||
self.optional_param = models.StateParameter.objects.create(
|
||||
key='humidity',
|
||||
value='',
|
||||
is_required=False,
|
||||
description='湿度(可选)'
|
||||
)
|
||||
self.state1.parameters.add(self.required_param, self.optional_param)
|
||||
|
||||
# 为状态2添加必填参数
|
||||
self.required_param2 = models.StateParameter.objects.create(
|
||||
key='pressure',
|
||||
value='',
|
||||
is_required=True,
|
||||
description='压力(必填)'
|
||||
)
|
||||
self.state2.parameters.add(self.required_param2)
|
||||
|
||||
# 创建流程
|
||||
self.process = models.Process.objects.create(name='测试流程')
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state1, order=0)
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state2, order=1)
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state3, order=2)
|
||||
|
||||
# 创建业务对象
|
||||
self.business_object = models.BusinessObject.objects.create(
|
||||
name='测试业务对象',
|
||||
process=self.process
|
||||
)
|
||||
|
||||
def test_advance_without_required_parameter_fails(self):
|
||||
"""测试不提供必填参数时推进失败"""
|
||||
# 尝试推进但不提供必填参数
|
||||
success, message, state_log = services.advance_to_next_state(
|
||||
self.business_object,
|
||||
self.user
|
||||
)
|
||||
|
||||
# 应该失败
|
||||
self.assertFalse(success)
|
||||
self.assertIn('缺失必填参数', message)
|
||||
self.assertIn('temperature', message)
|
||||
self.assertIsNone(state_log)
|
||||
|
||||
# 验证状态未改变
|
||||
current_state = services.get_business_object_current_state(self.business_object)
|
||||
self.assertIsNone(current_state)
|
||||
|
||||
# 验证没有创建状态流转记录
|
||||
self.assertEqual(self.business_object.state_logs.count(), 0)
|
||||
|
||||
def test_advance_with_only_required_parameter_succeeds(self):
|
||||
"""测试只提供必填参数时推进成功"""
|
||||
# 只提供必填参数
|
||||
success, message, state_log = services.advance_to_next_state(
|
||||
self.business_object,
|
||||
self.user,
|
||||
temperature='25.5' # 只提供必填的
|
||||
)
|
||||
|
||||
# 应该成功
|
||||
self.assertTrue(success)
|
||||
self.assertIsNotNone(state_log)
|
||||
|
||||
# 验证状态已推进
|
||||
current_state = services.get_business_object_current_state(self.business_object)
|
||||
self.assertEqual(current_state.name, self.state1.name)
|
||||
|
||||
# 验证参数记录已创建
|
||||
self.assertEqual(state_log.parameter_records.count(), 1)
|
||||
param_record = state_log.parameter_records.first()
|
||||
self.assertEqual(param_record.parameters['temperature'], '25.5')
|
||||
self.assertNotIn('humidity', param_record.parameters)
|
||||
|
||||
def test_advance_with_all_parameters(self):
|
||||
"""测试提供所有参数时推进成功"""
|
||||
success, message, state_log = services.advance_to_next_state(
|
||||
self.business_object,
|
||||
self.user,
|
||||
temperature='25.5',
|
||||
humidity='60%'
|
||||
)
|
||||
|
||||
# 应该成功
|
||||
self.assertTrue(success)
|
||||
self.assertIsNotNone(state_log)
|
||||
|
||||
# 验证参数记录包含所有提供的参数
|
||||
param_record = state_log.parameter_records.first()
|
||||
self.assertEqual(param_record.parameters['temperature'], '25.5')
|
||||
self.assertEqual(param_record.parameters['humidity'], '60%')
|
||||
|
||||
def test_advance_with_extra_parameters(self):
|
||||
"""测试提供额外参数时也能成功(不做验证)"""
|
||||
success, message, state_log = services.advance_to_next_state(
|
||||
self.business_object,
|
||||
self.user,
|
||||
temperature='25.5',
|
||||
humidity='60%',
|
||||
operator='张三', # 额外参数
|
||||
notes='测试备注' # 额外参数
|
||||
)
|
||||
|
||||
# 应该成功(不验证参数是否在定义中)
|
||||
self.assertTrue(success)
|
||||
|
||||
# 验证所有参数都被保存
|
||||
param_record = state_log.parameter_records.first()
|
||||
self.assertEqual(len(param_record.parameters), 4)
|
||||
self.assertEqual(param_record.parameters['operator'], '张三')
|
||||
self.assertEqual(param_record.parameters['notes'], '测试备注')
|
||||
|
||||
def test_add_parameters_after_advance(self):
|
||||
"""测试推进后补充参数"""
|
||||
# 先推进
|
||||
success, message, state_log = services.advance_to_next_state(
|
||||
self.business_object,
|
||||
self.user,
|
||||
temperature='25.5'
|
||||
)
|
||||
self.assertTrue(success)
|
||||
|
||||
# 补充参数
|
||||
new_record = services.add_parameters_to_state_log(
|
||||
state_log,
|
||||
remark='补充测量',
|
||||
humidity='65%',
|
||||
inspector='李四'
|
||||
)
|
||||
|
||||
# 验证创建了新的参数记录
|
||||
self.assertEqual(state_log.parameter_records.count(), 2)
|
||||
self.assertEqual(new_record.parameters['humidity'], '65%')
|
||||
self.assertEqual(new_record.parameters['inspector'], '李四')
|
||||
self.assertEqual(new_record.remark, '补充测量')
|
||||
|
||||
def test_parameter_summary_with_updates(self):
|
||||
"""测试参数汇总(包含更新的值)"""
|
||||
# 推进并提供初始参数
|
||||
success, message, state_log = services.advance_to_next_state(
|
||||
self.business_object,
|
||||
self.user,
|
||||
temperature='25.5',
|
||||
humidity='60%'
|
||||
)
|
||||
|
||||
# 补充参数(更新 temperature)
|
||||
services.add_parameters_to_state_log(
|
||||
state_log,
|
||||
temperature='26.0', # 重新测量
|
||||
remark='温度重新测量'
|
||||
)
|
||||
|
||||
# 获取参数汇总
|
||||
summary = state_log.get_all_parameters_summary()
|
||||
|
||||
# 验证最新值
|
||||
self.assertEqual(summary['temperature'], '26.0') # 应该是更新后的值
|
||||
self.assertEqual(summary['humidity'], '60%') # 保持原值
|
||||
|
||||
def test_parameter_history(self):
|
||||
"""测试参数历史记录"""
|
||||
# 推进
|
||||
success, message, state_log = services.advance_to_next_state(
|
||||
self.business_object,
|
||||
self.user,
|
||||
temperature='25.5'
|
||||
)
|
||||
|
||||
# 多次更新同一个参数
|
||||
services.add_parameters_to_state_log(
|
||||
state_log,
|
||||
temperature='26.0',
|
||||
remark='第一次重测'
|
||||
)
|
||||
services.add_parameters_to_state_log(
|
||||
state_log,
|
||||
temperature='26.5',
|
||||
remark='第二次重测'
|
||||
)
|
||||
|
||||
# 获取历史
|
||||
history = state_log.get_parameter_history('temperature')
|
||||
|
||||
# 验证历史记录
|
||||
self.assertEqual(len(history), 3)
|
||||
self.assertEqual(history[0]['value'], '25.5')
|
||||
self.assertEqual(history[1]['value'], '26.0')
|
||||
self.assertEqual(history[1]['remark'], '第一次重测')
|
||||
self.assertEqual(history[2]['value'], '26.5')
|
||||
self.assertEqual(history[2]['remark'], '第二次重测')
|
||||
|
||||
def test_step_back_excludes_cancelled_state_parameters(self):
|
||||
"""测试回退后,默认情况下查询参数时应排除已撤销状态的参数"""
|
||||
# 推进两次,每次都提供参数
|
||||
success, msg, state_log1 = services.advance_to_next_state(
|
||||
self.business_object,
|
||||
self.user,
|
||||
temperature='25.5',
|
||||
humidity='60%'
|
||||
)
|
||||
self.assertTrue(success)
|
||||
|
||||
success, msg, state_log2 = services.advance_to_next_state(
|
||||
self.business_object,
|
||||
self.user,
|
||||
pressure='1013'
|
||||
)
|
||||
self.assertTrue(success)
|
||||
|
||||
# 验证两个状态都有参数记录
|
||||
self.assertEqual(state_log1.parameter_records.count(), 1)
|
||||
self.assertEqual(state_log2.parameter_records.count(), 1)
|
||||
|
||||
# 回退一步(撤销 state2)
|
||||
success, message = services.step_back_one_state(self.business_object, self.user)
|
||||
self.assertTrue(success)
|
||||
|
||||
# 刷新 state_log2
|
||||
state_log2.refresh_from_db()
|
||||
self.assertTrue(state_log2.is_cancelled)
|
||||
|
||||
# 默认情况下,已撤销状态的参数不可见
|
||||
summary = state_log2.get_all_parameters_summary()
|
||||
self.assertEqual(len(summary), 0) # 应该返回空字典
|
||||
|
||||
# 使用 include_cancelled=True 可以查看已撤销状态的参数
|
||||
summary_with_cancelled = state_log2.get_all_parameters_summary(include_cancelled=True)
|
||||
self.assertEqual(len(summary_with_cancelled), 1)
|
||||
self.assertEqual(summary_with_cancelled['pressure'], '1013')
|
||||
|
||||
# 验证 state_log1 的参数仍然可见(未撤销)
|
||||
summary1 = state_log1.get_all_parameters_summary()
|
||||
self.assertEqual(len(summary1), 2)
|
||||
self.assertEqual(summary1['temperature'], '25.5')
|
||||
self.assertEqual(summary1['humidity'], '60%')
|
||||
|
||||
def test_cancelled_state_parameter_history(self):
|
||||
"""测试已撤销状态的参数历史记录不可见"""
|
||||
# 推进并提供参数
|
||||
success, msg, state_log = services.advance_to_next_state(
|
||||
self.business_object,
|
||||
self.user,
|
||||
temperature='25.5'
|
||||
)
|
||||
self.assertTrue(success)
|
||||
|
||||
# 补充参数
|
||||
services.add_parameters_to_state_log(
|
||||
state_log,
|
||||
temperature='26.0',
|
||||
remark='重新测量'
|
||||
)
|
||||
|
||||
# 验证参数历史存在
|
||||
history = state_log.get_parameter_history('temperature')
|
||||
self.assertEqual(len(history), 2)
|
||||
|
||||
# 回退(撤销状态)
|
||||
services.step_back_one_state(self.business_object, self.user)
|
||||
state_log.refresh_from_db()
|
||||
|
||||
# 默认情况下,已撤销状态的参数历史不可见
|
||||
history = state_log.get_parameter_history('temperature')
|
||||
self.assertEqual(len(history), 0)
|
||||
|
||||
# 使用 include_cancelled=True 可以查看
|
||||
history_with_cancelled = state_log.get_parameter_history('temperature', include_cancelled=True)
|
||||
self.assertEqual(len(history_with_cancelled), 2)
|
||||
|
||||
def test_cancelled_state_get_latest_parameter(self):
|
||||
"""测试已撤销状态无法获取最新参数值"""
|
||||
# 推进并提供参数
|
||||
success, msg, state_log = services.advance_to_next_state(
|
||||
self.business_object,
|
||||
self.user,
|
||||
temperature='25.5'
|
||||
)
|
||||
self.assertTrue(success)
|
||||
|
||||
# 验证可以获取参数
|
||||
temp = state_log.get_latest_parameter('temperature')
|
||||
self.assertEqual(temp, '25.5')
|
||||
|
||||
# 回退(撤销状态)
|
||||
services.step_back_one_state(self.business_object, self.user)
|
||||
state_log.refresh_from_db()
|
||||
|
||||
# 默认情况下返回默认值
|
||||
temp = state_log.get_latest_parameter('temperature', default='N/A')
|
||||
self.assertEqual(temp, 'N/A')
|
||||
|
||||
# 使用 include_cancelled=True 可以获取
|
||||
temp_with_cancelled = state_log.get_latest_parameter('temperature', include_cancelled=True)
|
||||
self.assertEqual(temp_with_cancelled, '25.5')
|
||||
|
||||
def test_multiple_states_with_different_required_parameters(self):
|
||||
"""测试多个状态各自的必填参数验证"""
|
||||
# 推进到 state1(需要 temperature)
|
||||
success, msg, _ = services.advance_to_next_state(
|
||||
self.business_object,
|
||||
self.user,
|
||||
temperature='25.5'
|
||||
)
|
||||
self.assertTrue(success)
|
||||
|
||||
# 尝试推进到 state2 但不提供 pressure
|
||||
success, msg, _ = services.advance_to_next_state(
|
||||
self.business_object,
|
||||
self.user,
|
||||
temperature='26.0' # 提供了 state1 的参数,但这是 state2
|
||||
)
|
||||
|
||||
# 应该失败(缺少 state2 的必填参数 pressure)
|
||||
self.assertFalse(success)
|
||||
self.assertIn('缺失必填参数', msg)
|
||||
self.assertIn('pressure', msg)
|
||||
|
||||
# 提供正确的必填参数
|
||||
success, msg, _ = services.advance_to_next_state(
|
||||
self.business_object,
|
||||
self.user,
|
||||
pressure='1013'
|
||||
)
|
||||
self.assertTrue(success)
|
||||
|
||||
def test_state_without_required_parameters_allows_advance_without_params(self):
|
||||
"""测试没有必填参数的状态允许不提供参数推进"""
|
||||
# 先推进两次到达 state3(没有任何参数要求)
|
||||
services.advance_to_next_state(self.business_object, self.user, temperature='25')
|
||||
services.advance_to_next_state(self.business_object, self.user, pressure='1013')
|
||||
|
||||
# 推进到 state3,不提供任何参数
|
||||
success, msg, state_log = services.advance_to_next_state(
|
||||
self.business_object,
|
||||
self.user
|
||||
)
|
||||
|
||||
# 应该成功
|
||||
self.assertTrue(success)
|
||||
self.assertIsNotNone(state_log)
|
||||
|
||||
# 验证没有创建参数记录
|
||||
self.assertEqual(state_log.parameter_records.count(), 0)
|
||||
288
stateflow/tests/test_services.py
Normal file
288
stateflow/tests/test_services.py
Normal file
@@ -0,0 +1,288 @@
|
||||
"""
|
||||
Stateflow 服务层单元测试
|
||||
测试新增的服务函数以及 current_state 语义修正
|
||||
"""
|
||||
from django.test import TestCase
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from stateflow import models, services
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class StateFlowServicesTestCase(TestCase):
|
||||
"""测试订单状态流转逻辑和新增服务函数"""
|
||||
|
||||
def setUp(self):
|
||||
"""设置测试数据"""
|
||||
# 创建测试用户
|
||||
self.user = User.objects.create_user(username='testuser', password='testpass')
|
||||
|
||||
# 创建状态节点
|
||||
self.state1 = models.State.objects.create(name='状态1', description='第一个状态')
|
||||
self.state2 = models.State.objects.create(name='状态2', description='第二个状态')
|
||||
self.state3 = models.State.objects.create(name='状态3', description='第三个状态')
|
||||
|
||||
# 创建流程
|
||||
self.process = models.Process.objects.create(name='测试流程', description='用于测试的流程')
|
||||
|
||||
# 添加流程节点
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state1, order=0)
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state2, order=1)
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state3, order=2)
|
||||
|
||||
# 创建业务对象
|
||||
ct = ContentType.objects.get_for_model(models.Process)
|
||||
self.business_object = models.BusinessObject.objects.create(
|
||||
name='测试订单',
|
||||
process=self.process,
|
||||
description='测试订单描述',
|
||||
content_type=ct,
|
||||
object_id=self.process.id
|
||||
)
|
||||
|
||||
def test_initial_state(self):
|
||||
"""测试初始状态 - 应该是 None(未开始)"""
|
||||
current_state = self.business_object.get_current_state()
|
||||
self.assertIsNone(current_state, '初始状态应该是 None(未开始)')
|
||||
self.assertEqual(self.business_object.get_progress_percentage(), 0.0)
|
||||
|
||||
# 整体状态应该是 not_started
|
||||
status = services.get_overall_status(self.business_object)
|
||||
self.assertEqual(status, 'not_started')
|
||||
|
||||
def test_advance_to_next_state(self):
|
||||
"""测试推进到下一个状态"""
|
||||
# 第一次推进应该完成第一个状态
|
||||
success, message, _ = services.advance_to_next_state(self.business_object, self.user)
|
||||
self.assertTrue(success)
|
||||
|
||||
# current_state 应该是最后完成的状态(state1)
|
||||
current_state = self.business_object.get_current_state()
|
||||
self.assertEqual(current_state.name, self.state1.name)
|
||||
self.assertAlmostEqual(self.business_object.get_progress_percentage(), 33.33, places=1)
|
||||
|
||||
# 推进到状态2(完成state2)
|
||||
success, message, _ = services.advance_to_next_state(self.business_object, self.user)
|
||||
self.assertTrue(success)
|
||||
|
||||
# current_state 应该是 state2
|
||||
current_state = self.business_object.get_current_state()
|
||||
self.assertEqual(current_state.name, self.state2.name)
|
||||
self.assertAlmostEqual(self.business_object.get_progress_percentage(), 66.67, places=1)
|
||||
|
||||
# 完成最后一个状态(state3)
|
||||
success, message, _ = services.advance_to_next_state(self.business_object, self.user)
|
||||
self.assertTrue(success)
|
||||
self.assertEqual(self.business_object.get_progress_percentage(), 100.0)
|
||||
|
||||
# current_state 应该是 state3(最后完成的状态)
|
||||
current_state = self.business_object.get_current_state()
|
||||
self.assertEqual(current_state.name, self.state3.name)
|
||||
|
||||
# 整体状态应该是 completed
|
||||
status = services.get_overall_status(self.business_object)
|
||||
self.assertEqual(status, 'completed')
|
||||
|
||||
def test_state_status(self):
|
||||
"""测试状态的状态"""
|
||||
# 初始状态,所有状态都应该是 not_started
|
||||
self.assertEqual(services.get_business_object_state_status(self.business_object, self.state1), 'not_started')
|
||||
self.assertEqual(services.get_business_object_state_status(self.business_object, self.state2), 'not_started')
|
||||
|
||||
# 完成第一个状态
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
self.assertEqual(services.get_business_object_state_status(self.business_object, self.state1), 'completed')
|
||||
self.assertEqual(services.get_business_object_state_status(self.business_object, self.state2), 'not_started')
|
||||
self.assertEqual(services.get_business_object_state_status(self.business_object, self.state3), 'not_started')
|
||||
|
||||
# 完成第二个状态
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
self.assertEqual(services.get_business_object_state_status(self.business_object, self.state1), 'completed')
|
||||
self.assertEqual(services.get_business_object_state_status(self.business_object, self.state2), 'completed')
|
||||
self.assertEqual(services.get_business_object_state_status(self.business_object, self.state3), 'not_started')
|
||||
|
||||
def test_timeline(self):
|
||||
"""测试时间线"""
|
||||
timeline = self.business_object.get_timeline()
|
||||
self.assertEqual(len(timeline), 3)
|
||||
|
||||
# 所有状态都应该是 not_started
|
||||
for item in timeline:
|
||||
self.assertEqual(item['status'], 'not_started')
|
||||
|
||||
# 完成第一个状态
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
|
||||
timeline = self.business_object.get_timeline()
|
||||
self.assertEqual(timeline[0]['status'], 'completed')
|
||||
self.assertEqual(timeline[1]['status'], 'not_started')
|
||||
self.assertEqual(timeline[2]['status'], 'not_started')
|
||||
self.assertIsNotNone(timeline[0]['completed_by'])
|
||||
self.assertEqual(timeline[0]['completed_by'].id, self.user.id)
|
||||
self.assertFalse(timeline[0]['is_cancelled'])
|
||||
|
||||
def test_reset_progress(self):
|
||||
"""测试重置进度 - 不删除记录,而是标记为已撤销"""
|
||||
# 完成两个状态
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
|
||||
# 确认有2条日志记录
|
||||
self.assertEqual(self.business_object.state_logs.count(), 2)
|
||||
|
||||
# 重置进度
|
||||
services.reset_business_object_progress(self.business_object)
|
||||
|
||||
# 日志记录应该还在(不删除)
|
||||
self.assertEqual(self.business_object.state_logs.count(), 2)
|
||||
|
||||
# 但所有记录都应该标记为已撤销
|
||||
cancelled_count = self.business_object.state_logs.filter(is_cancelled=True).count()
|
||||
self.assertEqual(cancelled_count, 2)
|
||||
|
||||
# 应该回到初始状态(未开始)
|
||||
current_state = self.business_object.get_current_state()
|
||||
self.assertIsNone(current_state)
|
||||
|
||||
# 所有撤销的记录都应该有撤销时间
|
||||
for log in self.business_object.state_logs.all():
|
||||
self.assertTrue(log.is_cancelled)
|
||||
self.assertIsNotNone(log.cancelled_at)
|
||||
|
||||
def test_timeline_with_cancelled(self):
|
||||
"""测试包含撤销记录的时间线"""
|
||||
# 推进并完成所有状态
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
|
||||
# 重置进度
|
||||
services.reset_business_object_progress(self.business_object)
|
||||
|
||||
timeline = self.business_object.get_timeline()
|
||||
|
||||
# 所有状态都应该显示为 cancelled
|
||||
for item in timeline:
|
||||
self.assertEqual(item['status'], 'cancelled')
|
||||
self.assertTrue(item['is_cancelled'])
|
||||
self.assertIsNotNone(item['cancelled_at'])
|
||||
|
||||
def test_get_next_pending_state(self):
|
||||
"""测试获取下一个待执行节点(新增功能)"""
|
||||
# 创建参数
|
||||
param1 = models.StateParameter.objects.create(
|
||||
key='param1', value='value1', is_required=True
|
||||
)
|
||||
param2 = models.StateParameter.objects.create(
|
||||
key='param2', value='value2', is_required=False
|
||||
)
|
||||
self.state1.parameters.add(param1, param2)
|
||||
|
||||
# 初始状态 - 应该返回第一个节点
|
||||
next_state = services.get_next_pending_state(self.business_object)
|
||||
self.assertIsNotNone(next_state)
|
||||
self.assertEqual(next_state['state'].name, self.state1.name)
|
||||
self.assertEqual(next_state['order'], 0)
|
||||
self.assertIn('parameters', next_state)
|
||||
self.assertEqual(len(next_state['parameters']), 2)
|
||||
|
||||
# 测试不包含参数
|
||||
next_state_no_params = services.get_next_pending_state(
|
||||
self.business_object, include_parameters=False
|
||||
)
|
||||
self.assertIsNotNone(next_state_no_params)
|
||||
self.assertNotIn('parameters', next_state_no_params)
|
||||
|
||||
# 完成第一个状态(提供必填参数)
|
||||
success, msg, _ = services.advance_to_next_state(
|
||||
self.business_object,
|
||||
self.user,
|
||||
param1='value1' # 提供必填参数
|
||||
)
|
||||
self.assertTrue(success)
|
||||
|
||||
# 应该返回第二个节点
|
||||
next_state = services.get_next_pending_state(self.business_object)
|
||||
self.assertIsNotNone(next_state)
|
||||
self.assertEqual(next_state['state'].name, self.state2.name)
|
||||
self.assertEqual(next_state['order'], 1)
|
||||
|
||||
# 完成所有状态
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
|
||||
# 应该返回 None(已完成)
|
||||
next_state = services.get_next_pending_state(self.business_object)
|
||||
self.assertIsNone(next_state)
|
||||
|
||||
def test_get_all_pending_states(self):
|
||||
"""测试获取所有待执行节点列表(新增功能)"""
|
||||
# 初始状态 - 所有节点都待执行
|
||||
pending_states = services.get_all_pending_states(self.business_object)
|
||||
self.assertEqual(len(pending_states), 3)
|
||||
self.assertEqual(pending_states[0]['state'].name, self.state1.name)
|
||||
self.assertEqual(pending_states[1]['state'].name, self.state2.name)
|
||||
self.assertEqual(pending_states[2]['state'].name, self.state3.name)
|
||||
|
||||
# 验证不包含 parameters 字段
|
||||
self.assertNotIn('parameters', pending_states[0])
|
||||
|
||||
# 完成第一个状态
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
|
||||
# 应该只剩下两个待执行节点
|
||||
pending_states = services.get_all_pending_states(self.business_object)
|
||||
self.assertEqual(len(pending_states), 2)
|
||||
self.assertEqual(pending_states[0]['state'].name, self.state2.name)
|
||||
self.assertEqual(pending_states[1]['state'].name, self.state3.name)
|
||||
|
||||
# 完成第二个状态
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
|
||||
# 应该只剩下一个待执行节点
|
||||
pending_states = services.get_all_pending_states(self.business_object)
|
||||
self.assertEqual(len(pending_states), 1)
|
||||
self.assertEqual(pending_states[0]['state'].name, self.state3.name)
|
||||
|
||||
# 完成所有状态
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
|
||||
# 应该没有待执行节点
|
||||
pending_states = services.get_all_pending_states(self.business_object)
|
||||
self.assertEqual(len(pending_states), 0)
|
||||
|
||||
def test_get_state_parameters(self):
|
||||
"""测试获取节点参数列表(新增功能)"""
|
||||
# 创建必填和可选参数
|
||||
required_param = models.StateParameter.objects.create(
|
||||
key='required_param', value='required_value', is_required=True
|
||||
)
|
||||
optional_param1 = models.StateParameter.objects.create(
|
||||
key='optional_param1', value='optional_value1', is_required=False
|
||||
)
|
||||
optional_param2 = models.StateParameter.objects.create(
|
||||
key='optional_param2', value='optional_value2', is_required=False
|
||||
)
|
||||
|
||||
# 关联参数到状态
|
||||
self.state1.parameters.add(required_param, optional_param1, optional_param2)
|
||||
|
||||
# 获取所有参数
|
||||
all_params = services.get_state_parameters(self.state1)
|
||||
self.assertEqual(len(all_params), 3)
|
||||
|
||||
# 只获取必填参数
|
||||
required_params = services.get_state_parameters(self.state1, required_only=True)
|
||||
self.assertEqual(len(required_params), 1)
|
||||
self.assertEqual(required_params[0].key, 'required_param')
|
||||
self.assertTrue(required_params[0].is_required)
|
||||
|
||||
# 测试没有参数的状态
|
||||
no_params = services.get_state_parameters(self.state2)
|
||||
self.assertEqual(len(no_params), 0)
|
||||
|
||||
# 测试只有必填参数的状态
|
||||
self.state3.parameters.add(required_param)
|
||||
params = services.get_state_parameters(self.state3, required_only=True)
|
||||
self.assertEqual(len(params), 1)
|
||||
@@ -24,9 +24,9 @@ class StepBackTestCase(TestCase):
|
||||
self.process = models.Process.objects.create(name='测试流程', description='三个状态的测试流程')
|
||||
|
||||
# 创建流程节点
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state1, order=1)
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state2, order=2)
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state3, order=3)
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state1, order=0)
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state2, order=1)
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state3, order=2)
|
||||
|
||||
# 创建业务对象
|
||||
self.business_object = models.BusinessObject.objects.create(
|
||||
@@ -47,13 +47,13 @@ class StepBackTestCase(TestCase):
|
||||
|
||||
def test_step_back_from_first_state(self):
|
||||
"""测试从第一个状态回退到未开始"""
|
||||
# 推进到第一个状态
|
||||
success, _ = services.advance_to_next_state(self.business_object, self.user)
|
||||
# 推进(完成第一个状态)
|
||||
success, _, _ = services.advance_to_next_state(self.business_object, self.user)
|
||||
self.assertTrue(success)
|
||||
|
||||
# 验证当前在第一个状态(进行中,准备完成第二个状态)
|
||||
# 验证 current_state 是 state1(最后完成的状态)
|
||||
current_state = services.get_business_object_current_state(self.business_object)
|
||||
self.assertEqual(current_state.id, self.state2.id)
|
||||
self.assertEqual(current_state.name, self.state1.name)
|
||||
|
||||
# 回退一步
|
||||
success, message = services.step_back_one_state(self.business_object, self.user)
|
||||
@@ -62,6 +62,8 @@ class StepBackTestCase(TestCase):
|
||||
|
||||
# 验证回到未开始状态
|
||||
self.assertEqual(services.get_overall_status(self.business_object), 'not_started')
|
||||
current_state = services.get_business_object_current_state(self.business_object)
|
||||
self.assertIsNone(current_state)
|
||||
|
||||
# 验证状态1的记录已被撤销
|
||||
record = models.StateFlowRecord.objects.filter(
|
||||
@@ -73,22 +75,22 @@ class StepBackTestCase(TestCase):
|
||||
|
||||
def test_step_back_from_middle_state(self):
|
||||
"""测试从中间状态回退"""
|
||||
# 推进到第二个状态
|
||||
# 推进两次(完成 state1 和 state2)
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
|
||||
# 验证当前在第二个状态(进行中,准备完成第三个状态)
|
||||
# 验证 current_state 是 state2(最后完成的状态)
|
||||
current_state = services.get_business_object_current_state(self.business_object)
|
||||
self.assertEqual(current_state.id, self.state3.id)
|
||||
self.assertEqual(current_state.name, self.state2.name)
|
||||
|
||||
# 回退一步
|
||||
success, message = services.step_back_one_state(self.business_object, self.user)
|
||||
self.assertTrue(success)
|
||||
self.assertIn('状态2', message)
|
||||
|
||||
# 验证回到第一个状态(进行中,准备完成第二个状态)
|
||||
# 验证 current_state 变为state1
|
||||
current_state = services.get_business_object_current_state(self.business_object)
|
||||
self.assertEqual(current_state.id, self.state2.id)
|
||||
self.assertEqual(current_state.name, self.state1.name)
|
||||
|
||||
# 验证状态2的记录已被撤销
|
||||
record = models.StateFlowRecord.objects.filter(
|
||||
@@ -99,23 +101,25 @@ class StepBackTestCase(TestCase):
|
||||
|
||||
def test_step_back_from_completed(self):
|
||||
"""测试从已完成状态回退"""
|
||||
# 推进到完成
|
||||
# 推进到完成(完成所有三个状态)
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
|
||||
# 验证已完成
|
||||
# 验证已完成,current_state 是 state3
|
||||
self.assertEqual(services.get_overall_status(self.business_object), 'completed')
|
||||
current_state = services.get_business_object_current_state(self.business_object)
|
||||
self.assertEqual(current_state.name, self.state3.name)
|
||||
|
||||
# 回退一步
|
||||
success, message = services.step_back_one_state(self.business_object, self.user)
|
||||
self.assertTrue(success)
|
||||
self.assertIn('状态3', message)
|
||||
|
||||
# 验证回到进行中状态(准备完成第三个状态)
|
||||
# 验证回到进行中状态,current_state 变为state2
|
||||
self.assertEqual(services.get_overall_status(self.business_object), 'in_progress')
|
||||
current_state = services.get_business_object_current_state(self.business_object)
|
||||
self.assertEqual(current_state.id, self.state3.id)
|
||||
self.assertEqual(current_state.name, self.state2.name)
|
||||
|
||||
def test_step_back_multiple_times(self):
|
||||
"""测试多次回退"""
|
||||
@@ -124,17 +128,17 @@ class StepBackTestCase(TestCase):
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
|
||||
# 第一次回退
|
||||
# 第一次回退(撤销 state3)
|
||||
success, _ = services.step_back_one_state(self.business_object, self.user)
|
||||
self.assertTrue(success)
|
||||
current = services.get_business_object_current_state(self.business_object)
|
||||
self.assertEqual(current.id, self.state3.id)
|
||||
self.assertEqual(current.name, self.state2.name)
|
||||
|
||||
# 第二次回退
|
||||
# 第二次回退(撤销 state2)
|
||||
success, _ = services.step_back_one_state(self.business_object, self.user)
|
||||
self.assertTrue(success)
|
||||
current = services.get_business_object_current_state(self.business_object)
|
||||
self.assertEqual(current.id, self.state2.id)
|
||||
self.assertEqual(current.name, self.state1.name)
|
||||
|
||||
# 第三次回退
|
||||
success, _ = services.step_back_one_state(self.business_object, self.user)
|
||||
@@ -148,20 +152,20 @@ class StepBackTestCase(TestCase):
|
||||
|
||||
def test_advance_after_step_back(self):
|
||||
"""测试回退后再前进"""
|
||||
# 推进两步
|
||||
# 推进两步(完成 state1 和 state2)
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
services.advance_to_next_state(self.business_object, self.user)
|
||||
|
||||
# 回退一步
|
||||
# 回退一步(撤销 state2)
|
||||
services.step_back_one_state(self.business_object, self.user)
|
||||
|
||||
# 再前进
|
||||
success, _ = services.advance_to_next_state(self.business_object, self.user)
|
||||
# 再前进(重新完成 state2)
|
||||
success, _, _ = services.advance_to_next_state(self.business_object, self.user)
|
||||
self.assertTrue(success)
|
||||
|
||||
# 验证当前在第二个状态
|
||||
# 验证 current_state 是 state2(最后完成的)
|
||||
current_state = services.get_business_object_current_state(self.business_object)
|
||||
self.assertEqual(current_state.id, self.state3.id)
|
||||
self.assertEqual(current_state.name, self.state2.name)
|
||||
|
||||
def test_step_back_preserves_history(self):
|
||||
"""测试回退操作保留历史记录"""
|
||||
|
||||
@@ -27,9 +27,9 @@ class StepBackAPITestCase(TestCase):
|
||||
|
||||
# 创建流程
|
||||
self.process = models.Process.objects.create(name='测试流程')
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state1, order=1)
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state2, order=2)
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state3, order=3)
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state1, order=0)
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state2, order=1)
|
||||
models.ProcessNode.objects.create(process=self.process, state=self.state3, order=2)
|
||||
|
||||
# 创建业务对象
|
||||
self.business_object = models.BusinessObject.objects.create(
|
||||
|
||||
Reference in New Issue
Block a user