diff --git a/docker-compose.yml b/docker-compose.yml index ee8610c8..0dc8219d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -39,7 +39,6 @@ services: JWT_SECRET: d2VhcmVuYXZ5c3dkZXZlbG9wZXJzLmFuZGlhbW1pbmp1bjMwMjE= JWT_EXPIRATION_HOURS: 24 JWT_ISSUER: minjun - depends_on: db: condition: service_healthy diff --git a/docs/open-api.json b/docs/open-api.json new file mode 100644 index 00000000..e69de29b diff --git a/docs/open-api.yaml b/docs/open-api.yaml index 871278bf..e1439494 100644 --- a/docs/open-api.yaml +++ b/docs/open-api.yaml @@ -64,67 +64,6 @@ paths: responses: "200": description: OK - /events/{id}: - get: - tags: - - event-controller - operationId: getEvent - parameters: - - name: id - in: path - required: true - schema: - type: integer - format: int64 - responses: - "200": - description: OK - content: - '*/*': - schema: - $ref: '#/components/schemas/EventResponse' - put: - tags: - - event-controller - operationId: updateEvent - parameters: - - name: id - in: path - required: true - schema: - type: integer - format: int64 - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/EventCreateRequest' - required: true - responses: - "200": - description: OK - content: - '*/*': - schema: - $ref: '#/components/schemas/EventResponse' - delete: - tags: - - event-controller - operationId: deleteEvent - parameters: - - name: id - in: path - required: true - schema: - type: integer - format: int64 - responses: - "200": - description: OK - content: - '*/*': - schema: - $ref: '#/components/schemas/Unit' /users/signup: post: tags: @@ -161,24 +100,6 @@ paths: '*/*': schema: $ref: '#/components/schemas/ReservationResponse' - /events: - post: - tags: - - event-controller - operationId: createEvent - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/EventCreateRequest' - required: true - responses: - "200": - description: OK - content: - '*/*': - schema: - $ref: '#/components/schemas/EventResponse' /bookmarks: get: tags: @@ -195,12 +116,12 @@ paths: tags: - bookmark-controller operationId: addBookmark - parameters: - - name: boardFormDto - in: query + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/BookmarkFromdto' required: true - schema: - $ref: '#/components/schemas/BookmarkFromdto' responses: "200": description: OK @@ -234,6 +155,25 @@ paths: '*/*': schema: type: object + /events/{id}: + get: + tags: + - event-controller + operationId: getEvent + parameters: + - name: id + in: path + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: OK + content: + '*/*': + schema: + $ref: '#/components/schemas/EventResponse' /events/: get: tags: @@ -327,56 +267,6 @@ components: bookedAt: type: string format: date-time - EventCreateRequest: - required: - - date - - maxAttendees - - reservationEndTime - - reservationStartTime - - title - type: object - properties: - title: - type: string - date: - type: string - format: date-time - reservationStartTime: - type: string - format: date-time - reservationEndTime: - type: string - format: date-time - maxAttendees: - type: integer - format: int32 - EventResponse: - required: - - date - - id - - maxAttendees - - reservationEndTime - - reservationStartTime - - title - type: object - properties: - id: - type: integer - format: int64 - title: - type: string - date: - type: string - format: date-time - reservationStartTime: - type: string - format: date-time - reservationEndTime: - type: string - format: date-time - maxAttendees: - type: integer - format: int32 SignUpRequest: required: - email @@ -410,26 +300,43 @@ components: ReservationCreateRequest: required: - eventId - - userId type: object properties: eventId: type: integer format: int64 - userId: - type: integer - format: int64 BookmarkFromdto: required: - - show_id - - user_id + - event_id type: object properties: - user_id: + event_id: type: integer format: int32 - show_id: + EventResponse: + required: + - date + - id + - maxAttendees + - reservationEndTime + - reservationStartTime + - title + type: object + properties: + id: + type: integer + format: int64 + title: + type: string + date: + type: string + format: date-time + reservationStartTime: + type: string + format: date-time + reservationEndTime: + type: string + format: date-time + maxAttendees: type: integer format: int32 - Unit: - type: object diff --git a/src/integrationTest/kotlin/com/group4/ticketingservice/Bookmark/BookmarkRepositoryTest.kt b/src/integrationTest/kotlin/com/group4/ticketingservice/Bookmark/BookmarkRepositoryTest.kt index e5194e65..79fb1ae3 100644 --- a/src/integrationTest/kotlin/com/group4/ticketingservice/Bookmark/BookmarkRepositoryTest.kt +++ b/src/integrationTest/kotlin/com/group4/ticketingservice/Bookmark/BookmarkRepositoryTest.kt @@ -2,61 +2,86 @@ package com.group4.ticketingservice.Bookmark import com.group4.ticketingservice.AbstractIntegrationTest import com.group4.ticketingservice.entity.Bookmark +import com.group4.ticketingservice.entity.Event +import com.group4.ticketingservice.entity.User import com.group4.ticketingservice.repository.BookmarkRepository +import com.group4.ticketingservice.repository.EventRepository +import com.group4.ticketingservice.repository.UserRepository +import com.group4.ticketingservice.utils.Authority import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Assertions.assertInstanceOf import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.data.repository.findByIdOrNull +import org.springframework.transaction.annotation.Transactional +import java.time.Clock +import java.time.OffsetDateTime class BookmarkRepositoryTest( - @Autowired val bookmarkRepository: BookmarkRepository + @Autowired val bookmarkRepository: BookmarkRepository, + @Autowired val userRepository: UserRepository, + @Autowired val eventRepository: EventRepository, + @Autowired private val clock: Clock ) : AbstractIntegrationTest() { + val sampleUser = User( + name = "james", + email = "james@example.com", + password = "12345678", + authority = Authority.USER + ) + + private val sampleEvent: Event = Event( + title = "test title", + date = OffsetDateTime.now(), + reservationEndTime = OffsetDateTime.now(), + reservationStartTime = OffsetDateTime.now(), + maxAttendees = 10 + ) + + private val sampleBookmark = Bookmark( + user = sampleUser, + event = sampleEvent + ) + @Test fun `bookmarkRepository_save should return savedBookmark`() { // given - val sampleBookmark = Bookmark( - user_id = 1, - show_id = 1 - ) + val savedUser = userRepository.save(sampleUser) + val savedEvent = eventRepository.save(sampleEvent) + val requestBookmark = Bookmark(user = savedUser, event = savedEvent) // when - val savedBookmark = bookmarkRepository.save(sampleBookmark) + val savedBookmark = bookmarkRepository.save(requestBookmark) // then - assertThat(savedBookmark).isEqualTo(sampleBookmark) + assertThat(savedBookmark).isEqualTo(requestBookmark) } @Test - fun `bookmarkRepository_findByIdOrNull should return foundBookmark`() { + fun `bookmarkRepository_findByIdAndUser should return foundBookmark`() { // given - val sampleBookmark = Bookmark( - user_id = 1, - show_id = 1 - ) - - val savedBookmark = bookmarkRepository.save(sampleBookmark) + val savedUser = userRepository.save(sampleUser) + val savedEvent = eventRepository.save(sampleEvent) + val savedBookmark = bookmarkRepository.save(Bookmark(user = savedUser, event = savedEvent)) // when - val foundBookmark = bookmarkRepository.findByIdOrNull(savedBookmark.id?.toLong()) + val foundBookmark = bookmarkRepository.findByIdAndUserId(savedBookmark.id!!, savedUser.id!!) // then - assert(savedBookmark.id == foundBookmark?.id) + assert(savedBookmark.id == foundBookmark.id) } @Test - fun `bookmarkRepository_deleteById should delete requestedBookmark`() { + @Transactional + fun `bookmarkRepository_deleteByIdAndUser should delete requestedBookmark`() { // given - val sampleBookmark = Bookmark( - user_id = 1, - show_id = 1 - ) - - val savedBookmark = bookmarkRepository.save(sampleBookmark) + val savedUser = userRepository.save(sampleUser) + val savedEvent = eventRepository.save(sampleEvent) + val savedBookmark = bookmarkRepository.save(Bookmark(user = savedUser, event = savedEvent)) // when - savedBookmark.id?.let { bookmarkRepository.deleteById(it.toLong()) } + bookmarkRepository.deleteByIdAndUserId(savedBookmark.id!!, savedUser.id!!) // then val deletedBookmark = bookmarkRepository.findByIdOrNull(savedBookmark.id?.toLong()) @@ -65,17 +90,14 @@ class BookmarkRepositoryTest( } @Test - fun `bookmarkRepository_findAll should return list of Bookmarks`() { + fun `bookmarkRepository_findByUser should return list of Bookmarks`() { // given - val sampleBookmark = Bookmark( - user_id = 1, - show_id = 1 - ) - - bookmarkRepository.save(sampleBookmark) + val savedUser = userRepository.save(sampleUser) + val savedEvent = eventRepository.save(sampleEvent) + bookmarkRepository.save(Bookmark(user = savedUser, event = savedEvent)) // when - val listofBookmarks = bookmarkRepository.findAll() + val listofBookmarks = bookmarkRepository.findByUserId(savedUser.id!!) // then assertInstanceOf(ArrayList::class.java, listofBookmarks) diff --git a/src/integrationTest/kotlin/com/group4/ticketingservice/Event/EventRepositoryTest.kt b/src/integrationTest/kotlin/com/group4/ticketingservice/Event/EventRepositoryTest.kt index b06b6d9e..986aa349 100644 --- a/src/integrationTest/kotlin/com/group4/ticketingservice/Event/EventRepositoryTest.kt +++ b/src/integrationTest/kotlin/com/group4/ticketingservice/Event/EventRepositoryTest.kt @@ -1,20 +1,39 @@ package com.group4.ticketingservice.Event import com.group4.ticketingservice.AbstractIntegrationTest +import com.group4.ticketingservice.Reservation.ReservationTest import com.group4.ticketingservice.entity.Event +import com.group4.ticketingservice.entity.User import com.group4.ticketingservice.repository.EventRepository +import com.group4.ticketingservice.repository.UserRepository +import com.group4.ticketingservice.utils.Authority import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Assertions.assertInstanceOf +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.data.repository.findByIdOrNull +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder import java.time.Duration.ofHours import java.time.OffsetDateTime import java.time.ZoneOffset -class EventRepositoryTest( - @Autowired val eventRepository: EventRepository +class EventRepositoryTest @Autowired constructor( + @Autowired val eventRepository: EventRepository, + @Autowired val userRepository: UserRepository ) : AbstractIntegrationTest() { + + val sampleUser = User( + name = ReservationTest.testFields.testName, + email = ReservationTest.testFields.testUserName, + password = BCryptPasswordEncoder().encode(ReservationTest.testFields.password), + authority = Authority.USER + ) + + @BeforeEach fun saveUser() { + userRepository.save(sampleUser) + } + @Test fun `EventRepository_save should return savedEvent`() { // given @@ -25,6 +44,7 @@ class EventRepositoryTest( reservationEndTime = now + ofHours(2), reservationStartTime = now + ofHours(1), maxAttendees = 10 + ) // when @@ -44,6 +64,7 @@ class EventRepositoryTest( reservationEndTime = now + ofHours(2), reservationStartTime = now + ofHours(1), maxAttendees = 10 + ) val savedEvent = eventRepository.save(sampleEvent) @@ -66,6 +87,7 @@ class EventRepositoryTest( reservationEndTime = now + ofHours(2), reservationStartTime = now + ofHours(1), maxAttendees = 10 + ) eventRepository.save(sampleEvent) @@ -87,6 +109,7 @@ class EventRepositoryTest( reservationEndTime = now + ofHours(2), reservationStartTime = now + ofHours(1), maxAttendees = 10 + ) val savedEvent = eventRepository.save(sampleEvent) diff --git a/src/integrationTest/kotlin/com/group4/ticketingservice/Reservation/ReservationRepositoryTest.kt b/src/integrationTest/kotlin/com/group4/ticketingservice/Reservation/ReservationRepositoryTest.kt index 64505a6b..f2220565 100644 --- a/src/integrationTest/kotlin/com/group4/ticketingservice/Reservation/ReservationRepositoryTest.kt +++ b/src/integrationTest/kotlin/com/group4/ticketingservice/Reservation/ReservationRepositoryTest.kt @@ -14,15 +14,13 @@ 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 ReservationRepositoryTest @Autowired constructor( val userRepository: UserRepository, val eventRepository: EventRepository, - val reservationRepository: ReservationRepository, - clock: Clock + val reservationRepository: ReservationRepository ) : AbstractIntegrationTest() { object testFields { @@ -38,15 +36,16 @@ class ReservationRepositoryTest @Autowired constructor( ) private val sampleEvent = Event( title = "test title", - date = OffsetDateTime.now(clock), - reservationEndTime = OffsetDateTime.now(clock), - reservationStartTime = OffsetDateTime.now(clock), + date = OffsetDateTime.now(), + reservationEndTime = OffsetDateTime.now(), + reservationStartTime = OffsetDateTime.now(), maxAttendees = 10 + ) private val sampleReservation = Reservation( user = sampleUser, event = sampleEvent, - bookedAt = OffsetDateTime.now(clock) + bookedAt = OffsetDateTime.now() ) @Test @@ -58,6 +57,7 @@ class ReservationRepositoryTest @Autowired constructor( reservationEndTime = OffsetDateTime.now(), reservationStartTime = OffsetDateTime.now(), maxAttendees = 10 + ) val sampleReservation = Reservation( user = sampleUser, diff --git a/src/integrationTest/kotlin/com/group4/ticketingservice/Reservation/ReservationTest.kt b/src/integrationTest/kotlin/com/group4/ticketingservice/Reservation/ReservationTest.kt index 9c9da350..5b8b8661 100644 --- a/src/integrationTest/kotlin/com/group4/ticketingservice/Reservation/ReservationTest.kt +++ b/src/integrationTest/kotlin/com/group4/ticketingservice/Reservation/ReservationTest.kt @@ -53,7 +53,7 @@ class ReservationTest @Autowired constructor( eventRepository.save(sampleEvent) val event = eventRepository.findById(1).get() - event.availableAttendees = 100 + event.currentReservationCount = 0 eventRepository.save(event) } diff --git a/src/integrationTest/kotlin/com/group4/ticketingservice/TimeE2ETest.kt b/src/integrationTest/kotlin/com/group4/ticketingservice/TimeE2ETest.kt index d6f90839..d7eb0883 100644 --- a/src/integrationTest/kotlin/com/group4/ticketingservice/TimeE2ETest.kt +++ b/src/integrationTest/kotlin/com/group4/ticketingservice/TimeE2ETest.kt @@ -1,14 +1,25 @@ package com.group4.ticketingservice +import com.google.gson.GsonBuilder +import com.google.gson.JsonObject +import com.group4.ticketingservice.TimeE2ETest.testFields.password +import com.group4.ticketingservice.TimeE2ETest.testFields.testName +import com.group4.ticketingservice.TimeE2ETest.testFields.testUserName 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 com.group4.ticketingservice.dto.SignInRequest +import com.group4.ticketingservice.entity.User +import com.group4.ticketingservice.repository.UserRepository +import com.group4.ticketingservice.utils.Authority +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach 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 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 @@ -17,21 +28,49 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status @AutoConfigureMockMvc @Import(ClockConfig::class, GsonConfig::class, WebConfig::class) class TimeE2ETest @Autowired constructor( + val userRepository: UserRepository, private val mockMvc: MockMvc ) : AbstractIntegrationTest() { + object testFields { + const val testUserId = 1 + const val testName = "minjun" + const val testUserName = "minjun3021@qwer.com" + const val password = "1234" + } + + val sampleSignInRequest = SignInRequest().apply { + email = testUserName + password = password + } + val sampleUser = User( + name = testName, + email = testUserName, + password = password, + authority = Authority.USER + ) + + @BeforeEach + fun addUser() { + userRepository.save(sampleUser) + } + + @AfterEach + fun removeUser() { + userRepository.deleteAll() + } - @Test fun `All API only returns OffsetDateTime in UTC without any offset info`() { val eventCreateRequest = "{\"title\":\"test title\"," + "\"date\":\"2022-09-01T21:00:00.001+09:00\"," + "\"reservationStartTime\":\"2022-09-01T22:00:00.001+09:00\"," + "\"reservationEndTime\":\"2022-09-01T23:00:00.001+09:00\"," + "\"maxAttendees\":10}" - + val jwt = getJwt() mockMvc.perform( post("/events") .contentType(MediaType.APPLICATION_JSON) .content(eventCreateRequest) + .header("Authorization", jwt) ) .andExpect(status().isOk) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) @@ -39,4 +78,16 @@ class TimeE2ETest @Autowired constructor( .andExpect(jsonPath("$.reservationStartTime").value("2022-09-01T13:00:00.001Z")) .andExpect(jsonPath("$.reservationEndTime").value("2022-09-01T14:00:00.001Z")) } + + fun getJwt(): String { + val gson = GsonBuilder().create() + + val result = mockMvc.perform( + MockMvcRequestBuilders.post("/users/signin") + .content(gson.toJson(sampleSignInRequest).toString()) + .contentType(MediaType.APPLICATION_JSON) + ).andReturn() + val jwt = gson.fromJson(result.response.contentAsString, JsonObject::class.java) + return jwt.get("Authorization").asString + } } diff --git a/src/integrationTest/kotlin/com/group4/ticketingservice/User/UserControllerTest.kt b/src/integrationTest/kotlin/com/group4/ticketingservice/User/UserControllerTest.kt index 7897a8cd..13fce728 100644 --- a/src/integrationTest/kotlin/com/group4/ticketingservice/User/UserControllerTest.kt +++ b/src/integrationTest/kotlin/com/group4/ticketingservice/User/UserControllerTest.kt @@ -38,6 +38,7 @@ class UserControllerTest : AbstractIntegrationTest() { private lateinit var tokenProvider: TokenProvider object testFields { + const val testUserId = 1 const val testName = "minjun" const val testUserName = "minjun3021@qwer.com" const val password = "1234" @@ -111,7 +112,7 @@ class UserControllerTest : AbstractIntegrationTest() { } @Test - fun `GET_api_users_access_token_info should return username with HTTPStatus 200 OK`() { + fun `GET_api_users_access_token_info should return expires_in with HTTPStatus 200 OK`() { val jwt = getJwt() val resultActions: ResultActions = @@ -120,7 +121,8 @@ class UserControllerTest : AbstractIntegrationTest() { .header("Authorization", jwt) ) resultActions.andExpect(status().isOk) - .andExpect(MockMvcResultMatchers.jsonPath("$.username").value(testFields.testUserName)) + .andExpect(MockMvcResultMatchers.jsonPath("$.expires_in").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$.userId").exists()) } @Test diff --git a/src/main/kotlin/com/group4/ticketingservice/controller/BookmarkController.kt b/src/main/kotlin/com/group4/ticketingservice/controller/BookmarkController.kt index 114e5eaa..3893c637 100644 --- a/src/main/kotlin/com/group4/ticketingservice/controller/BookmarkController.kt +++ b/src/main/kotlin/com/group4/ticketingservice/controller/BookmarkController.kt @@ -6,11 +6,13 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.HttpHeaders import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity +import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.web.bind.MethodArgumentNotValidException import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable 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 @@ -20,8 +22,8 @@ class BookmarkController @Autowired constructor(val bookmarkService: BookmarkSer // 북마크 등록 @PostMapping - fun addBookmark(boardFormDto: BookmarkFromdto): ResponseEntity { - val savedBookmarkId = bookmarkService.create(boardFormDto) + fun addBookmark(@AuthenticationPrincipal userId: Long, @RequestBody boardFormDto: BookmarkFromdto): ResponseEntity { + val savedBookmarkId = bookmarkService.create(userId, boardFormDto) val headers = HttpHeaders() headers.set("Content-Location", "/bookmark/%d".format(savedBookmarkId)) return ResponseEntity.status(HttpStatus.CREATED).headers(headers).body(savedBookmarkId) @@ -29,9 +31,9 @@ class BookmarkController @Autowired constructor(val bookmarkService: BookmarkSer // 특정 북마크 조회하기 @GetMapping("/{id}") - fun getBookmark(@PathVariable id: Int): ResponseEntity { + fun getBookmark(@AuthenticationPrincipal userId: Long, @PathVariable id: Int): ResponseEntity { try { - val foundBookmark = bookmarkService.get(id) + val foundBookmark = bookmarkService.get(userId, id) return ResponseEntity.status(HttpStatus.OK).body(foundBookmark ?: "null") } catch (e: MethodArgumentNotValidException) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).build() @@ -40,14 +42,15 @@ class BookmarkController @Autowired constructor(val bookmarkService: BookmarkSer // 북마크 삭제 @DeleteMapping("/{id}") - fun deleteBookmark(@PathVariable id: Int): ResponseEntity { - bookmarkService.delete(id) + fun deleteBookmark(@AuthenticationPrincipal userId: Long, @PathVariable id: Int): ResponseEntity { + bookmarkService.delete(userId, id) return ResponseEntity.status(HttpStatus.NO_CONTENT).build() } - // 전체사용자 북마크 목록 + // 로그인한 사용자의 북마크 목록 @GetMapping() - fun getBookmarks(): ResponseEntity { - return ResponseEntity.status(HttpStatus.OK).body(bookmarkService.getList()) + fun getBookmarks(@AuthenticationPrincipal userId: Long): ResponseEntity { + val bookmarks = bookmarkService.getList(userId) + return ResponseEntity.status(HttpStatus.OK).body(bookmarks) } } diff --git a/src/main/kotlin/com/group4/ticketingservice/controller/EventController.kt b/src/main/kotlin/com/group4/ticketingservice/controller/EventController.kt index 2de3ad58..731af15f 100644 --- a/src/main/kotlin/com/group4/ticketingservice/controller/EventController.kt +++ b/src/main/kotlin/com/group4/ticketingservice/controller/EventController.kt @@ -1,17 +1,12 @@ package com.group4.ticketingservice.controller -import com.group4.ticketingservice.dto.EventCreateRequest import com.group4.ticketingservice.dto.EventResponse import com.group4.ticketingservice.service.EventService import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.GetMapping 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.RequestMapping import org.springframework.web.bind.annotation.RestController @@ -21,26 +16,6 @@ class EventController @Autowired constructor( val eventService: EventService ) { - @PostMapping - fun createEvent(@RequestBody request: EventCreateRequest): ResponseEntity { - val event = eventService.createEvent( - request.title, - request.date, - request.reservationStartTime, - request.reservationEndTime, - request.maxAttendees - ) - val response = EventResponse( - id = event.id!!, - title = event.title, - date = event.date, - reservationStartTime = event.reservationStartTime, - reservationEndTime = event.reservationEndTime, - maxAttendees = event.maxAttendees - ) - return ResponseEntity.status(HttpStatus.OK).body(response) - } - @GetMapping("/{id}") fun getEvent(@PathVariable id: Long): ResponseEntity { return eventService.getEvent(id)?.let { @@ -78,34 +53,4 @@ class EventController @Autowired constructor( ResponseEntity.status(HttpStatus.OK).body(response) } } - - @PutMapping("/{id}") - fun updateEvent( - @PathVariable id: Long, - @RequestBody request: EventCreateRequest - ): ResponseEntity { - val event = eventService.updateEvent( - id, - request.title, - request.date, - request.reservationStartTime, - request.reservationEndTime, - request.maxAttendees - ) - val response = EventResponse( - id = event.id!!, - title = event.title, - date = event.date, - reservationStartTime = event.reservationStartTime, - reservationEndTime = event.reservationEndTime, - maxAttendees = event.maxAttendees - ) - return ResponseEntity.status(HttpStatus.OK).body(response) - } - - @DeleteMapping("/{id}") - fun deleteEvent(@PathVariable id: Long): ResponseEntity { - eventService.deleteEvent(id) - return ResponseEntity.status(HttpStatus.NO_CONTENT).build() - } } diff --git a/src/main/kotlin/com/group4/ticketingservice/controller/ReservationController.kt b/src/main/kotlin/com/group4/ticketingservice/controller/ReservationController.kt index 0c1fc242..8d0b5df5 100644 --- a/src/main/kotlin/com/group4/ticketingservice/controller/ReservationController.kt +++ b/src/main/kotlin/com/group4/ticketingservice/controller/ReservationController.kt @@ -6,6 +6,7 @@ import com.group4.ticketingservice.dto.ReservationUpdateRequest import com.group4.ticketingservice.entity.Reservation import com.group4.ticketingservice.service.ReservationService import org.springframework.http.ResponseEntity +import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable @@ -19,10 +20,10 @@ import org.springframework.web.bind.annotation.RestController @RequestMapping("/reservations") class ReservationController(val reservationService: ReservationService) { @PostMapping - fun createReservation(@RequestBody request: ReservationCreateRequest): ResponseEntity { + fun createReservation(@AuthenticationPrincipal userId: Long, @RequestBody request: ReservationCreateRequest): ResponseEntity { val reservation: Reservation = reservationService.createReservation( request.eventId, - request.userId + userId ) val response = ReservationResponse( id = reservation.id!!, @@ -61,8 +62,8 @@ class ReservationController(val reservationService: ReservationService) { } @DeleteMapping("/{id}") - fun deleteReservation(@PathVariable id: Long): ResponseEntity { - reservationService.deleteReservation(id) + fun deleteReservation(@AuthenticationPrincipal userId: Long, @PathVariable id: Long): ResponseEntity { + reservationService.deleteReservation(userId, id) return ResponseEntity.noContent().build() } } diff --git a/src/main/kotlin/com/group4/ticketingservice/controller/UserController.kt b/src/main/kotlin/com/group4/ticketingservice/controller/UserController.kt index 5b2a2d74..92ce2b94 100644 --- a/src/main/kotlin/com/group4/ticketingservice/controller/UserController.kt +++ b/src/main/kotlin/com/group4/ticketingservice/controller/UserController.kt @@ -43,11 +43,11 @@ class UserController( * @author MinJun Kim */ @GetMapping("/access_token_info") - fun getAccessTokenInfo(@AuthenticationPrincipal username: String): ResponseEntity> { + fun getAccessTokenInfo(@AuthenticationPrincipal userId: Long): ResponseEntity> { val jwt = SecurityContextHolder.getContext().authentication.credentials.toString() val expiresInMillis = tokenProvider.parseTokenExpirationTime(jwt) val map = mapOf( - "username" to username, + "userId" to userId, "expires_in" to expiresInMillis ) return ResponseEntity.ok(map) diff --git a/src/main/kotlin/com/group4/ticketingservice/dto/BookmarkFromdto.kt b/src/main/kotlin/com/group4/ticketingservice/dto/BookmarkFromdto.kt index edb98bb1..87f24297 100644 --- a/src/main/kotlin/com/group4/ticketingservice/dto/BookmarkFromdto.kt +++ b/src/main/kotlin/com/group4/ticketingservice/dto/BookmarkFromdto.kt @@ -1,6 +1,5 @@ package com.group4.ticketingservice.dto data class BookmarkFromdto( - var user_id: Int, - var show_id: Int + var event_id: Int ) diff --git a/src/main/kotlin/com/group4/ticketingservice/dto/ReservationDto.kt b/src/main/kotlin/com/group4/ticketingservice/dto/ReservationDto.kt index c302e6b6..47a5b8ab 100644 --- a/src/main/kotlin/com/group4/ticketingservice/dto/ReservationDto.kt +++ b/src/main/kotlin/com/group4/ticketingservice/dto/ReservationDto.kt @@ -3,8 +3,7 @@ package com.group4.ticketingservice.dto import java.time.OffsetDateTime data class ReservationCreateRequest( - val eventId: Long, - val userId: Long + val eventId: Long ) data class ReservationUpdateRequest( diff --git a/src/main/kotlin/com/group4/ticketingservice/entity/Bookmark.kt b/src/main/kotlin/com/group4/ticketingservice/entity/Bookmark.kt index 9300047f..68c7408a 100644 --- a/src/main/kotlin/com/group4/ticketingservice/entity/Bookmark.kt +++ b/src/main/kotlin/com/group4/ticketingservice/entity/Bookmark.kt @@ -4,12 +4,20 @@ import jakarta.persistence.Entity import jakarta.persistence.GeneratedValue import jakarta.persistence.GenerationType import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne @Entity class Bookmark( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) var id: Int? = null, - var user_id: Int, // 사용자 식별자 - var show_id: Int // 공연 식별자 + + @ManyToOne + @JoinColumn(name = "user_id", nullable = false) + var user: User, + + @ManyToOne + @JoinColumn(name = "event_id", nullable = false) + var event: Event ) diff --git a/src/main/kotlin/com/group4/ticketingservice/entity/Event.kt b/src/main/kotlin/com/group4/ticketingservice/entity/Event.kt index 906662b4..11ffa468 100644 --- a/src/main/kotlin/com/group4/ticketingservice/entity/Event.kt +++ b/src/main/kotlin/com/group4/ticketingservice/entity/Event.kt @@ -1,13 +1,9 @@ package com.group4.ticketingservice.entity -import jakarta.persistence.Column import jakarta.persistence.Entity -import jakarta.persistence.FetchType import jakarta.persistence.GeneratedValue import jakarta.persistence.GenerationType import jakarta.persistence.Id -import jakarta.persistence.OneToMany -import jakarta.persistence.Version import java.time.OffsetDateTime @Entity @@ -20,21 +16,12 @@ class Event( var date: OffsetDateTime, - @Column(name = "reservation_start_time") var reservationStartTime: OffsetDateTime, - @Column(name = "reservation_end_time") var reservationEndTime: OffsetDateTime, - @Column(name = "max_attendees") var maxAttendees: Int, - @Column(name = "available_attendees") - var availableAttendees: Int = maxAttendees -) { - @OneToMany(fetch = FetchType.LAZY, mappedBy = "event", targetEntity = Reservation::class) - var reservations: List? = null + var currentReservationCount: Int = 0 - @Version - private val version: Long? = null -} +) diff --git a/src/main/kotlin/com/group4/ticketingservice/entity/User.kt b/src/main/kotlin/com/group4/ticketingservice/entity/User.kt index 30b826db..014c7cef 100644 --- a/src/main/kotlin/com/group4/ticketingservice/entity/User.kt +++ b/src/main/kotlin/com/group4/ticketingservice/entity/User.kt @@ -16,7 +16,7 @@ import org.springframework.security.core.userdetails.UserDetails @Entity @Table(name = "user") -class User(name: String, email: String, password: String, authority: Authority) : BaseTimeEntity(), UserDetails { +class User(name: String, email: String, password: String, authority: Authority, id: Long? = null) : BaseTimeEntity(), UserDetails { companion object { fun toDto(user: User) = UserDto( name = user.name, @@ -28,7 +28,7 @@ class User(name: String, email: String, password: String, authority: Authority) @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(nullable = false) - var id: Long? = null + var id: Long? = id @Column(nullable = false) var name: String = name diff --git a/src/main/kotlin/com/group4/ticketingservice/filter/JwtAuthenticationFilter.kt b/src/main/kotlin/com/group4/ticketingservice/filter/JwtAuthenticationFilter.kt index d3d72971..2b0364bd 100644 --- a/src/main/kotlin/com/group4/ticketingservice/filter/JwtAuthenticationFilter.kt +++ b/src/main/kotlin/com/group4/ticketingservice/filter/JwtAuthenticationFilter.kt @@ -55,7 +55,7 @@ class JwtAuthenticationFilter( authResult: Authentication ) { val user = authResult.principal as User - val jwt = tokenProvider.createToken("${user.username}:${user.role}") + val jwt = tokenProvider.createToken("${user.id}") val gson = GsonBuilder().create() val body = gson.toJson(mapOf("Authorization" to "Bearer $jwt")) response?.contentType = "application/json" diff --git a/src/main/kotlin/com/group4/ticketingservice/filter/JwtAuthorizationFilter.kt b/src/main/kotlin/com/group4/ticketingservice/filter/JwtAuthorizationFilter.kt index 62afef23..b612326b 100644 --- a/src/main/kotlin/com/group4/ticketingservice/filter/JwtAuthorizationFilter.kt +++ b/src/main/kotlin/com/group4/ticketingservice/filter/JwtAuthorizationFilter.kt @@ -23,13 +23,13 @@ class JwtAuthorizationFilter( val jwt = tokenProvider.parseBearerToken(authorizationHeader) if (tokenProvider.validateToken(jwt)) { - val (username, authority) = tokenProvider.parseUserSpecification(jwt) + val userId = tokenProvider.parseUserSpecification(jwt) val authorties = mutableListOf() - authorties.add(SimpleGrantedAuthority(authority)) + authorties.add(SimpleGrantedAuthority("USER")) val authentication = - UsernamePasswordAuthenticationToken.authenticated(username, jwt, authorties) + UsernamePasswordAuthenticationToken.authenticated(userId.toLong(), jwt, authorties) SecurityContextHolder.getContext().authentication = authentication } chain.doFilter(request, response) diff --git a/src/main/kotlin/com/group4/ticketingservice/repository/BookmarkRepository.kt b/src/main/kotlin/com/group4/ticketingservice/repository/BookmarkRepository.kt index 5428e0b9..54969dc9 100644 --- a/src/main/kotlin/com/group4/ticketingservice/repository/BookmarkRepository.kt +++ b/src/main/kotlin/com/group4/ticketingservice/repository/BookmarkRepository.kt @@ -5,4 +5,8 @@ import org.springframework.data.jpa.repository.JpaRepository import org.springframework.stereotype.Repository @Repository -interface BookmarkRepository : JpaRepository +interface BookmarkRepository : JpaRepository { + fun findByIdAndUserId(id: Int, userId: Long): Bookmark + fun deleteByIdAndUserId(id: Int, userId: Long) + fun findByUserId(userId: Long): List +} diff --git a/src/main/kotlin/com/group4/ticketingservice/service/BookmarkService.kt b/src/main/kotlin/com/group4/ticketingservice/service/BookmarkService.kt index 514741b2..981a698d 100644 --- a/src/main/kotlin/com/group4/ticketingservice/service/BookmarkService.kt +++ b/src/main/kotlin/com/group4/ticketingservice/service/BookmarkService.kt @@ -2,31 +2,40 @@ package com.group4.ticketingservice.service import com.group4.ticketingservice.dto.BookmarkFromdto import com.group4.ticketingservice.entity.Bookmark +import com.group4.ticketingservice.entity.Event +import com.group4.ticketingservice.entity.User import com.group4.ticketingservice.repository.BookmarkRepository -import org.modelmapper.ModelMapper +import com.group4.ticketingservice.repository.EventRepository +import com.group4.ticketingservice.repository.UserRepository import org.springframework.beans.factory.annotation.Autowired -import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service @Service class BookmarkService @Autowired constructor( - val bookmarkRepository: BookmarkRepository, - val modelMapper: ModelMapper + val userRepository: UserRepository, + val eventRepository: EventRepository, + val bookmarkRepository: BookmarkRepository ) { - fun create(bookmarkFormDto: BookmarkFromdto): Int? { - return bookmarkRepository.save(modelMapper.map(bookmarkFormDto, Bookmark::class.java)).id + fun create(userId: Long, bookmarkFormDto: BookmarkFromdto): Int? { + val user: User = userRepository.getReferenceById(userId) + + val event: Event = eventRepository.getReferenceById(bookmarkFormDto.event_id.toLong()) + + val bookmark = Bookmark(user = user, event = event) + + return bookmarkRepository.save(bookmark).id } - fun get(id: Int): Bookmark? { - return bookmarkRepository.findByIdOrNull(id.toLong()) + fun get(userId: Long, id: Int): Bookmark? { + return bookmarkRepository.findByIdAndUserId(id, userId) } - fun delete(id: Int) { - bookmarkRepository.deleteById(id.toLong()) + fun delete(userId: Long, id: Int) { + bookmarkRepository.deleteByIdAndUserId(id, userId) } - fun getList(): List { - return bookmarkRepository.findAll() + fun getList(userId: Long): List { + return bookmarkRepository.findByUserId(userId) } } diff --git a/src/main/kotlin/com/group4/ticketingservice/service/EventService.kt b/src/main/kotlin/com/group4/ticketingservice/service/EventService.kt index 627685d4..b35c969c 100644 --- a/src/main/kotlin/com/group4/ticketingservice/service/EventService.kt +++ b/src/main/kotlin/com/group4/ticketingservice/service/EventService.kt @@ -3,28 +3,11 @@ package com.group4.ticketingservice.service import com.group4.ticketingservice.entity.Event import com.group4.ticketingservice.repository.EventRepository import org.springframework.stereotype.Service -import java.time.OffsetDateTime @Service class EventService( - val eventRepository: EventRepository + private val eventRepository: EventRepository ) { - fun createEvent( - title: String, - date: OffsetDateTime, - reservationStartTime: OffsetDateTime, - reservationEndTime: OffsetDateTime, - maxAttendees: Int - ): Event { - val event = Event( - title = title, - date = date, - reservationStartTime = reservationStartTime, - reservationEndTime = reservationEndTime, - maxAttendees = maxAttendees - ) - return eventRepository.save(event) - } fun getEvent(id: Long): Event? { return eventRepository.findById(id).orElse(null) @@ -33,32 +16,4 @@ class EventService( fun getEvents(): List { return eventRepository.findAll() } - - fun updateEvent( - id: Long, - title: String, - date: OffsetDateTime, - reservationStartTime: OffsetDateTime, - reservationEndTime: OffsetDateTime, - maxAttendees: Int - ): Event { - val event = eventRepository.findById(id).orElseThrow { - IllegalArgumentException("Event not found") - } - event.title = title - event.date = date - event.reservationStartTime = reservationStartTime - event.reservationEndTime = reservationEndTime - event.maxAttendees = maxAttendees - - return eventRepository.save(event) - } - - fun deleteEvent(id: Long) { - if (eventRepository.existsById(id)) { - eventRepository.deleteById(id) - } else { - throw IllegalArgumentException("Event not found") - } - } } diff --git a/src/main/kotlin/com/group4/ticketingservice/service/ReservationService.kt b/src/main/kotlin/com/group4/ticketingservice/service/ReservationService.kt index 35088f71..3e9190ba 100644 --- a/src/main/kotlin/com/group4/ticketingservice/service/ReservationService.kt +++ b/src/main/kotlin/com/group4/ticketingservice/service/ReservationService.kt @@ -5,11 +5,11 @@ import com.group4.ticketingservice.repository.EventRepository import com.group4.ticketingservice.repository.ReservationRepository import com.group4.ticketingservice.repository.UserRepository import org.springframework.beans.factory.annotation.Autowired +import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import java.time.Clock import java.time.OffsetDateTime -import kotlin.RuntimeException @Service class ReservationService @Autowired constructor( @@ -20,15 +20,13 @@ class ReservationService @Autowired constructor( ) { @Transactional fun createReservation(eventId: Long, userId: Long): Reservation { - val user = userRepository.findById(userId).orElseThrow { - IllegalArgumentException("User not found") - } + val user = userRepository.getReferenceById(userId) val event = eventRepository.findByIdWithPesimisticLock(eventId) ?: throw RuntimeException("") - val reservation = Reservation(user = user, event = event, bookedAt = OffsetDateTime.now(clock)) + val reservation = Reservation(user = user, event = event, bookedAt = OffsetDateTime.now()) - if (event.availableAttendees > 0) { - event.availableAttendees -= 1 + if (event.maxAttendees > event.currentReservationCount) { + event.currentReservationCount += 1 eventRepository.saveAndFlush(event) reservationRepository.saveAndFlush(reservation) @@ -56,11 +54,9 @@ class ReservationService @Autowired constructor( return reservationRepository.save(reservation) } - fun deleteReservation(id: Long) { - if (reservationRepository.existsById(id)) { - reservationRepository.deleteById(id) - } else { - throw IllegalArgumentException("Reservation not found") - } + fun deleteReservation(userId: Long, id: Long) { + val reservation = reservationRepository.findByIdOrNull(id) ?: throw IllegalArgumentException("Reservation not found") + if (reservation.user.id != userId) throw IllegalArgumentException("It's not your reservation") + reservationRepository.delete(reservation) } } diff --git a/src/main/kotlin/com/group4/ticketingservice/utils/TokenProvider.kt b/src/main/kotlin/com/group4/ticketingservice/utils/TokenProvider.kt index a2ad5228..30ab1121 100644 --- a/src/main/kotlin/com/group4/ticketingservice/utils/TokenProvider.kt +++ b/src/main/kotlin/com/group4/ticketingservice/utils/TokenProvider.kt @@ -74,7 +74,7 @@ class TokenProvider( .parseClaimsJws(token) .body fun parseUserSpecification(token: String) = - getClaimsFromToken(token).subject.split(":") + getClaimsFromToken(token).subject fun parseBearerToken(header: String) = header.substring(7) fun parseTokenExpirationTime(token: String): Long { diff --git a/src/test/kotlin/com/group4/ticketingservice/bookmark/BookmarkControllerTest.kt b/src/test/kotlin/com/group4/ticketingservice/bookmark/BookmarkControllerTest.kt index fec8e34f..f3871bcd 100644 --- a/src/test/kotlin/com/group4/ticketingservice/bookmark/BookmarkControllerTest.kt +++ b/src/test/kotlin/com/group4/ticketingservice/bookmark/BookmarkControllerTest.kt @@ -1,16 +1,25 @@ package com.group4.ticketingservice.bookmark +import com.google.gson.GsonBuilder import com.group4.ticketingservice.JwtAuthorizationEntryPoint +import com.group4.ticketingservice.bookmark.BookmarkControllerTest.testFields.testUserId +import com.group4.ticketingservice.bookmark.BookmarkControllerTest.testFields.testUserName import com.group4.ticketingservice.config.SecurityConfig import com.group4.ticketingservice.controller.BookmarkController import com.group4.ticketingservice.dto.BookmarkFromdto import com.group4.ticketingservice.entity.Bookmark +import com.group4.ticketingservice.entity.Event +import com.group4.ticketingservice.entity.User import com.group4.ticketingservice.service.BookmarkService +import com.group4.ticketingservice.user.WithAuthUser +import com.group4.ticketingservice.utils.Authority import com.group4.ticketingservice.utils.TokenProvider import com.ninjasquad.springmockk.MockkBean import io.mockk.every +import io.mockk.junit5.MockKExtension import io.mockk.verify import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest import org.springframework.context.annotation.ComponentScan @@ -24,51 +33,79 @@ 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 +import java.time.OffsetDateTime +@ExtendWith(MockKExtension::class) @WebMvcTest( controllers = [BookmarkController::class], includeFilters = [ComponentScan.Filter(value = [(SecurityConfig::class), (JwtAuthorizationEntryPoint::class), (TokenProvider::class)], type = FilterType.ASSIGNABLE_TYPE)] ) -class BookmarkControllerTest(@Autowired val mockMvc: MockMvc) { +class BookmarkControllerTest( + @Autowired val mockMvc: MockMvc +) { @MockkBean private lateinit var service: BookmarkService + object testFields { + const val testUserId = 1L + const val testUserName = "james@example.com" + const val testUserRole = "USER" + const val password = "12345678" + } + + private val sampleUser = User( + name = "james", + email = "james@example.com", + password = "12345678", + authority = Authority.USER + ) + + private val sampleEvent: Event = Event( + id = 1, + title = "test title", + date = OffsetDateTime.now(), + reservationEndTime = OffsetDateTime.now(), + reservationStartTime = OffsetDateTime.now(), + maxAttendees = 10 + ) + private val sampleBookmark = Bookmark( - user_id = 1, - show_id = 1 + id = 1, + user = sampleUser, + event = sampleEvent ) + private val sampleBookmarkDto = BookmarkFromdto( - user_id = 1, - show_id = 1 + event_id = 1 ) @Test + @WithAuthUser(email = testUserName, id = testUserId) fun `POST_api_bookmark should invoke service_create`() { // given - every { service.create(sampleBookmarkDto) } returns 1 + every { service.create(testUserId, sampleBookmarkDto) } returns 1 // when mockMvc.perform( post("/bookmarks") - .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE) - .param("user_id", sampleBookmark.user_id.toString()) - .param("show_id", sampleBookmark.show_id.toString()) + .contentType(MediaType.APPLICATION_JSON) + .content(GsonBuilder().create().toJson(sampleBookmarkDto).toString()) ) // then - verify(exactly = 1) { service.create(sampleBookmarkDto) } + verify(exactly = 1) { service.create(testUserId, sampleBookmarkDto) } } @Test + @WithAuthUser(email = testUserName, id = testUserId) fun `POST_api_bookmark should return saved bookmark id with HTTP 201 Created`() { // given - every { service.create(sampleBookmarkDto) } returns 1 + every { service.create(testUserId, sampleBookmarkDto) } returns 1 // when val resultActions: ResultActions = mockMvc.perform( post("/bookmarks") - .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE) - .param("user_id", sampleBookmark.user_id.toString()) - .param("show_id", sampleBookmark.show_id.toString()) + .contentType(MediaType.APPLICATION_JSON) + .content(GsonBuilder().create().toJson(sampleBookmarkDto).toString()) ) // then @@ -77,64 +114,66 @@ class BookmarkControllerTest(@Autowired val mockMvc: MockMvc) { } @Test + @WithAuthUser(email = testUserName, id = testUserId) fun `POST_api_bookmark should return HTTP ERROR 400 for invalid parameter`() { // given - every { service.create(sampleBookmarkDto) } returns 1 + every { service.create(testUserId, sampleBookmarkDto) } returns 1 // when val resultActions: ResultActions = mockMvc.perform( post("/bookmarks") - .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE) - .param("user_id", sampleBookmark.user_id.toString()) - .param("show_id", sampleBookmark.show_id.toString()) + .contentType(MediaType.APPLICATION_JSON) ) // then - resultActions.andExpect(status().isCreated) - .andExpect(content().json("1")) + resultActions.andExpect(status().isBadRequest) } @Test + @WithAuthUser(email = testUserName, id = testUserId) fun `GET_api_bookmarks should invoke service_getList`() { // given - every { service.getList() } returns mutableListOf(sampleBookmark) + every { service.getList(testUserId) } returns mutableListOf(sampleBookmark) // when mockMvc.perform(MockMvcRequestBuilders.get("/bookmarks")) // then - verify(exactly = 1) { service.getList() } + verify(exactly = 1) { service.getList(testUserId) } } @Test + @WithAuthUser(email = testUserName, id = testUserId) fun `GET_api_bookmarks should return list of bookmarks with HTTP 200 OK`() { // given - every { service.getList() } returns mutableListOf(sampleBookmark) + every { service.getList(testUserId) } returns mutableListOf(sampleBookmark) // when val resultActions: ResultActions = mockMvc.perform(MockMvcRequestBuilders.get("/bookmarks")) // then resultActions.andExpect(status().isOk) - .andExpect(jsonPath("$[0].user_id").value(sampleBookmark.user_id)) + .andExpect(jsonPath("$[0].id").value(1)) } @Test + @WithAuthUser(email = testUserName, id = testUserId) fun `GET_api_bookmark should invoke service_get`() { // given - every { service.get(1) } returns sampleBookmark + every { service.get(testUserId, 1) } returns sampleBookmark // when mockMvc.perform(MockMvcRequestBuilders.get("/bookmarks/1")) // then - verify(exactly = 1) { service.get(1) } + verify(exactly = 1) { service.get(testUserId, 1) } } @Test + @WithAuthUser(email = testUserName, id = testUserId) fun `GET_api_bookmark should return found bookmark with HTTP 200 OK`() { // given - every { service.get(1) } returns sampleBookmark + every { service.get(testUserId, 1) } returns sampleBookmark // when val resultActions: ResultActions = mockMvc.perform(MockMvcRequestBuilders.get("/bookmarks/1")) @@ -142,14 +181,15 @@ class BookmarkControllerTest(@Autowired val mockMvc: MockMvc) { // then resultActions.andExpect(status().isOk) .andExpect(jsonPath("$.id").value(sampleBookmark.id)) - .andExpect(jsonPath("$.user_id").value(sampleBookmark.user_id)) - .andExpect(jsonPath("$.show_id").value(sampleBookmark.show_id)) + .andExpect(jsonPath("$.user.id").value(sampleBookmark.user.id)) + .andExpect(jsonPath("$.event.id").value(sampleBookmark.event.id)) } @Test + @WithAuthUser(email = testUserName, id = testUserId) fun `GET_api_bookmark should return null with HTTP 200 OK if element is not found`() { // given - every { service.get(1) } returns null + every { service.get(testUserId, 1) } returns null // when val resultActions: ResultActions = mockMvc.perform(MockMvcRequestBuilders.get("/bookmarks/1")) @@ -160,9 +200,10 @@ class BookmarkControllerTest(@Autowired val mockMvc: MockMvc) { } @Test + @WithAuthUser(email = testUserName, id = testUserId) fun `DELETE_api_bookmark_{bookmarkId} should invoke service_delete`() { // given - every { service.delete(1) } returns Unit + every { service.delete(testUserId, 1) } returns Unit // when mockMvc.perform( @@ -172,13 +213,14 @@ class BookmarkControllerTest(@Autowired val mockMvc: MockMvc) { ) // then - verify(exactly = 1) { service.delete(1) } + verify(exactly = 1) { service.delete(testUserId, 1) } } @Test + @WithAuthUser(email = testUserName, id = testUserId) fun `DELETE_api_bookmark_{bookmarkId} should return HTTP 204 No Content`() { // given - every { service.delete(1) } returns Unit + every { service.delete(testUserId, 1) } returns Unit // when val resultActions: ResultActions = mockMvc.perform( diff --git a/src/test/kotlin/com/group4/ticketingservice/bookmark/BookmarkServiceTest.kt b/src/test/kotlin/com/group4/ticketingservice/bookmark/BookmarkServiceTest.kt index 5acccf11..3e024a21 100644 --- a/src/test/kotlin/com/group4/ticketingservice/bookmark/BookmarkServiceTest.kt +++ b/src/test/kotlin/com/group4/ticketingservice/bookmark/BookmarkServiceTest.kt @@ -2,94 +2,117 @@ package com.group4.ticketingservice.bookmark import com.group4.ticketingservice.dto.BookmarkFromdto import com.group4.ticketingservice.entity.Bookmark +import com.group4.ticketingservice.entity.Event +import com.group4.ticketingservice.entity.User import com.group4.ticketingservice.repository.BookmarkRepository +import com.group4.ticketingservice.repository.EventRepository +import com.group4.ticketingservice.repository.UserRepository import com.group4.ticketingservice.service.BookmarkService +import com.group4.ticketingservice.utils.Authority import io.mockk.every import io.mockk.mockk import io.mockk.verify import org.junit.jupiter.api.Test import org.modelmapper.ModelMapper -import org.springframework.data.repository.findByIdOrNull +import java.time.OffsetDateTime -class BookmarkServiceTest { +class BookmarkServiceTest() { + private val userRepository: UserRepository = mockk() + private val eventRepository: EventRepository = mockk() private val repository: BookmarkRepository = mockk() private val modelMapper: ModelMapper = ModelMapper() - private val bookmarkService: BookmarkService = BookmarkService(repository, modelMapper) + private val bookmarkService: BookmarkService = BookmarkService(userRepository, eventRepository, repository) - val EmptyBookmark = Bookmark( - id = -1, - user_id = -1, - show_id = -1 + val sampleUser = User( + name = "james", + email = "james@example.com", + password = "12345678", + authority = Authority.USER ) + + val sampleUserId = 1L + + private val sampleEvent: Event = Event( + id = 1, + title = "test title", + date = OffsetDateTime.now(), + reservationEndTime = OffsetDateTime.now(), + reservationStartTime = OffsetDateTime.now(), + maxAttendees = 10 + ) + private val sampleBookmark = Bookmark( - user_id = 1, - show_id = 1 + user = sampleUser, + event = sampleEvent ) private val sampleBookmarkDto = BookmarkFromdto( - user_id = 1, - show_id = 1 + event_id = 1 ) @Test - fun `bookmarkService_getList() invoke repository_findAll`() { + fun `bookmarkService_getList() invoke repository_findByUser`() { // given - every { repository.findAll() } returns listOf(EmptyBookmark) + + every { repository.findByUserId(sampleUserId) } returns listOf(sampleBookmark) // when - bookmarkService.getList() + bookmarkService.getList(sampleUserId) // then - verify(exactly = 1) { repository.findAll() } + verify(exactly = 1) { repository.findByUserId(sampleUserId) } } @Test fun `bookmarkService_getList() should return emptyList`() { // given - every { repository.findAll() } returns listOf() + every { repository.findByUserId(sampleUserId) } returns listOf() // when - val result: List = bookmarkService.getList() + val result: List = bookmarkService.getList(sampleUserId) // then - verify(exactly = 1) { repository.findAll() } + verify(exactly = 1) { repository.findByUserId(sampleUserId) } assert(result == listOf()) } @Test - fun `bookmarkService_get() invoke repository_findById`() { + fun `bookmarkService_get() invoke repository_findByIdAndUser`() { // given - every { repository.findByIdOrNull(1) } returns sampleBookmark + every { repository.findByIdAndUserId(1, sampleUserId) } returns sampleBookmark // when - val result: Bookmark? = bookmarkService.get(1) + val result: Bookmark? = bookmarkService.get(sampleUserId, 1) // then - verify(exactly = 1) { repository.findById(1) } + verify(exactly = 1) { repository.findByIdAndUserId(1, sampleUserId) } assert(result == sampleBookmark) } @Test fun `bookmarkService_create() invoke repository_save`() { // given + every { userRepository.getReferenceById(any()) } returns sampleUser + every { eventRepository.getReferenceById(any()) } returns sampleEvent every { repository.save(any()) } returns sampleBookmark // when - bookmarkService.create(sampleBookmarkDto) + bookmarkService.create(sampleUserId, sampleBookmarkDto) // then verify(exactly = 1) { repository.save(any()) } } @Test - fun `bookmarkService_delete() invoke repository_deleteById`() { + fun `bookmarkService_delete() invoke repository_deleteByIdAndUser`() { // given - every { repository.deleteById(1) } returns Unit + + every { repository.deleteByIdAndUserId(1, sampleUserId) } returns Unit // when - bookmarkService.delete(1) + bookmarkService.delete(sampleUserId, 1) // then - verify(exactly = 1) { repository.deleteById(1) } + verify(exactly = 1) { repository.deleteByIdAndUserId(1, sampleUserId) } } } diff --git a/src/test/kotlin/com/group4/ticketingservice/event/EventControllerTest.kt b/src/test/kotlin/com/group4/ticketingservice/event/EventControllerTest.kt index b2a42baa..16e1aaae 100644 --- a/src/test/kotlin/com/group4/ticketingservice/event/EventControllerTest.kt +++ b/src/test/kotlin/com/group4/ticketingservice/event/EventControllerTest.kt @@ -3,14 +3,15 @@ package com.group4.ticketingservice.event import com.google.gson.Gson import com.google.gson.GsonBuilder import com.group4.ticketingservice.JwtAuthorizationEntryPoint -import com.group4.ticketingservice.config.ClockConfig import com.group4.ticketingservice.config.SecurityConfig import com.group4.ticketingservice.controller.EventController import com.group4.ticketingservice.dto.EventCreateRequest import com.group4.ticketingservice.dto.EventDeleteRequest import com.group4.ticketingservice.entity.Event +import com.group4.ticketingservice.entity.User import com.group4.ticketingservice.service.EventService import com.group4.ticketingservice.util.DateTimeConverter +import com.group4.ticketingservice.utils.Authority import com.group4.ticketingservice.utils.TokenProvider import com.ninjasquad.springmockk.MockkBean import io.mockk.every @@ -21,22 +22,15 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest import org.springframework.context.annotation.ComponentScan import org.springframework.context.annotation.FilterType -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.delete import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put 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 -import java.time.Clock -import java.time.Duration import java.time.OffsetDateTime @ExtendWith(MockKExtension::class) -@Import(ClockConfig::class) @WebMvcTest( EventController::class, includeFilters = arrayOf( @@ -44,25 +38,39 @@ import java.time.OffsetDateTime ) ) class EventControllerTest( - @Autowired val mockMvc: MockMvc, - @Autowired val clock: Clock + @Autowired val mockMvc: MockMvc ) { @MockkBean private lateinit var eventService: EventService + object testFields { + const val testName = "minjun" + const val testUserId = 1L + const val testUserName = "minjun3021@qwer.com" + const val testUserRole = "USER" + const val password = "123456789" + } + + val sampleUser = User( + name = "james", + email = "james@example.com", + password = "12345678", + authority = Authority.USER + ) private val sampleEvent: Event = Event( id = 1, title = "test title", - date = OffsetDateTime.now(clock), - reservationEndTime = OffsetDateTime.now(clock) + Duration.ofHours(2), - reservationStartTime = OffsetDateTime.now(clock) + Duration.ofHours(1), + date = OffsetDateTime.now(), + reservationEndTime = OffsetDateTime.now(), + reservationStartTime = OffsetDateTime.now(), maxAttendees = 10 + ) private val sampleEventCreateRequest: EventCreateRequest = EventCreateRequest( title = "test title", - date = OffsetDateTime.now(clock), - reservationEndTime = OffsetDateTime.now(clock) + Duration.ofHours(2), - reservationStartTime = OffsetDateTime.now(clock) + Duration.ofHours(1), + date = OffsetDateTime.now(), + reservationEndTime = OffsetDateTime.now(), + reservationStartTime = OffsetDateTime.now(), maxAttendees = 10 ) private val sampleEventDeleteRequest: EventDeleteRequest = EventDeleteRequest( @@ -70,22 +78,6 @@ class EventControllerTest( ) private val gson: Gson = GsonBuilder().registerTypeAdapter(OffsetDateTime::class.java, DateTimeConverter()).create() - @Test - fun `POST events should return created event`() { - every { eventService.createEvent(any(), any(), any(), any(), any()) } returns sampleEvent - - mockMvc.perform( - post("/events") - .contentType(MediaType.APPLICATION_JSON) - .content(gson.toJson(sampleEventCreateRequest)) - ) - .andExpect(status().isOk) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$.id").value(sampleEvent.id)) - .andExpect(jsonPath("$.title").value(sampleEvent.title)) - .andExpect(jsonPath("$.maxAttendees").value(sampleEvent.maxAttendees)) - } - @Test fun `GET events should return event`() { every { eventService.getEvent(any()) } returns sampleEvent @@ -131,38 +123,4 @@ class EventControllerTest( .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$").isEmpty) } - - @Test - fun `PUT event should return updated event`() { - every { - eventService.updateEvent( - any(), - any(), - any(), - any(), - any(), - any() - ) - } returns sampleEvent - mockMvc.perform( - put("/events/${sampleEvent.id}") - .contentType(MediaType.APPLICATION_JSON) - .content(gson.toJson(sampleEventCreateRequest)) - ) - .andExpect(status().isOk) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$.id").value(sampleEvent.id)) - .andExpect(jsonPath("$.title").value(sampleEvent.title)) - .andExpect(jsonPath("$.maxAttendees").value(sampleEvent.maxAttendees)) - } - - @Test - fun `DELETE event should return no content`() { - every { eventService.deleteEvent(any()) } returns Unit - mockMvc.perform( - delete("/events/${sampleEventDeleteRequest.id}") - .contentType(MediaType.APPLICATION_JSON) - ) - .andExpect(status().isNoContent) - } } diff --git a/src/test/kotlin/com/group4/ticketingservice/event/EventServiceTest.kt b/src/test/kotlin/com/group4/ticketingservice/event/EventServiceTest.kt index 09595efd..c48f154d 100644 --- a/src/test/kotlin/com/group4/ticketingservice/event/EventServiceTest.kt +++ b/src/test/kotlin/com/group4/ticketingservice/event/EventServiceTest.kt @@ -1,42 +1,41 @@ package com.group4.ticketingservice.event import com.group4.ticketingservice.entity.Event +import com.group4.ticketingservice.entity.User import com.group4.ticketingservice.repository.EventRepository +import com.group4.ticketingservice.repository.UserRepository import com.group4.ticketingservice.service.EventService +import com.group4.ticketingservice.utils.Authority import io.mockk.every import io.mockk.mockk import io.mockk.verify import org.junit.jupiter.api.Test -import java.time.Duration import java.time.OffsetDateTime -import java.util.* +import java.util.Optional class EventServiceTest { private val eventRepository: EventRepository = mockk() + private val userRepository: UserRepository = mockk() private val eventService: EventService = EventService( eventRepository = eventRepository ) + val sampleUserId = 1L + + val sampleUser = User( + name = "james", + email = "james@example.com", + password = "12345678", + authority = Authority.USER + ) private val sampleEvent: Event = Event( id = 1, title = "test title", date = OffsetDateTime.now(), - reservationEndTime = OffsetDateTime.now() + Duration.ofHours(2), - reservationStartTime = OffsetDateTime.now() + Duration.ofHours(1), + reservationEndTime = OffsetDateTime.now(), + reservationStartTime = OffsetDateTime.now(), maxAttendees = 10 - ) - @Test - fun `EventService_createEvent invoke EventRepository_findById`() { - every { eventRepository.save(any()) } returns sampleEvent - eventService.createEvent( - title = sampleEvent.title, - date = sampleEvent.date, - reservationStartTime = sampleEvent.reservationStartTime, - reservationEndTime = sampleEvent.reservationEndTime, - maxAttendees = sampleEvent.maxAttendees - ) - verify(exactly = 1) { eventRepository.save(any()) } - } + ) @Test fun `EventService_getEvent invoke EventRepository_findById`() { @@ -51,38 +50,4 @@ class EventServiceTest { eventService.getEvents() verify(exactly = 1) { eventRepository.findAll() } } - - @Test - fun `EventService_updateEvent invoke EventRepository_findById`() { - val updatedEvent = Event( - id = sampleEvent.id!!, - title = "updated title", - date = sampleEvent.date, - reservationEndTime = sampleEvent.reservationEndTime, - reservationStartTime = sampleEvent.reservationStartTime, - maxAttendees = sampleEvent.maxAttendees - ) - every { eventRepository.findById(any()) } returns Optional.of(sampleEvent) - every { eventRepository.save(any()) } returns updatedEvent - - val result: Event = eventService.updateEvent( - id = sampleEvent.id!!, - title = updatedEvent.title, - date = updatedEvent.date, - reservationStartTime = updatedEvent.reservationStartTime, - reservationEndTime = updatedEvent.reservationEndTime, - maxAttendees = updatedEvent.maxAttendees - ) - assert(result == updatedEvent) - verify(exactly = 1) { eventRepository.findById(any()) } - } - - @Test - fun `EventService_deleteEvent invoke EventRepository_findById`() { - every { eventRepository.existsById(any()) } returns true - every { eventRepository.deleteById(any()) } returns Unit - eventService.deleteEvent(sampleEvent.id!!) - verify(exactly = 1) { eventRepository.existsById(any()) } - verify(exactly = 1) { eventRepository.deleteById(any()) } - } } diff --git a/src/test/kotlin/com/group4/ticketingservice/filter/JwtAuthorizationFilterTest.kt b/src/test/kotlin/com/group4/ticketingservice/filter/JwtAuthorizationFilterTest.kt index c8fe3294..cabe3bd8 100644 --- a/src/test/kotlin/com/group4/ticketingservice/filter/JwtAuthorizationFilterTest.kt +++ b/src/test/kotlin/com/group4/ticketingservice/filter/JwtAuthorizationFilterTest.kt @@ -8,7 +8,6 @@ import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import org.mockito.ArgumentMatchers.any import org.mockito.Mockito.spy import org.springframework.mock.web.MockFilterChain import org.springframework.mock.web.MockHttpServletRequest @@ -25,6 +24,7 @@ class JwtAuthorizationFilterTest { val testUserName = "minjun3021@qwer.com" val testUserRole = "USER" + val testUserID = 1L @BeforeEach fun resetAuthentication() { val strategy: SecurityContextHolderStrategy = spy(SecurityContextHolder.getContextHolderStrategy()) @@ -36,7 +36,7 @@ class JwtAuthorizationFilterTest { // given every { tokenProvider.parseBearerToken(any()) } returns "" every { tokenProvider.validateToken(any()) } returns true - every { tokenProvider.parseUserSpecification(any()) } returns listOf(testUserName, testUserRole) + every { tokenProvider.parseUserSpecification(any()) } returns testUserID.toString() // when val req = MockHttpServletRequest() @@ -49,7 +49,7 @@ class JwtAuthorizationFilterTest { val authenticationPrincipal = strategy.context.authentication.principal // then - assertEquals(testUserName, authenticationPrincipal) + assertEquals(testUserID, authenticationPrincipal) } @Test @@ -57,7 +57,7 @@ class JwtAuthorizationFilterTest { // given every { tokenProvider.parseBearerToken(any()) } returns "" every { tokenProvider.validateToken(any()) } returns false - every { tokenProvider.parseUserSpecification(any()) } returns listOf(testUserName, testUserRole) + every { tokenProvider.parseUserSpecification(any()) } returns testUserID.toString() // when val req = MockHttpServletRequest() diff --git a/src/test/kotlin/com/group4/ticketingservice/reservation/ReservationControllerTest.kt b/src/test/kotlin/com/group4/ticketingservice/reservation/ReservationControllerTest.kt index 32f76c98..7f060a30 100644 --- a/src/test/kotlin/com/group4/ticketingservice/reservation/ReservationControllerTest.kt +++ b/src/test/kotlin/com/group4/ticketingservice/reservation/ReservationControllerTest.kt @@ -13,7 +13,10 @@ import com.group4.ticketingservice.dto.ReservationUpdateRequest import com.group4.ticketingservice.entity.Event import com.group4.ticketingservice.entity.Reservation import com.group4.ticketingservice.entity.User +import com.group4.ticketingservice.reservation.ReservationControllerTest.testFields.testUserId +import com.group4.ticketingservice.reservation.ReservationControllerTest.testFields.testUserName import com.group4.ticketingservice.service.ReservationService +import com.group4.ticketingservice.user.WithAuthUser import com.group4.ticketingservice.util.DateTimeConverter import com.group4.ticketingservice.utils.Authority import com.group4.ticketingservice.utils.TokenProvider @@ -38,7 +41,6 @@ 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 import java.time.Clock -import java.time.Duration import java.time.OffsetDateTime @ExtendWith(MockKExtension::class) @@ -58,6 +60,7 @@ class ReservationControllerTest( object testFields { const val testName = "minjun" const val testUserName = "minjun3021@qwer.com" + const val testUserId = 1L const val testUserRole = "USER" const val password = "1234" } @@ -70,8 +73,7 @@ class ReservationControllerTest( ) private val sampleReservationCreateRequest = ReservationCreateRequest( - eventId = 1, - userId = 1 + eventId = 1 ) private val sampleReservationDeleteRequest = ReservationDeleteRequest( id = 1 @@ -80,9 +82,9 @@ class ReservationControllerTest( private val sampleEvent: Event = Event( id = 1, title = "test title", - date = OffsetDateTime.now(clock), - reservationEndTime = OffsetDateTime.now(clock) + Duration.ofHours(2), - reservationStartTime = OffsetDateTime.now(clock) + Duration.ofHours(1), + date = OffsetDateTime.now(), + reservationEndTime = OffsetDateTime.now(), + reservationStartTime = OffsetDateTime.now(), maxAttendees = 10 ) private val sampleReservation: Reservation = Reservation( @@ -95,6 +97,7 @@ class ReservationControllerTest( private val gson: Gson = GsonBuilder().registerTypeAdapter(OffsetDateTime::class.java, DateTimeConverter()).create() @Test + @WithAuthUser(email = testUserName, id = testUserId) fun `POST reservations should return created reservation`() { every { reservationService.createReservation(1, 1) } returns sampleReservation val sampleReservationResponse = ReservationResponse( @@ -123,6 +126,7 @@ class ReservationControllerTest( } @Test + @WithAuthUser(email = testUserName, id = testUserId) fun `GET reservations should return reservation`() { every { reservationService.getReservation(1) } returns sampleReservation @@ -138,6 +142,7 @@ class ReservationControllerTest( } @Test + @WithAuthUser(email = testUserName, id = testUserId) fun `PUT reservations should return updated reservation`() { val reservationUpdateRequest = ReservationUpdateRequest( eventId = 2 @@ -148,12 +153,12 @@ class ReservationControllerTest( event = Event( id = 2, title = "test title 2", - date = OffsetDateTime.now(clock), - reservationEndTime = OffsetDateTime.now(clock) + Duration.ofHours(2), - reservationStartTime = OffsetDateTime.now(clock) + Duration.ofHours(1), + date = OffsetDateTime.now(), + reservationEndTime = OffsetDateTime.now(), + reservationStartTime = OffsetDateTime.now(), maxAttendees = 10 ), - bookedAt = OffsetDateTime.now(clock) + bookedAt = OffsetDateTime.now() ) every { reservationService.updateReservation(1, 2) } returns updatedReservation @@ -170,8 +175,9 @@ class ReservationControllerTest( } @Test + @WithAuthUser(email = testUserName, id = testUserId) fun `DELETE reservations should return no content`() { - every { reservationService.deleteReservation(1) } returns Unit + every { reservationService.deleteReservation(any(), any()) } returns Unit mockMvc.perform( delete("/reservations/${sampleReservationDeleteRequest.id}") diff --git a/src/test/kotlin/com/group4/ticketingservice/reservation/ReservationServiceTest.kt b/src/test/kotlin/com/group4/ticketingservice/reservation/ReservationServiceTest.kt index d9490e9f..a6290b9b 100644 --- a/src/test/kotlin/com/group4/ticketingservice/reservation/ReservationServiceTest.kt +++ b/src/test/kotlin/com/group4/ticketingservice/reservation/ReservationServiceTest.kt @@ -19,7 +19,6 @@ import org.springframework.boot.test.context.SpringBootTest import org.springframework.test.context.ContextConfiguration import org.springframework.test.context.support.AnnotationConfigContextLoader import java.time.Clock -import java.time.Duration.ofHours import java.time.OffsetDateTime import java.util.* @@ -40,30 +39,35 @@ class ReservationServiceTest( reservationRepository = reservationRepository, clock = clock ) + + val sampleUserId = 1L + val sampleUser = User( name = "minjun3021@qwer.com", email = "minjun", password = "1234", - authority = Authority.USER + authority = Authority.USER, + id = sampleUserId ) + private val sampleEvent: Event = Event( id = 1, title = "test title", - date = OffsetDateTime.now(clock), - reservationEndTime = OffsetDateTime.now(clock) + ofHours(2), - reservationStartTime = OffsetDateTime.now(clock) + ofHours(1), + date = OffsetDateTime.now(), + reservationEndTime = OffsetDateTime.now(), + reservationStartTime = OffsetDateTime.now(), maxAttendees = 10 ) private val sampleReservation: Reservation = Reservation( user = sampleUser, event = sampleEvent, - bookedAt = OffsetDateTime.now(clock) + bookedAt = OffsetDateTime.now() ) @Test fun `ReservationService_createReservation invoke ReservationRepository_save`() { - every { userRepository.findById(any()) } returns Optional.of(sampleUser) + every { userRepository.getReferenceById(any()) } returns sampleUser every { eventRepository.findByIdWithPesimisticLock(any()) } returns sampleEvent every { eventRepository.findByIdWithOptimisicLock(any()) } returns sampleEvent @@ -91,17 +95,28 @@ class ReservationServiceTest( @Test fun `ReservationService_deleteReservation invoke ReservationRepository_deleteById`() { - every { reservationRepository.existsById(any()) } returns true - every { reservationRepository.deleteById(any()) } returns Unit - reservationService.deleteReservation(1) - verify(exactly = 1) { reservationRepository.deleteById(any()) } + every { reservationRepository.findById(any()) } returns Optional.of(sampleReservation) + every { reservationRepository.delete(any()) } returns Unit + reservationService.deleteReservation(sampleUserId, 1) + verify(exactly = 1) { reservationRepository.delete(any()) } } @Test fun `ReservationService_deleteReservation throw IllegalArgumentException`() { - every { reservationRepository.existsById(any()) } returns false - val exception = assertThrows { reservationService.deleteReservation(1) } + every { reservationRepository.findById(any()) } returns Optional.ofNullable(null) + every { reservationRepository.delete(any()) } returns Unit + + val exception = assertThrows { reservationService.deleteReservation(sampleUserId, 1) } assert(exception.message == "Reservation not found") - verify(exactly = 0) { reservationRepository.deleteById(any()) } + verify(exactly = 0) { reservationRepository.delete(any()) } + } + + @Test + fun `ReservationService_deleteReservation throw IllegalArgumentException when deleting other's reservation`() { + every { reservationRepository.findById(any()) } returns Optional.of(sampleReservation) + every { reservationRepository.delete(any()) } returns Unit + + val exception = assertThrows { reservationService.deleteReservation(2L, 1) } + assert(exception.message == "It's not your reservation") } } diff --git a/src/test/kotlin/com/group4/ticketingservice/user/UserControllerTest.kt b/src/test/kotlin/com/group4/ticketingservice/user/UserControllerTest.kt index 9976f83e..d1465d76 100644 --- a/src/test/kotlin/com/group4/ticketingservice/user/UserControllerTest.kt +++ b/src/test/kotlin/com/group4/ticketingservice/user/UserControllerTest.kt @@ -9,8 +9,8 @@ import com.group4.ticketingservice.dto.UserDto import com.group4.ticketingservice.service.UserService import com.group4.ticketingservice.user.UserControllerTest.testFields.password import com.group4.ticketingservice.user.UserControllerTest.testFields.testName +import com.group4.ticketingservice.user.UserControllerTest.testFields.testUserId import com.group4.ticketingservice.user.UserControllerTest.testFields.testUserName -import com.group4.ticketingservice.user.UserControllerTest.testFields.testUserRole import com.group4.ticketingservice.utils.TokenProvider import com.ninjasquad.springmockk.MockkBean import io.mockk.every @@ -43,6 +43,7 @@ class UserControllerTest( object testFields { const val testName = "minjun" + const val testUserId = 1L const val testUserName = "minjun3021@qwer.com" const val testUserRole = "USER" const val password = "123456789" @@ -70,13 +71,14 @@ class UserControllerTest( * Controller가 Spring SecurityContext에 들어있는 유저 인증정보를 주입받는지를 확인할수있음 */ @Test - @WithAuthUser(email = testUserName, role = testUserRole) - fun `GET_api_users_access_token_info should return username injected by Spring Security with HTTP 200 OK`() { + @WithAuthUser(email = testUserName, id = testUserId) + fun `GET_api_users_access_token_info should return userId injected by Spring Security with HTTP 200 OK`() { // when val resultActions: ResultActions = mockMvc.perform(MockMvcRequestBuilders.get("/users/access_token_info")) resultActions.andExpect(MockMvcResultMatchers.status().isOk) - .andExpect(MockMvcResultMatchers.jsonPath("$.username").value(testUserName)) + .andExpect(MockMvcResultMatchers.jsonPath("$.expires_in").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$.userId").value(testUserId)) } @Test diff --git a/src/test/kotlin/com/group4/ticketingservice/user/WithAuthUser.kt b/src/test/kotlin/com/group4/ticketingservice/user/WithAuthUser.kt index ea0b04b6..09cb34eb 100644 --- a/src/test/kotlin/com/group4/ticketingservice/user/WithAuthUser.kt +++ b/src/test/kotlin/com/group4/ticketingservice/user/WithAuthUser.kt @@ -12,12 +12,12 @@ import org.springframework.security.test.context.support.WithSecurityContextFact @WithSecurityContext(factory = WithAuthUserSecurityContextFactory::class) annotation class WithAuthUser( val email: String, - val role: String + val id: Long ) class WithAuthUserSecurityContextFactory(private val tokenProvider: TokenProvider) : WithSecurityContextFactory { override fun createSecurityContext(annotation: WithAuthUser): SecurityContext { - val token = UsernamePasswordAuthenticationToken(annotation.email, tokenProvider.createToken("${annotation.email}:${annotation.role}"), listOf(SimpleGrantedAuthority(annotation.role))) + val token = UsernamePasswordAuthenticationToken(annotation.id, tokenProvider.createToken("${annotation.email}:${annotation.id}"), listOf(SimpleGrantedAuthority("USER"))) val context = SecurityContextHolder.getContext() context.authentication = token return context