diff --git a/src/main/kotlin/com/dcd/server/core/domain/application/service/impl/CreateContainerServiceImpl.kt b/src/main/kotlin/com/dcd/server/core/domain/application/service/impl/CreateContainerServiceImpl.kt index 8d2fa70b..c003ef98 100644 --- a/src/main/kotlin/com/dcd/server/core/domain/application/service/impl/CreateContainerServiceImpl.kt +++ b/src/main/kotlin/com/dcd/server/core/domain/application/service/impl/CreateContainerServiceImpl.kt @@ -5,6 +5,7 @@ import com.dcd.server.core.domain.application.model.Application import com.dcd.server.core.domain.application.service.CreateContainerService import com.dcd.server.core.domain.application.spi.CheckExitValuePort import com.dcd.server.core.domain.application.util.FailureCase +import com.dcd.server.core.domain.volume.spi.QueryVolumePort import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.springframework.stereotype.Service @@ -12,14 +13,26 @@ import org.springframework.stereotype.Service @Service class CreateContainerServiceImpl( private val commandPort: CommandPort, + private val queryVolumePort: QueryVolumePort, private val checkExitValuePort: CheckExitValuePort ) : CreateContainerService { override suspend fun createContainer(application: Application, externalPort: Int) { withContext(Dispatchers.IO) { + val volumeMountBuilder = StringBuilder() + queryVolumePort.findAllMountByApplication(application) + .forEach { + val volume = it.volume + volumeMountBuilder.append("-v ${volume.name}:${it.mountPath}") + if (it.readOnly) + volumeMountBuilder.append(":ro") + volumeMountBuilder.append(" ") + } + val volumeMountFlags = volumeMountBuilder.toString() val cmd = "docker create --network ${application.workspace.networkName} " + - "--name ${application.containerName} " + - "-p ${externalPort}:${application.port} ${application.containerName}:latest" + "--name ${application.containerName} " + + volumeMountFlags + + "-p ${externalPort}:${application.port} ${application.containerName}:latest" commandPort.executeShellCommand(cmd) .also {exitValue -> diff --git a/src/main/kotlin/com/dcd/server/core/domain/volume/dto/request/MountVolumeReqDto.kt b/src/main/kotlin/com/dcd/server/core/domain/volume/dto/request/MountVolumeReqDto.kt new file mode 100644 index 00000000..2bb4f1a0 --- /dev/null +++ b/src/main/kotlin/com/dcd/server/core/domain/volume/dto/request/MountVolumeReqDto.kt @@ -0,0 +1,6 @@ +package com.dcd.server.core.domain.volume.dto.request + +data class MountVolumeReqDto( + val mountPath: String, + val readOnly: Boolean, +) diff --git a/src/main/kotlin/com/dcd/server/core/domain/volume/spi/CommandVolumePort.kt b/src/main/kotlin/com/dcd/server/core/domain/volume/spi/CommandVolumePort.kt index 8cd9df73..913e07cd 100644 --- a/src/main/kotlin/com/dcd/server/core/domain/volume/spi/CommandVolumePort.kt +++ b/src/main/kotlin/com/dcd/server/core/domain/volume/spi/CommandVolumePort.kt @@ -1,9 +1,12 @@ package com.dcd.server.core.domain.volume.spi import com.dcd.server.core.domain.volume.model.Volume +import com.dcd.server.core.domain.volume.model.VolumeMount interface CommandVolumePort { fun save(volume: Volume) fun delete(volume: Volume) + + fun saveMount(volumeMount: VolumeMount) } \ No newline at end of file diff --git a/src/main/kotlin/com/dcd/server/core/domain/volume/usecase/MountVolumeUseCase.kt b/src/main/kotlin/com/dcd/server/core/domain/volume/usecase/MountVolumeUseCase.kt new file mode 100644 index 00000000..710c0923 --- /dev/null +++ b/src/main/kotlin/com/dcd/server/core/domain/volume/usecase/MountVolumeUseCase.kt @@ -0,0 +1,52 @@ +package com.dcd.server.core.domain.volume.usecase + +import com.dcd.server.core.common.annotation.UseCase +import com.dcd.server.core.common.data.WorkspaceInfo +import com.dcd.server.core.domain.application.event.DeployApplicationEvent +import com.dcd.server.core.domain.application.exception.ApplicationNotFoundException +import com.dcd.server.core.domain.application.spi.QueryApplicationPort +import com.dcd.server.core.domain.volume.dto.request.MountVolumeReqDto +import com.dcd.server.core.domain.volume.exception.VolumeNotFoundException +import com.dcd.server.core.domain.volume.model.VolumeMount +import com.dcd.server.core.domain.volume.spi.CommandVolumePort +import com.dcd.server.core.domain.volume.spi.QueryVolumePort +import com.dcd.server.core.domain.workspace.exception.WorkspaceNotFoundException +import org.springframework.context.ApplicationEventPublisher +import java.util.UUID + +@UseCase +class MountVolumeUseCase( + private val queryVolumePort: QueryVolumePort, + private val queryApplicationPort: QueryApplicationPort, + private val commandVolumePort: CommandVolumePort, + private val workspaceInfo: WorkspaceInfo, + private val eventPublisher: ApplicationEventPublisher, +) { + fun execute(volumeId: UUID, applicationId: String, mountVolumeReqDto: MountVolumeReqDto) { + val workspace = (workspaceInfo.workspace + ?: throw WorkspaceNotFoundException()) + + val volume = (queryVolumePort.findById(volumeId) + ?: throw VolumeNotFoundException()) + + if (volume.workspace != workspace) + throw VolumeNotFoundException() + + val application = (queryApplicationPort.findById(applicationId) + ?: throw ApplicationNotFoundException()) + if (application.workspace != workspace) + throw ApplicationNotFoundException() + + val volumeMount = VolumeMount( + id = UUID.randomUUID(), + application = application, + volume = volume, + mountPath = mountVolumeReqDto.mountPath, + readOnly = mountVolumeReqDto.readOnly + ) + commandVolumePort.saveMount(volumeMount) + + //마운트 생성후 대상 애플리케이션 재배포 + eventPublisher.publishEvent(DeployApplicationEvent(listOf(application.id))) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/dcd/server/infrastructure/global/config/SecurityConfig.kt b/src/main/kotlin/com/dcd/server/infrastructure/global/config/SecurityConfig.kt index 94ddd214..88cd03ed 100644 --- a/src/main/kotlin/com/dcd/server/infrastructure/global/config/SecurityConfig.kt +++ b/src/main/kotlin/com/dcd/server/infrastructure/global/config/SecurityConfig.kt @@ -104,6 +104,7 @@ class SecurityConfig( it.requestMatchers(HttpMethod.PUT, "/{workspaceId}/volume/{volumeId}").authenticated() it.requestMatchers(HttpMethod.GET, "/{workspaceId}/volume").authenticated() it.requestMatchers(HttpMethod.GET, "/{workspaceId}/volume/{volumeId}").authenticated() + it.requestMatchers(HttpMethod.POST, "/{workspaceId}/volume/{volumeId}/mount").authenticated() //when url not set it.anyRequest().denyAll() diff --git a/src/main/kotlin/com/dcd/server/persistence/volume/VolumePersistenceAdapter.kt b/src/main/kotlin/com/dcd/server/persistence/volume/VolumePersistenceAdapter.kt index e70dfca6..c3943894 100644 --- a/src/main/kotlin/com/dcd/server/persistence/volume/VolumePersistenceAdapter.kt +++ b/src/main/kotlin/com/dcd/server/persistence/volume/VolumePersistenceAdapter.kt @@ -28,6 +28,10 @@ class VolumePersistenceAdapter( volumeRepository.deleteById(volume.id) } + override fun saveMount(volumeMount: VolumeMount) { + volumeMountRepository.save(volumeMount.toEntity()) + } + override fun findById(id: UUID): Volume? = volumeRepository.findByIdOrNull(id) ?.toDomain() diff --git a/src/main/kotlin/com/dcd/server/presentation/domain/volume/VolumeWebAdapter.kt b/src/main/kotlin/com/dcd/server/presentation/domain/volume/VolumeWebAdapter.kt index de233d10..e60b28bc 100644 --- a/src/main/kotlin/com/dcd/server/presentation/domain/volume/VolumeWebAdapter.kt +++ b/src/main/kotlin/com/dcd/server/presentation/domain/volume/VolumeWebAdapter.kt @@ -5,11 +5,13 @@ import com.dcd.server.core.domain.volume.usecase.CreateVolumeUseCase import com.dcd.server.core.domain.volume.usecase.DeleteVolumeUseCase import com.dcd.server.core.domain.volume.usecase.GetAllVolumeUseCase import com.dcd.server.core.domain.volume.usecase.GetOneVolumeUseCase +import com.dcd.server.core.domain.volume.usecase.MountVolumeUseCase import com.dcd.server.core.domain.volume.usecase.UpdateVolumeUseCase import com.dcd.server.presentation.common.annotation.WebAdapter import com.dcd.server.presentation.domain.volume.data.extension.toDto import com.dcd.server.presentation.domain.volume.data.extension.toResponse import com.dcd.server.presentation.domain.volume.data.request.CreateVolumeRequest +import com.dcd.server.presentation.domain.volume.data.request.MountVolumeRequest import com.dcd.server.presentation.domain.volume.data.request.UpdateVolumeRequest import com.dcd.server.presentation.domain.volume.data.response.VolumeDetailResponse import com.dcd.server.presentation.domain.volume.data.response.VolumeListResponse @@ -21,6 +23,7 @@ import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.PutMapping import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestParam import java.util.UUID @WebAdapter("/{workspaceId}/volume") @@ -29,7 +32,8 @@ class VolumeWebAdapter( private val deleteVolumeUseCase: DeleteVolumeUseCase, private val updateVolumeUseCase: UpdateVolumeUseCase, private val getAllVolumeUseCase: GetAllVolumeUseCase, - private val getOneVolumeUseCase: GetOneVolumeUseCase + private val getOneVolumeUseCase: GetOneVolumeUseCase, + private val mountVolumeUseCase: MountVolumeUseCase ) { @PostMapping @WorkspaceOwnerVerification("#workspaceId") @@ -73,4 +77,15 @@ class VolumeWebAdapter( ): ResponseEntity = getOneVolumeUseCase.execute(volumeId) .let { ResponseEntity.ok(it.toResponse()) } + + @PostMapping("/{volumeId}/mount") + @WorkspaceOwnerVerification("#workspaceId") + fun mountVolume( + @PathVariable workspaceId: String, + @PathVariable volumeId: UUID, + @RequestParam applicationId: String, + @Validated @RequestBody mountVolumeRequest: MountVolumeRequest + ): ResponseEntity = + mountVolumeUseCase.execute(volumeId, applicationId, mountVolumeRequest.toDto()) + .run { ResponseEntity.ok().build() } } \ No newline at end of file diff --git a/src/main/kotlin/com/dcd/server/presentation/domain/volume/data/extension/VolumeRequestExtension.kt b/src/main/kotlin/com/dcd/server/presentation/domain/volume/data/extension/VolumeRequestExtension.kt index a7810f53..c70c7778 100644 --- a/src/main/kotlin/com/dcd/server/presentation/domain/volume/data/extension/VolumeRequestExtension.kt +++ b/src/main/kotlin/com/dcd/server/presentation/domain/volume/data/extension/VolumeRequestExtension.kt @@ -1,8 +1,10 @@ package com.dcd.server.presentation.domain.volume.data.extension import com.dcd.server.core.domain.volume.dto.request.CreateVolumeReqDto +import com.dcd.server.core.domain.volume.dto.request.MountVolumeReqDto import com.dcd.server.core.domain.volume.dto.request.UpdateVolumeReqDto import com.dcd.server.presentation.domain.volume.data.request.CreateVolumeRequest +import com.dcd.server.presentation.domain.volume.data.request.MountVolumeRequest import com.dcd.server.presentation.domain.volume.data.request.UpdateVolumeRequest fun CreateVolumeRequest.toDto(): CreateVolumeReqDto = @@ -15,4 +17,10 @@ fun UpdateVolumeRequest.toDto(): UpdateVolumeReqDto = UpdateVolumeReqDto( name = this.name, description = this.description + ) + +fun MountVolumeRequest.toDto(): MountVolumeReqDto = + MountVolumeReqDto( + mountPath = this.mountPath, + readOnly = this.readOnly ) \ No newline at end of file diff --git a/src/main/kotlin/com/dcd/server/presentation/domain/volume/data/request/MountVolumeRequest.kt b/src/main/kotlin/com/dcd/server/presentation/domain/volume/data/request/MountVolumeRequest.kt new file mode 100644 index 00000000..2d6b225d --- /dev/null +++ b/src/main/kotlin/com/dcd/server/presentation/domain/volume/data/request/MountVolumeRequest.kt @@ -0,0 +1,11 @@ +package com.dcd.server.presentation.domain.volume.data.request + +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.Pattern + +data class MountVolumeRequest( + @field:NotBlank + @field:Pattern(regexp = "^(?!(?:/(?:proc|sys|dev|etc)?$))/(?:(?:[A-Za-z0-9._-]+/)*[A-Za-z0-9._-]+)?$") + val mountPath: String, + val readOnly: Boolean, +) diff --git a/src/test/kotlin/com/dcd/server/core/domain/application/service/CreateContainerServiceImplTest.kt b/src/test/kotlin/com/dcd/server/core/domain/application/service/CreateContainerServiceImplTest.kt index cb9ffd3b..2efe52c4 100644 --- a/src/test/kotlin/com/dcd/server/core/domain/application/service/CreateContainerServiceImplTest.kt +++ b/src/test/kotlin/com/dcd/server/core/domain/application/service/CreateContainerServiceImplTest.kt @@ -4,7 +4,9 @@ import com.dcd.server.core.common.command.CommandPort import com.dcd.server.core.domain.application.service.impl.CreateContainerServiceImpl import com.dcd.server.core.domain.application.spi.CheckExitValuePort import com.dcd.server.core.domain.application.util.FailureCase +import com.dcd.server.core.domain.volume.spi.QueryVolumePort import io.kotest.core.spec.style.BehaviorSpec +import io.mockk.every import io.mockk.mockk import io.mockk.verify import kotlinx.coroutines.CoroutineScope @@ -12,14 +14,16 @@ import util.application.ApplicationGenerator class CreateContainerServiceImplTest : BehaviorSpec({ val commandPort = mockk(relaxed = true) + val queryVolumePort = mockk(relaxed = true) val checkExitValuePort = mockk(relaxUnitFun = true) - val createContainerService = CreateContainerServiceImpl(commandPort, checkExitValuePort) + val createContainerService = CreateContainerServiceImpl(commandPort, queryVolumePort, checkExitValuePort) given("애플리케이션이 주어지고") { val application = ApplicationGenerator.generateApplication() `when`("service를 실행할때") { createContainerService.createContainer(application, application.externalPort) + every { queryVolumePort.findAllMountByApplication(application) } returns listOf() then("컨테이너를 실행하는 명령을 실행해야함") { verify { diff --git a/src/test/kotlin/com/dcd/server/core/domain/application/usecase/DeployApplicationUseCaseTest.kt b/src/test/kotlin/com/dcd/server/core/domain/application/usecase/DeployApplicationUseCaseTest.kt index 16541755..2f65cb06 100644 --- a/src/test/kotlin/com/dcd/server/core/domain/application/usecase/DeployApplicationUseCaseTest.kt +++ b/src/test/kotlin/com/dcd/server/core/domain/application/usecase/DeployApplicationUseCaseTest.kt @@ -4,6 +4,7 @@ import com.dcd.server.core.common.command.CommandPort import com.dcd.server.core.domain.application.exception.ApplicationNotFoundException import com.dcd.server.core.domain.application.exception.CanNotDeployApplicationException import com.dcd.server.core.domain.application.model.enums.ApplicationStatus +import com.dcd.server.core.domain.application.service.CreateContainerService import com.dcd.server.core.domain.application.service.impl.CreateDockerFileServiceImpl import com.dcd.server.core.domain.application.spi.CommandApplicationPort import com.dcd.server.core.domain.application.spi.QueryApplicationPort @@ -18,6 +19,7 @@ import com.ninjasquad.springmockk.MockkBean import io.kotest.assertions.throwables.shouldThrow import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe +import io.mockk.verify import org.springframework.boot.test.context.SpringBootTest import org.springframework.test.context.ActiveProfiles import org.springframework.transaction.annotation.Transactional @@ -32,6 +34,8 @@ class DeployApplicationUseCaseTest( private val commandPort: CommandPort, @MockkBean(relaxUnitFun = true) private val createDockerFileService: CreateDockerFileServiceImpl, + @MockkBean(relaxUnitFun = true) + private val createContainerService: CreateContainerService, private val commandUserPort: CommandUserPort, private val commandWorkspacePort: CommandWorkspacePort, private val commandApplicationPort: CommandApplicationPort, @@ -64,13 +68,7 @@ class DeployApplicationUseCaseTest( coVerify { commandPort.executeShellCommand("docker rmi ${result.containerName}") } coVerify { commandPort.executeShellCommand("git clone ${result.githubUrl} '${result.name}'") } coVerify { commandPort.executeShellCommand("cd ./'${result.name}' && docker build -t ${result.containerName}:latest .") } - coVerify { - commandPort.executeShellCommand( - "docker create --network ${result.workspace.title.replace(' ', '_')} " + - "--name ${result.containerName} " + - "-p ${result.externalPort}:${result.port} ${result.containerName}:latest" - ) - } + coVerify { createContainerService.createContainer(result, result.externalPort) } coVerify { commandPort.executeShellCommand("rm -rf '${result.name}'") } } } diff --git a/src/test/kotlin/com/dcd/server/core/domain/volume/usecase/MountVolumeUseCaseTest.kt b/src/test/kotlin/com/dcd/server/core/domain/volume/usecase/MountVolumeUseCaseTest.kt new file mode 100644 index 00000000..077d1e53 --- /dev/null +++ b/src/test/kotlin/com/dcd/server/core/domain/volume/usecase/MountVolumeUseCaseTest.kt @@ -0,0 +1,128 @@ +package com.dcd.server.core.domain.volume.usecase + +import com.dcd.server.core.common.command.CommandPort +import com.dcd.server.core.common.data.WorkspaceInfo +import com.dcd.server.core.domain.application.exception.ApplicationNotFoundException +import com.dcd.server.core.domain.volume.dto.request.MountVolumeReqDto +import com.dcd.server.core.domain.volume.exception.VolumeNotFoundException +import com.dcd.server.core.domain.volume.model.Volume +import com.dcd.server.core.domain.workspace.exception.WorkspaceNotFoundException +import com.dcd.server.core.domain.workspace.spi.QueryWorkspacePort +import com.dcd.server.persistence.volume.adapter.toEntity +import com.dcd.server.persistence.volume.repository.VolumeMountRepository +import com.dcd.server.persistence.volume.repository.VolumeRepository +import com.dcd.server.persistence.workspace.adapter.toEntity +import com.dcd.server.persistence.workspace.repository.WorkspaceRepository +import com.ninjasquad.springmockk.MockkBean +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.matchers.shouldBe +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.ActiveProfiles +import org.springframework.transaction.annotation.Transactional +import util.workspace.WorkspaceGenerator +import java.util.UUID + +@Transactional +@SpringBootTest +@ActiveProfiles("test") +class MountVolumeUseCaseTest( + private val mountVolumeUseCase: MountVolumeUseCase, + @MockkBean(relaxed = true) + private val commandPort: CommandPort, + private val queryWorkspacePort: QueryWorkspacePort, + private val volumeRepository: VolumeRepository, + private val volumeMountRepository: VolumeMountRepository, + private val workspaceRepository: WorkspaceRepository, + private val workspaceInfo: WorkspaceInfo +) : BehaviorSpec({ + val targetVolumeId = UUID.randomUUID() + val targetApplicationId = "2fb0f315-8272-422f-8e9f-c4f765c022b2" + + beforeSpec { + val targetWorkspace = queryWorkspacePort.findById("d57b42f5-5cc4-440b-8dce-b4fc2e372eff")!! + val volume = Volume( + id = targetVolumeId, + name = "test1Volume", + description = "testDescription", + workspace = targetWorkspace + ).toEntity() + volumeRepository.save(volume) + } + + given("마운트 요청 객체가 주어지고") { + beforeContainer { + val targetWorkspace = queryWorkspacePort.findById("d57b42f5-5cc4-440b-8dce-b4fc2e372eff")!! + workspaceInfo.workspace = targetWorkspace + } + + val request = MountVolumeReqDto("/test", false) + + `when`("유스케이스를 실행하면") { + mountVolumeUseCase.execute(targetVolumeId, targetApplicationId, request) + + then("볼륨 마운트가 생성되어야함") { + val volumeMountList = volumeMountRepository.findAll() + volumeMountList.size shouldBe 1 + + val first = volumeMountList.first() + first.mountPath shouldBe request.mountPath + first.readOnly shouldBe request.readOnly + first.application.id.toString() shouldBe targetApplicationId + first.volume.id shouldBe targetVolumeId + } + } + + `when`("존재하지 않는 볼륨 아이디로 실행하면") { + val invalidVolumeId = UUID.randomUUID() + + then("에러가 발생해야함") { + shouldThrow { + mountVolumeUseCase.execute(invalidVolumeId, targetApplicationId, request) + } + } + } + + `when`("존재하지 않는 애플리케이션 아이디로 실행하면") { + val invalidApplicationId = UUID.randomUUID().toString() + + then("에러가 발생행해야함") { + shouldThrow { + mountVolumeUseCase.execute(targetVolumeId, invalidApplicationId, request) + } + } + } + + `when`("세팅된 워크스페이스가 볼륨이 속한 워크스페이스가 아닐때") { + beforeTest { + val targetWorkspace = queryWorkspacePort.findById("d57b42f5-5cc4-440b-8dce-b4fc2e372eff")!! + val otherWorkspace = WorkspaceGenerator.generateWorkspace(user = targetWorkspace.owner) + workspaceRepository.save(otherWorkspace.toEntity()) + workspaceInfo.workspace = otherWorkspace + } + + then("에러가 발생해야함") { + shouldThrow { + mountVolumeUseCase.execute(targetVolumeId, targetApplicationId, request) + } + } + } + } + + given("올바르지 않은 워크스페이스가 세팅되고") { + beforeContainer { + workspaceInfo.workspace = null + } + + val request = MountVolumeReqDto("/test", false) + + `when`("유스케이스를 실행할때") { + + then("에러가 발생해야함") { + shouldThrow { + mountVolumeUseCase.execute(targetVolumeId, targetApplicationId, request) + } + } + } + } +}) \ No newline at end of file diff --git a/src/test/kotlin/com/dcd/server/presentation/domain/volume/VolumeWebAdapterTest.kt b/src/test/kotlin/com/dcd/server/presentation/domain/volume/VolumeWebAdapterTest.kt index 1519ad06..0094c241 100644 --- a/src/test/kotlin/com/dcd/server/presentation/domain/volume/VolumeWebAdapterTest.kt +++ b/src/test/kotlin/com/dcd/server/presentation/domain/volume/VolumeWebAdapterTest.kt @@ -1,6 +1,7 @@ package com.dcd.server.presentation.domain.volume import com.dcd.server.core.domain.volume.dto.request.CreateVolumeReqDto +import com.dcd.server.core.domain.volume.dto.request.MountVolumeReqDto import com.dcd.server.core.domain.volume.dto.request.UpdateVolumeReqDto import com.dcd.server.core.domain.volume.dto.response.VolumeDetailResDto import com.dcd.server.core.domain.volume.dto.response.VolumeListResDto @@ -9,9 +10,11 @@ import com.dcd.server.core.domain.volume.usecase.CreateVolumeUseCase import com.dcd.server.core.domain.volume.usecase.DeleteVolumeUseCase import com.dcd.server.core.domain.volume.usecase.GetAllVolumeUseCase import com.dcd.server.core.domain.volume.usecase.GetOneVolumeUseCase +import com.dcd.server.core.domain.volume.usecase.MountVolumeUseCase import com.dcd.server.core.domain.volume.usecase.UpdateVolumeUseCase import com.dcd.server.presentation.domain.volume.data.extension.toResponse import com.dcd.server.presentation.domain.volume.data.request.CreateVolumeRequest +import com.dcd.server.presentation.domain.volume.data.request.MountVolumeRequest import com.dcd.server.presentation.domain.volume.data.request.UpdateVolumeRequest import io.kotest.core.spec.style.BehaviorSpec import io.kotest.matchers.shouldBe @@ -27,13 +30,15 @@ class VolumeWebAdapterTest : BehaviorSpec({ val updateVolumeUseCase = mockk(relaxUnitFun = true) val getAllVolumeUseCase = mockk(relaxUnitFun = true) val getOneVolumeUseCase = mockk(relaxUnitFun = true) + val mountVolumeUseCase = mockk(relaxUnitFun = true) val volumeWebAdapter = VolumeWebAdapter( createVolumeUseCase, deleteVolumeUseCase, updateVolumeUseCase, getAllVolumeUseCase, - getOneVolumeUseCase + getOneVolumeUseCase, + mountVolumeUseCase ) given("워크스페이스 아이디와 볼륨 생성 요청이 주어지고") { @@ -125,4 +130,22 @@ class VolumeWebAdapterTest : BehaviorSpec({ } } } + + given("워크스페이스 아이디, 볼륨 아이디, 애플리케이션 아이디, 볼륨 마운트 요청 객체가 주어지고") { + val testWorkspaceId = UUID.randomUUID().toString() + val testVolumeId = UUID.randomUUID() + val testApplicationId = UUID.randomUUID().toString() + val request = MountVolumeRequest("/test", false) + + `when`("볼륨 마운트 메서드를 실행할때") { + val result = volumeWebAdapter.mountVolume(testWorkspaceId, testVolumeId, testApplicationId, request) + + then("볼륨 마운트 유스케이스가 실행되어야함") { + verify { mountVolumeUseCase.execute(testVolumeId, testApplicationId, any() as MountVolumeReqDto) } + } + then("상태코드 OK가 응답되어야함") { + result.statusCode shouldBe HttpStatus.OK + } + } + } }) \ No newline at end of file