Skip to content
This repository has been archived by the owner on May 24, 2022. It is now read-only.

Commit

Permalink
Merge pull request #103 from SELab-2/check-coach-exists
Browse files Browse the repository at this point in the history
Check if coach actually exists
  • Loading branch information
TomAlard authored Mar 21, 2022
2 parents b4bd6cd + 0b68234 commit 67dcc58
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
package be.osoc.team1.backend.services

import be.osoc.team1.backend.entities.Communication
import be.osoc.team1.backend.entities.Role
import be.osoc.team1.backend.entities.StatusEnum
import be.osoc.team1.backend.entities.StatusSuggestion
import be.osoc.team1.backend.entities.Student
import be.osoc.team1.backend.entities.User
import be.osoc.team1.backend.exceptions.FailedOperationException
import be.osoc.team1.backend.exceptions.ForbiddenOperationException
import be.osoc.team1.backend.exceptions.InvalidStudentIdException
import be.osoc.team1.backend.exceptions.InvalidUserIdException
import be.osoc.team1.backend.repositories.StudentRepository
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import java.util.UUID

@Service
class StudentService(private val repository: StudentRepository) {
class StudentService(private val repository: StudentRepository, private val userService: UserService) {

fun getAllStudents(): Iterable<Student> = repository.findAll()

Expand Down Expand Up @@ -53,13 +56,20 @@ class StudentService(private val repository: StudentRepository) {
* on the information in the given [statusSuggestion] and add it to the [Student]'s list.
* A coach is only allowed to make one [StatusSuggestion] for one particular [Student].
* If the coach making this [statusSuggestion] has already made one for this [Student],
* the method will throw a [FailedOperationException]. If a coach wants to change their suggestion,
* the method will throw a [ForbiddenOperationException]. If a coach wants to change their suggestion,
* the API caller should first delete the original suggestion with the [deleteStudentStatusSuggestion] method,
* and then call this method. Throws an [InvalidStudentIdException] if no student with that [studentId] exists.
* and then call this method. Additionally throws an [InvalidStudentIdException]
* if no student with that [studentId] exists, an [InvalidUserIdException] if no [User] with
* the given coachId exists, and a [ForbiddenOperationException] if the [User] exists but doesn't have
* the coach role.
*/
fun addStudentStatusSuggestion(studentId: UUID, statusSuggestion: StatusSuggestion) {
val coach = userService.getUserById(statusSuggestion.coachId)
if (!coach.role.hasPermissionLevel(Role.Coach)) {
throw ForbiddenOperationException("Only coaches and admins can make status suggestions.")
}
val student = getStudentById(studentId)
val sameCoachSuggestion = student.statusSuggestions.find { it.coachId == statusSuggestion.coachId }
val sameCoachSuggestion = student.statusSuggestions.find { it.coachId == coach.id }
if (sameCoachSuggestion !== null) {
throw ForbiddenOperationException("This coach has already made a suggestion for this student.")
}
Expand All @@ -73,12 +83,14 @@ class StudentService(private val repository: StudentRepository) {
* Retrieve the [Student] with the specified [studentId], then get the [StatusSuggestion]
* that was made by the coach identified by the given [coachId] and delete it.
* Throws an [InvalidStudentIdException] if no [Student] with that [studentId] exists,
* an [InvalidUserIdException] if no [User] with that [coachId] exists,
* or a [FailedOperationException] if the student and the coach exist, but the coach
* hasn't made a [StatusSuggestion] for this student.
*/
fun deleteStudentStatusSuggestion(studentId: UUID, coachId: UUID) {
val coach = userService.getUserById(coachId)
val student = getStudentById(studentId)
val suggestion = student.statusSuggestions.find { it.coachId == coachId }
val suggestion = student.statusSuggestions.find { it.coachId == coach.id }
if (suggestion === null) {
throw FailedOperationException("This coach hasn't made a suggestion for the given student.")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import be.osoc.team1.backend.exceptions.FailedOperationException
import be.osoc.team1.backend.exceptions.ForbiddenOperationException
import be.osoc.team1.backend.exceptions.InvalidIdException
import be.osoc.team1.backend.exceptions.InvalidStudentIdException
import be.osoc.team1.backend.exceptions.InvalidUserIdException
import be.osoc.team1.backend.services.StudentService
import com.fasterxml.jackson.databind.ObjectMapper
import com.ninjasquad.springmockk.MockkBean
Expand Down Expand Up @@ -139,6 +140,16 @@ class StudentControllerTests(@Autowired private val mockMvc: MockMvc) {
).andExpect(status().isForbidden)
}

@Test
fun `addStudentStatusSuggestion returns 404 Not Found if coach doesn't exist`() {
every { studentService.addStudentStatusSuggestion(studentId, any()) }.throws(InvalidUserIdException())
mockMvc.perform(
post("/students/$studentId/suggestions")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(testSuggestion))
).andExpect(status().isNotFound)
}

@Test
fun `deleteStudentStatusSuggestion succeeds when student, suggestion and coach exist`() {
every { studentService.deleteStudentStatusSuggestion(studentId, coachId) } just Runs
Expand All @@ -156,4 +167,10 @@ class StudentControllerTests(@Autowired private val mockMvc: MockMvc) {
every { studentService.deleteStudentStatusSuggestion(studentId, coachId) }.throws(FailedOperationException())
mockMvc.perform(delete("/students/$studentId/suggestions/$coachId")).andExpect(status().isBadRequest)
}

@Test
fun `deleteStudentStatusSuggestion returns 404 Not Found if coach doesn't exist`() {
every { studentService.deleteStudentStatusSuggestion(studentId, coachId) }.throws(InvalidUserIdException())
mockMvc.perform(delete("/students/$studentId/suggestions/$coachId")).andExpect(status().isNotFound)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ package be.osoc.team1.backend.unittests

import be.osoc.team1.backend.entities.Communication
import be.osoc.team1.backend.entities.CommunicationTypeEnum
import be.osoc.team1.backend.entities.Role
import be.osoc.team1.backend.entities.StatusEnum
import be.osoc.team1.backend.entities.StatusSuggestion
import be.osoc.team1.backend.entities.Student
import be.osoc.team1.backend.entities.SuggestionEnum
import be.osoc.team1.backend.entities.User
import be.osoc.team1.backend.exceptions.FailedOperationException
import be.osoc.team1.backend.exceptions.ForbiddenOperationException
import be.osoc.team1.backend.exceptions.InvalidStudentIdException
import be.osoc.team1.backend.exceptions.InvalidUserIdException
import be.osoc.team1.backend.repositories.StudentRepository
import be.osoc.team1.backend.services.StudentService
import be.osoc.team1.backend.services.UserService
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
Expand All @@ -21,14 +25,14 @@ import org.junit.jupiter.api.Assertions.assertNotEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.springframework.data.repository.findByIdOrNull
import java.util.UUID

class StudentServiceTests {

private val testStudent = Student("Tom", "Alard")
private val studentId = testStudent.id
private val coachId = UUID.randomUUID()
private val testSuggestion = StatusSuggestion(coachId, SuggestionEnum.Yes, "test motivation")
private val testCoach = User("", "", Role.Coach, "")
private val testSuggestion = StatusSuggestion(testCoach.id, SuggestionEnum.Yes, "test motivation")
private val userService = mockk<UserService>()

private fun getRepository(studentAlreadyExists: Boolean): StudentRepository {
val repository: StudentRepository = mockk()
Expand All @@ -42,48 +46,48 @@ class StudentServiceTests {

@Test
fun `getStudentById succeeds when student with id exists`() {
val service = StudentService(getRepository(true))
val service = StudentService(getRepository(true), userService)
assertEquals(service.getStudentById(studentId), testStudent)
}

@Test
fun `getStudentById fails when no student with that id exists`() {
val service = StudentService(getRepository(false))
val service = StudentService(getRepository(false), userService)
assertThrows<InvalidStudentIdException> { service.getStudentById(studentId) }
}

@Test
fun `deleteStudentById succeeds when student with id exists`() {
val repository = getRepository(true)
val service = StudentService(repository)
val service = StudentService(repository, userService)
service.deleteStudentById(studentId)
verify { repository.deleteById(studentId) }
}

@Test
fun `deleteStudentById fails when no student with that id exists`() {
val service = StudentService(getRepository(false))
val service = StudentService(getRepository(false), userService)
assertThrows<InvalidStudentIdException> { service.deleteStudentById(studentId) }
}

@Test
fun `addStudent saves student`() {
val repository = getRepository(false)
val service = StudentService(repository)
val service = StudentService(repository, userService)
service.addStudent(testStudent)
verify { repository.save(testStudent) }
}

@Test
fun `addStudent returns some other id than what was passed`() {
val service = StudentService(getRepository(false))
val service = StudentService(getRepository(false), userService)
assertNotEquals(service.addStudent(testStudent), studentId)
}

@Test
fun `setStudentStatus changes student status when student with id exists`() {
val repository = getRepository(true)
val service = StudentService(repository)
val service = StudentService(repository, userService)
service.setStudentStatus(studentId, StatusEnum.Yes)
testStudent.status = StatusEnum.Yes // Bit of a hack
verify { repository.save(testStudent) }
Expand All @@ -92,7 +96,7 @@ class StudentServiceTests {

@Test
fun `setStudentStatus fails when no student with that id exists`() {
val service = StudentService(getRepository(false))
val service = StudentService(getRepository(false), userService)
assertThrows<InvalidStudentIdException> { service.setStudentStatus(studentId, StatusEnum.Yes) }
}

Expand All @@ -104,14 +108,18 @@ class StudentServiceTests {
every { student.statusSuggestions.iterator() } returns mutableListOf<StatusSuggestion>().iterator()
every { repository.findByIdOrNull(studentId) } returns student
every { repository.save(student) } returns student
val service = StudentService(repository)
val customUserService: UserService = mockk()
every { customUserService.getUserById(testSuggestion.coachId) } returns testCoach
val service = StudentService(repository, customUserService)
service.addStudentStatusSuggestion(studentId, testSuggestion)
verify { student.statusSuggestions.add(testSuggestion) }
}

@Test
fun `addStudentStatusSuggestion fails when no student with that id exists`() {
val service = StudentService(getRepository(false))
val customUserService: UserService = mockk()
every { customUserService.getUserById(testSuggestion.coachId) } returns testCoach
val service = StudentService(getRepository(false), customUserService)
assertThrows<InvalidStudentIdException> { service.addStudentStatusSuggestion(studentId, testSuggestion) }
}

Expand All @@ -121,7 +129,26 @@ class StudentServiceTests {
val student: Student = mockk()
every { student.statusSuggestions.iterator() } returns mutableListOf(testSuggestion).iterator()
every { repository.findByIdOrNull(studentId) } returns student
val service = StudentService(repository)
val customUserService: UserService = mockk()
every { customUserService.getUserById(testSuggestion.coachId) } returns testCoach
val service = StudentService(repository, customUserService)
assertThrows<ForbiddenOperationException> { service.addStudentStatusSuggestion(studentId, testSuggestion) }
}

@Test
fun `addStudentStatusSuggestion fails when coach does not exist`() {
val customUserService: UserService = mockk()
every { customUserService.getUserById(testSuggestion.coachId) }.throws(InvalidUserIdException())
val service = StudentService(getRepository(true), customUserService)
assertThrows<InvalidUserIdException> { service.addStudentStatusSuggestion(studentId, testSuggestion) }
}

@Test
fun `addStudentStatusSuggestion fails when user does not have coach role`() {
val disabledUser = User("", "", Role.Disabled, "")
val customUserService: UserService = mockk()
every { customUserService.getUserById(testSuggestion.coachId) } returns disabledUser
val service = StudentService(getRepository(true), customUserService)
assertThrows<ForbiddenOperationException> { service.addStudentStatusSuggestion(studentId, testSuggestion) }
}

Expand All @@ -133,27 +160,41 @@ class StudentServiceTests {
every { student.statusSuggestions.iterator() } returns mutableListOf(testSuggestion).iterator()
every { repository.findByIdOrNull(studentId) } returns student
every { repository.save(student) } returns student
val service = StudentService(repository)
service.deleteStudentStatusSuggestion(studentId, coachId)
val customUserService: UserService = mockk()
every { customUserService.getUserById(testSuggestion.coachId) } returns testCoach
val service = StudentService(repository, customUserService)
service.deleteStudentStatusSuggestion(studentId, testCoach.id)
verify { student.statusSuggestions.remove(testSuggestion) }
}

@Test
fun `deleteStudentStatusSuggestion fails when no student with that id exists`() {
val service = StudentService(getRepository(false))
assertThrows<InvalidStudentIdException> { service.deleteStudentStatusSuggestion(studentId, coachId) }
val customUserService: UserService = mockk()
every { customUserService.getUserById(testSuggestion.coachId) } returns testCoach
val service = StudentService(getRepository(false), customUserService)
assertThrows<InvalidStudentIdException> { service.deleteStudentStatusSuggestion(studentId, testCoach.id) }
}

@Test
fun `deleteStudentStatusSuggestion fails when given coach hasn't made a suggestion for this student`() {
val service = StudentService(getRepository(true))
assertThrows<FailedOperationException> { service.deleteStudentStatusSuggestion(studentId, coachId) }
val customUserService: UserService = mockk()
every { customUserService.getUserById(testSuggestion.coachId) } returns testCoach
val service = StudentService(getRepository(true), customUserService)
assertThrows<FailedOperationException> { service.deleteStudentStatusSuggestion(studentId, testCoach.id) }
}

@Test
fun `deleteStudentStatusSuggestion fails when coach does not exist`() {
val customUserService: UserService = mockk()
every { customUserService.getUserById(testSuggestion.coachId) }.throws(InvalidUserIdException())
val service = StudentService(getRepository(true), customUserService)
assertThrows<InvalidUserIdException> { service.deleteStudentStatusSuggestion(studentId, testCoach.id) }
}

@Test
fun `addCommunicationToStudent adds communication to list of student`() {
val repository = getRepository(true)
val service = StudentService(repository)
val service = StudentService(repository, userService)
val testCommunication = Communication("test message", CommunicationTypeEnum.Email)
service.addCommunicationToStudent(studentId, testCommunication)
testStudent.communications.add(testCommunication) // Bit of a hack
Expand All @@ -163,7 +204,7 @@ class StudentServiceTests {

@Test
fun `addCommunicationToStudent fails when no student with that id exists`() {
val service = StudentService(getRepository(false))
val service = StudentService(getRepository(false), userService)
val testCommunication = Communication("test message", CommunicationTypeEnum.Email)
assertThrows<InvalidStudentIdException> { service.addCommunicationToStudent(studentId, testCommunication) }
}
Expand Down

0 comments on commit 67dcc58

Please sign in to comment.