|
|
#region << 版 本 注 释 >>
|
|
|
/*--------------------------------------------------------------------
|
|
|
|* 版权所有 (c) 2026 WenJY 保留所有权利。
|
|
|
|* CLR版本:4.0.30319.42000
|
|
|
|* 机器名称:Mr.Wen's MacBook Pro
|
|
|
|* 命名空间:Sln.Wcs.Business.Tests
|
|
|
|* 唯一标识:
|
|
|
|*
|
|
|
|* 创建者:WenJY
|
|
|
|* 电子邮箱:
|
|
|
|* 创建时间:2026-05-17
|
|
|
|* 版本:V1.0.0
|
|
|
|* 描述:StoreTaskBusiness 单元测试
|
|
|
|*
|
|
|
|*--------------------------------------------------------------------
|
|
|
|* 修改人:
|
|
|
|* 时间:
|
|
|
|* 修改说明:
|
|
|
|*
|
|
|
|* 版本:V1.0.0
|
|
|
|*--------------------------------------------------------------------*/
|
|
|
#endregion << 版 本 注 释 >>
|
|
|
|
|
|
using Moq;
|
|
|
using Sln.Wcs.Business.Domain.Dto.CreateTask;
|
|
|
using Sln.Wcs.Business.Domain.Dto.FilterLocation;
|
|
|
using Sln.Wcs.Business.Domain.Dto.SaveTask;
|
|
|
using Sln.Wcs.Business.Domain.Enum;
|
|
|
using Sln.Wcs.Model.Domain;
|
|
|
using Sln.Wcs.Repository.service;
|
|
|
using Xunit;
|
|
|
|
|
|
namespace Sln.Wcs.Business.Tests;
|
|
|
|
|
|
/// <summary>
|
|
|
/// 可测试的 StoreTaskBusiness 子类,用于访问 protected 方法
|
|
|
/// </summary>
|
|
|
public class TestableStoreTaskBusiness : StoreTaskBusiness
|
|
|
{
|
|
|
public TestableStoreTaskBusiness(
|
|
|
IBasePathInfoService basePathInfoService,
|
|
|
ILiveTaskQueueService liveTaskQueueService,
|
|
|
IBaseStoreInfoService baseStoreInfoService)
|
|
|
: base(basePathInfoService, liveTaskQueueService, baseStoreInfoService)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
public void PublicValidateCreateTaskParams(CreateTaskDto dto) =>
|
|
|
ValidateCreateTaskParams(dto);
|
|
|
|
|
|
public void PublicValidateFilterLocationParams(FilterLocationDto dto) =>
|
|
|
ValidateFilterLocationParams(dto);
|
|
|
|
|
|
public string PublicGenerateTaskCode() => GenerateTaskCode();
|
|
|
|
|
|
public string PublicGetCategoryName(TaskCategoryEnum category) =>
|
|
|
GetCategoryName(category);
|
|
|
|
|
|
public string PublicGetTaskTypeName(TaskTypeEnum taskType) =>
|
|
|
GetTaskTypeName(taskType);
|
|
|
|
|
|
public string PublicGetTaskSuccessMessage(
|
|
|
string taskCode, string pathName, TaskTypeEnum taskType, TaskCategoryEnum taskCategory) =>
|
|
|
GetTaskSuccessMessage(taskCode, pathName, taskType, taskCategory);
|
|
|
|
|
|
public BasePathInfo PublicGetPathInfo(
|
|
|
CreateTaskDto createTaskDto, TaskTypeEnum taskType, TaskCategoryEnum taskCategory) =>
|
|
|
GetPathInfo(createTaskDto, taskType, taskCategory);
|
|
|
}
|
|
|
|
|
|
public class StoreTaskBusinessTests
|
|
|
{
|
|
|
private readonly Mock<IBasePathInfoService> _mockBasePathInfoService;
|
|
|
private readonly Mock<ILiveTaskQueueService> _mockLiveTaskQueueService;
|
|
|
private readonly Mock<IBaseStoreInfoService> _mockBaseStoreInfoService;
|
|
|
private readonly TestableStoreTaskBusiness _storeTaskBusiness;
|
|
|
|
|
|
public StoreTaskBusinessTests()
|
|
|
{
|
|
|
_mockBasePathInfoService = new Mock<IBasePathInfoService>();
|
|
|
_mockLiveTaskQueueService = new Mock<ILiveTaskQueueService>();
|
|
|
_mockBaseStoreInfoService = new Mock<IBaseStoreInfoService>();
|
|
|
_storeTaskBusiness = new TestableStoreTaskBusiness(
|
|
|
_mockBasePathInfoService.Object,
|
|
|
_mockLiveTaskQueueService.Object,
|
|
|
_mockBaseStoreInfoService.Object);
|
|
|
}
|
|
|
|
|
|
#region GetCategoryName 测试
|
|
|
|
|
|
[Theory]
|
|
|
[InlineData(TaskCategoryEnum.Material, "包材")]
|
|
|
[InlineData(TaskCategoryEnum.Product, "成品")]
|
|
|
[InlineData(TaskCategoryEnum.Pallet, "托盘")]
|
|
|
public void GetCategoryName_ValidCategory_ReturnsCorrectName(TaskCategoryEnum category, string expected)
|
|
|
{
|
|
|
var result = _storeTaskBusiness.PublicGetCategoryName(category);
|
|
|
Assert.Equal(expected, result);
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region GetTaskTypeName 测试
|
|
|
|
|
|
[Theory]
|
|
|
[InlineData(TaskTypeEnum.InStore, "入库")]
|
|
|
[InlineData(TaskTypeEnum.OutStore, "出库")]
|
|
|
public void GetTaskTypeName_ValidType_ReturnsCorrectName(TaskTypeEnum taskType, string expected)
|
|
|
{
|
|
|
var result = _storeTaskBusiness.PublicGetTaskTypeName(taskType);
|
|
|
Assert.Equal(expected, result);
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region ValidateCreateTaskParams 测试
|
|
|
|
|
|
[Fact]
|
|
|
public void ValidateCreateTaskParams_NullMaterialCode_ThrowsException()
|
|
|
{
|
|
|
var dto = new CreateTaskDto
|
|
|
{
|
|
|
materialCode = null!,
|
|
|
palletBarcode = "PLT001",
|
|
|
startPoint = "A",
|
|
|
endPoint = "B"
|
|
|
};
|
|
|
|
|
|
var exception = Assert.Throws<InvalidOperationException>(() =>
|
|
|
_storeTaskBusiness.PublicValidateCreateTaskParams(dto));
|
|
|
|
|
|
Assert.Contains("物料编号不允许为 NULL", exception.Message);
|
|
|
}
|
|
|
|
|
|
[Fact]
|
|
|
public void ValidateCreateTaskParams_NullPalletBarcode_ThrowsException()
|
|
|
{
|
|
|
var dto = new CreateTaskDto
|
|
|
{
|
|
|
materialCode = "MAT001",
|
|
|
palletBarcode = null!,
|
|
|
startPoint = "A",
|
|
|
endPoint = "B",
|
|
|
taskCategory = TaskCategoryEnum.Material
|
|
|
};
|
|
|
|
|
|
var exception = Assert.Throws<InvalidOperationException>(() =>
|
|
|
_storeTaskBusiness.PublicValidateCreateTaskParams(dto));
|
|
|
|
|
|
Assert.Contains("条码不允许为 NULL", exception.Message);
|
|
|
}
|
|
|
|
|
|
[Fact]
|
|
|
public void ValidateCreateTaskParams_NullStartPoint_ThrowsException()
|
|
|
{
|
|
|
var dto = new CreateTaskDto
|
|
|
{
|
|
|
materialCode = "MAT001",
|
|
|
palletBarcode = "PLT001",
|
|
|
startPoint = null!,
|
|
|
endPoint = "B"
|
|
|
};
|
|
|
|
|
|
var exception = Assert.Throws<InvalidOperationException>(() =>
|
|
|
_storeTaskBusiness.PublicValidateCreateTaskParams(dto));
|
|
|
|
|
|
Assert.Contains("起始位置、终点位置不允许为 NULL", exception.Message);
|
|
|
}
|
|
|
|
|
|
[Fact]
|
|
|
public void ValidateCreateTaskParams_EmptyEndPoint_ThrowsException()
|
|
|
{
|
|
|
var dto = new CreateTaskDto
|
|
|
{
|
|
|
materialCode = "MAT001",
|
|
|
palletBarcode = "PLT001",
|
|
|
startPoint = "A",
|
|
|
endPoint = ""
|
|
|
};
|
|
|
|
|
|
var exception = Assert.Throws<InvalidOperationException>(() =>
|
|
|
_storeTaskBusiness.PublicValidateCreateTaskParams(dto));
|
|
|
|
|
|
Assert.Contains("起始位置、终点位置不允许为 NULL", exception.Message);
|
|
|
}
|
|
|
|
|
|
[Fact]
|
|
|
public void ValidateCreateTaskParams_ValidParams_NoException()
|
|
|
{
|
|
|
var dto = new CreateTaskDto
|
|
|
{
|
|
|
materialCode = "MAT001",
|
|
|
palletBarcode = "PLT001",
|
|
|
startPoint = "A",
|
|
|
endPoint = "B",
|
|
|
taskCategory = TaskCategoryEnum.Material
|
|
|
};
|
|
|
|
|
|
var exception = Record.Exception(() =>
|
|
|
_storeTaskBusiness.PublicValidateCreateTaskParams(dto));
|
|
|
|
|
|
Assert.Null(exception);
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region ValidateFilterLocationParams 测试
|
|
|
|
|
|
[Fact]
|
|
|
public void ValidateFilterLocationParams_NullMaterialCode_ThrowsException()
|
|
|
{
|
|
|
var dto = new FilterLocationDto
|
|
|
{
|
|
|
materialCode = null!
|
|
|
};
|
|
|
|
|
|
var exception = Assert.Throws<InvalidOperationException>(() =>
|
|
|
_storeTaskBusiness.PublicValidateFilterLocationParams(dto));
|
|
|
|
|
|
Assert.Contains("物料编号不允许为 NULL", exception.Message);
|
|
|
}
|
|
|
|
|
|
[Fact]
|
|
|
public void ValidateFilterLocationParams_ValidParams_NoException()
|
|
|
{
|
|
|
var dto = new FilterLocationDto
|
|
|
{
|
|
|
materialCode = "MAT001"
|
|
|
};
|
|
|
|
|
|
var exception = Record.Exception(() =>
|
|
|
_storeTaskBusiness.PublicValidateFilterLocationParams(dto));
|
|
|
|
|
|
Assert.Null(exception);
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region GenerateTaskCode 测试
|
|
|
|
|
|
[Fact]
|
|
|
public void GenerateTaskCode_ReturnsCorrectFormat()
|
|
|
{
|
|
|
var taskCode = _storeTaskBusiness.PublicGenerateTaskCode();
|
|
|
|
|
|
Assert.NotNull(taskCode);
|
|
|
Assert.Equal(18, taskCode.Length);
|
|
|
Assert.True(long.TryParse(taskCode[..14], out _));
|
|
|
Assert.True(int.TryParse(taskCode[14..], out var randomPart));
|
|
|
Assert.InRange(randomPart, 1000, 9999);
|
|
|
}
|
|
|
|
|
|
[Fact]
|
|
|
public void GenerateTaskCode_MultipleCalls_GeneratesUniqueCodes()
|
|
|
{
|
|
|
var codes = new HashSet<string>();
|
|
|
for (int i = 0; i < 100; i++)
|
|
|
{
|
|
|
codes.Add(_storeTaskBusiness.PublicGenerateTaskCode());
|
|
|
}
|
|
|
|
|
|
Assert.Equal(100, codes.Count);
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region CreateTask 测试
|
|
|
|
|
|
[Fact]
|
|
|
public void CreateTask_ValidInput_ReturnsSuccessResult()
|
|
|
{
|
|
|
var dto = new CreateTaskDto
|
|
|
{
|
|
|
materialCode = "MAT001",
|
|
|
materialBarcode = "MAT001BAR",
|
|
|
palletBarcode = "PLT001",
|
|
|
amount = 100,
|
|
|
startPoint = "START",
|
|
|
endPoint = "END",
|
|
|
taskType = TaskTypeEnum.InStore,
|
|
|
taskCategory = TaskCategoryEnum.Material
|
|
|
};
|
|
|
|
|
|
var pathDetails = new List<BasePathDetails>
|
|
|
{
|
|
|
new BasePathDetails
|
|
|
{
|
|
|
objId = 1,
|
|
|
pathCode = "PATH1",
|
|
|
startPoint = "START",
|
|
|
endPoint = "MID",
|
|
|
deviceType = 0
|
|
|
},
|
|
|
new BasePathDetails
|
|
|
{
|
|
|
objId = 2,
|
|
|
pathCode = "PATH2",
|
|
|
startPoint = "MID",
|
|
|
endPoint = "END",
|
|
|
deviceType = 0
|
|
|
}
|
|
|
};
|
|
|
|
|
|
var pathInfo = new BasePathInfo
|
|
|
{
|
|
|
objId = 1,
|
|
|
pathCode = "MAIN_PATH",
|
|
|
pathName = "主输送线",
|
|
|
pathType = 1,
|
|
|
pathCategory = 1,
|
|
|
startPoint = "START",
|
|
|
endPoint = "END",
|
|
|
pathDetails = pathDetails
|
|
|
};
|
|
|
|
|
|
_mockBasePathInfoService
|
|
|
.Setup(x => x.GetBasePathInfo(It.IsAny<System.Linq.Expressions.Expression<Func<BasePathInfo, bool>>>()))
|
|
|
.Returns(new List<BasePathInfo> { pathInfo });
|
|
|
|
|
|
var result = _storeTaskBusiness.CreateTask(dto);
|
|
|
|
|
|
Assert.Equal(BusinessStatusEnum.成功, result.code);
|
|
|
Assert.NotNull(result.data);
|
|
|
Assert.NotNull(result.data.taskCode);
|
|
|
Assert.Equal(2, result.data.taskDetails.Count);
|
|
|
Assert.Equal(2, result.data.taskQueue.taskSteps);
|
|
|
}
|
|
|
|
|
|
[Fact]
|
|
|
public void CreateTask_PathNotFound_ReturnsErrorResult()
|
|
|
{
|
|
|
var dto = new CreateTaskDto
|
|
|
{
|
|
|
materialCode = "MAT001",
|
|
|
palletBarcode = "PLT001",
|
|
|
startPoint = "INVALID",
|
|
|
endPoint = "INVALID",
|
|
|
taskType = TaskTypeEnum.InStore,
|
|
|
taskCategory = TaskCategoryEnum.Material
|
|
|
};
|
|
|
|
|
|
_mockBasePathInfoService
|
|
|
.Setup(x => x.GetBasePathInfo(It.IsAny<System.Linq.Expressions.Expression<Func<BasePathInfo, bool>>>()))
|
|
|
.Returns(new List<BasePathInfo>());
|
|
|
|
|
|
var result = _storeTaskBusiness.CreateTask(dto);
|
|
|
|
|
|
Assert.Equal(BusinessStatusEnum.方法执行异常, result.code);
|
|
|
Assert.Contains("输送路径为 NULL", result.msg);
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region FilterLocation 测试
|
|
|
|
|
|
[Fact]
|
|
|
public void FilterLocation_ValidInput_ReturnsSuccessResult()
|
|
|
{
|
|
|
var dto = new FilterLocationDto
|
|
|
{
|
|
|
materialCode = "MAT001",
|
|
|
taskType = TaskTypeEnum.InStore,
|
|
|
taskCategory = TaskCategoryEnum.Material
|
|
|
};
|
|
|
|
|
|
var locationInfos = new List<BaseLocationInfo>
|
|
|
{
|
|
|
new BaseLocationInfo
|
|
|
{
|
|
|
locationCode = "LOC001",
|
|
|
locationRows = 1,
|
|
|
locationColumns = 1,
|
|
|
locationLayers = 1,
|
|
|
locationStatus = 0,
|
|
|
materialCode = "MAT001",
|
|
|
isFlag = 1
|
|
|
}
|
|
|
};
|
|
|
|
|
|
var storeInfo = new BaseStoreInfo
|
|
|
{
|
|
|
storeCode = "STORE001",
|
|
|
storeName = "测试仓库",
|
|
|
storeType = 1,
|
|
|
isFlag = 1,
|
|
|
locationInfos = locationInfos
|
|
|
};
|
|
|
|
|
|
_mockBaseStoreInfoService
|
|
|
.Setup(x => x.GetBasePathInfo(
|
|
|
It.IsAny<System.Linq.Expressions.Expression<Func<BaseStoreInfo, bool>>>(),
|
|
|
It.IsAny<System.Linq.Expressions.Expression<Func<BaseLocationInfo, bool>>>()))
|
|
|
.Returns(new List<BaseStoreInfo> { storeInfo });
|
|
|
|
|
|
var result = _storeTaskBusiness.FilterLocation(dto);
|
|
|
|
|
|
Assert.Equal(BusinessStatusEnum.成功, result.code);
|
|
|
Assert.NotNull(result.data);
|
|
|
Assert.NotNull(result.data.storeInfo);
|
|
|
Assert.NotNull(result.data.locationInfo);
|
|
|
Assert.Single(result.data.locationInfos);
|
|
|
}
|
|
|
|
|
|
[Fact]
|
|
|
public void FilterLocation_NoAvailableStore_ReturnsErrorResult()
|
|
|
{
|
|
|
var dto = new FilterLocationDto
|
|
|
{
|
|
|
materialCode = "MAT001",
|
|
|
taskType = TaskTypeEnum.InStore,
|
|
|
taskCategory = TaskCategoryEnum.Material
|
|
|
};
|
|
|
|
|
|
_mockBaseStoreInfoService
|
|
|
.Setup(x => x.GetBasePathInfo(
|
|
|
It.IsAny<System.Linq.Expressions.Expression<Func<BaseStoreInfo, bool>>>(),
|
|
|
It.IsAny<System.Linq.Expressions.Expression<Func<BaseLocationInfo, bool>>>()))
|
|
|
.Returns(new List<BaseStoreInfo>());
|
|
|
|
|
|
var result = _storeTaskBusiness.FilterLocation(dto);
|
|
|
|
|
|
Assert.Equal(BusinessStatusEnum.方法执行异常, result.code);
|
|
|
Assert.Contains("未获取到可用仓库", result.msg);
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region SaveTask 测试
|
|
|
|
|
|
[Fact]
|
|
|
public void SaveTask_ValidInput_ReturnsSuccessResult()
|
|
|
{
|
|
|
var dto = new SaveTaskDto
|
|
|
{
|
|
|
taskQueue = new LiveTaskQueue
|
|
|
{
|
|
|
taskCode = "TASK001",
|
|
|
materialCode = "MAT001",
|
|
|
palletBarcode = "PLT001",
|
|
|
startPoint = "A",
|
|
|
endPoint = "B",
|
|
|
taskType = 1,
|
|
|
taskCategory = 1
|
|
|
}
|
|
|
};
|
|
|
|
|
|
_mockLiveTaskQueueService
|
|
|
.Setup(x => x.InsertTaskQueue(It.IsAny<LiveTaskQueue>()))
|
|
|
.Returns(true);
|
|
|
|
|
|
var result = _storeTaskBusiness.SaveTask(dto);
|
|
|
|
|
|
Assert.Equal(BusinessStatusEnum.成功, result.code);
|
|
|
Assert.NotNull(result.data);
|
|
|
Assert.True(result.data.isRes);
|
|
|
}
|
|
|
|
|
|
[Fact]
|
|
|
public void SaveTask_InsertFails_ReturnsFalse()
|
|
|
{
|
|
|
var dto = new SaveTaskDto
|
|
|
{
|
|
|
taskQueue = new LiveTaskQueue
|
|
|
{
|
|
|
taskCode = "TASK001",
|
|
|
materialCode = "MAT001",
|
|
|
palletBarcode = "PLT001",
|
|
|
startPoint = "A",
|
|
|
endPoint = "B",
|
|
|
taskType = 1,
|
|
|
taskCategory = 1
|
|
|
}
|
|
|
};
|
|
|
|
|
|
_mockLiveTaskQueueService
|
|
|
.Setup(x => x.InsertTaskQueue(It.IsAny<LiveTaskQueue>()))
|
|
|
.Returns(false);
|
|
|
|
|
|
var result = _storeTaskBusiness.SaveTask(dto);
|
|
|
|
|
|
Assert.Equal(BusinessStatusEnum.成功, result.code);
|
|
|
Assert.NotNull(result.data);
|
|
|
Assert.False(result.data.isRes);
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region GetTaskSuccessMessage 测试
|
|
|
|
|
|
[Fact]
|
|
|
public void GetTaskSuccessMessage_ReturnsCorrectMessage()
|
|
|
{
|
|
|
var message = _storeTaskBusiness.PublicGetTaskSuccessMessage(
|
|
|
"TASK001",
|
|
|
"主输送线",
|
|
|
TaskTypeEnum.InStore,
|
|
|
TaskCategoryEnum.Material);
|
|
|
|
|
|
Assert.Equal("包材入库任务创建成功:TASK001;关联路径:主输送线", message);
|
|
|
}
|
|
|
|
|
|
[Fact]
|
|
|
public void GetTaskSuccessMessage_ProductOutStore_ReturnsCorrectMessage()
|
|
|
{
|
|
|
var message = _storeTaskBusiness.PublicGetTaskSuccessMessage(
|
|
|
"TASK002",
|
|
|
"成品出库线",
|
|
|
TaskTypeEnum.OutStore,
|
|
|
TaskCategoryEnum.Product);
|
|
|
|
|
|
Assert.Equal("成品出库任务创建成功:TASK002;关联路径:成品出库线", message);
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
}
|