diff --git a/src/main/kotlin/com/dcd/server/core/common/error/ErrorCode.kt b/src/main/kotlin/com/dcd/server/core/common/error/ErrorCode.kt index 9eb05073..209077df 100644 --- a/src/main/kotlin/com/dcd/server/core/common/error/ErrorCode.kt +++ b/src/main/kotlin/com/dcd/server/core/common/error/ErrorCode.kt @@ -47,6 +47,7 @@ enum class ErrorCode( AUTH_CODE_NOT_FOUND("인증 코드를 찾을 수 없습니다. 코드를 다시 전송 해주세요.", 404), DOMAIN_NOT_FOUND("해당 도메인을 찾을 수 없음", 404), VOLUME_NOT_FOUND("해당 볼륨을 찾을 수 없음", 404), + VOLUME_MOUNT_NOT_FOUND("해당 볼륨 마운트를 찾을 수 없음", 404), CONFLICT("해당 요청은 서버의 상태와 충돌됩니다.", 409), CAN_NOT_DEPLOY_APPLICATION("애플리케이션을 배포할 수 없습니다. 애플리케이션을 정지시킨 후 실행해주세요.", 409), diff --git a/src/main/kotlin/com/dcd/server/core/domain/volume/exception/VolumeMountNotFoundException.kt b/src/main/kotlin/com/dcd/server/core/domain/volume/exception/VolumeMountNotFoundException.kt new file mode 100644 index 00000000..750e00aa --- /dev/null +++ b/src/main/kotlin/com/dcd/server/core/domain/volume/exception/VolumeMountNotFoundException.kt @@ -0,0 +1,6 @@ +package com.dcd.server.core.domain.volume.exception + +import com.dcd.server.core.common.error.BasicException +import com.dcd.server.core.common.error.ErrorCode + +class VolumeMountNotFoundException : BasicException(ErrorCode.VOLUME_MOUNT_NOT_FOUND) diff --git a/src/main/kotlin/com/dcd/server/core/domain/volume/model/VolumeMount.kt b/src/main/kotlin/com/dcd/server/core/domain/volume/model/VolumeMount.kt index a311b72c..da1dd9b6 100644 --- a/src/main/kotlin/com/dcd/server/core/domain/volume/model/VolumeMount.kt +++ b/src/main/kotlin/com/dcd/server/core/domain/volume/model/VolumeMount.kt @@ -4,7 +4,6 @@ import com.dcd.server.core.domain.application.model.Application import java.util.UUID class VolumeMount( - val id: UUID, val application: Application, val volume: Volume, val mountPath: String, 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 913e07cd..4074f84c 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 @@ -9,4 +9,6 @@ interface CommandVolumePort { fun delete(volume: Volume) fun saveMount(volumeMount: VolumeMount) + + fun deleteMount(volumeMount: VolumeMount) } \ No newline at end of file diff --git a/src/main/kotlin/com/dcd/server/core/domain/volume/spi/QueryVolumePort.kt b/src/main/kotlin/com/dcd/server/core/domain/volume/spi/QueryVolumePort.kt index d805299c..20ef16cc 100644 --- a/src/main/kotlin/com/dcd/server/core/domain/volume/spi/QueryVolumePort.kt +++ b/src/main/kotlin/com/dcd/server/core/domain/volume/spi/QueryVolumePort.kt @@ -16,4 +16,6 @@ interface QueryVolumePort { fun findAllMountByApplication(application: Application): List fun findAllMountByVolume(volume: Volume): List + + fun findMountByApplicationAndVolume(application: Application, volume: Volume): 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 index 710c0923..b3213c04 100644 --- 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 @@ -6,6 +6,7 @@ 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.AlreadyExistsVolumeMountException 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 @@ -37,8 +38,10 @@ class MountVolumeUseCase( if (application.workspace != workspace) throw ApplicationNotFoundException() + if (queryVolumePort.findMountByApplicationAndVolume(application, volume) != null) + throw AlreadyExistsVolumeMountException() + val volumeMount = VolumeMount( - id = UUID.randomUUID(), application = application, volume = volume, mountPath = mountVolumeReqDto.mountPath, diff --git a/src/main/kotlin/com/dcd/server/core/domain/volume/usecase/UnMountVolumeUseCase.kt b/src/main/kotlin/com/dcd/server/core/domain/volume/usecase/UnMountVolumeUseCase.kt new file mode 100644 index 00000000..458c7ce3 --- /dev/null +++ b/src/main/kotlin/com/dcd/server/core/domain/volume/usecase/UnMountVolumeUseCase.kt @@ -0,0 +1,44 @@ +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.exception.VolumeMountNotFoundException +import com.dcd.server.core.domain.volume.exception.VolumeNotFoundException +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 UnMountVolumeUseCase( + 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) { + 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 = queryVolumePort.findMountByApplicationAndVolume(application, volume) + ?: throw VolumeMountNotFoundException() + commandVolumePort.deleteMount(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 88cd03ed..63536d35 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 @@ -105,6 +105,7 @@ class SecurityConfig( it.requestMatchers(HttpMethod.GET, "/{workspaceId}/volume").authenticated() it.requestMatchers(HttpMethod.GET, "/{workspaceId}/volume/{volumeId}").authenticated() it.requestMatchers(HttpMethod.POST, "/{workspaceId}/volume/{volumeId}/mount").authenticated() + it.requestMatchers(HttpMethod.DELETE, "/{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 c3943894..c8e46186 100644 --- a/src/main/kotlin/com/dcd/server/persistence/volume/VolumePersistenceAdapter.kt +++ b/src/main/kotlin/com/dcd/server/persistence/volume/VolumePersistenceAdapter.kt @@ -32,6 +32,10 @@ class VolumePersistenceAdapter( volumeMountRepository.save(volumeMount.toEntity()) } + override fun deleteMount(volumeMount: VolumeMount) { + volumeMountRepository.delete(volumeMount.toEntity()) + } + override fun findById(id: UUID): Volume? = volumeRepository.findByIdOrNull(id) ?.toDomain() @@ -53,4 +57,8 @@ class VolumePersistenceAdapter( override fun findAllMountByVolume(volume: Volume): List = volumeMountRepository.findAllByVolume(volume.toEntity()) .map { it.toDomain() } + + override fun findMountByApplicationAndVolume(application: Application, volume: Volume): VolumeMount? = + volumeMountRepository.findByVolumeAndApplication(volume.toEntity(), application.toEntity()) + ?.toDomain() } \ No newline at end of file diff --git a/src/main/kotlin/com/dcd/server/persistence/volume/adapter/VolumeAdapter.kt b/src/main/kotlin/com/dcd/server/persistence/volume/adapter/VolumeAdapter.kt index 9fb91346..9ace0a98 100644 --- a/src/main/kotlin/com/dcd/server/persistence/volume/adapter/VolumeAdapter.kt +++ b/src/main/kotlin/com/dcd/server/persistence/volume/adapter/VolumeAdapter.kt @@ -27,7 +27,6 @@ fun VolumeJpaEntity.toDomain(): Volume = fun VolumeMount.toEntity(): VolumeMountJpaEntity = VolumeMountJpaEntity( - id = this.id, application = this.application.toEntity(), volume = this.volume.toEntity(), mountPath = this.mountPath, @@ -36,7 +35,6 @@ fun VolumeMount.toEntity(): VolumeMountJpaEntity = fun VolumeMountJpaEntity.toDomain(): VolumeMount = VolumeMount( - id = this.id, application = this.application.toDomain(), volume = this.volume.toDomain(), mountPath = this.mountPath, diff --git a/src/main/kotlin/com/dcd/server/persistence/volume/entity/VolumeMountJpaEntity.kt b/src/main/kotlin/com/dcd/server/persistence/volume/entity/VolumeMountJpaEntity.kt index 10e322e2..ad9afb8d 100644 --- a/src/main/kotlin/com/dcd/server/persistence/volume/entity/VolumeMountJpaEntity.kt +++ b/src/main/kotlin/com/dcd/server/persistence/volume/entity/VolumeMountJpaEntity.kt @@ -2,25 +2,38 @@ package com.dcd.server.persistence.volume.entity import com.dcd.server.persistence.application.entity.ApplicationJpaEntity import jakarta.persistence.Column +import jakarta.persistence.Embeddable +import jakarta.persistence.EmbeddedId import jakarta.persistence.Entity import jakarta.persistence.Id import jakarta.persistence.JoinColumn import jakarta.persistence.ManyToOne +import jakarta.persistence.MapsId import jakarta.persistence.Table +import java.io.Serializable import java.util.UUID @Entity @Table(name = "volume_mount_entity") class VolumeMountJpaEntity( - @Id - @Column(columnDefinition = "BINARY(16)") - val id: UUID = UUID.randomUUID(), + @MapsId("applicationId") @ManyToOne @JoinColumn(name = "application_id") val application: ApplicationJpaEntity, + @MapsId("volumeId") @ManyToOne @JoinColumn(name = "volume_id") val volume: VolumeJpaEntity, val mountPath: String, val readOnly: Boolean -) \ No newline at end of file +) { + @EmbeddedId + val id: VolumeMountId = VolumeMountId(application.id, volume.id) + @Embeddable + data class VolumeMountId( + @Column(nullable = false) + val applicationId: UUID, + @Column(nullable = false) + val volumeId: UUID + ) : Serializable +} \ No newline at end of file diff --git a/src/main/kotlin/com/dcd/server/persistence/volume/repository/VolumeMountRepository.kt b/src/main/kotlin/com/dcd/server/persistence/volume/repository/VolumeMountRepository.kt index 5fbac7e8..48e97dc8 100644 --- a/src/main/kotlin/com/dcd/server/persistence/volume/repository/VolumeMountRepository.kt +++ b/src/main/kotlin/com/dcd/server/persistence/volume/repository/VolumeMountRepository.kt @@ -6,7 +6,8 @@ import com.dcd.server.persistence.volume.entity.VolumeMountJpaEntity import org.springframework.data.jpa.repository.JpaRepository import java.util.UUID -interface VolumeMountRepository : JpaRepository { +interface VolumeMountRepository : JpaRepository { fun findAllByVolume(volume: VolumeJpaEntity): List fun findAllByApplication(application: ApplicationJpaEntity): List + fun findByVolumeAndApplication(volume: VolumeJpaEntity, application: ApplicationJpaEntity): VolumeMountJpaEntity? } \ No newline at end of file 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 e60b28bc..58ddc0b1 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 @@ -6,6 +6,7 @@ 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.UnMountVolumeUseCase 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 @@ -33,7 +34,8 @@ class VolumeWebAdapter( private val updateVolumeUseCase: UpdateVolumeUseCase, private val getAllVolumeUseCase: GetAllVolumeUseCase, private val getOneVolumeUseCase: GetOneVolumeUseCase, - private val mountVolumeUseCase: MountVolumeUseCase + private val mountVolumeUseCase: MountVolumeUseCase, + private val unMountVolumeUseCase: UnMountVolumeUseCase ) { @PostMapping @WorkspaceOwnerVerification("#workspaceId") @@ -88,4 +90,14 @@ class VolumeWebAdapter( ): ResponseEntity = mountVolumeUseCase.execute(volumeId, applicationId, mountVolumeRequest.toDto()) .run { ResponseEntity.ok().build() } + + @DeleteMapping("/{volumeId}/mount") + @WorkspaceOwnerVerification("#workspaceId") + fun unMountVolume( + @PathVariable workspaceId: String, + @PathVariable volumeId: UUID, + @RequestParam applicationId: String, + ): ResponseEntity = + unMountVolumeUseCase.execute(volumeId, applicationId) + .run { ResponseEntity.ok().build() } } \ No newline at end of file diff --git a/src/test/kotlin/com/dcd/server/core/domain/volume/usecase/DeleteVolumeUseCaseTest.kt b/src/test/kotlin/com/dcd/server/core/domain/volume/usecase/DeleteVolumeUseCaseTest.kt index 613262cd..b7fcbf9a 100644 --- a/src/test/kotlin/com/dcd/server/core/domain/volume/usecase/DeleteVolumeUseCaseTest.kt +++ b/src/test/kotlin/com/dcd/server/core/domain/volume/usecase/DeleteVolumeUseCaseTest.kt @@ -96,7 +96,6 @@ class DeleteVolumeUseCaseTest( val application = queryApplicationPort.findById("2fb0f315-8272-422f-8e9f-c4f765c022b2")!! val volume = volumeRepository.findByIdOrNull(targetVolumeId)!!.toDomain() val volumeMount = VolumeMount( - id = UUID.randomUUID(), application = application, volume = volume, mountPath = "/test/volume", 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 index 077d1e53..f773c3ea 100644 --- 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 @@ -3,11 +3,15 @@ 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.application.spi.QueryApplicationPort import com.dcd.server.core.domain.volume.dto.request.MountVolumeReqDto +import com.dcd.server.core.domain.volume.exception.AlreadyExistsVolumeMountException import com.dcd.server.core.domain.volume.exception.VolumeNotFoundException import com.dcd.server.core.domain.volume.model.Volume +import com.dcd.server.core.domain.volume.model.VolumeMount 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.toDomain import com.dcd.server.persistence.volume.adapter.toEntity import com.dcd.server.persistence.volume.repository.VolumeMountRepository import com.dcd.server.persistence.volume.repository.VolumeRepository @@ -18,6 +22,7 @@ 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.data.repository.findByIdOrNull import org.springframework.test.context.ActiveProfiles import org.springframework.transaction.annotation.Transactional import util.workspace.WorkspaceGenerator @@ -31,6 +36,7 @@ class MountVolumeUseCaseTest( @MockkBean(relaxed = true) private val commandPort: CommandPort, private val queryWorkspacePort: QueryWorkspacePort, + private val queryApplicationPort: QueryApplicationPort, private val volumeRepository: VolumeRepository, private val volumeMountRepository: VolumeMountRepository, private val workspaceRepository: WorkspaceRepository, @@ -107,6 +113,26 @@ class MountVolumeUseCaseTest( } } } + + `when`("이미 볼륨에 마운트 됐을때") { + beforeContainer { + val application = queryApplicationPort.findById(targetApplicationId)!! + val volume = volumeRepository.findByIdOrNull(targetVolumeId)!!.toDomain() + val volumeMount = VolumeMount( + application = application, + volume = volume, + mountPath = "/test/volume", + readOnly = false + ) + volumeMountRepository.save(volumeMount.toEntity()) + } + + then("에러가 발생해야함") { + shouldThrow { + mountVolumeUseCase.execute(targetVolumeId, targetApplicationId, request) + } + } + } } given("올바르지 않은 워크스페이스가 세팅되고") { diff --git a/src/test/kotlin/com/dcd/server/core/domain/volume/usecase/UnMountVolumeUseCaseTest.kt b/src/test/kotlin/com/dcd/server/core/domain/volume/usecase/UnMountVolumeUseCaseTest.kt new file mode 100644 index 00000000..949e3cdb --- /dev/null +++ b/src/test/kotlin/com/dcd/server/core/domain/volume/usecase/UnMountVolumeUseCaseTest.kt @@ -0,0 +1,144 @@ +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.application.spi.QueryApplicationPort +import com.dcd.server.core.domain.volume.exception.VolumeMountNotFoundException +import com.dcd.server.core.domain.volume.exception.VolumeNotFoundException +import com.dcd.server.core.domain.volume.model.Volume +import com.dcd.server.core.domain.volume.model.VolumeMount +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 UnMountVolumeUseCaseTest( + private val unMountVolumeUseCase: UnMountVolumeUseCase, + @MockkBean(relaxed = true) + private val commandPort: CommandPort, + private val queryWorkspacePort: QueryWorkspacePort, + private val queryApplicationPort: QueryApplicationPort, + 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 + ) + volumeRepository.save(volume.toEntity()) + + val application = queryApplicationPort.findById(targetApplicationId)!! + val volumeMount = VolumeMount( + application = application, + volume = volume, + mountPath = "/test/volume", + readOnly = false + ) + volumeMountRepository.save(volumeMount.toEntity()) + } + + given("볼륨과 같은 워크스페이스가 세팅되었고") { + beforeContainer { + val targetWorkspace = queryWorkspacePort.findById("d57b42f5-5cc4-440b-8dce-b4fc2e372eff")!! + workspaceInfo.workspace = targetWorkspace + } + + `when`("유스케이스를 실행할때") { + unMountVolumeUseCase.execute(targetVolumeId, targetApplicationId) + + then("볼륨 마운트 엔티티가 삭제되어야함") { + volumeMountRepository.findAll().isEmpty() shouldBe true + } + } + + `when`("존재하지 않는 볼륨 아이디로 실행할때") { + val notFoundVolumeId = UUID.randomUUID() + + then("에러가 발생해야함") { + shouldThrow { + unMountVolumeUseCase.execute(notFoundVolumeId, targetApplicationId) + } + } + } + + `when`("존재하지 않는 애플리케이션 아이디로 실행할때") { + val notFoundApplicationId = UUID.randomUUID().toString() + + then("에러가 발생해야함") { + shouldThrow { + unMountVolumeUseCase.execute(targetVolumeId, notFoundApplicationId) + } + } + } + + `when`("마운트된 볼륨이 아닐때") { + beforeContainer { + volumeMountRepository.deleteAll() + } + + then("에러가 발생해야함") { + shouldThrow { + unMountVolumeUseCase.execute(targetVolumeId, targetApplicationId) + } + } + } + } + + given("세팅된 워크스페이스가 볼륨이 속한 워크스페이스가 아니고") { + beforeContainer { + val targetWorkspace = queryWorkspacePort.findById("d57b42f5-5cc4-440b-8dce-b4fc2e372eff")!! + val otherWorkspace = WorkspaceGenerator.generateWorkspace(user = targetWorkspace.owner) + workspaceRepository.save(otherWorkspace.toEntity()) + workspaceInfo.workspace = otherWorkspace + } + + `when`("유스케이스를 실행할때") { + + then("에러가 발생해야함") { + shouldThrow { + unMountVolumeUseCase.execute(targetVolumeId, targetApplicationId) + } + } + } + } + + given("워크스페이스가 세팅되지 않고") { + beforeContainer { + workspaceInfo.workspace = null + } + + `when`("유스케이스를 실행할때") { + + then("에러가 발생해야함") { + shouldThrow { + unMountVolumeUseCase.execute(targetVolumeId, targetApplicationId) + } + } + } + } +}) \ No newline at end of file diff --git a/src/test/kotlin/com/dcd/server/core/domain/volume/usecase/UpdateVolumeUseCaseTest.kt b/src/test/kotlin/com/dcd/server/core/domain/volume/usecase/UpdateVolumeUseCaseTest.kt index 3c78b718..04e5ce7e 100644 --- a/src/test/kotlin/com/dcd/server/core/domain/volume/usecase/UpdateVolumeUseCaseTest.kt +++ b/src/test/kotlin/com/dcd/server/core/domain/volume/usecase/UpdateVolumeUseCaseTest.kt @@ -104,7 +104,6 @@ class UpdateVolumeUseCaseTest( val application = queryApplicationPort.findById("2fb0f315-8272-422f-8e9f-c4f765c022b2")!! val volume = volumeRepository.findByIdOrNull(targetVolumeId)!!.toDomain() val volumeMount = VolumeMount( - id = UUID.randomUUID(), application = application, volume = volume, mountPath = "/test/volume", 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 0094c241..45d605b3 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 @@ -11,6 +11,7 @@ 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.UnMountVolumeUseCase 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 @@ -31,6 +32,7 @@ class VolumeWebAdapterTest : BehaviorSpec({ val getAllVolumeUseCase = mockk(relaxUnitFun = true) val getOneVolumeUseCase = mockk(relaxUnitFun = true) val mountVolumeUseCase = mockk(relaxUnitFun = true) + val unMountVolumeUseCase = mockk(relaxUnitFun = true) val volumeWebAdapter = VolumeWebAdapter( createVolumeUseCase, @@ -38,7 +40,8 @@ class VolumeWebAdapterTest : BehaviorSpec({ updateVolumeUseCase, getAllVolumeUseCase, getOneVolumeUseCase, - mountVolumeUseCase + mountVolumeUseCase, + unMountVolumeUseCase ) given("워크스페이스 아이디와 볼륨 생성 요청이 주어지고") { @@ -148,4 +151,21 @@ class VolumeWebAdapterTest : BehaviorSpec({ } } } + + given("워크스페이스 아이디, 볼륨 아이디, 애플리케이션 아이디가 주어지고") { + val testWorkspaceId = UUID.randomUUID().toString() + val testVolumeId = UUID.randomUUID() + val testApplicationId = UUID.randomUUID().toString() + + `when`("마운트 해제 메서드를 실행할때") { + val result = volumeWebAdapter.unMountVolume(testWorkspaceId, testVolumeId, testApplicationId) + + then("볼륨 마운트 해제 유스케이스가 실행되어야함") { + verify { unMountVolumeUseCase.execute(testVolumeId, testApplicationId) } + } + then("상태코드 OK가 응답되어야함") { + result.statusCode shouldBe HttpStatus.OK + } + } + } }) \ No newline at end of file diff --git a/src/test/resources/data.sql b/src/test/resources/data.sql index b90a5fcd..688f83e7 100644 --- a/src/test/resources/data.sql +++ b/src/test/resources/data.sql @@ -24,7 +24,7 @@ create table application_env_detail_entity (id BINARY(16) not null, encryption b create table application_env_matcher_entity (id BINARY(16) not null, application_id BINARY(16), env_id BINARY(16), primary key (id)); create table application_env_label_entity (application_env_id BINARY(16) not null, label varchar(255)); create table volume_entity (id BINARY(16) not null, description varchar(255), name varchar(255), workspace_id BINARY(16) not null, primary key (id)); -create table volume_mount_entity (id BINARY(16) not null, mount_path varchar(255) not null, application_id BINARY(16) not null, volume_id BINARY(16) not null, read_only bit(1) not null, primary key (id)); +create table volume_mount_entity (application_id binary(16) not null, volume_id binary(16) not null, mount_path varchar(255) not null, read_only bit not null, primary key (application_id, volume_id)); alter table if exists volume_entity add constraint FKhp1ggnqtveky8po2cwsb45una foreign key (workspace_id) references workspace_entity (id) on delete cascade; alter table if exists volume_mount_entity add constraint FKr5bb2ev813ioxtylyobgdphel foreign key (application_id) references application_entity (id) on delete cascade; alter table if exists volume_mount_entity add constraint FKq6dppr5p20mvgjditmpypicey foreign key (volume_id) references volume_entity (id) on delete cascade;