Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ plugins {
id 'org.springframework.boot' version '3.5.8'
id 'io.spring.dependency-management' version '1.1.7'
id 'com.google.protobuf' version '0.9.4'
id 'org.jetbrains.kotlin.jvm' version '2.0.21'
id 'org.jetbrains.kotlin.plugin.spring' version '2.0.21'
id 'org.jetbrains.kotlin.plugin.jpa' version '2.0.21'
}

group = 'me'
Expand All @@ -15,6 +18,10 @@ java {
}
}

kotlin {
jvmToolchain(21)
}

repositories {
mavenCentral()
}
Expand All @@ -36,6 +43,12 @@ dependencies {
testImplementation 'org.springframework.amqp:spring-rabbit-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

//kotlin
implementation 'org.jetbrains.kotlin:kotlin-reflect'
implementation 'org.jetbrains.kotlin:kotlin-stdlib'
implementation 'com.fasterxml.jackson.module:jackson-module-kotlin'
testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5'

// Actuator
implementation 'org.springframework.boot:spring-boot-starter-actuator'

Expand Down Expand Up @@ -87,6 +100,12 @@ tasks.named('test') {
tasks.named('compileJava') {
dependsOn(tasks.named('generateProto'))
}
tasks.named('compileKotlin') {
dependsOn(tasks.named('generateProto'))
}
tasks.named('compileTestJava') {
dependsOn(tasks.named('generateTestProto'))
}
}
tasks.named('compileTestKotlin') {
dependsOn(tasks.named('generateTestProto'))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package me.pinitnotification.domain.notification

import org.assertj.core.api.Assertions
import org.junit.jupiter.api.Test
import java.time.Clock
import java.time.LocalDateTime
import java.time.OffsetDateTime
import java.time.ZoneId
import java.time.ZoneOffset.UTC

internal class UpcomingScheduleNotificationTest {
val clock = Clock.fixed(
LocalDateTime.of(
2026,
1,
1,
0,
0,
0
).toInstant(UTC), ZoneId.systemDefault()
)

@Test
fun updateScheduleStartTime() {
//given
val notification = getSampleUpcomingScheduleNotification()

//when
notification.updateScheduleStartTime("2024-06-01T10:00:00Z", "123-idempotency-key")

//then
Assertions.assertThat(notification.scheduleStartTime).isEqualTo("2024-06-01T10:00:00Z")
Assertions.assertThat(notification.idempotentKey).isEqualTo("123-idempotency-key")
}
Comment on lines +23 to +34
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

문제점: 테스트가 updateScheduleStartTime 메서드를 호출하지만, 실제로는 두 개의 필드(scheduleStartTimeidempotentKey)를 함께 업데이트하고 있습니다. 테스트 메서드명이 메서드의 전체 동작을 충분히 설명하지 못합니다.

영향: 테스트의 의도가 명확하지 않아 향후 유지보수 시 혼란을 줄 수 있습니다.

수정 제안: 테스트 메서드명을 updateScheduleStartTimeAndIdempotencyKey처럼 더 구체적으로 변경하거나, 각 필드 업데이트를 개별적으로 테스트하는 것을 고려하세요.

Copilot uses AI. Check for mistakes.

@Test
fun isDue() {
//given
val notification = getSampleUpcomingScheduleNotification(
scheduleStartTime = "2024-06-01T10:00:00Z"
)

//when
val isDue = notification.isDue(OffsetDateTime.now(clock))

//then
Assertions.assertThat(isDue).isTrue()
}
Comment on lines +36 to +48
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

문제점: 2026년 1월 1일로 고정된 Clock을 사용하고 있으나, 실제 테스트에서는 2024년 6월 1일의 스케줄 시작 시간과 비교하고 있습니다. 이로 인해 테스트의 의도가 명확하지 않습니다.

영향: 테스트가 과거 날짜로 인해 항상 true를 반환하는 경우만 검증하고 있어, isDue 메서드의 경계 조건이나 false 케이스가 테스트되지 않습니다.

수정 제안: 테스트 시나리오를 명확히 하고, 스케줄 시작 시간이 현재 시간보다 이전인 경우(true)와 이후인 경우(false) 모두를 검증하는 테스트 케이스를 추가하세요.

Copilot uses AI. Check for mistakes.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package me.pinitnotification.domain.notification

import org.junit.platform.commons.util.ReflectionUtils

fun getSampleUpcomingScheduleNotification(
id: Long = 1L,
ownerId: Long = 1L,
scheduleId: Long = 1L,
scheduleTitle: String = "sample",
scheduleStartTime: String = "2024-06-01T10:00:00Z",
idempotencyKey: String = "",
): UpcomingScheduleNotification {
val sample = UpcomingScheduleNotification(
ownerId,
scheduleId,
scheduleTitle,
scheduleStartTime,
idempotencyKey
)
ReflectionUtils.findFields(
UpcomingScheduleNotification::class.java,
{ field -> field.name == "id" },
ReflectionUtils.HierarchyTraversalMode.TOP_DOWN
).forEach { field ->
field.isAccessible = true
field.set(sample, id)
}
Comment on lines +20 to +27
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

문제점: field.isAccessible = true 사용은 Java 9 이상에서 deprecated되었으며, reflection을 통한 private 필드 접근은 캡슐화를 깨뜨립니다.

영향: 향후 Java 버전 업그레이드 시 호환성 문제가 발생할 수 있으며, 테스트가 내부 구현에 지나치게 의존하게 됩니다.

수정 제안: 테스트를 위한 builder 패턴이나 factory 메서드를 도메인 객체에 추가하거나, @PersistenceConstructor 같은 JPA 관련 애노테이션을 활용하여 reflection 사용을 최소화하세요.

Copilot uses AI. Check for mistakes.
return sample
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package me.pinitnotification.domain.push

import org.assertj.core.api.Assertions
import org.junit.jupiter.api.Test
import java.time.Clock
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZoneOffset.UTC

internal class PushSubscriptionTest {
val clock = Clock.fixed(
LocalDateTime.of(
2026,
1,
1,
0,
0,
0
).toInstant(UTC), ZoneId.systemDefault()
)
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

문제점: clock 변수가 선언되어 있지만 테스트 메서드에서 실제로 사용되지 않습니다.

영향: 불필요한 코드가 테스트 파일에 포함되어 있어 코드 가독성이 저하되고, 미래의 개발자에게 혼란을 줄 수 있습니다.

수정 제안: 사용되지 않는 clock 변수 선언부(11-20라인)를 제거하세요.

Suggested change
import java.time.Clock
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZoneOffset.UTC
internal class PushSubscriptionTest {
val clock = Clock.fixed(
LocalDateTime.of(
2026,
1,
1,
0,
0,
0
).toInstant(UTC), ZoneId.systemDefault()
)
internal class PushSubscriptionTest {

Copilot uses AI. Check for mistakes.

@Test
fun updateToken() {
//given
val subscription = getSamplePushSubscription()

//when
subscription.updateToken("new-sample-token")

//then
Assertions.assertThat(subscription.token).isEqualTo("new-sample-token")
}
Comment on lines +8 to +18
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

문제점: PushSubscription.updateToken() 메서드는 modifiedAt이 현재 시간보다 이후일 경우 토큰 업데이트를 건너뛰는 로직이 있지만, 이 테스트는 해당 조건 분기를 검증하지 않고 있습니다.

영향: 중요한 비즈니스 로직(시간 기반 조건부 업데이트)이 테스트되지 않아, 향후 코드 변경 시 버그가 발생할 수 있습니다.

수정 제안: modifiedAt이 미래 시간으로 설정된 경우 토큰이 업데이트되지 않는지 확인하는 추가 테스트 케이스를 작성하세요.

Copilot uses AI. Check for mistakes.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package me.pinitnotification.domain.push

import org.junit.platform.commons.util.ReflectionUtils
import java.time.Instant

fun getSamplePushSubscription(
id: Long = 1L,
memberId: Long = 1L,
deviceId: String = "sample-device-id",
token: String = "sample-token",
modifiedAt: Instant = Instant.EPOCH,
): PushSubscription {
val sample = PushSubscription(memberId, deviceId, token)
ReflectionUtils.findFields(
PushSubscription::class.java,
{ field -> field.name == "id" },
ReflectionUtils.HierarchyTraversalMode.TOP_DOWN
).forEach { field ->
field.isAccessible = true
field.set(sample, id)
}
Comment on lines +14 to +21
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

문제점: field.isAccessible = true 사용은 Java 9 이상에서 deprecated되었으며, reflection을 통한 private 필드 접근은 캡슐화를 깨뜨립니다.

영향: 향후 Java 버전 업그레이드 시 호환성 문제가 발생할 수 있으며, 테스트가 내부 구현에 지나치게 의존하게 됩니다.

수정 제안: 테스트를 위한 builder 패턴이나 factory 메서드를 도메인 객체에 추가하거나, @PersistenceConstructor 같은 JPA 관련 애노테이션을 활용하여 reflection 사용을 최소화하세요.

Copilot uses AI. Check for mistakes.
ReflectionUtils.findFields(
PushSubscription::class.java,
{ field -> field.name == "modifiedAt" },
ReflectionUtils.HierarchyTraversalMode.TOP_DOWN
).forEach { field ->
field.isAccessible = true
field.set(sample, modifiedAt)
}
Comment on lines +22 to +29
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

문제점: field.isAccessible = true 사용은 Java 9 이상에서 deprecated되었으며, reflection을 통한 private 필드 접근은 캡슐화를 깨뜨립니다.

영향: 향후 Java 버전 업그레이드 시 호환성 문제가 발생할 수 있으며, 테스트가 내부 구현에 지나치게 의존하게 됩니다.

수정 제안: 테스트를 위한 builder 패턴이나 factory 메서드를 도메인 객체에 추가하거나, @PersistenceConstructor 같은 JPA 관련 애노테이션을 활용하여 reflection 사용을 최소화하세요.

Copilot uses AI. Check for mistakes.
return sample
}