Skip to content

Commit

Permalink
More workout score validations
Browse files Browse the repository at this point in the history
  • Loading branch information
khy committed Dec 1, 2016
1 parent 5f8f0a3 commit ce6ffa8
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 39 deletions.
30 changes: 18 additions & 12 deletions modules/apis/workouts/app/services/WorkoutsService.scala
Expand Up @@ -57,35 +57,41 @@ class WorkoutsService(
val movementsQuery = Movements.filter(_.guid.inSet(movementGuids))

db.run(movementsQuery.result).flatMap { movements =>
var errors = Seq.empty[Errors]

// Validation 1: Find unknown workout guids
// Validate that all workout GUIDs are known
val badGuids = movementGuids.filterNot { movementGuid =>
movements.map(_.guid).contains(movementGuid)
}

val optUnknownGuidErrors = if (badGuids.size > 0) {
Some(Errors.scalar(badGuids.map { badGuid =>
if (badGuids.size > 0) {
errors = errors :+ Errors.scalar(badGuids.map { badGuid =>
Message(
key = "unknownWorkoutGuid",
details = "guid" -> badGuid.toString
)
}))
} else {
None
})
}

// Validation 2: Ensure that there's exactly one score
val scores = workout.score.toSeq ++
workout.movement.flatMap(_.score).toSeq ++
workout.tasks.map(_.flatMap(_.movement.flatMap(_.score))).getOrElse(Nil)

val optScoreErrors = if (scores.size == 0) {
Some(Errors.scalar(Seq(Message(key = "noScoreSpecified"))))
} else {
None
// Validate that there's exactly one score
if (scores.size == 0) {
errors = errors :+ Errors.scalar(Seq(Message(key = "noScoreSpecified")))
}

if (scores.size > 1) {
errors = errors :+ Errors.scalar(Seq(Message(key = "multipleScoresSpecified")))
}

val errors = optUnknownGuidErrors.toSeq ++ optScoreErrors.toSeq
// Validate that, if there's a top-level score, that it's either 'time' or 'reps'
workout.score.foreach { topLevelScore =>
if (topLevelScore != "time" && topLevelScore != "reps") {
errors = errors :+ Errors.scalar(Seq(Message(key = "invalidTopLevelScore", "score" -> topLevelScore)))
}
}

if (!errors.isEmpty) {
Future.successful(Validation.failure(errors))
Expand Down
27 changes: 0 additions & 27 deletions modules/apis/workouts/test/integration/WorkoutsSpec.scala
Expand Up @@ -53,33 +53,6 @@ class WorkoutsSpec extends IntegrationSpec {
message.details("guid") mustBe badMovementGuid.toString
}

"reject a workout that has no score" in {
val movement = testHelper.createMovement()
val response = await { request("/workouts").post(Json.parse(s"""
{
"name": "1 Rep",
"reps": 1,
"movement": {
"guid": "${movement.guid}",
"variables": [
{
"name": "Barbell Weight",
"measurement": {
"unitOfMeasure": "lbs",
"value": 95
}
}
]
}
}
""")) }

response.status mustBe BAD_REQUEST
val scalarErrors = response.json.as[Seq[Errors]].head
val message = scalarErrors.messages.head
message.key mustBe "noScoreSpecified"
}

"create a workout with subtasks" in {
val pullUp = testHelper.createMovement("Pull Up")

Expand Down
104 changes: 104 additions & 0 deletions modules/apis/workouts/test/integration/workout/WorkoutScoreSpec.scala
@@ -0,0 +1,104 @@
package test.workouts.integration.workout

import java.util.UUID
import play.api.test._
import play.api.test.Helpers._
import play.api.libs.json._
import io.useless.validation.Errors
import io.useless.play.json.validation.ErrorsJson._

import models.workouts._
import models.workouts.JsonImplicits._
import test.workouts._

class WorkoutScoreSpec extends IntegrationSpec {

val movement = testHelper.createMovement()

"POST /workouts" must {

"reject a workout that has no score" in {
val response = await { request("/workouts").post(Json.parse(s"""
{
"name": "1 Rep",
"reps": 1,
"movement": {
"guid": "${movement.guid}",
"variables": [
{
"name": "Barbell Weight",
"measurement": {
"unitOfMeasure": "lbs",
"value": 95
}
}
]
}
}
""")) }

response.status mustBe BAD_REQUEST
val scalarErrors = response.json.as[Seq[Errors]].head
val message = scalarErrors.messages.head
message.key mustBe "noScoreSpecified"
}

"reject a workout that has multiple scores" in {
val response = await { request("/workouts").post(Json.parse(s"""
{
"name": "1 Rep",
"score": "time",
"reps": 1,
"movement": {
"guid": "${movement.guid}",
"score": "Barbell Weight",
"variables": [
{
"name": "Barbell Weight",
"measurement": {
"unitOfMeasure": "lbs",
"value": 95
}
}
]
}
}
""")) }

response.status mustBe BAD_REQUEST
val scalarErrors = response.json.as[Seq[Errors]].head
val message = scalarErrors.messages.head
message.key mustBe "multipleScoresSpecified"
}

"reject a workout that has a top-level score that is neither 'time' or 'reps'" in {
val response = await { request("/workouts").post(Json.parse(s"""
{
"name": "1 Rep",
"score": "Barbell Weight",
"reps": 1,
"movement": {
"guid": "${movement.guid}",
"variables": [
{
"name": "Barbell Weight",
"measurement": {
"unitOfMeasure": "lbs",
"value": 95
}
}
]
}
}
""")) }

response.status mustBe BAD_REQUEST
val scalarErrors = response.json.as[Seq[Errors]].head
val message = scalarErrors.messages.head
message.key mustBe "invalidTopLevelScore"
message.details("score") mustBe "Barbell Weight"
}

}

}

0 comments on commit ce6ffa8

Please sign in to comment.