-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
start exam implementation: with the notate method, driven by test #5
base: develop
Are you sure you want to change the base?
Changes from all commits
fe30edd
b284f44
a0589aa
7797654
bf9fbbb
364a213
e400624
59b25bb
403c540
dd61d5b
6d4b919
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
distributionBase=GRADLE_USER_HOME | ||
distributionPath=wrapper/dists | ||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-bin.zip | ||
zipStoreBase=GRADLE_USER_HOME | ||
zipStorePath=wrapper/dists | ||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-bin.zip |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.students.results.entities | ||
|
||
import arrow.core.Either | ||
import services.Grade | ||
|
||
data class Exam(val id: Long) { | ||
fun validateGrade(notation: Grade): Either<InvalidNotationForThisExamException, Unit> = | ||
when (notation) { | ||
in (0.toBigDecimal()..20.toBigDecimal()) -> Either.right(Unit) | ||
else -> Either.left(InvalidNotationForThisExamException("This notation, $notation, is not in bound")) | ||
} | ||
} | ||
|
||
class InvalidNotationForThisExamException(msg: String?) : Exception(msg) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.students.results.entities | ||
|
||
import arrow.core.Either | ||
import services.Grade | ||
import java.math.BigDecimal | ||
|
||
typealias Grades = Map<Exam, Grade> | ||
|
||
data class Student(val id: Long, val grades: Grades = emptyMap()) { | ||
fun getNotation(exam: Exam): Either<NotEvaluatedException, BigDecimal> = | ||
when { | ||
grades.containsKey(exam) -> Either.right(grades.getValue(exam)) | ||
else -> Either.left(NotEvaluatedException()) | ||
} | ||
|
||
fun notate(exam: Exam, note: BigDecimal): Either<InvalidNotationForThisExamException, Student> = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oops should be grade not notate There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And a Grade not a BigDecimal |
||
exam.validateGrade(note).map { | ||
copy(grades = grades + mapOf(exam to note)) | ||
} | ||
} | ||
|
||
class NotEvaluatedException(msg: String? = null, throwable: Throwable? = null) : Exception(msg, throwable) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package com.students.results.interactors | ||
|
||
import com.students.results.repository.ExamsRepository | ||
import com.students.results.services.Exams | ||
|
||
class ExamsInteractor(private val examsRepository: ExamsRepository) : Exams |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.students.results.interactors | ||
|
||
import arrow.core.Either | ||
import arrow.core.flatMap | ||
import com.students.results.repository.ExamsRepository | ||
import com.students.results.repository.StudentsRepository | ||
import com.students.results.services.GradeExamException | ||
import com.students.results.services.Students | ||
import com.students.results.services.requests.GradeExam | ||
|
||
class StudentsInteractor(private val studentsRepository: StudentsRepository, private val examsRepository: ExamsRepository) : Students { | ||
|
||
override fun grade(gradeExam: GradeExam): Either<GradeExamException, Unit> = | ||
gradeExam.run { | ||
studentsRepository.findStudentById(studentId).flatMap { student -> | ||
examsRepository.findExamById(examId).flatMap { exam -> | ||
student.notate(exam, grade).flatMap { studentsRepository.save(it) } | ||
} | ||
}.mapLeft { GradeExamException(throwable = it) } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.students.results.repository | ||
|
||
import arrow.core.Either | ||
import com.students.results.entities.Exam | ||
|
||
interface ExamsRepository { | ||
|
||
fun findExamById(examId: Long): Either<NotFoundException, Exam> | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.students.results.repository | ||
|
||
|
||
sealed class RepositoryException(msg: String? = null, throwable: Throwable? = null) : Exception(msg, throwable) | ||
|
||
class NotFoundException(msg: String? = null, throwable: Throwable? = null) : RepositoryException(msg, throwable) | ||
class NotWrittenException(msg: String? = null, throwable: Throwable? = null) : RepositoryException(msg, throwable) | ||
class RepositoryNotAvailableException(msg: String? = null, throwable: Throwable? = null) : RepositoryException(msg, throwable) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.students.results.repository | ||
|
||
import arrow.core.Either | ||
import com.students.results.entities.Student | ||
|
||
interface StudentsRepository { | ||
|
||
fun findStudentById(studentId: Long): Either<RepositoryException, Student> | ||
fun save(student: Student): Either<RepositoryException, Unit> | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package com.students.results.services | ||
|
||
interface Exams { | ||
} | ||
|
||
class GradeExamException(message: String? = null, throwable: Throwable? = null) : Exception(message, throwable) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package services | ||
|
||
import java.math.BigDecimal | ||
|
||
typealias Grade = BigDecimal | ||
sealed class ServicesException(msg: String? = null, throwable: Throwable? = null) : Exception(msg, throwable) | ||
|
||
class NotFoundException(msg: String? = null, throwable: Throwable? = null) : ServicesException(msg, throwable) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.students.results.services | ||
|
||
import arrow.core.Either | ||
import com.students.results.services.requests.GradeExam | ||
|
||
interface Students { | ||
|
||
fun grade(gradeExam: GradeExam): Either<GradeExamException, Unit> | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.students.results.services.requests | ||
|
||
import services.Grade | ||
|
||
data class GradeExam(val examId: Long, val studentId: Long, val grade: Grade) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package entities | ||
|
||
import com.students.results.entities.Exam | ||
import com.students.results.entities.Student | ||
import org.amshove.kluent.shouldBe | ||
import org.amshove.kluent.shouldEqual | ||
import org.jetbrains.spek.api.Spek | ||
import org.jetbrains.spek.api.dsl.given | ||
import org.jetbrains.spek.api.dsl.it | ||
|
||
class StudentTest : Spek({ | ||
|
||
given("a student") { | ||
val student = Student(id = 40L) | ||
val exam = Exam(id = 50L) | ||
|
||
it("getNot without grade should returns NotEvaluatedException") { | ||
student.getNotation(exam).isLeft() shouldBe true | ||
} | ||
it("grade an exam with 20 and get notation should return 20") { | ||
student.notate(exam, "20".toBigDecimal()).apply { | ||
isRight() shouldBe true | ||
map { | ||
it.getNotation(exam).apply { | ||
isRight() shouldBe true | ||
map { it shouldEqual "20".toBigDecimal() } | ||
} | ||
} | ||
} | ||
} | ||
it("grade less than zero should fail to register grade") { | ||
student.notate(exam, "-1".toBigDecimal()).isLeft() shouldBe true | ||
} | ||
} | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.students.results.interactors | ||
|
||
import org.jetbrains.spek.api.Spek | ||
|
||
class ExamsInteractorTest : Spek({ | ||
|
||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package com.students.results.interactors | ||
|
||
import arrow.core.Either | ||
import com.students.results.entities.Exam | ||
import com.students.results.entities.Student | ||
import com.students.results.repository.ExamsRepository | ||
import com.students.results.repository.StudentsRepository | ||
import com.students.results.services.requests.GradeExam | ||
import io.mockk.every | ||
import io.mockk.mockk | ||
import io.mockk.slot | ||
import io.mockk.verify | ||
import org.amshove.kluent.shouldBe | ||
import org.amshove.kluent.shouldEqual | ||
import org.jetbrains.spek.api.Spek | ||
import org.jetbrains.spek.api.dsl.given | ||
import org.jetbrains.spek.api.dsl.it | ||
|
||
|
||
class StudentsInteractorTest : Spek({ | ||
|
||
val student = Student(id = 10L) | ||
given("a student interactor") { | ||
|
||
val studentSlot = slot<Student>() | ||
val studentRepository = mockk<StudentsRepository>() { | ||
every { findStudentById(3) } returns Either.right(student) | ||
every { save(student = capture(studentSlot)) } returns Either.right(Unit) | ||
} | ||
val exam = Exam(id = 5) | ||
val examsRepository = mockk<ExamsRepository>().apply { | ||
every { findExamById(5) } returns Either.right(exam) | ||
} | ||
val studentsInteractor = StudentsInteractor(studentRepository, examsRepository) | ||
studentsInteractor.notate20().apply { | ||
it("should retrieve student by his id") { | ||
verify { studentRepository.findStudentById(3) } | ||
} | ||
it("should retrieve exam by id") { | ||
verify { examsRepository.findExamById(5) } | ||
} | ||
it("should save student to the repository with the expected notation of 20 for this exam") { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Grade not notation |
||
studentSlot.captured.getNotation(exam).apply { | ||
isRight() shouldBe true | ||
map { it shouldEqual "20".toBigDecimal() } | ||
} | ||
} | ||
} | ||
} | ||
}) | ||
|
||
private fun StudentsInteractor.notate20() = grade(GradeExam( | ||
examId = 5, | ||
studentId = 3, | ||
grade = "20".toBigDecimal() | ||
)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,21 @@ | ||
|
||
apply plugin: 'kotlin-spring' | ||
|
||
dependencies { | ||
compile project(':business-rules') | ||
compile project(':interactors') | ||
compile group: 'org.springframework.data', name: 'spring-data-redis', version: '2.0.7.RELEASE' | ||
compile group: 'org.apache.commons', name: 'commons-pool2', version: '2.5.0' | ||
compile group: 'redis.clients', name: 'jedis', version: '2.9.0' | ||
|
||
testCompile 'it.ozimov:embedded-redis:0.7.2' | ||
testCompile group: 'org.springframework', name: 'spring-test', version: '5.0.6.RELEASE' | ||
testImplementation( | ||
'org.junit.jupiter:junit-jupiter-api:5.1.0' | ||
) | ||
testRuntimeOnly( | ||
'org.junit.jupiter:junit-jupiter-engine:5.1.0' | ||
) | ||
} | ||
|
||
test { | ||
useJUnitPlatform() | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package com.students.results.redis.configuration | ||
|
||
import org.springframework.context.annotation.Bean | ||
import org.springframework.context.annotation.Configuration | ||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory | ||
import org.springframework.data.redis.core.RedisTemplate | ||
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories | ||
|
||
@Configuration | ||
@EnableRedisRepositories | ||
class RedisConfiguration { | ||
|
||
@Bean | ||
fun connectionFactory() = JedisConnectionFactory() | ||
|
||
@Bean | ||
fun redisTemplate(jedisConnectionFactory: JedisConnectionFactory) = RedisTemplate<Any, Any>().apply { | ||
connectionFactory = jedisConnectionFactory | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package repository.impl | ||
|
||
import arrow.core.Either | ||
import com.students.results.entities.Exam | ||
import com.students.results.repository.ExamsRepository | ||
import com.students.results.repository.NotFoundException | ||
|
||
class ExamsRepositoryImpl : ExamsRepository { | ||
override fun findExamById(examId: Long): Either<NotFoundException, Exam> { | ||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates. | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package repository.impl | ||
|
||
import arrow.core.Either | ||
import com.students.results.entities.Student | ||
import com.students.results.repository.RepositoryException | ||
import com.students.results.repository.StudentsRepository | ||
|
||
class StudentsRepositoryImpl : StudentsRepository { | ||
|
||
override fun findStudentById(studentId: Long): Either<RepositoryException, Student> { | ||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates. | ||
} | ||
|
||
override fun save(student: Student): Either<RepositoryException, Unit> { | ||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates. | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package embeded.redis.startup | ||
|
||
import com.students.results.redis.configuration.RedisConfiguration | ||
import org.junit.jupiter.api.AfterAll | ||
import org.junit.jupiter.api.BeforeAll | ||
import org.junit.jupiter.api.Test | ||
import org.junit.jupiter.api.TestInstance | ||
import org.junit.jupiter.api.TestInstance.* | ||
import org.junit.jupiter.api.extension.ExtendWith | ||
import org.junit.runner.RunWith | ||
import org.springframework.beans.factory.annotation.Autowired | ||
import org.springframework.test.context.ContextConfiguration | ||
import org.springframework.test.context.junit.jupiter.SpringExtension | ||
import org.springframework.test.context.junit4.SpringRunner | ||
import redis.embedded.RedisServer | ||
|
||
@ExtendWith(SpringExtension::class) | ||
@ContextConfiguration(classes = [RedisConfiguration::class, RedisEmbededConfiguration::class]) | ||
@TestInstance(Lifecycle.PER_CLASS) | ||
abstract class RedisTestConfiguration protected constructor() { | ||
@Autowired | ||
private lateinit var redisServer: RedisServer | ||
|
||
@BeforeAll | ||
fun beforeStart() { | ||
redisServer.start() | ||
} | ||
|
||
@AfterAll | ||
fun afterStart() { | ||
redisServer.stop() | ||
} | ||
} | ||
|
||
class EmbededRedisTest : RedisTestConfiguration() { | ||
|
||
|
||
@Test | ||
fun runATEst() { | ||
println("I am running") | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops ! get grade !