From d630a891f64748486ec9429bf598b97bd2942503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=A1=D0=BE?= =?UTF-8?q?=D0=BB=D0=BE=D0=B2=D1=8C=D0=B5=D0=B2?= Date: Tue, 2 Jun 2026 11:32:16 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A2=D0=B5=D1=81=D1=82:=20=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BB=20=D0=B2=D0=B8=D0=B4=D0=B8=D0=BC=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D0=B8=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BA=D1=80=D1=8B=D0=B2?= =?UTF-8?q?=D0=B0=D0=B5=D1=82=D1=81=D1=8F=20=D0=BE=D0=B4=D0=BD=D0=B8=D0=BC?= =?UTF-8?q?=20=D0=BF=D0=BB=D0=B0=D0=BD=D0=BE=D0=BC=20(=D1=84=D0=BE=D1=80?= =?UTF-8?q?=D0=BC=D0=B0=20Reflexio)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Воспроизводит форму с экрана Reflexio (раннее окно, большой провал видимости, далёкий кластер) и проверяет, что цепочка перекрывает провал одним длинным планом и доходит до далёкого кластера без разрыва. --- .../service/PlanCalculationServiceTest.kt | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/services/pcp-tgu-service/src/test/kotlin/space/nstart/pcp_tgu_service/service/PlanCalculationServiceTest.kt b/services/pcp-tgu-service/src/test/kotlin/space/nstart/pcp_tgu_service/service/PlanCalculationServiceTest.kt index a6474a0..5dde633 100644 --- a/services/pcp-tgu-service/src/test/kotlin/space/nstart/pcp_tgu_service/service/PlanCalculationServiceTest.kt +++ b/services/pcp-tgu-service/src/test/kotlin/space/nstart/pcp_tgu_service/service/PlanCalculationServiceTest.kt @@ -237,14 +237,16 @@ class PlanCalculationServiceTest { fun `calculatePlans never leaves a gap between plans across a visibility gap`() { val service = createService(targetMinutes = 180L, deltaMinutes = 60L) val start = LocalDateTime.of(2026, 3, 10, 10, 0) - // Плотный кластер окон, затем большой провал видимости, затем далёкий кластер. - // Провал допустимо перекрыть одним длинным планом, но разрыва между планами быть не должно. + // Воспроизводит форму с экрана Reflexio: раннее окно, затем большой провал + // видимости, затем далёкий кластер. Провал допустимо перекрыть одним длинным + // планом, но разрыва между планами быть не должно. + val gap = start.plusDays(3) val windows = buildList { for (minutes in 0..240 step 20) { add(window("NEAR-$minutes", start.plusMinutes(minutes.toLong()))) } - for (minutes in 0..120 step 20) { - add(window("FAR-$minutes", start.plusDays(3).plusMinutes(minutes.toLong()))) + for (minutes in 0..240 step 20) { + add(window("FAR-$minutes", gap.plusMinutes(minutes.toLong()))) } } @@ -252,7 +254,16 @@ class PlanCalculationServiceTest { // Главное требование: каждый следующий план стартует строго внутри предыдущего (без разрыва). assertChainInvariant(plans) - assertTrue(plans.size >= 2, "expected a multi-plan chain, got ${plans.size}") + // Провал перекрыт одним длинным планом: есть план, начинающийся до провала и кончающийся после. + assertTrue( + plans.any { plan -> plan.startTime < gap && plan.endTime > gap }, + "the visibility gap must be bridged by a single plan: $plans" + ) + // И цепочка действительно доходит до далёкого кластера, а не обрывается до провала. + assertTrue( + plans.any { plan -> plan.startTime >= gap }, + "the chain must reach the far cluster after the gap: $plans" + ) } /**