pcp-tgu интерфейс
This commit is contained in:
+47
@@ -26,6 +26,7 @@ import space.nstart.pcp.slots_service.dto.groupstate.GroupStateSatelliteDTO
|
||||
import space.nstart.pcp.slots_service.dto.pdcm.SatellitePdcmAvailabilityDTO
|
||||
import space.nstart.pcp.slots_service.dto.pdcm.SatellitePdcmDetailsDTO
|
||||
import space.nstart.pcp.slots_service.dto.pdcm.SatellitePdcmSummaryDTO
|
||||
import space.nstart.pcp.slots_service.dto.tgu.TguPlanResponse
|
||||
import space.nstart.pcp.pcp_types_lib.dto.requests.slots.SlotCalculationSummaryDTO
|
||||
import space.nstart.pcp.pcp_types_lib.dto.satellite.SatelliteCreateDTO
|
||||
import space.nstart.pcp.pcp_types_lib.dto.satellite.SatelliteDTO
|
||||
@@ -38,6 +39,7 @@ import space.nstart.pcp.slots_service.service.SatelliteCatalogService
|
||||
import space.nstart.pcp.slots_service.service.SatellitePdcmService
|
||||
import space.nstart.pcp.slots_service.service.SlotService
|
||||
import space.nstart.pcp.slots_service.service.StationService
|
||||
import space.nstart.pcp.slots_service.service.TguPlanningService
|
||||
import java.time.LocalDateTime
|
||||
import java.util.UUID
|
||||
import kotlin.test.assertEquals
|
||||
@@ -72,6 +74,9 @@ class CatalogControllerTest {
|
||||
@MockitoBean
|
||||
private lateinit var satellitePdcmService: SatellitePdcmService
|
||||
|
||||
@MockitoBean
|
||||
private lateinit var tguPlanningService: TguPlanningService
|
||||
|
||||
@Test
|
||||
fun `groups page is rendered`() {
|
||||
val body = WebClient.create("http://localhost:$port")
|
||||
@@ -155,6 +160,48 @@ class CatalogControllerTest {
|
||||
assertFalse(body.contains("<th>UUID</th>"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `tgu planning page is rendered from menu`() {
|
||||
val body = WebClient.create("http://localhost:$port")
|
||||
.get()
|
||||
.uri("/tgu-planning")
|
||||
.retrieve()
|
||||
.bodyToMono(String::class.java)
|
||||
.block()!!
|
||||
|
||||
assertTrue(body.contains("Планирование ТГУ"))
|
||||
assertTrue(body.contains("/tgu_planning_scripts.js"))
|
||||
assertTrue(body.contains("href=\"/tgu-planning\""))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `tgu spacecraft plans endpoint proxies norad spacecraft id`() {
|
||||
val planId = UUID.fromString("6d0941e4-61d6-4f96-8fa8-90ba11db121e")
|
||||
val response = listOf(
|
||||
TguPlanResponse(
|
||||
planId = planId,
|
||||
spacecraftId = "56756",
|
||||
startTime = LocalDateTime.of(2026, 5, 29, 10, 0),
|
||||
endTime = LocalDateTime.of(2026, 5, 29, 10, 15),
|
||||
kppId = "KPP-1",
|
||||
status = "WAITING_DECISION"
|
||||
)
|
||||
)
|
||||
doReturn(response).`when`(tguPlanningService).spacecraftPlans("56756", 14L)
|
||||
|
||||
val actual = WebClient.create("http://localhost:$port")
|
||||
.get()
|
||||
.uri("/api/tgu-planning/spacecraft/56756/plans?historyDays=14")
|
||||
.retrieve()
|
||||
.bodyToFlux(TguPlanResponse::class.java)
|
||||
.collectList()
|
||||
.block()!!
|
||||
|
||||
assertEquals(planId, actual.single().planId)
|
||||
assertEquals("56756", actual.single().spacecraftId)
|
||||
verify(tguPlanningService).spacecraftPlans("56756", 14L)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `group create endpoint proxies request`() {
|
||||
val response = SatelliteGroupDTO(
|
||||
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
package space.nstart.pcp.slots_service.service
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange
|
||||
import com.sun.net.httpserver.HttpServer
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import org.springframework.beans.factory.ObjectProvider
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.test.util.ReflectionTestUtils
|
||||
import org.springframework.web.reactive.function.client.WebClient
|
||||
import org.springframework.web.server.ResponseStatusException
|
||||
import space.nstart.pcp.slots_service.dto.tgu.TguPlanDecision
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.URI
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class TguPlanningServiceTest {
|
||||
|
||||
private var server: HttpServer? = null
|
||||
|
||||
@AfterEach
|
||||
fun tearDown() {
|
||||
server?.stop(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `read methods call existing tgu endpoints`() {
|
||||
val requests = CopyOnWriteArrayList<URI>()
|
||||
val service = createService(startServer(requests))
|
||||
|
||||
val platforms = service.platforms()
|
||||
val plans = service.plans(7)
|
||||
val spacecraftPlans = service.spacecraftPlans("56756", 14)
|
||||
|
||||
assertEquals("Kondor-FKA No.2", platforms.single().name)
|
||||
assertEquals("56756", plans.single().spacecraftId)
|
||||
assertEquals("WAITING_DECISION", spacecraftPlans.single().status)
|
||||
assertEquals(
|
||||
listOf("/api/platforms", "/api/plans", "/api/plans/56756"),
|
||||
requests.map { it.path }
|
||||
)
|
||||
assertEquals(listOf(null, "historyDays=7", "historyDays=14"), requests.map { it.rawQuery })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `decision method calls test controller endpoint`() {
|
||||
val requests = CopyOnWriteArrayList<URI>()
|
||||
val service = createService(startServer(requests))
|
||||
|
||||
val response = service.sendDecision(
|
||||
java.util.UUID.fromString("6d0941e4-61d6-4f96-8fa8-90ba11db121e"),
|
||||
TguPlanDecision.ACCEPTED,
|
||||
"UI TEST ACCEPTED"
|
||||
)
|
||||
|
||||
assertEquals(TguPlanDecision.ACCEPTED, response.decision)
|
||||
assertEquals("/test/tgu/plans/6d0941e4-61d6-4f96-8fa8-90ba11db121e/decision", requests.last().path)
|
||||
assertEquals("decision=ACCEPTED&reason=UI%20TEST%20ACCEPTED", requests.last().rawQuery)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `decision conflict is mapped to operator message`() {
|
||||
val service = createService(
|
||||
startServer(
|
||||
statusByPath = mapOf(
|
||||
"/test/tgu/plans/00000000-0000-0000-0000-000000000000/decision" to 409
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val exception = assertThrows<ResponseStatusException> {
|
||||
service.sendDecision(
|
||||
java.util.UUID.fromString("00000000-0000-0000-0000-000000000000"),
|
||||
TguPlanDecision.REJECTED,
|
||||
"reason"
|
||||
)
|
||||
}
|
||||
|
||||
assertEquals(HttpStatus.CONFLICT, exception.statusCode)
|
||||
assertEquals(
|
||||
"План не является активным планом, ожидающим решения, или для него нет active attempt.",
|
||||
exception.reason
|
||||
)
|
||||
}
|
||||
|
||||
private fun createService(serverUrl: String): TguPlanningService {
|
||||
val provider = object : ObjectProvider<WebClient.Builder> {
|
||||
override fun getObject(vararg args: Any?): WebClient.Builder = WebClient.builder()
|
||||
override fun getObject(): WebClient.Builder = WebClient.builder()
|
||||
override fun getIfAvailable(): WebClient.Builder = WebClient.builder()
|
||||
override fun getIfUnique(): WebClient.Builder = WebClient.builder()
|
||||
override fun iterator(): MutableIterator<WebClient.Builder> = mutableListOf<WebClient.Builder>().iterator()
|
||||
}
|
||||
val service = TguPlanningService(provider)
|
||||
ReflectionTestUtils.setField(service, "tguServiceUrl", serverUrl)
|
||||
return service
|
||||
}
|
||||
|
||||
private fun startServer(
|
||||
requests: MutableList<URI> = CopyOnWriteArrayList(),
|
||||
statusByPath: Map<String, Int> = emptyMap()
|
||||
): String {
|
||||
val startedServer = HttpServer.create(InetSocketAddress(0), 0)
|
||||
startedServer.createContext("/") { exchange ->
|
||||
requests.add(exchange.requestURI)
|
||||
val forcedStatus = statusByPath[exchange.requestURI.path]
|
||||
when {
|
||||
forcedStatus != null -> respond(exchange, forcedStatus, """{"error":"conflict"}""")
|
||||
exchange.requestURI.path == "/api/platforms" -> respond(
|
||||
exchange,
|
||||
200,
|
||||
"""[{"id":"nsi-id","businessKey":"2024-149E","name":"Kondor-FKA No.2","status":"OPERATIONAL","noradId":56756,"mission":"KONDOR_FKA","validFrom":"2026-05-21T15:06:00Z","validTo":"9999-12-31T23:59:59Z"}]"""
|
||||
)
|
||||
exchange.requestURI.path == "/api/plans" || exchange.requestURI.path == "/api/plans/56756" -> respond(
|
||||
exchange,
|
||||
200,
|
||||
"""[{"planId":"6d0941e4-61d6-4f96-8fa8-90ba11db121e","spacecraftId":"56756","startTime":"2026-05-29T10:00:00","endTime":"2026-05-29T10:15:00","kppId":"KPP-1","status":"WAITING_DECISION"}]"""
|
||||
)
|
||||
exchange.requestURI.path.startsWith("/test/tgu/plans/") -> respond(
|
||||
exchange,
|
||||
200,
|
||||
"""{"eventId":"1c857555-f02b-4051-b56a-f112b842624a","spacecraftId":"56756","planId":"6d0941e4-61d6-4f96-8fa8-90ba11db121e","attemptId":"a82394f2-545f-4cd6-9ab1-876ca403d33a","decision":"ACCEPTED","decisionTime":"2026-05-29T10:01:00","reason":"UI TEST ACCEPTED"}"""
|
||||
)
|
||||
else -> respond(exchange, 404, """{"error":"not found"}""")
|
||||
}
|
||||
}
|
||||
startedServer.start()
|
||||
server = startedServer
|
||||
return "http://localhost:${startedServer.address.port}"
|
||||
}
|
||||
|
||||
private fun respond(exchange: HttpExchange, status: Int, body: String) {
|
||||
exchange.responseHeaders.add("Content-Type", "application/json")
|
||||
val bytes = body.toByteArray()
|
||||
exchange.sendResponseHeaders(status, bytes.size.toLong())
|
||||
exchange.responseBody.use { output -> output.write(bytes) }
|
||||
}
|
||||
}
|
||||
@@ -13,3 +13,4 @@ spring:
|
||||
|
||||
settings:
|
||||
dynamic-plan-service: http://localhost:65535
|
||||
tgu-service: http://localhost:65535
|
||||
|
||||
Reference in New Issue
Block a user