Skip to content

Feat/39 컨벤션 정의: Time #55

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Aug 5, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
9 changes: 3 additions & 6 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ plugins {
kotlin("jvm") version "1.8.21"
kotlin("plugin.spring") version "1.8.21"
kotlin("plugin.jpa") version "1.8.21"
kotlin("plugin.allopen") version "1.6.21"
kotlin("plugin.noarg") version "1.6.21"
id("org.jlleitschuh.gradle.ktlint") version "11.5.0"
id("jacoco")
}
Expand Down Expand Up @@ -54,24 +56,19 @@ dependencies {
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("com.ninja-squad:springmockk:4.0.2")
implementation("com.google.code.gson:gson:2.10.1")
implementation("com.ninja-squad:springmockk:4.0.2")
developmentOnly("org.springframework.boot:spring-boot-devtools")
runtimeOnly("com.mysql:mysql-connector-j")
testImplementation("org.springframework.boot:spring-boot-starter-test")
implementation("org.modelmapper:modelmapper:2.4.2")
testImplementation("org.junit.jupiter:junit-jupiter-params:5.1.0")
implementation("org.modelmapper:modelmapper:2.4.2")
integrationTestImplementation("org.springframework.boot:spring-boot-testcontainers")
integrationTestImplementation("org.testcontainers:junit-jupiter")
integrationTestImplementation("org.testcontainers:mysql")
testImplementation("org.springframework.security:spring-security-test")
implementation("com.ninja-squad:springmockk:4.0.2")

implementation("org.springframework.boot:spring-boot-starter-security")
implementation("io.jsonwebtoken:jjwt-api:0.11.5")
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.5")
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.11.5")

implementation("com.google.code.gson:gson:2.10.1")
}

tasks.withType<KotlinCompile> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.repository.findByIdOrNull
import java.time.Duration.ofHours
import java.time.LocalDateTime
import java.time.OffsetDateTime
import java.time.ZoneOffset

class PerformanceRepositoryTest(
Expand All @@ -18,7 +18,7 @@ class PerformanceRepositoryTest(
@Test
fun `PerformanceRepository_save should return savedPerformance`() {
// given
val now = LocalDateTime.now(ZoneOffset.UTC)
val now = OffsetDateTime.now(ZoneOffset.UTC)
val samplePerformance = Performance(
title = "test title",
date = now,
Expand All @@ -37,7 +37,7 @@ class PerformanceRepositoryTest(
@Test
fun `PerformanceRepository_findByIdOrNull should return performance`() {
// given
val now = LocalDateTime.now(ZoneOffset.UTC)
val now = OffsetDateTime.now(ZoneOffset.UTC)
val samplePerformance = Performance(
title = "test title",
date = now,
Expand All @@ -59,7 +59,7 @@ class PerformanceRepositoryTest(
@Test
fun `PerformanceRepository_findAll should return list of performances`() {
// given
val now = LocalDateTime.now(ZoneOffset.UTC)
val now = OffsetDateTime.now(ZoneOffset.UTC)
val samplePerformance = Performance(
title = "test title",
date = now,
Expand All @@ -80,7 +80,7 @@ class PerformanceRepositoryTest(
@Test
fun `PerformanceRepository_delete should delete performance`() {
// given
val now = LocalDateTime.now(ZoneOffset.UTC)
val now = OffsetDateTime.now(ZoneOffset.UTC)
val samplePerformance = Performance(
title = "test title",
date = now,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.group4.ticketingservice

import com.group4.ticketingservice.config.ClockConfig
import com.group4.ticketingservice.config.GsonConfig
import com.group4.ticketingservice.config.WebConfig
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.context.annotation.Import
import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.content
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status

@AutoConfigureMockMvc
@Import(ClockConfig::class, GsonConfig::class, WebConfig::class)
class TimeE2ETest @Autowired constructor(
private val mockMvc: MockMvc
) : AbstractIntegrationTest() {

@Test
fun `All API only returns OffsetDateTime in UTC without any offset info`() {
val performanceCreateRequest = "{\"title\":\"test title\"," +
"\"date\":\"2022-09-01T21:00:00.001+09:00\"," +
"\"bookingStartTime\":\"2022-09-01T22:00:00.001+09:00\"," +
"\"bookingEndTime\":\"2022-09-01T23:00:00.001+09:00\"," +
"\"maxAttendees\":10}"

mockMvc.perform(
post("/performances")
.contentType(MediaType.APPLICATION_JSON)
.content(performanceCreateRequest)
)
.andExpect(status().isOk)
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.date").value("2022-09-01T12:00:00.001Z"))
.andExpect(jsonPath("$.bookingStartTime").value("2022-09-01T13:00:00.001Z"))
.andExpect(jsonPath("$.bookingEndTime").value("2022-09-01T14:00:00.001Z"))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package com.group4.ticketingservice.booking

import com.group4.ticketingservice.AbstractIntegrationTest
import com.group4.ticketingservice.config.ClockConfig
import com.group4.ticketingservice.entity.Booking
import com.group4.ticketingservice.entity.Performance
import com.group4.ticketingservice.entity.User
import com.group4.ticketingservice.repository.BookingRepository
import com.group4.ticketingservice.repository.PerformanceRepository
import com.group4.ticketingservice.repository.UserRepository
import com.group4.ticketingservice.utils.Authority
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Import
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import java.time.Clock
import java.time.OffsetDateTime

@Import(ClockConfig::class)
class BookingRepositoryTest @Autowired constructor(
val userRepository: UserRepository,
val performanceRepository: PerformanceRepository,
val bookingRepository: BookingRepository,
clock: Clock
) : AbstractIntegrationTest() {

object testFields {
const val testName = "minjun"
const val testUserName = "minjun3021@qwer.com"
const val password = "1234"
}
private val sampleUser = User(
name = testFields.testName,
email = testFields.testUserName,
password = BCryptPasswordEncoder().encode(testFields.password),
authority = Authority.USER
)
private val samplePerformance = Performance(
title = "test title",
date = OffsetDateTime.now(clock),
bookingEndTime = OffsetDateTime.now(clock),
bookingStartTime = OffsetDateTime.now(clock),
maxAttendees = 10
)
private val sampleBooking = Booking(
user = sampleUser,
performance = samplePerformance,
bookedAt = OffsetDateTime.now(clock)
)

@Test
fun `BookingRepository_save without mocked clock and OffsetDateTime should return savedBooking`() {
// given
val samplePerformance = Performance(
title = "test title 2",
date = OffsetDateTime.now(),
bookingEndTime = OffsetDateTime.now(),
bookingStartTime = OffsetDateTime.now(),
maxAttendees = 10
)
val sampleBooking = Booking(
user = sampleUser,
performance = samplePerformance,
bookedAt = OffsetDateTime.now()
)
userRepository.save(sampleUser)
performanceRepository.save(samplePerformance)

// when
val savedBooking = bookingRepository.save(sampleBooking)

// then
assertThat(savedBooking).isEqualTo(sampleBooking)
}

@Test
fun `BookingRepository_save with mocked UTC clock and OffsetDateTime should return savedBooking`() {
// given
userRepository.save(sampleUser)
performanceRepository.save(samplePerformance)

// when
val savedBooking = bookingRepository.save(sampleBooking)

// then
assertThat(savedBooking).isEqualTo(sampleBooking)
}
}
5 changes: 4 additions & 1 deletion src/integrationTest/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
spring.jpa.hibernate.ddl-auto=create
spring.jpa.hibernate.ddl-auto=update
spring.jpa.generate-ddl=true
time-travel.enabled=true
time-travel.instant=2022-09-01T12:00:00.001Z

ticketing.jwt.secret=d2VhcmVuYXZ5c3dkZXZlbG9wZXJzLmFuZGlhbW1pbmp1bjMwMjE=
ticketing.jwt.expiration-hours=24
Expand Down
29 changes: 29 additions & 0 deletions src/main/kotlin/com/group4/ticketingservice/config/ClockConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.group4.ticketingservice.config

import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.time.Clock
import java.time.Instant
import java.time.ZoneId

@Configuration
class ClockConfig {
@Value("\${time-travel.instant:null}")
private val timeTravelInstant: String? = null

private val timeTravelZone: String = "UTC"

@Bean
@ConditionalOnProperty(value = ["time-travel.enabled"], havingValue = "false", matchIfMissing = true)
fun defaultClock(): Clock {
return Clock.systemDefaultZone()
}

@Bean
@ConditionalOnProperty(value = ["time-travel.enabled"], havingValue = "true")
fun clock(): Clock {
return Clock.fixed(Instant.parse(timeTravelInstant), ZoneId.of(timeTravelZone))
}
}
20 changes: 20 additions & 0 deletions src/main/kotlin/com/group4/ticketingservice/config/GsonConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.group4.ticketingservice.config

import com.google.gson.FieldNamingPolicy
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.group4.ticketingservice.util.DateTimeConverter
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.time.OffsetDateTime

@Configuration
class GsonConfig {
@Bean
fun gson(): Gson {
return GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.registerTypeAdapter(OffsetDateTime::class.java, DateTimeConverter())
.create()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ import org.springframework.data.jpa.repository.config.EnableJpaAuditing

@Configuration
@EnableJpaAuditing
public class JpaAuditingConfig
class JpaAuditingConfig
16 changes: 16 additions & 0 deletions src/main/kotlin/com/group4/ticketingservice/config/WebConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.group4.ticketingservice.config

import com.google.gson.Gson
import org.springframework.context.annotation.Configuration
import org.springframework.http.converter.HttpMessageConverter
import org.springframework.http.converter.json.GsonHttpMessageConverter
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer

@Configuration
class WebConfig(private val gson: Gson) : WebMvcConfigurer {
override fun configureMessageConverters(converters: MutableList<HttpMessageConverter<*>?>) {
val gsonHttpMessageConverter = GsonHttpMessageConverter()
gsonHttpMessageConverter.gson = gson
converters.add(gsonHttpMessageConverter)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import java.lang.IllegalArgumentException

@RestController
@RequestMapping("/users")
Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/com/group4/ticketingservice/dto/BookingDto.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.group4.ticketingservice.dto

import java.time.LocalDateTime
import java.time.OffsetDateTime

data class BookingCreateRequest(
val performanceId: Long,
Expand All @@ -19,5 +19,5 @@ data class BookingResponse(
val id: Long,
val performanceId: Long,
val userId: Long,
val bookedAt: LocalDateTime
val bookedAt: OffsetDateTime
)
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package com.group4.ticketingservice.dto

import java.time.LocalDateTime
import java.time.OffsetDateTime

data class PerformanceCreateRequest(
val title: String,
val date: LocalDateTime,
val bookingStartTime: LocalDateTime,
val bookingEndTime: LocalDateTime,
val date: OffsetDateTime,
val bookingStartTime: OffsetDateTime,
val bookingEndTime: OffsetDateTime,
val maxAttendees: Int
)

Expand All @@ -17,8 +17,8 @@ data class PerformanceDeleteRequest(
data class PerformanceResponse(
val id: Long,
val title: String,
val date: LocalDateTime,
val bookingStartTime: LocalDateTime,
val bookingEndTime: LocalDateTime,
val date: OffsetDateTime,
val bookingStartTime: OffsetDateTime,
val bookingEndTime: OffsetDateTime,
val maxAttendees: Int
)
4 changes: 2 additions & 2 deletions src/main/kotlin/com/group4/ticketingservice/entity/Booking.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import jakarta.persistence.GenerationType
import jakarta.persistence.Id
import jakarta.persistence.JoinColumn
import jakarta.persistence.ManyToOne
import java.time.LocalDateTime
import java.time.OffsetDateTime

@Entity
class Booking(
Expand All @@ -24,5 +24,5 @@ class Booking(
var performance: Performance,

@Column(nullable = false)
var bookedAt: LocalDateTime = LocalDateTime.now()
var bookedAt: OffsetDateTime
)
Loading