import 'package:flutter_test/flutter_test.dart'; import 'package:dmc_app/domain/mission/drone_profile.dart'; import 'package:dmc_app/domain/mission/mission_warnings.dart'; import 'package:dmc_app/domain/mission/route_geometry.dart'; import 'package:dmc_app/domain/mission/terrain_math.dart'; import 'package:dmc_app/domain/waypoint/flat_waypoint_list.dart'; void main() { const waypoints = [ Waypoint(lat: 0, lon: 0, altitudeM: 60, speedMs: 15, catchRadiusM: 60), Waypoint(lat: 0, lon: 0.01, altitudeM: 60, speedMs: 15, catchRadiusM: 60), ]; final drone = DroneProfile.t1Ranger(); const okGeometry = RouteGeometry(segments: [], vertexBad: [false, false], legClimbBad: [false]); group('computeMissionWarnings', () { test('keine Warnungen bei unauffaelliger Route', () { final warnings = computeMissionWarnings( waypoints: waypoints, routeGeometry: okGeometry, legWindBad: const [false], droneProfile: drone, totalDistanceM: 100, totalDurationSec: 60, ); expect(warnings, isEmpty); }); test('meldet eine Routen-Warnung bei vertexBad und verweist auf die Karte', () { const badGeometry = RouteGeometry(segments: [], vertexBad: [true, false], legClimbBad: [false]); final warnings = computeMissionWarnings( waypoints: waypoints, routeGeometry: badGeometry, legWindBad: const [false], droneProfile: drone, totalDistanceM: 100, totalDurationSec: 60, ); expect(warnings, hasLength(1)); expect(warnings.single.action, WarningAction.map); }); test('meldet eine Wind-Warnung und verweist auf den Speed-Tab', () { final warnings = computeMissionWarnings( waypoints: waypoints, routeGeometry: okGeometry, legWindBad: const [true], droneProfile: drone, totalDistanceM: 100, totalDurationSec: 60, ); expect(warnings.single.action, WarningAction.speed); }); test('meldet eine Gelaende-Warnung bei zum routeKey passendem Profil und verweist auf den Altitude-Tab', () { final profile = TerrainProfile(routeKey: routeKeyForTerrain(waypoints), points: const [ TerrainSamplePoint(distanceM: 0, elevationRelM: 55), // clearance 5 < 10 TerrainSamplePoint(distanceM: 500, elevationRelM: 55), ]); final warnings = computeMissionWarnings( waypoints: waypoints, routeGeometry: okGeometry, legWindBad: const [false], droneProfile: drone, totalDistanceM: 100, totalDurationSec: 60, terrain: profile, ); expect(warnings.single.action, WarningAction.altitude); }); test('ignoriert ein Gelaendeprofil, dessen routeKey nicht mehr zur Route passt', () { const staleProfile = TerrainProfile(routeKey: 'stale', points: [ TerrainSamplePoint(distanceM: 0, elevationRelM: 55), ]); final warnings = computeMissionWarnings( waypoints: waypoints, routeGeometry: okGeometry, legWindBad: const [false], droneProfile: drone, totalDistanceM: 100, totalDurationSec: 60, terrain: staleProfile, ); expect(warnings, isEmpty); }); test('meldet Reichweiten- und Ausdauer-Ueberschreitung, beide verweisen auf die Karte', () { final tightDrone = drone.copyWith(maxRangeM: 50, maxEnduranceMin: 0.5); final warnings = computeMissionWarnings( waypoints: waypoints, routeGeometry: okGeometry, legWindBad: const [false], droneProfile: tightDrone, totalDistanceM: 100, totalDurationSec: 60, ); expect(warnings, hasLength(2)); expect(warnings.every((w) => w.action == WarningAction.map), isTrue); }); test('maxRangeM/maxEnduranceMin <= 0 bedeutet "kein Limit" und wird nie ueberschritten', () { final unlimitedDrone = drone.copyWith(maxRangeM: 0, maxEnduranceMin: 0); final warnings = computeMissionWarnings( waypoints: waypoints, routeGeometry: okGeometry, legWindBad: const [false], droneProfile: unlimitedDrone, totalDistanceM: 1000000, totalDurationSec: 1000000, ); expect(warnings, isEmpty); }); }); }