-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/main' into convert-to-jpa/place-…
…domain
- Loading branch information
Showing
45 changed files
with
1,371 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 46 additions & 0 deletions
46
...grationTest/kotlin/club/staircrusher/accessibility/application/port/in/BlurFacesITBase.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package club.staircrusher.accessibility.application.port.`in` | ||
|
||
import club.staircrusher.accessibility.application.port.`in`.image.ImageProcessor | ||
import club.staircrusher.accessibility.application.port.out.DetectFacesResponse | ||
import club.staircrusher.accessibility.application.port.out.DetectFacesService | ||
import club.staircrusher.accessibility.domain.model.DetectedFacePosition | ||
import club.staircrusher.stdlib.Size | ||
import club.staircrusher.testing.spring_it.ITDataGenerator | ||
import club.staircrusher.testing.spring_it.base.SccSpringITBase | ||
import club.staircrusher.testing.spring_it.mock.MockSccClock | ||
import kotlinx.coroutines.runBlocking | ||
import org.mockito.Mockito | ||
import org.mockito.kotlin.eq | ||
import org.springframework.beans.factory.annotation.Autowired | ||
import org.springframework.boot.test.mock.mockito.MockBean | ||
|
||
class BlurFacesITBase : SccSpringITBase() { | ||
@Autowired | ||
lateinit var dataGenerator: ITDataGenerator | ||
|
||
@Autowired | ||
lateinit var clock: MockSccClock | ||
|
||
@MockBean | ||
lateinit var imageProcessor: ImageProcessor | ||
|
||
@MockBean | ||
lateinit var detectFacesService: DetectFacesService | ||
|
||
|
||
fun mockDetectFacesWithFaceImage(imageUrl: String, imageBytes: ByteArray) = runBlocking { | ||
Mockito.`when`(detectFacesService.detect(eq(imageUrl))).thenReturn( | ||
DetectFacesResponse( | ||
imageBytes = imageBytes, imageSize = Size(100, 100), positions = listOf(DetectedFacePosition(0, 0, 10, 10)) | ||
) | ||
) | ||
} | ||
|
||
fun mockDetectFacesWithNoFaceImage(imageUrl: String, imageBytes: ByteArray) = runBlocking { | ||
Mockito.`when`(detectFacesService.detect(eq(imageUrl))).thenReturn( | ||
DetectFacesResponse( | ||
imageBytes = imageBytes, imageSize = Size(100, 100), positions = emptyList() | ||
) | ||
) | ||
} | ||
} |
149 changes: 149 additions & 0 deletions
149
...essibility/application/port/in/BlurFacesInLatestBuildingAccessibilityImagesUseCaseTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
package club.staircrusher.accessibility.application.port.`in` | ||
|
||
import club.staircrusher.accessibility.application.port.out.persistence.AccessibilityImageFaceBlurringHistoryRepository | ||
import club.staircrusher.accessibility.application.port.out.persistence.BuildingAccessibilityRepository | ||
import club.staircrusher.accessibility.application.port.out.persistence.PlaceAccessibilityRepository | ||
import club.staircrusher.accessibility.domain.model.AccessibilityImage | ||
import club.staircrusher.accessibility.domain.model.AccessibilityImageFaceBlurringHistory | ||
import club.staircrusher.stdlib.testing.SccRandom | ||
import club.staircrusher.testing.spring_it.base.SccSpringITApplication | ||
import club.staircrusher.testing.spring_it.mock.MockDetectFacesService | ||
import kotlinx.coroutines.runBlocking | ||
import org.junit.jupiter.api.Assertions | ||
import org.junit.jupiter.api.BeforeEach | ||
import org.junit.jupiter.api.Test | ||
import org.mockito.Mockito | ||
import org.mockito.kotlin.any | ||
import org.springframework.beans.factory.annotation.Autowired | ||
import org.springframework.boot.test.context.SpringBootTest | ||
import java.time.Duration | ||
|
||
@SpringBootTest(classes = [SccSpringITApplication::class]) | ||
class BlurFacesInLatestBuildingAccessibilityImagesUseCaseTest : BlurFacesITBase() { | ||
@Autowired | ||
private lateinit var accessibilityImageFaceBlurringHistoryRepository: AccessibilityImageFaceBlurringHistoryRepository | ||
|
||
@Autowired | ||
private lateinit var blurFacesInLatestBuildingAccessibilityImagesUseCase: BlurFacesInLatestBuildingAccessibilityImagesUseCase | ||
|
||
@Autowired | ||
private lateinit var buildingAccessibilityRepository: BuildingAccessibilityRepository | ||
|
||
@Autowired | ||
private lateinit var placeAccessibilityRepository: PlaceAccessibilityRepository | ||
|
||
@BeforeEach | ||
fun setUp() = runBlocking { | ||
val imageBytes = ByteArray(10) { it.toByte() } | ||
mockDetectFacesWithFaceImage(MockDetectFacesService.URL_WITH_FACES, imageBytes) | ||
mockDetectFacesWithNoFaceImage(MockDetectFacesService.URL_WITHOUT_FACES, imageBytes) | ||
Mockito.`when`(imageProcessor.blur(any(), any(), any())).thenReturn(imageBytes) | ||
|
||
placeAccessibilityRepository.removeAll() | ||
buildingAccessibilityRepository.removeAll() | ||
accessibilityImageFaceBlurringHistoryRepository.removeAll() | ||
} | ||
|
||
@Test | ||
fun `얼굴 블러링 기록이 없으면 가장 오래된 building accessibility 의 이미지부터 얼굴 블러링한다`() { | ||
val (_, _, oldestBuildingAccessibility) = registerPlaceAccessibilityAndBuildingAccessibility( | ||
listOf(MockDetectFacesService.URL_WITH_FACES) | ||
) | ||
clock.advanceTime(Duration.ofMinutes(1)) | ||
val (_, _, secondOldestBuildingAccessibility) = registerPlaceAccessibilityAndBuildingAccessibility( | ||
listOf(MockDetectFacesService.URL_WITH_FACES) | ||
) | ||
|
||
blurFacesInLatestBuildingAccessibilityImagesUseCase.handle() | ||
|
||
val blurredResult = accessibilityImageFaceBlurringHistoryRepository.findByBuildingAccessibilityId( | ||
oldestBuildingAccessibility.id | ||
) | ||
Assertions.assertTrue(blurredResult.isNotEmpty()) | ||
val notBlurredResult = accessibilityImageFaceBlurringHistoryRepository.findByBuildingAccessibilityId( | ||
secondOldestBuildingAccessibility.id | ||
) | ||
Assertions.assertTrue(notBlurredResult.isEmpty()) | ||
} | ||
|
||
@Test | ||
fun `얼굴 블러링 기록이 이후 가장 오래된 building accessibility 의 이미지부터 얼굴 블러링한다`() { | ||
val (_, _, oldestBuildingAccessibility) = registerPlaceAccessibilityAndBuildingAccessibility( | ||
listOf(MockDetectFacesService.URL_WITH_FACES) | ||
) | ||
clock.advanceTime(Duration.ofMinutes(1)) | ||
val (_, _, secondOldestBuildingAccessibility) = registerPlaceAccessibilityAndBuildingAccessibility( | ||
listOf(MockDetectFacesService.URL_WITH_FACES) | ||
) | ||
transactionManager.doInTransaction { | ||
accessibilityImageFaceBlurringHistoryRepository.save( | ||
AccessibilityImageFaceBlurringHistory( | ||
id = "", placeAccessibilityId = null, | ||
buildingAccessibilityId = oldestBuildingAccessibility.id, | ||
originalImageUrls = listOf("image_url"), | ||
blurredImageUrls = listOf("blurred_image_url"), | ||
detectedPeopleCounts = emptyList(), | ||
createdAt = clock.instant(), | ||
updatedAt = clock.instant() | ||
) | ||
) | ||
} | ||
|
||
blurFacesInLatestBuildingAccessibilityImagesUseCase.handle() | ||
|
||
val result = accessibilityImageFaceBlurringHistoryRepository.findByBuildingAccessibilityId( | ||
secondOldestBuildingAccessibility.id | ||
) | ||
Assertions.assertTrue(result.isNotEmpty()) | ||
} | ||
|
||
@Test | ||
fun `BuildingAccessibility 이미지 중 얼굴이 감지된 사진만 업데이트한다`() = runBlocking { | ||
val (_, _, buildingAccessibility) = registerPlaceAccessibilityAndBuildingAccessibility( | ||
listOf(MockDetectFacesService.URL_WITH_FACES, MockDetectFacesService.URL_WITHOUT_FACES) | ||
) | ||
|
||
blurFacesInLatestBuildingAccessibilityImagesUseCase.handle() | ||
|
||
val reloadBuildingAccessibility = transactionManager.doInTransaction { | ||
buildingAccessibilityRepository.findByIdOrNull(buildingAccessibility.id) | ||
} | ||
val entranceImages = reloadBuildingAccessibility?.entranceImages?.map { it.imageUrl } ?: emptyList() | ||
Assertions.assertFalse(entranceImages.contains(MockDetectFacesService.URL_WITH_FACES)) | ||
Assertions.assertTrue(entranceImages.contains(MockDetectFacesService.URL_WITHOUT_FACES)) | ||
val elevatorImages = reloadBuildingAccessibility?.entranceImages?.map { it.imageUrl } ?: emptyList() | ||
Assertions.assertFalse(elevatorImages.contains(MockDetectFacesService.URL_WITH_FACES)) | ||
Assertions.assertTrue(elevatorImages.contains(MockDetectFacesService.URL_WITHOUT_FACES)) | ||
} | ||
|
||
|
||
@Test | ||
fun `이미 썸네일 처리가 된 BuildingAccessibility 의 경우 블러링 한 이미지 사용을 위해 썸네일 url을 제거한다`() = runBlocking { | ||
val (_, _, buildingAccessibility) = registerPlaceAccessibilityAndBuildingAccessibility( | ||
imageUrls = listOf(MockDetectFacesService.URL_WITH_FACES, MockDetectFacesService.URL_WITHOUT_FACES) | ||
) | ||
|
||
blurFacesInLatestBuildingAccessibilityImagesUseCase.handle() | ||
|
||
val reloadBuildingAccessibility = transactionManager.doInTransaction { | ||
buildingAccessibilityRepository.findByIdOrNull(buildingAccessibility.id) | ||
} | ||
val entranceImages = reloadBuildingAccessibility?.entranceImages ?: emptyList() | ||
Assertions.assertTrue(entranceImages.isNotEmpty()) | ||
Assertions.assertTrue(entranceImages.mapNotNull { it.thumbnailUrl }.isEmpty()) | ||
val elevatorImages = reloadBuildingAccessibility?.entranceImages ?: emptyList() | ||
Assertions.assertTrue(elevatorImages.isNotEmpty()) | ||
Assertions.assertTrue(elevatorImages.mapNotNull { it.thumbnailUrl }.isEmpty()) | ||
} | ||
|
||
private fun registerPlaceAccessibilityAndBuildingAccessibility(imageUrls: List<String>) = | ||
transactionManager.doInTransaction { | ||
val user = dataGenerator.createUser() | ||
val building = dataGenerator.createBuilding() | ||
val place = dataGenerator.createPlace(placeName = SccRandom.string(32), building = building) | ||
val (placeAccessibility, buildingAccessibility) = dataGenerator.registerBuildingAndPlaceAccessibility( | ||
place = place, user = user, imageUrls = imageUrls, images = imageUrls.map { AccessibilityImage(it, it) } | ||
) | ||
Triple(user, placeAccessibility, buildingAccessibility) | ||
} | ||
} |
140 changes: 140 additions & 0 deletions
140
...accessibility/application/port/in/BlurFacesInLatestPlaceAccessibilityImagesUseCaseTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
package club.staircrusher.accessibility.application.port.`in` | ||
|
||
import club.staircrusher.accessibility.application.port.out.persistence.AccessibilityImageFaceBlurringHistoryRepository | ||
import club.staircrusher.accessibility.application.port.out.persistence.BuildingAccessibilityRepository | ||
import club.staircrusher.accessibility.application.port.out.persistence.PlaceAccessibilityRepository | ||
import club.staircrusher.accessibility.domain.model.AccessibilityImage | ||
import club.staircrusher.accessibility.domain.model.AccessibilityImageFaceBlurringHistory | ||
import club.staircrusher.stdlib.testing.SccRandom | ||
import club.staircrusher.testing.spring_it.base.SccSpringITApplication | ||
import club.staircrusher.testing.spring_it.mock.MockDetectFacesService | ||
import kotlinx.coroutines.runBlocking | ||
import org.junit.jupiter.api.Assertions | ||
import org.junit.jupiter.api.BeforeEach | ||
import org.junit.jupiter.api.Test | ||
import org.mockito.Mockito | ||
import org.mockito.kotlin.any | ||
import org.springframework.beans.factory.annotation.Autowired | ||
import org.springframework.boot.test.context.SpringBootTest | ||
import java.time.Duration | ||
|
||
@SpringBootTest(classes = [SccSpringITApplication::class]) | ||
class BlurFacesInLatestPlaceAccessibilityImagesUseCaseTest : BlurFacesITBase() { | ||
@Autowired | ||
private lateinit var accessibilityImageFaceBlurringHistoryRepository: AccessibilityImageFaceBlurringHistoryRepository | ||
|
||
@Autowired | ||
private lateinit var blurFacesInLatestPlaceAccessibilityImagesUseCase: BlurFacesInLatestPlaceAccessibilityImagesUseCase | ||
|
||
@Autowired | ||
private lateinit var buildingAccessibilityRepository: BuildingAccessibilityRepository | ||
|
||
@Autowired | ||
private lateinit var placeAccessibilityRepository: PlaceAccessibilityRepository | ||
|
||
@BeforeEach | ||
fun setUp() = runBlocking { | ||
val imageBytes = ByteArray(10) { it.toByte() } | ||
mockDetectFacesWithFaceImage(MockDetectFacesService.URL_WITH_FACES, imageBytes) | ||
mockDetectFacesWithNoFaceImage(MockDetectFacesService.URL_WITHOUT_FACES, imageBytes) | ||
Mockito.`when`(imageProcessor.blur(any(), any(), any())).thenReturn(imageBytes) | ||
|
||
placeAccessibilityRepository.removeAll() | ||
buildingAccessibilityRepository.removeAll() | ||
accessibilityImageFaceBlurringHistoryRepository.removeAll() | ||
} | ||
|
||
@Test | ||
fun `얼굴 블러링 기록이 없으면 가장 오래된 place accessibility 의 이미지부터 얼굴 블러링한다`() { | ||
val (_, oldestPlaceAccessibility, _) = registerPlaceAccessibilityAndBuildingAccessibility( | ||
listOf(MockDetectFacesService.URL_WITH_FACES) | ||
) | ||
clock.advanceTime(Duration.ofMinutes(1)) | ||
val (_, secondOldestPlaceAccessibility, _) = registerPlaceAccessibilityAndBuildingAccessibility( | ||
listOf(MockDetectFacesService.URL_WITH_FACES) | ||
) | ||
|
||
blurFacesInLatestPlaceAccessibilityImagesUseCase.handle() | ||
|
||
val blurredResult = | ||
accessibilityImageFaceBlurringHistoryRepository.findByPlaceAccessibilityId(oldestPlaceAccessibility.id) | ||
Assertions.assertTrue(blurredResult.isNotEmpty()) | ||
val notBlurredResult = | ||
accessibilityImageFaceBlurringHistoryRepository.findByPlaceAccessibilityId(secondOldestPlaceAccessibility.id) | ||
Assertions.assertTrue(notBlurredResult.isEmpty()) | ||
} | ||
|
||
@Test | ||
fun `얼굴 블러링 기록이 이후 가장 오래된 place accessibility 의 이미지부터 얼굴 블러링한다`() { | ||
val (_, oldestPlaceAccessibility, _) = registerPlaceAccessibilityAndBuildingAccessibility( | ||
listOf(MockDetectFacesService.URL_WITH_FACES) | ||
) | ||
clock.advanceTime(Duration.ofMinutes(1)) | ||
val (_, secondOldestPlaceAccessibility, _) = registerPlaceAccessibilityAndBuildingAccessibility( | ||
listOf(MockDetectFacesService.URL_WITH_FACES) | ||
) | ||
transactionManager.doInTransaction { | ||
accessibilityImageFaceBlurringHistoryRepository.save( | ||
AccessibilityImageFaceBlurringHistory( | ||
id = "", | ||
placeAccessibilityId = oldestPlaceAccessibility.id, | ||
buildingAccessibilityId = null, | ||
originalImageUrls = listOf("image_url"), | ||
blurredImageUrls = listOf("blurred_image_url"), | ||
detectedPeopleCounts = emptyList(), | ||
createdAt = clock.instant(), | ||
updatedAt = clock.instant() | ||
) | ||
) | ||
} | ||
|
||
blurFacesInLatestPlaceAccessibilityImagesUseCase.handle() | ||
|
||
val result = | ||
accessibilityImageFaceBlurringHistoryRepository.findByPlaceAccessibilityId(secondOldestPlaceAccessibility.id) | ||
Assertions.assertTrue(result.isNotEmpty()) | ||
} | ||
|
||
@Test | ||
fun `PlaceAccessibility 이미지 중 얼굴이 감지된 사진만 업데이트한다`() = runBlocking { | ||
val (_, placeAccessibility, _) = registerPlaceAccessibilityAndBuildingAccessibility( | ||
listOf(MockDetectFacesService.URL_WITH_FACES, MockDetectFacesService.URL_WITHOUT_FACES) | ||
) | ||
|
||
blurFacesInLatestPlaceAccessibilityImagesUseCase.handle() | ||
|
||
val reloadPlaceAccessibility = transactionManager.doInTransaction { | ||
placeAccessibilityRepository.findByIdOrNull(placeAccessibility.id) | ||
} | ||
val imageUrls = reloadPlaceAccessibility?.images?.map { it.imageUrl } ?: emptyList() | ||
Assertions.assertFalse(imageUrls.contains(MockDetectFacesService.URL_WITH_FACES)) | ||
Assertions.assertTrue(imageUrls.contains(MockDetectFacesService.URL_WITHOUT_FACES)) | ||
} | ||
|
||
@Test | ||
fun `이미 썸네일 처리가 된 PlaceAccessibility 의 경우 블러링 한 이미지 사용을 위해 썸네일 url을 제거한다`() = runBlocking { | ||
val (_, placeAccessibility, _) = registerPlaceAccessibilityAndBuildingAccessibility( | ||
listOf(MockDetectFacesService.URL_WITH_FACES, MockDetectFacesService.URL_WITHOUT_FACES) | ||
) | ||
|
||
blurFacesInLatestPlaceAccessibilityImagesUseCase.handle() | ||
|
||
val reloadPlaceAccessibility = transactionManager.doInTransaction { | ||
placeAccessibilityRepository.findByIdOrNull(placeAccessibility.id) | ||
} | ||
val images = reloadPlaceAccessibility?.images ?: emptyList() | ||
Assertions.assertTrue(images.isNotEmpty()) | ||
Assertions.assertTrue(images.mapNotNull { it.thumbnailUrl }.isEmpty()) | ||
} | ||
|
||
private fun registerPlaceAccessibilityAndBuildingAccessibility(imageUrls: List<String>) = | ||
transactionManager.doInTransaction { | ||
val user = dataGenerator.createUser() | ||
val building = dataGenerator.createBuilding() | ||
val place = dataGenerator.createPlace(placeName = SccRandom.string(32), building = building) | ||
val (placeAccessibility, buildingAccessibility) = dataGenerator.registerBuildingAndPlaceAccessibility( | ||
place = place, user = user, imageUrls = imageUrls, images = imageUrls.map { AccessibilityImage(it, it) } | ||
) | ||
Triple(user, placeAccessibility, buildingAccessibility) | ||
} | ||
} |
Oops, something went wrong.