CounterDroneBackend/test/unit/CounterDrone.Core.Tests/DefenseRecommendationTests.cs
tian 9d81ba0b28 新增 GetDefenseRecommendation 防御推荐方案 + 去掉 Critical
- IScenarioService 新增 GetDefenseRecommendation(scenarioId)
- ScenarioService 构造函数加可选 IPathProvider + IDefensePlanner
- DefenseRecommendation / RecommendOption 返回类型
- 前端调用获取最佳抛撒参数 → 用户一键应用写入 CloudDispersal
- 去掉 Critical(省弹药到 50% 概率,无决策价值)
- Unity ScenarioManager 桥接 + 构造注入 planner
- 对接文档加推荐用法示例
- 265 测试通过(+3 推荐),Unity 编译通过
2026-06-22 08:05:09 +08:00

98 lines
3.9 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using CounterDrone.Core;
using CounterDrone.Core.Algorithms;
using CounterDrone.Core.Models;
using CounterDrone.Core.Repository;
using CounterDrone.Core.Services;
using SQLite;
using Xunit;
namespace CounterDrone.Core.Tests
{
public class DefenseRecommendationTests : IDisposable
{
private readonly string _testDir;
private readonly IPathProvider _paths;
private readonly SQLiteConnection _db;
private readonly ScenarioService _scenario;
private string _scenarioId = string.Empty;
public DefenseRecommendationTests()
{
_testDir = Path.Combine(Path.GetTempPath(), $"cd_rec_{Guid.NewGuid():N}");
_paths = new TestPathProvider(_testDir);
var dbm = new DatabaseManager(_paths);
_db = dbm.OpenMainDb();
var planner = new DefensePlanner(
DefaultData.Load(_paths).Ammunition,
PlannerConfig.Load(_paths));
_scenario = new ScenarioService(
new ScenarioRepository(_db), new CombatSceneRepository(_db),
new ControlZoneRepository(_db), new ScenarioDroneRepository(_db),
new ScenarioUnitRepository(_db), new CloudDispersalRepository(_db),
new RoutePlanRepository(_db), new WaypointRepository(_db),
_paths, planner);
}
public void Dispose()
{
_db?.Close();
if (Directory.Exists(_testDir)) Directory.Delete(_testDir, true);
}
[Fact]
public void GetDefenseRecommendation_PresetScenario_ReturnsBest()
{
// 用预设想定
var results = _scenario.SearchScenarios("活塞", null, null, 1, 10);
Assert.True(results.TotalCount > 0, "预设想定未找到");
_scenarioId = results.Items[0].Id;
var rec = _scenario.GetDefenseRecommendation(_scenarioId);
Assert.True(rec.Success, $"推荐失败: {rec.Summary}");
Assert.True(rec.Best.SalvoRounds > 0, "最佳方案弹药数应 > 0");
Assert.True(rec.Best.EstimatedProbability > 0, "最佳方案概率应 > 0");
Assert.NotEmpty(rec.Summary);
}
[Fact]
public void GetDefenseRecommendation_NoFireUnits_ReturnsFailure()
{
// 创建无火力单元的想定
var sc = _scenario.CreateScenario("无防御测试", "");
_scenarioId = sc.Id;
_scenario.SaveScene(_scenarioId, new CombatScene { WindSpeed = 0, Visibility = 10000 });
_scenario.SaveScenarioDrone(_scenarioId, TestData.CreateScenarioDrone("default", 1));
_scenario.SaveRoute(_scenarioId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single },
new List<Waypoint> {
new() { PosX = 0, PosY = 500, PosZ = 0, Speed = 200 },
new() { PosX = 10000, PosY = 500, PosZ = 0, Speed = 200 },
});
_scenario.SaveCloudDispersal(_scenarioId, new CloudDispersal { AerosolType = (int)AerosolType.InertGas });
var rec = _scenario.GetDefenseRecommendation(_scenarioId);
Assert.False(rec.Success);
Assert.Contains("火力单元", rec.Summary);
}
[Fact]
public void GetDefenseRecommendation_NoPlanner_Throws()
{
// 不传 planner 的 ScenarioService
var svcNoPlanner = new ScenarioService(
new ScenarioRepository(_db), new CombatSceneRepository(_db),
new ControlZoneRepository(_db), new ScenarioDroneRepository(_db),
new ScenarioUnitRepository(_db), new CloudDispersalRepository(_db),
new RoutePlanRepository(_db), new WaypointRepository(_db));
Assert.Throws<InvalidOperationException>(() => svcNoPlanner.GetDefenseRecommendation("any"));
}
}
}