pcp: единообразие времени (UTC) по группе сервисов
Фронт (pcp-tgu-ops-ui): - тест-страж utcTime.guard.test.ts: запрещает Date.parse / new Date(string) / toLocale* без timeZone вне utils/utcTime; тестовые фикстуры переведены на parseUtc. Бэкенд: - UTC-now: LocalDateTime.now() -> LocalDateTime.now(ZoneOffset.UTC) по всем сервисам (entity-дефолты, воркеры, use case'ы) и в тестовых фикстурах. - Конвертации к UTC (баги на не-UTC JVM, каждая с регресс-тестом в не-UTC поясе): - tgu: VisibilityPayloadParser, SyncSpacecraftFromNsiUseCase (Instant -> UTC LocalDateTime); - mission-planing: MissionPlaningService (OffsetDateTime -> UTC через toUtcLocalDateTime); - dynamic-plan: RequestServiceClient (OffsetDateTime -> UTC); - slots: SlotRepositoryImpl (getObject(LocalDateTime) вместо getTimestamp().toLocalDateTime()). - Контрактные сериализационные тесты формата на проводе (naive без зоны / Z): PlanResponse (tgu), RequestResponseDto (request, эталон), TguPlanning DTO (ui). - KDoc-инвариант «=UTC» на UI-facing DTO (tgu PlanResponse, ui-service DTO вкладки ТГУ). Документация: docs/standards/DATETIME_UTC.md (соглашение, таблица сервисов, под-задачи на миграцию к Z), docs/standards/CHECKLIST.md (пункт про UTC на API).
This commit is contained in:
+35
@@ -153,6 +153,41 @@ class MissionPlaningServiceTest {
|
||||
assertEquals(listOf(2L), (result[2] as DropModeResponseDTO).surveys)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `modesBySatelliteAndInterval normalizes non-UTC mode offsets to UTC before matching`() {
|
||||
val mission = MissionEntity(missionId = UUID.randomUUID(), satelliteId = 77)
|
||||
val plan = PlanEntity(id = 101L, mission = mission, planNumber = 1)
|
||||
val intervalStart = LocalDateTime.of(2026, 3, 16, 10, 0, 0)
|
||||
val intervalStop = LocalDateTime.of(2026, 3, 16, 10, 30, 0)
|
||||
|
||||
// То же мгновение, что 10:10 UTC (внутри окна), но выражено со смещением +03:00 → 13:10.
|
||||
// Голый toLocalDateTime() дал бы 13:10 и выкинул мод из окна; нормализация к UTC оставляет его.
|
||||
val offsetSurvey = SurveyModeEntity(
|
||||
id = 1L,
|
||||
plan = plan,
|
||||
timeStart = OffsetDateTime.of(2026, 3, 16, 13, 10, 0, 0, ZoneOffset.ofHours(3)),
|
||||
revolution = 11,
|
||||
lat = 56.0,
|
||||
long = 38.0,
|
||||
duration = 300.0,
|
||||
contourWkt = "POLYGON((0 0,1 0,1 1,0 0))",
|
||||
roll = 1.0,
|
||||
source = SurveyModeSource.SLOT
|
||||
)
|
||||
|
||||
`when`(
|
||||
modeRepository.findAllByPlan_Mission_SatelliteIdAndTimeStartIsNotNullAndTimeStartLessThanEqualOrderByTimeStartAsc(
|
||||
77,
|
||||
intervalStop.atOffset(ZoneOffset.UTC)
|
||||
)
|
||||
).thenReturn(listOf(offsetSurvey))
|
||||
`when`(modeBookingRepository.findAllByMode_IdIn(listOf(1L))).thenReturn(emptyList())
|
||||
|
||||
val result = service.modesBySatelliteAndInterval(77, intervalStart, intervalStop)
|
||||
|
||||
assertEquals(listOf(1L), result.map { it.id })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `modesBySatelliteAndInterval rejects inverted interval`() {
|
||||
val exception = assertThrows(ResponseStatusException::class.java) {
|
||||
|
||||
Reference in New Issue
Block a user