修改了测试框架和测试用例,解决超时问题

This commit is contained in:
Tian jianyong 2025-04-30 11:55:14 +08:00
parent a0d3564f84
commit 574dfc8b40
12 changed files with 581 additions and 92 deletions

25
pom.xml
View File

@ -166,6 +166,31 @@
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<!-- 设置超时时间为60秒 -->
<forkedProcessTimeoutInSeconds>60</forkedProcessTimeoutInSeconds>
<!-- 强制关闭非正常终止的JVM进程 -->
<shutdown>kill</shutdown>
<!-- 在两个失败后停止执行剩余测试 -->
<skipAfterFailureCount>2</skipAfterFailureCount>
<!-- 禁用系统类加载器 -->
<useSystemClassLoader>false</useSystemClassLoader>
<forkCount>1</forkCount>
<reuseForks>false</reuseForks>
<!-- 强制退出测试进程 -->
<forkedProcessExitTimeoutInSeconds>5</forkedProcessExitTimeoutInSeconds>
<systemPropertyVariables>
<!-- 禁用外部服务连接,防止测试时连接超时 -->
<spring.data.mongodb.auto-index-creation>false</spring.data.mongodb.auto-index-creation>
<spring.autoconfigure.exclude>org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration</spring.autoconfigure.exclude>
<!-- 测试模式标记 -->
<spring.profiles.active>test</spring.profiles.active>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>

View File

@ -1,16 +0,0 @@
package com.dongni.collisionavoidance.area.model;
/**
* 机场区域类型枚举
*/
public enum AreaType {
RUNWAY, // 跑道
TAXIWAY, // 滑行道
APRON, // 停机坪
SERVICE_AREA, // 服务区
CARGO_AREA, // 货运区
TERMINAL_AREA, // 航站楼区域
MAINTENANCE, // 维修区
RESTRICTED, // 限制区
PROTECTION // 保护区
}

View File

@ -1,33 +0,0 @@
package com.dongni.collisionavoidance.area.service;
import com.dongni.collisionavoidance.config.properties.AirportAreasProperties;
import com.dongni.collisionavoidance.config.properties.AreaProperties;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class AirportAreaService {
private final List<AreaProperties> areas;
public AirportAreaService(AirportAreasProperties properties) {
this.areas = properties.getAreas();
}
public List<AreaProperties> getAllAreas() {
return areas;
}
public Optional<AreaProperties> getAreaById(String id) {
return areas.stream()
.filter(area -> area.getId().equals(id))
.findFirst();
}
public List<AreaProperties> getAreasByType(String type) {
return areas.stream()
.filter(area -> area.getType().equals(type))
.toList();
}
}

View File

@ -0,0 +1,25 @@
package com.dongni.collisionavoidance.areas.model;
import lombok.Builder;
import lombok.Value;
import org.locationtech.jts.geom.Polygon;
import java.time.ZonedDateTime;
import java.util.List;
@Value
@Builder
public class AreaInfo {
String id; // 区域唯一标识
String name; // 区域名称
AreaType type; // 区域类型跑道机坪等
Double speedLimitKph; // 限速公里/小时
String description; // 区域用途描述
boolean restricted; // 是否限制进入
List<String> allowedVehicleTypes; // 允许的车辆类型
List<String> allowedAircraftTypes; // 允许的航空器类型
Double maxHeight; // 最大高度限制
Double maxWeight; // 最大重量限制
Polygon boundary; // JTS 多边形边界
ZonedDateTime activeTime; // 生效时间用于临时区域
ZonedDateTime expiryTime; // 失效时间用于临时区域
}

View File

@ -0,0 +1,16 @@
package com.dongni.collisionavoidance.areas.model;
/**
* 机场区域类型枚举
*/
public enum AreaType {
RUNWAY, // 跑道
TAXIWAY, // 滑行道
APRON, // 停机坪
SERVICE_AREA, // 服务区
CARGO_AREA, // 货运区
TERMINAL_AREA, // 航站楼区域
MAINTENANCE, // 维修区
RESTRICTED, // 限制区
PROTECTION // 保护区
}

View File

@ -0,0 +1,176 @@
package com.dongni.collisionavoidance.areas.service;
import com.dongni.collisionavoidance.areas.model.AreaInfo;
import com.dongni.collisionavoidance.areas.model.AreaType;
import com.dongni.collisionavoidance.common.model.GeoPosition;
import com.dongni.collisionavoidance.config.properties.AirportAreasProperties;
import com.dongni.collisionavoidance.config.properties.AreaProperties;
import com.dongni.collisionavoidance.config.properties.GeometryProperties;
import lombok.extern.slf4j.Slf4j;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.Polygon;
import org.locationtech.jts.index.strtree.STRtree;
import org.springframework.stereotype.Service;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Slf4j
@Service
public class AirportAreaService {
private final List<AreaInfo> areas;
private final STRtree spatialIndex;
private final GeometryFactory geometryFactory;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ISO_DATE_TIME;
public AirportAreaService(AirportAreasProperties properties) {
this.geometryFactory = new GeometryFactory();
this.areas = convertToAreaInfo(properties.getAreas());
this.spatialIndex = buildSpatialIndex();
}
private List<AreaInfo> convertToAreaInfo(List<AreaProperties> properties) {
return properties.stream()
.map(this::convertToAreaInfo)
.collect(Collectors.toList());
}
private AreaInfo convertToAreaInfo(AreaProperties properties) {
return AreaInfo.builder()
.id(properties.getId())
.name(properties.getName())
.type(AreaType.valueOf(properties.getType()))
.speedLimitKph(properties.getSpeedLimit())
.description(properties.getPurpose())
.restricted(properties.getRestrictions() != null && !properties.getRestrictions().isEmpty())
.allowedVehicleTypes(properties.getAllowedVehicleTypes())
.allowedAircraftTypes(properties.getAllowedAircraftTypes())
.maxHeight(properties.getMaxHeight())
.maxWeight(properties.getMaxWeight())
.boundary(convertToPolygon(properties.getGeometry()))
.activeTime(parseDateTime(properties.getActiveTime()))
.expiryTime(parseDateTime(properties.getExpiryTime()))
.build();
}
private Polygon convertToPolygon(GeometryProperties geometry) {
if (geometry == null || geometry.getCoordinates() == null || geometry.getCoordinates().isEmpty()) {
return null;
}
try {
List<Coordinate> coordinates = geometry.getCoordinates().stream()
.map(coord -> {
double lon = coord.get(0);
double lat = coord.get(1);
log.info("转换坐标: [{}{}] -> JTS坐标", lon, lat);
return new Coordinate(lon, lat);
})
.collect(Collectors.toList());
// 确保多边形是闭合的
if (!coordinates.get(0).equals(coordinates.get(coordinates.size() - 1))) {
log.info("多边形未闭合,添加闭合点");
coordinates.add(coordinates.get(0));
}
Polygon polygon = geometryFactory.createPolygon(coordinates.toArray(new Coordinate[0]));
log.info("创建多边形: {}", polygon);
return polygon;
} catch (Exception e) {
log.error("转换多边形失败: {}", e.getMessage(), e);
return null;
}
}
private ZonedDateTime parseDateTime(String dateTimeStr) {
if (dateTimeStr == null || dateTimeStr.isEmpty()) {
return null;
}
try {
return ZonedDateTime.parse(dateTimeStr, DATE_TIME_FORMATTER);
} catch (Exception e) {
log.warn("Failed to parse date time: {}", dateTimeStr, e);
return null;
}
}
private STRtree buildSpatialIndex() {
STRtree index = new STRtree();
for (AreaInfo area : areas) {
if (area.getBoundary() != null) {
index.insert(area.getBoundary().getEnvelopeInternal(), area);
}
}
index.build();
return index;
}
public List<AreaInfo> getAllAreas() {
return new ArrayList<>(areas);
}
public Optional<AreaInfo> getAreaById(String id) {
return areas.stream()
.filter(area -> area.getId().equals(id))
.findFirst();
}
public List<AreaInfo> getAreasByType(AreaType type) {
return areas.stream()
.filter(area -> area.getType() == type)
.collect(Collectors.toList());
}
public List<AreaInfo> findAreasContainingPoint(GeoPosition position) {
log.info("查询包含点的区域: lat={}, lon={}", position.getLatitude(), position.getLongitude());
Point point = geometryFactory.createPoint(new Coordinate(position.getLongitude(), position.getLatitude()));
log.info("创建JTS点: {}", point);
@SuppressWarnings("unchecked")
List<AreaInfo> candidates = spatialIndex.query(point.getEnvelopeInternal());
log.info("空间索引查询结果数量: {}", candidates.size());
return candidates.stream()
.filter(area -> {
boolean contains = area.getBoundary() != null && area.getBoundary().contains(point);
log.info("区域 {} ({}): boundary={}, contains={}",
area.getId(), area.getName(),
area.getBoundary() != null ? area.getBoundary().toString() : "null",
contains);
return contains;
})
.collect(Collectors.toList());
}
public Optional<AreaInfo> findDominantAreaAt(GeoPosition position) {
List<AreaInfo> containingAreas = findAreasContainingPoint(position);
if (containingAreas.isEmpty()) {
return Optional.empty();
}
// 按照区域优先级返回最优先的区域
return containingAreas.stream()
.max((a1, a2) -> a2.getType().ordinal() - a1.getType().ordinal());
}
public Double getSpeedLimitKphAt(GeoPosition position) {
return findDominantAreaAt(position)
.map(AreaInfo::getSpeedLimitKph)
.orElse(null);
}
public boolean isPositionInRestrictedArea(GeoPosition position) {
return findAreasContainingPoint(position).stream()
.anyMatch(AreaInfo::isRestricted);
}
public boolean isAreaActive(AreaInfo area) {
ZonedDateTime now = ZonedDateTime.now();
return (area.getActiveTime() == null || !now.isBefore(area.getActiveTime())) &&
(area.getExpiryTime() == null || !now.isAfter(area.getExpiryTime()));
}
}

View File

@ -20,7 +20,6 @@ spring:
value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
properties:
spring.json.trusted.packages: "com.airport.common.model"
# Redis配置
redis:
host: localhost
@ -35,6 +34,8 @@ spring:
min-idle: 0
key-serialization: org.springframework.data.redis.serialization.StringRedisSerializer
value-serialization: org.springframework.data.redis.serialization.Jackson2JsonRedisSerializer
main:
allow-bean-definition-overriding: true
# 数据采集配置
data:

View File

@ -1,16 +1,22 @@
package com.dongni.collisionavoidance.areas.service;
import com.dongni.collisionavoidance.areas.model.AreaInfo;
import com.dongni.collisionavoidance.areas.model.AreaType;
import com.dongni.collisionavoidance.common.model.GeoPosition;
import com.dongni.collisionavoidance.config.AirportAreaConfig;
import com.dongni.collisionavoidance.config.properties.AreaProperties;
import com.dongni.collisionavoidance.area.service.AirportAreaService;
import com.dongni.collisionavoidance.config.TestConfig;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource;
import java.util.List;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
@ -18,9 +24,12 @@ import static org.assertj.core.api.Assertions.assertThat;
* 机场区域服务的集成测试类
* 确保区域配置正确加载并且服务方法按预期工作
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
@ExtendWith(MockitoExtension.class)
@Import(AirportAreaConfig.class)
@Import({AirportAreaConfig.class, TestConfig.class})
@TestPropertySource(locations = "classpath:config/airport_areas.yaml")
@ActiveProfiles("test")
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
class AirportAreaServiceIntegrationTest {
@Autowired
@ -34,12 +43,12 @@ class AirportAreaServiceIntegrationTest {
@Test
void getAllAreas_shouldReturnAllAreas() {
List<AreaProperties> areas = airportAreaService.getAllAreas();
List<AreaInfo> areas = airportAreaService.getAllAreas();
assertThat(areas)
.isNotNull()
.isNotEmpty()
.hasSize(2); // 配置文件中有两个区域跑道区域和停机坪区域
.hasSize(2); // 配置文件中有两个区域跑道区域和滑行道区域
System.out.println("获取到 " + areas.size() + " 个区域");
}
@ -47,18 +56,17 @@ class AirportAreaServiceIntegrationTest {
@Test
void getAreaById_shouldReturnArea_whenIdExists() {
String areaId = "1"; // 跑道区域的ID
AreaProperties area = airportAreaService.getAreaById(areaId).orElse(null);
AreaInfo area = airportAreaService.getAreaById(areaId).orElse(null);
assertThat(area)
.isNotNull()
.satisfies(a -> {
assertThat(a.getId()).isEqualTo("1");
assertThat(a.getName()).isEqualTo("跑道区域");
assertThat(a.getType()).isEqualTo("RUNWAY");
assertThat(a.getSpeedLimit()).isEqualTo(0.0);
assertThat(a.getPurpose()).isEqualTo("用于航空器起降的主要跑道");
assertThat(a.getRestrictions())
.containsExactly("禁止停车", "禁止通行");
assertThat(a.getType()).isEqualTo(AreaType.RUNWAY);
assertThat(a.getSpeedLimitKph()).isEqualTo(0.0);
assertThat(a.getDescription()).isEqualTo("用于航空器起降的主要跑道");
assertThat(a.isRestricted()).isTrue();
assertThat(a.getAllowedVehicleTypes())
.containsExactly("AIRCRAFT");
assertThat(a.getAllowedAircraftTypes())
@ -71,39 +79,116 @@ class AirportAreaServiceIntegrationTest {
@Test
void getAreaById_shouldReturnNull_whenIdDoesNotExist() {
String nonExistentId = "non-existent-area-id";
AreaProperties area = airportAreaService.getAreaById(nonExistentId).orElse(null);
AreaInfo area = airportAreaService.getAreaById(nonExistentId).orElse(null);
assertThat(area).isNull();
}
@Test
void getAreasByType_shouldReturnAreas_whenTypeExists() {
String type = "RUNWAY";
List<AreaProperties> areas = airportAreaService.getAreasByType(type);
AreaType type = AreaType.RUNWAY;
List<AreaInfo> areas = airportAreaService.getAreasByType(type);
assertThat(areas)
.isNotNull()
.isNotEmpty()
.hasSize(1)
.allMatch(area -> type.equals(area.getType()));
.allMatch(area -> type == area.getType());
// 验证返回的是跑道区域
AreaProperties runwayArea = areas.get(0);
AreaInfo runwayArea = areas.get(0);
assertThat(runwayArea)
.satisfies(area -> {
assertThat(area.getName()).isEqualTo("跑道区域");
assertThat(area.getSpeedLimit()).isEqualTo(0.0);
assertThat(area.getPurpose()).isEqualTo("用于航空器起降的主要跑道");
assertThat(area.getSpeedLimitKph()).isEqualTo(0.0);
assertThat(area.getDescription()).isEqualTo("用于航空器起降的主要跑道");
});
}
@Test
void getAreasByType_shouldReturnEmpty_whenTypeDoesNotExist() {
String nonExistentType = "non-existent-type";
List<AreaProperties> areas = airportAreaService.getAreasByType(nonExistentType);
AreaType nonExistentType = AreaType.RESTRICTED;
List<AreaInfo> areas = airportAreaService.getAreasByType(nonExistentType);
assertThat(areas)
.isNotNull()
.isEmpty();
}
@Test
void findAreasContainingPoint_shouldReturnAllMatchingAreas_whenPointIsIn() {
// 测试点在跑道和滑行道重叠区域
GeoPosition position = new GeoPosition(39.123500, 116.123600, 0.0);
List<AreaInfo> areas = airportAreaService.findAreasContainingPoint(position);
assertThat(areas).isNotEmpty()
.extracting(AreaInfo::getType)
.contains(AreaType.RUNWAY, AreaType.TAXIWAY);
// 测试点在区域外
position = new GeoPosition(39.124000, 116.124000, 0.0);
areas = airportAreaService.findAreasContainingPoint(position);
assertThat(areas).isEmpty();
}
@Test
void findDominantAreaAt_shouldReturnHighestPriorityArea_whenPointIsInMultipleAreas() {
// 测试点在跑道和滑行道重叠区域跑道应该是优先级最高的
GeoPosition position = new GeoPosition(39.123500, 116.123600, 0.0);
Optional<AreaInfo> dominantArea = airportAreaService.findDominantAreaAt(position);
assertThat(dominantArea)
.isPresent()
.map(AreaInfo::getType)
.hasValue(AreaType.RUNWAY);
// 测试点在区域外
position = new GeoPosition(39.124000, 116.124000, 0.0);
dominantArea = airportAreaService.findDominantAreaAt(position);
assertThat(dominantArea).isEmpty();
}
@Test
void getSpeedLimitKphAt_shouldReturnSpeedLimit_whenPointIsInArea() {
// 测试点在跑道和滑行道重叠区域根据优先级应返回跑道的速度限制
GeoPosition position = new GeoPosition(39.123500, 116.123600, 0.0);
Double speedLimit = airportAreaService.getSpeedLimitKphAt(position);
assertThat(speedLimit)
.isNotNull()
.isEqualTo(0.0);
// 测试点在区域外
position = new GeoPosition(39.124000, 116.124000, 0.0);
speedLimit = airportAreaService.getSpeedLimitKphAt(position);
assertThat(speedLimit).isNull();
}
@Test
void isPositionInRestrictedArea_shouldReturnTrue_whenPointIsInRestrictedArea() {
// 测试点在跑道内
GeoPosition position = new GeoPosition(39.123600, 116.123600, 0.0);
boolean isRestricted = airportAreaService.isPositionInRestrictedArea(position);
assertThat(isRestricted).isTrue();
// 测试点在滑行道内
position = new GeoPosition(39.123500, 116.123600, 0.0);
isRestricted = airportAreaService.isPositionInRestrictedArea(position);
assertThat(isRestricted).isTrue();
}
@Test
void isAreaActive_shouldReturnTrue_whenAreaIsActive() {
Optional<AreaInfo> area = airportAreaService.getAreaById("1");
assertThat(area)
.isPresent()
.hasValueSatisfying(a ->
assertThat(airportAreaService.isAreaActive(a)).isTrue());
}
}

View File

@ -0,0 +1,151 @@
package com.dongni.collisionavoidance.config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import com.dongni.collisionavoidance.common.model.MovingObject;
import com.dongni.collisionavoidance.common.model.MovingObjectType;
import com.dongni.collisionavoidance.common.model.repository.MovingObjectRepository;
import com.dongni.collisionavoidance.dataCollector.service.AuthService;
import com.dongni.collisionavoidance.dataCollector.service.DataCollectorService;
import com.dongni.collisionavoidance.dataProcessing.service.CoordinateSystemService;
import com.dongni.collisionavoidance.dataProcessing.service.DataProcessor;
import com.dongni.collisionavoidance.dataProcessing.service.SpeedCalculationService;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import org.springframework.web.client.RestTemplate;
/**
* 测试专用配置类用于禁用后台线程和外部连接
*/
@Configuration
@Profile("test")
public class TestConfig {
/**
* 提供一个测试专用的线程池会立即关闭而不会等待任务执行完成
*/
@Bean(name = "processingExecutor")
@Primary
public Executor testProcessingExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setMaxPoolSize(1);
executor.setQueueCapacity(1);
executor.setWaitForTasksToCompleteOnShutdown(false);
executor.setAwaitTerminationSeconds(1);
executor.setThreadNamePrefix("test-proc-");
return executor;
}
/**
* 提供一个用于调度的线程池会立即关闭而不会等待任务执行完成
*/
@Bean
@Primary
public ThreadPoolTaskScheduler testTaskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(1);
scheduler.setThreadNamePrefix("test-sched-");
scheduler.setWaitForTasksToCompleteOnShutdown(false);
scheduler.setAwaitTerminationSeconds(1);
return scheduler;
}
/**
* 提供一个不执行任何操作的数据处理器
*/
@Bean
@Primary
public DataProcessor noOpDataProcessor(MovingObjectRepository movingObjectRepository,
CoordinateSystemService coordinateSystemService,
SpeedCalculationService speedCalculationService,
Executor processingExecutor) {
return new TestDataProcessor(movingObjectRepository, coordinateSystemService,
speedCalculationService, processingExecutor);
}
/**
* 提供一个不进行HTTP请求的测试用认证服务
*/
@Bean
@Primary
public AuthService testAuthService(RestTemplate restTemplate) {
return new TestAuthService(restTemplate);
}
/**
* 提供一个不调用外部API的数据采集服务
*/
@Bean
@Primary
public DataCollectorService testDataCollectorService() {
return new TestDataCollectorService();
}
/**
* 测试用数据处理器实现不会启动实际的处理线程
*/
static class TestDataProcessor extends DataProcessor {
public TestDataProcessor(MovingObjectRepository movingObjectRepository,
CoordinateSystemService coordinateSystemService,
SpeedCalculationService speedCalculationService,
Executor processingExecutor) {
super();
// 注入依赖但不启动后台线程
}
@Override
public void init() {
// 不启动处理线程
}
}
/**
* 测试用认证服务实现返回固定的测试令牌
*/
static class TestAuthService extends AuthService {
public TestAuthService(RestTemplate restTemplate) {
super(restTemplate);
}
@Override
public String loginAndGetToken() {
return "test-token";
}
@Override
public String refreshToken() {
return "test-token";
}
@Override
public String getToken() {
return "test-token";
}
}
/**
* 测试用数据采集服务不执行实际的数据采集
*/
static class TestDataCollectorService extends DataCollectorService {
// 覆盖所有定时任务方法不执行实际操作
@Override
public void collectAircraftData() {
// 测试环境下不执行实际数据采集
}
@Override
public void collectVehicleData() {
// 测试环境下不执行实际数据采集
}
}
}

View File

@ -4,13 +4,16 @@ import com.dongni.collisionavoidance.common.model.GeoPosition;
import com.dongni.collisionavoidance.dataCollector.service.DataCollectorService;
import com.dongni.collisionavoidance.dataProcessing.service.DataProcessor;
import com.dongni.collisionavoidance.roads.model.RoadInfo;
import com.dongni.collisionavoidance.config.TestConfig;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles; // Optional: if you need specific test profile
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import java.util.List;
import java.util.Optional;
@ -26,12 +29,17 @@ import static org.junit.jupiter.api.Assertions.*;
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) // Load context without web server
@ExtendWith(MockitoExtension.class)
// @ActiveProfiles("test") // Activate a specific test profile if needed (e.g., for application-test.yml)
@ActiveProfiles("test") // 激活测试配置文件
@Import(TestConfig.class) // 导入测试配置
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) // 确保测试后销毁上下文
class RoadNetworkServiceIntegrationTest {
@Autowired
private RoadNetworkService roadNetworkService;
// 这些Mock对象在Spring上下文中不会生效只会在测试类内部生效
// 由于我们现在使用TestConfig这些Mock对象实际上是不必要的
// 但我们暂时保留它们以避免大量代码变更
@Mock
private DataCollectorService dataCollectorService;

View File

@ -0,0 +1,51 @@
spring:
# 禁用MongoDB自动配置
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration
- org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration
# 禁用数据服务
data:
mongodb:
auto-index-creation: false
redis:
repositories:
enabled: false
# 禁用Kafka
kafka:
bootstrap-servers:
producer:
bootstrap-servers:
consumer:
bootstrap-servers:
auto-startup: false
# 禁用调度和异步任务
task:
scheduling:
enabled: false
execution:
enabled: false
# 测试模式标记
test-mode: true
# 数据采集器配置
data:
collector:
disabled: true
airport-api:
base-url: http://localhost:8090
auth:
username: test
password: test
data-refresh-interval-ms: 0
# 日志配置
logging:
level:
root: INFO
com.dongni.collisionavoidance: DEBUG
org.locationtech.jts: INFO

View File

@ -19,33 +19,33 @@ airport:
geometry:
type: "Polygon"
coordinates: [
[1.0, 1.0],
[1.0, 2.0],
[2.0, 2.0]
[116.123456, 39.123456],
[116.123789, 39.123456],
[116.123789, 39.123789],
[116.123456, 39.123789],
[116.123456, 39.123456]
]
- id: "2"
name: "停机坪区域"
type: "APRON"
name: "滑行道区域"
type: "TAXIWAY"
speedLimit: 30 # 单位km/h
purpose: "用于航空器停放和地面服务"
restrictions:
- "限速30公里/小时"
purpose: "连接跑道和停机坪"
restrictions: []
allowedVehicleTypes:
- "AIRCRAFT"
- "TUG"
- "FOLLOW_ME"
- "TOW_TRUCK"
- "FUEL_TRUCK"
- "BAGGAGE_CART"
allowedAircraftTypes:
- "A320"
- "B737"
- "A330"
maxHeight: 15.0 # 单位:米
maxWeight: 200.0 # 单位:吨
- "AIRCRAFT"
maxHeight: 50.0 # 单位:米
maxWeight: 500.0 # 单位:吨
geometry:
type: "Polygon"
coordinates: [
[2.0, 2.0],
[2.0, 3.0],
[3.0, 3.0]
[116.123456, 39.123456],
[116.123789, 39.123456],
[116.123789, 39.123567],
[116.123456, 39.123567],
[116.123456, 39.123456]
]