565 lines
21 KiB
C#
565 lines
21 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using CounterDrone.Core;
|
||
using CounterDrone.Core.Models;
|
||
using CounterDrone.Core.Repository;
|
||
using CounterDrone.Core.Services;
|
||
using SQLite;
|
||
using Xunit;
|
||
using ScenarioStatus = CounterDrone.Core.Models.ScenarioStatus;
|
||
|
||
namespace CounterDrone.Core.Tests
|
||
{
|
||
public class ScenarioServiceTests : IDisposable
|
||
{
|
||
private readonly string _testDir;
|
||
private readonly SQLiteConnection _db;
|
||
private readonly IScenarioService _service;
|
||
|
||
public ScenarioServiceTests()
|
||
{
|
||
_testDir = Path.Combine(Path.GetTempPath(), $"cd_test_{Guid.NewGuid():N}");
|
||
var paths = new TestPathProvider(_testDir);
|
||
var dbManager = new DatabaseManager(paths);
|
||
_db = dbManager.OpenMainDb();
|
||
_service = new ScenarioService(
|
||
new ScenarioRepository(_db),
|
||
new CombatSceneRepository(_db),
|
||
new ControlZoneRepository(_db),
|
||
new DroneProfileRepository(_db),
|
||
new EquipmentDeploymentRepository(_db),
|
||
new CloudDispersalRepository(_db),
|
||
new RoutePlanRepository(_db),
|
||
new WaypointRepository(_db));
|
||
}
|
||
|
||
public void Dispose()
|
||
{
|
||
_db?.Close();
|
||
if (Directory.Exists(_testDir))
|
||
Directory.Delete(_testDir, true);
|
||
}
|
||
|
||
// ========== scenario CRUD ==========
|
||
|
||
[Fact]
|
||
public void CreateScenario_Valid_ReturnsScenario()
|
||
{
|
||
var scenario = _service.CreateScenario("测试任务", "SIM-20260611-001");
|
||
|
||
Assert.NotNull(scenario);
|
||
Assert.Equal("测试任务", scenario.Name);
|
||
Assert.Equal("SIM-20260611-001", scenario.ScenarioNumber);
|
||
Assert.Equal((int)ScenarioStatus.Configuring, scenario.Status);
|
||
Assert.Equal(1, scenario.CurrentStep);
|
||
Assert.NotEmpty(scenario.Id);
|
||
}
|
||
|
||
[Fact]
|
||
public void CreateScenario_AutoGeneratesScenarioNumber()
|
||
{
|
||
var scenario = _service.CreateScenario("自动编号", "");
|
||
Assert.StartsWith("SIM-", scenario.ScenarioNumber);
|
||
}
|
||
|
||
[Fact]
|
||
public void CreateScenario_EmptyName_Throws()
|
||
{
|
||
Assert.Throws<ArgumentException>(() =>
|
||
_service.CreateScenario("", "SIM-001"));
|
||
}
|
||
|
||
[Fact]
|
||
public void CreateScenario_DuplicateNumber_Throws()
|
||
{
|
||
_service.CreateScenario("scenario A", "SIM-001");
|
||
Assert.Throws<ArgumentException>(() =>
|
||
_service.CreateScenario("scenario B", "SIM-001"));
|
||
}
|
||
|
||
[Fact]
|
||
public void DeleteScenario_RemovesScenario()
|
||
{
|
||
var scenario = _service.CreateScenario("ToDelete", "");
|
||
_service.DeleteScenario(scenario.Id);
|
||
|
||
var detail = _service.GetScenarioDetail(scenario.Id);
|
||
Assert.Null(detail);
|
||
}
|
||
|
||
[Fact]
|
||
public void GetScenarioDetail_ReturnsFullConfig()
|
||
{
|
||
var scenario = _service.CreateScenario("Full Config", "");
|
||
|
||
// 保存各步骤配置
|
||
_service.SaveScene(scenario.Id, new CombatScene
|
||
{
|
||
SceneType = (int)SceneType.Mountain,
|
||
WindSpeed = 10.0,
|
||
Temperature = 25.0,
|
||
});
|
||
|
||
_service.SaveDroneProfile(scenario.Id, new DroneProfile
|
||
{
|
||
WaveId = "default",
|
||
DroneType = (int)DroneType.FixedWing,
|
||
Quantity = 3,
|
||
});
|
||
|
||
_service.SaveCloudDispersal(scenario.Id, new CloudDispersal
|
||
{
|
||
AerosolType = (int)AerosolType.ActiveMaterial,
|
||
Duration = 45.0,
|
||
});
|
||
|
||
_service.UpdateStep(scenario.Id, 4);
|
||
|
||
// 读取完整配置
|
||
var detail = _service.GetScenarioDetail(scenario.Id);
|
||
|
||
Assert.Equal("Full Config", detail.Info.Name);
|
||
Assert.Equal(4, detail.Info.CurrentStep);
|
||
Assert.Equal((int)SceneType.Mountain, detail.Scene.SceneType);
|
||
Assert.Equal(10.0, detail.Scene.WindSpeed);
|
||
Assert.Equal(3, detail.Drones[0].Quantity);
|
||
Assert.Equal((int)AerosolType.ActiveMaterial, detail.Cloud.AerosolType);
|
||
}
|
||
|
||
// ========== Step Save ==========
|
||
|
||
[Fact]
|
||
public void SaveScene_FirstTime_Inserts()
|
||
{
|
||
var scenario = _service.CreateScenario("Scene Test", "");
|
||
_service.SaveScene(scenario.Id, new CombatScene
|
||
{
|
||
WeatherType = (int)WeatherType.Fog,
|
||
Visibility = 2000.0,
|
||
TimeOfDay = "03:00",
|
||
});
|
||
|
||
var detail = _service.GetScenarioDetail(scenario.Id);
|
||
Assert.Equal((int)WeatherType.Fog, detail.Scene.WeatherType);
|
||
Assert.Equal(2000.0, detail.Scene.Visibility);
|
||
Assert.Equal("03:00", detail.Scene.TimeOfDay);
|
||
}
|
||
|
||
[Fact]
|
||
public void SaveScene_SecondTime_Updates()
|
||
{
|
||
var scenario = _service.CreateScenario("Update Scene", "");
|
||
_service.SaveScene(scenario.Id, new CombatScene { WindSpeed = 5.0 });
|
||
_service.SaveScene(scenario.Id, new CombatScene { WindSpeed = 15.0 });
|
||
|
||
var detail = _service.GetScenarioDetail(scenario.Id);
|
||
Assert.Equal(15.0, detail.Scene.WindSpeed);
|
||
}
|
||
|
||
[Fact]
|
||
public void SaveControlZones_SavesAndRetrieves()
|
||
{
|
||
var scenario = _service.CreateScenario("Zone Test", "");
|
||
var zones = new List<ControlZone>
|
||
{
|
||
new ControlZone { Name = "禁区A", VerticesJson = "[{\"x\":0,\"y\":0,\"z\":0},{\"x\":100,\"y\":0,\"z\":0},{\"x\":100,\"y\":100,\"z\":0}]", MinAltitude = 0, MaxAltitude = 500 },
|
||
new ControlZone { Name = "禁区B", VerticesJson = "[{\"x\":200,\"y\":200,\"z\":0}]", MinAltitude = 100, MaxAltitude = 300 },
|
||
};
|
||
|
||
_service.SaveControlZones(scenario.Id, zones);
|
||
|
||
var detail = _service.GetScenarioDetail(scenario.Id);
|
||
Assert.Equal(2, detail.ControlZones.Count);
|
||
Assert.Equal("禁区A", detail.ControlZones[0].Name);
|
||
Assert.Equal(0, detail.ControlZones[0].OrderIndex);
|
||
Assert.Equal("禁区B", detail.ControlZones[1].Name);
|
||
Assert.Equal(1, detail.ControlZones[1].OrderIndex);
|
||
}
|
||
|
||
[Fact]
|
||
public void SaveControlZones_ReplaceExisting()
|
||
{
|
||
var scenario = _service.CreateScenario("Zone Replace", "");
|
||
_service.SaveControlZones(scenario.Id, new List<ControlZone>
|
||
{
|
||
new ControlZone { Name = "Old" }
|
||
});
|
||
_service.SaveControlZones(scenario.Id, new List<ControlZone>
|
||
{
|
||
new ControlZone { Name = "New1" },
|
||
new ControlZone { Name = "New2" },
|
||
});
|
||
|
||
var detail = _service.GetScenarioDetail(scenario.Id);
|
||
Assert.Equal(2, detail.ControlZones.Count);
|
||
}
|
||
|
||
[Fact]
|
||
public void SaveDroneProfile_SavesCorrectly()
|
||
{
|
||
var scenario = _service.CreateScenario("Target Test", "");
|
||
_service.SaveDroneProfile(scenario.Id, new DroneProfile
|
||
{
|
||
WaveId = "default",
|
||
DroneType = (int)DroneType.FixedWing,
|
||
Quantity = 5,
|
||
PowerType = (int)PowerType.Piston,
|
||
Wingspan = 2.5,
|
||
TypicalSpeed = 120.0,
|
||
TypicalAltitude = 800.0,
|
||
});
|
||
|
||
var detail = _service.GetScenarioDetail(scenario.Id);
|
||
var target = detail.Drones[0];
|
||
Assert.Equal((int)DroneType.FixedWing, target.DroneType);
|
||
Assert.Equal(5, target.Quantity);
|
||
Assert.Equal(120.0, target.TypicalSpeed);
|
||
}
|
||
|
||
[Fact]
|
||
public void SaveDeployment_SavesMultiple()
|
||
{
|
||
var scenario = _service.CreateScenario("Equip Test", "");
|
||
var equips = new List<EquipmentDeployment>
|
||
{
|
||
new EquipmentDeployment
|
||
{
|
||
EquipmentRole = (int)EquipmentRole.LaunchPlatform,
|
||
PlatformType = (int)PlatformType.GroundBased,
|
||
Quantity = 2,
|
||
PositionX = 1000, PositionY = 0, PositionZ = 50,
|
||
MuzzleVelocity = 800.0,
|
||
MunitionCount = 3,
|
||
},
|
||
new EquipmentDeployment
|
||
{
|
||
EquipmentRole = (int)EquipmentRole.Detection,
|
||
Quantity = 1,
|
||
RadarRange = 5000.0,
|
||
},
|
||
};
|
||
|
||
_service.SaveDeployment(scenario.Id, equips);
|
||
|
||
var detail = _service.GetScenarioDetail(scenario.Id);
|
||
Assert.Equal(2, detail.Equipment.Count);
|
||
|
||
var platform = detail.Equipment.Find(e => e.EquipmentRole == (int)EquipmentRole.LaunchPlatform);
|
||
Assert.NotNull(platform);
|
||
Assert.Equal(2, platform.Quantity);
|
||
Assert.Equal(800.0, platform.MuzzleVelocity);
|
||
}
|
||
|
||
[Fact]
|
||
public void SaveRoute_SavesWaypoints()
|
||
{
|
||
var scenario = _service.CreateScenario("Route Test", "");
|
||
var route = new RoutePlan
|
||
{
|
||
FormationMode = (int)FormationMode.Formation,
|
||
LateralSpacing = 60.0,
|
||
};
|
||
var waypoints = new List<Waypoint>
|
||
{
|
||
new Waypoint { PosX = 0, PosY = 0, PosZ = 100, Altitude = 500, Speed = 100 },
|
||
new Waypoint { PosX = 5000, PosY = 0, PosZ = 100, Altitude = 500, Speed = 100 },
|
||
new Waypoint { PosX = 10000, PosY = 0, PosZ = 100, Altitude = 300, Speed = 80 },
|
||
};
|
||
|
||
_service.SaveRoute(scenario.Id, "default", route, waypoints);
|
||
|
||
var detail = _service.GetScenarioDetail(scenario.Id);
|
||
Assert.Equal((int)FormationMode.Formation, detail.Routes[0].FormationMode);
|
||
Assert.Equal(3, detail.WaypointGroups["default"].Count);
|
||
Assert.Equal(0, detail.WaypointGroups["default"][0].OrderIndex);
|
||
Assert.Equal(2, detail.WaypointGroups["default"][2].OrderIndex);
|
||
Assert.Equal(5000.0, detail.WaypointGroups["default"][1].PosX);
|
||
}
|
||
|
||
// ========== Search & Pagination ==========
|
||
|
||
[Fact]
|
||
public void SearchScenarios_KeywordFilter()
|
||
{
|
||
_service.CreateScenario("城市防御演习", "");
|
||
_service.CreateScenario("平原拦截测试", "");
|
||
_service.CreateScenario("海岸边防演练", "");
|
||
|
||
var result = _service.SearchScenarios("城市", null, null, 1, 10);
|
||
Assert.Equal(1, result.TotalCount);
|
||
Assert.Equal("城市防御演习", result.Items[0].Name);
|
||
}
|
||
|
||
[Fact]
|
||
public void SearchScenarios_NoResults()
|
||
{
|
||
_service.CreateScenario("scenario A", "");
|
||
var result = _service.SearchScenarios("不存在的任务", null, null, 1, 10);
|
||
Assert.Equal(0, result.TotalCount);
|
||
}
|
||
|
||
[Fact]
|
||
public void SearchScenarios_Pagination()
|
||
{
|
||
for (int i = 0; i < 15; i++)
|
||
_service.CreateScenario($"scenario {i:D2}", "");
|
||
|
||
var page1 = _service.SearchScenarios(null, null, null, 1, 5);
|
||
Assert.Equal(21, page1.TotalCount); // 15 + 6 预设想定
|
||
Assert.Equal(5, page1.Items.Count);
|
||
Assert.Equal(5, page1.TotalPages); // 21 ÷ 5 = 5 页
|
||
|
||
var page3 = _service.SearchScenarios(null, null, null, 3, 5);
|
||
Assert.Equal(5, page3.Items.Count);
|
||
}
|
||
|
||
[Fact]
|
||
public void SearchScenarios_DateRangeFilter()
|
||
{
|
||
// All scenarios created today, so date filter should include them
|
||
_service.CreateScenario("Recent scenario", "");
|
||
|
||
var yesterday = DateTime.UtcNow.AddDays(-1).ToString("yyyy-MM-dd");
|
||
var tomorrow = DateTime.UtcNow.AddDays(1).ToString("yyyy-MM-dd");
|
||
|
||
var result = _service.SearchScenarios(null, yesterday, tomorrow, 1, 10);
|
||
Assert.Equal(7, result.TotalCount); // 1 + 6 预设想定
|
||
|
||
var oldResult = _service.SearchScenarios(null, "2020-01-01", "2020-12-31", 1, 10);
|
||
Assert.Equal(0, oldResult.TotalCount);
|
||
}
|
||
|
||
// ========== UpdateStep ==========
|
||
|
||
[Fact]
|
||
public void UpdateStep_ValidSteps()
|
||
{
|
||
var scenario = _service.CreateScenario("Step Test", "");
|
||
Assert.Equal(1, scenario.CurrentStep);
|
||
|
||
_service.UpdateStep(scenario.Id, 3);
|
||
var detail = _service.GetScenarioDetail(scenario.Id);
|
||
Assert.Equal(3, detail.Info.CurrentStep);
|
||
|
||
_service.UpdateStep(scenario.Id, 5);
|
||
detail = _service.GetScenarioDetail(scenario.Id);
|
||
Assert.Equal(5, detail.Info.CurrentStep);
|
||
}
|
||
|
||
[Fact]
|
||
public void UpdateStep_ClampedToRange()
|
||
{
|
||
var scenario = _service.CreateScenario("Clamp Test", "");
|
||
|
||
_service.UpdateStep(scenario.Id, 0);
|
||
Assert.Equal(1, _service.GetScenarioDetail(scenario.Id).Info.CurrentStep);
|
||
|
||
_service.UpdateStep(scenario.Id, 10);
|
||
Assert.Equal(5, _service.GetScenarioDetail(scenario.Id).Info.CurrentStep);
|
||
}
|
||
|
||
// ========== Full Workflow ==========
|
||
|
||
[Fact]
|
||
public void FullWorkflow_CreateConfigureSearch()
|
||
{
|
||
// 1. 创建任务
|
||
var scenario = _service.CreateScenario("完整流程测试", "");
|
||
Assert.NotNull(scenario);
|
||
|
||
// 2. 步骤1:作战场景
|
||
_service.SaveScene(scenario.Id, new CombatScene
|
||
{
|
||
SceneType = (int)SceneType.Urban,
|
||
WeatherType = (int)WeatherType.Rain,
|
||
WindSpeed = 8.0,
|
||
WindDirection = (int)WindDirection.SE,
|
||
Temperature = 18.0,
|
||
TimeOfDay = "14:00",
|
||
});
|
||
_service.UpdateStep(scenario.Id, 1);
|
||
|
||
// 3. 步骤1扩展:管控区域
|
||
_service.SaveControlZones(scenario.Id, new List<ControlZone>
|
||
{
|
||
new ControlZone
|
||
{
|
||
Name = "核心区",
|
||
VerticesJson = "[{\"x\":0,\"y\":0,\"z\":0},{\"x\":500,\"y\":0,\"z\":0},{\"x\":500,\"y\":500,\"z\":0},{\"x\":0,\"y\":500,\"z\":0}]",
|
||
MinAltitude = 0, MaxAltitude = 1000,
|
||
}
|
||
});
|
||
|
||
// 4. 步骤2:目标配置
|
||
_service.SaveDroneProfile(scenario.Id, new DroneProfile
|
||
{
|
||
WaveId = "default",
|
||
DroneType = (int)DroneType.HighSpeed,
|
||
Quantity = 2,
|
||
PowerType = (int)PowerType.Jet,
|
||
TypicalSpeed = 300.0,
|
||
});
|
||
_service.UpdateStep(scenario.Id, 2);
|
||
|
||
// 5. 步骤3:装备部署
|
||
_service.SaveDeployment(scenario.Id, new List<EquipmentDeployment>
|
||
{
|
||
new EquipmentDeployment
|
||
{
|
||
EquipmentRole = (int)EquipmentRole.Detection,
|
||
Quantity = 1,
|
||
RadarRange = 6000.0,
|
||
},
|
||
new EquipmentDeployment
|
||
{
|
||
EquipmentRole = (int)EquipmentRole.LaunchPlatform,
|
||
PlatformType = (int)PlatformType.GroundBased,
|
||
Quantity = 3,
|
||
PositionX = 800, PositionY = 0, PositionZ = 50,
|
||
MuzzleVelocity = 850.0,
|
||
MunitionCount = 2,
|
||
},
|
||
});
|
||
_service.UpdateStep(scenario.Id, 3);
|
||
|
||
// 6. 步骤4:云团抛撒
|
||
_service.SaveCloudDispersal(scenario.Id, new CloudDispersal
|
||
{
|
||
AerosolType = (int)AerosolType.ActiveMaterial,
|
||
TriggerMode = (int)TriggerMode.Area,
|
||
Duration = 50.0,
|
||
PositionX = 3000,
|
||
PositionY = 0,
|
||
PositionZ = 300,
|
||
});
|
||
_service.UpdateStep(scenario.Id, 4);
|
||
|
||
// 7. 步骤5:航路规划
|
||
_service.SaveRoute(scenario.Id, "default", new RoutePlan
|
||
{
|
||
FormationMode = (int)FormationMode.Swarm,
|
||
LateralSpacing = 30.0,
|
||
}, new List<Waypoint>
|
||
{
|
||
new Waypoint { PosX = 0, PosY = 100, PosZ = 200, Altitude = 600, Speed = 250 },
|
||
new Waypoint { PosX = 10000, PosY = 100, PosZ = 200, Altitude = 600, Speed = 250 },
|
||
});
|
||
_service.UpdateStep(scenario.Id, 5);
|
||
|
||
// 8. 验证完整配置
|
||
var detail = _service.GetScenarioDetail(scenario.Id);
|
||
|
||
Assert.Equal("完整流程测试", detail.Info.Name);
|
||
Assert.Equal(5, detail.Info.CurrentStep);
|
||
Assert.Equal((int)ScenarioStatus.Configuring, detail.Info.Status);
|
||
|
||
// 场景
|
||
Assert.Equal((int)SceneType.Urban, detail.Scene.SceneType);
|
||
Assert.Equal((int)WeatherType.Rain, detail.Scene.WeatherType);
|
||
Assert.Equal("14:00", detail.Scene.TimeOfDay);
|
||
|
||
// 管控区域
|
||
Assert.Single(detail.ControlZones);
|
||
Assert.Equal("核心区", detail.ControlZones[0].Name);
|
||
|
||
// 目标
|
||
Assert.Single(detail.Drones);
|
||
Assert.Equal(2, detail.Drones[0].Quantity);
|
||
Assert.Equal(300.0, detail.Drones[0].TypicalSpeed);
|
||
|
||
// 装备
|
||
Assert.Equal(2, detail.Equipment.Count);
|
||
|
||
// 云团
|
||
Assert.Equal((int)AerosolType.ActiveMaterial, detail.Cloud.AerosolType);
|
||
Assert.Equal(50.0, detail.Cloud.Duration);
|
||
|
||
// 航路
|
||
Assert.Equal((int)FormationMode.Swarm, detail.Routes[0].FormationMode);
|
||
Assert.Equal(2, detail.WaypointGroups["default"].Count);
|
||
Assert.Equal(250.0, detail.WaypointGroups["default"][0].Speed);
|
||
|
||
// 9. 搜索验证
|
||
var searchResult = _service.SearchScenarios("完整流程", null, null, 1, 10);
|
||
Assert.Equal(1, searchResult.TotalCount);
|
||
}
|
||
|
||
// ═══════════════════════════════════════
|
||
// 探测设备独立 CRUD
|
||
// ═══════════════════════════════════════
|
||
|
||
[Fact]
|
||
public void AddDetection_PersistsAndQueryable()
|
||
{
|
||
var scenario = _service.CreateScenario("探测设备测试", "");
|
||
_service.AddDetection(scenario.Id, new EquipmentDeployment
|
||
{
|
||
Quantity = 1,
|
||
PositionX = 5000, PositionY = 0, PositionZ = 0,
|
||
RadarRange = 8000, EORange = 4000, IRRange = 3000,
|
||
DetectionAccuracy = 50,
|
||
});
|
||
|
||
var detections = _service.GetDetections(scenario.Id);
|
||
Assert.Single(detections);
|
||
Assert.Equal((int)EquipmentRole.Detection, detections[0].EquipmentRole);
|
||
Assert.Equal(8000.0, detections[0].RadarRange);
|
||
Assert.Equal(50.0, detections[0].DetectionAccuracy);
|
||
}
|
||
|
||
[Fact]
|
||
public void GetDetections_FiltersOutLaunchPlatforms()
|
||
{
|
||
var scenario = _service.CreateScenario("探测过滤测试", "");
|
||
// 一个火力单元
|
||
_service.SaveDeployment(scenario.Id, new List<EquipmentDeployment>
|
||
{
|
||
new EquipmentDeployment
|
||
{
|
||
EquipmentRole = (int)EquipmentRole.LaunchPlatform,
|
||
Quantity = 1, PositionX = 1000,
|
||
},
|
||
});
|
||
// 两个探测设备(独立添加)
|
||
_service.AddDetection(scenario.Id, new EquipmentDeployment { RadarRange = 5000 });
|
||
_service.AddDetection(scenario.Id, new EquipmentDeployment { EORange = 3000 });
|
||
|
||
var detections = _service.GetDetections(scenario.Id);
|
||
Assert.Equal(2, detections.Count); // 只有探测设备,不含火力单元
|
||
}
|
||
|
||
[Fact]
|
||
public void DeleteDetection_RemovesOnlyOne()
|
||
{
|
||
var scenario = _service.CreateScenario("探测删除测试", "");
|
||
_service.AddDetection(scenario.Id, new EquipmentDeployment { RadarRange = 5000 });
|
||
var det2 = new EquipmentDeployment { EORange = 3000 };
|
||
_service.AddDetection(scenario.Id, det2);
|
||
|
||
_service.DeleteDetection(det2.Id);
|
||
|
||
var detections = _service.GetDetections(scenario.Id);
|
||
Assert.Single(detections);
|
||
Assert.Equal(5000.0, detections[0].RadarRange);
|
||
}
|
||
|
||
[Fact]
|
||
public void GetScenarioDetail_IncludesDetections()
|
||
{
|
||
var scenario = _service.CreateScenario("想定含探测", "");
|
||
_service.SaveDeployment(scenario.Id, new List<EquipmentDeployment>
|
||
{
|
||
new EquipmentDeployment { EquipmentRole = (int)EquipmentRole.LaunchPlatform, Quantity = 1 },
|
||
});
|
||
_service.AddDetection(scenario.Id, new EquipmentDeployment { RadarRange = 10000 });
|
||
|
||
var detail = _service.GetScenarioDetail(scenario.Id);
|
||
// Equipment 包含火力单元 + 探测设备
|
||
Assert.Equal(2, detail.Equipment.Count);
|
||
Assert.Contains(detail.Equipment, e => e.EquipmentRole == (int)EquipmentRole.Detection);
|
||
Assert.Contains(detail.Equipment, e => e.EquipmentRole == (int)EquipmentRole.LaunchPlatform);
|
||
}
|
||
}
|
||
}
|