Skip to content
This repository has been archived by the owner on Mar 22, 2021. It is now read-only.

Commit

Permalink
VCFEF-216 Added traits with enforcing constraints on the value types
Browse files Browse the repository at this point in the history
  • Loading branch information
Jakub Chrobasik committed Sep 28, 2016
1 parent 41d7d84 commit dd466cf
Show file tree
Hide file tree
Showing 20 changed files with 379 additions and 40 deletions.
10 changes: 5 additions & 5 deletions project/VoaBuild.scala → project/LibraryBuild.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import uk.gov.hmrc.SbtAutoBuildPlugin
import uk.gov.hmrc.versioning.SbtGitVersioning
import scoverage.ScoverageKeys

object VoaBuild extends Build {
object LibraryBuild extends Build {

val appName = "valuetype"

Expand All @@ -22,7 +22,7 @@ object VoaBuild extends Build {
.settings(scalaSettings ++ scoverageSettings: _*)
.settings(
targetJvm := "jvm-1.8",
libraryDependencies ++= AppDependencies(),
libraryDependencies ++= LibraryDependencies(),
resolvers := Seq(
Resolver.bintrayRepo("hmrc", "releases"), "typesafe-releases" at "http://repo.typesafe.com/typesafe/releases/"
),
Expand All @@ -31,17 +31,17 @@ object VoaBuild extends Build {

}

private object AppDependencies {
private object LibraryDependencies {

val compile = Seq(
"com.typesafe.play" %% "play" % "2.3.10" % "provided",
"com.typesafe.play" %% "play-json" % "2.3.10" % "provided"
)

val test = Seq(
"org.scalacheck" %% "scalacheck" % "1.12.5" % "test",
"org.scalatest" %% "scalatest" % "2.2.6" % "test",
"org.pegdown" % "pegdown" % "1.4.2" % "test",
"org.scalacheck" %% "scalacheck" % "1.12.5" % "test"
"org.pegdown" % "pegdown" % "1.4.2" % "test"
)

def apply() = compile ++ test
Expand Down
14 changes: 13 additions & 1 deletion src/main/scala/uk/gov/voa/valuetype/ValueType.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package uk.gov.voa.valuetype
import scala.math.BigDecimal.RoundingMode
import scala.math.BigDecimal.RoundingMode.RoundingMode

trait ValueType[T] {
trait ValueType[T] extends TypeName {

def value: T

Expand Down Expand Up @@ -55,3 +55,15 @@ trait RoundedBigDecimalValue extends ValueType[BigDecimal] {

final override def hashCode: Int = (41 * value).toInt
}

trait TypeName {

protected lazy val typeName = getClass.getSimpleName.foldLeft("" -> false) { case ((name, wasDollarFound), char) =>
if (wasDollarFound) name -> true
else char match {
case c if c == '$' => name -> true
case c => s"$name$char" -> false
}
}._1

}
27 changes: 27 additions & 0 deletions src/main/scala/uk/gov/voa/valuetype/constraints/NonEmpty.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2016 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package uk.gov.voa.valuetype.constraints

import uk.gov.voa.valuetype.StringValue

trait NonEmpty {

self: StringValue =>

require(value.length > 0, s"$typeName cannot be empty")

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2016 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package uk.gov.voa.valuetype.constraints

import uk.gov.voa.valuetype.{IntValue, LongValue, ValueType}

trait PositiveInt extends IntValue {

require(value > 0, s"$typeName's value has to be positive")

}

trait PositiveLong extends LongValue {

require(value > 0, s"$typeName's value has to be positive")

}

trait PositiveBigDecimal {

self: ValueType[BigDecimal] =>

require(value > 0, s"$typeName's value has to be positive")

}
4 changes: 2 additions & 2 deletions src/test/scala/uk/gov/voa/valuetype/IntApplySpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@

package uk.gov.voa.valuetype

import org.scalatest.{Matchers, WordSpec}
import uk.gov.voa.valuetype.tooling.UnitSpec

class IntApplySpec extends WordSpec with Matchers {
class IntApplySpec extends UnitSpec {

sealed trait TestValue extends IntValue

Expand Down
4 changes: 2 additions & 2 deletions src/test/scala/uk/gov/voa/valuetype/IntOptionsSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@

package uk.gov.voa.valuetype

import org.scalatest.{Matchers, WordSpec}
import uk.gov.voa.valuetype.tooling.UnitSpec

class IntOptionsSpec extends WordSpec with Matchers {
class IntOptionsSpec extends UnitSpec {

import TestIntOption._

Expand Down
4 changes: 2 additions & 2 deletions src/test/scala/uk/gov/voa/valuetype/LongApplySpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@

package uk.gov.voa.valuetype

import org.scalatest.{Matchers, WordSpec}
import uk.gov.voa.valuetype.tooling.UnitSpec

class LongApplySpec extends WordSpec with Matchers {
class LongApplySpec extends UnitSpec {

sealed trait TestValue extends LongValue

Expand Down
4 changes: 2 additions & 2 deletions src/test/scala/uk/gov/voa/valuetype/LongOptionsSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@

package uk.gov.voa.valuetype

import org.scalatest.{Matchers, WordSpec}
import uk.gov.voa.valuetype.tooling.UnitSpec

class LongOptionsSpec extends WordSpec with Matchers {
class LongOptionsSpec extends UnitSpec {

import TestLongOption._

Expand Down
6 changes: 3 additions & 3 deletions src/test/scala/uk/gov/voa/valuetype/StringApplySpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@

package uk.gov.voa.valuetype

import org.scalatest.{Matchers, WordSpec}
import uk.gov.voa.valuetype.tooling.UnitSpec

class StringApplySpec extends WordSpec with Matchers {
class StringApplySpec extends UnitSpec {

sealed trait TestValue extends StringValue

Expand All @@ -42,7 +42,7 @@ class StringApplySpec extends WordSpec with Matchers {
}

"throw an IllegalArgumentException for an invalid value" in {
an [IllegalArgumentException] should be thrownBy TestValue("C")
an[IllegalArgumentException] should be thrownBy TestValue("C")
}
}
}
4 changes: 2 additions & 2 deletions src/test/scala/uk/gov/voa/valuetype/StringOptionsSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@

package uk.gov.voa.valuetype

import org.scalatest.{Matchers, WordSpec}
import uk.gov.voa.valuetype.tooling.UnitSpec

class StringOptionsSpec extends WordSpec with Matchers {
class StringOptionsSpec extends UnitSpec {

import TestStringOption._

Expand Down
42 changes: 35 additions & 7 deletions src/test/scala/uk/gov/voa/valuetype/ValueTypeSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ package uk.gov.voa.valuetype
import org.scalacheck.Arbitrary._
import org.scalacheck.Gen
import org.scalatest.prop.PropertyChecks
import org.scalatest.{Matchers, WordSpec}
import uk.gov.voa.valuetype.tooling.UnitSpec

import scala.math.BigDecimal.RoundingMode

class StringValueSpec extends WordSpec with PropertyChecks with Matchers {
class StringValueSpec extends UnitSpec with PropertyChecks {

val generatedStrings = Gen.alphaStr

Expand All @@ -46,7 +46,7 @@ class StringValueSpec extends WordSpec with PropertyChecks with Matchers {
}
}

class IntValueSpec extends WordSpec with PropertyChecks with Matchers {
class IntValueSpec extends UnitSpec with PropertyChecks {

val generatedInts = Gen.choose(Int.MinValue, Int.MaxValue)

Expand All @@ -69,7 +69,7 @@ class IntValueSpec extends WordSpec with PropertyChecks with Matchers {
}
}

class LongValueSpec extends WordSpec with PropertyChecks with Matchers {
class LongValueSpec extends UnitSpec with PropertyChecks {

val generatedLongs = Gen.choose(Long.MinValue, Long.MaxValue)

Expand All @@ -92,7 +92,7 @@ class LongValueSpec extends WordSpec with PropertyChecks with Matchers {
}
}

class BooleanValueSpec extends WordSpec with PropertyChecks with Matchers {
class BooleanValueSpec extends UnitSpec with PropertyChecks {

val booleans = Table("Boolean", true, false)

Expand All @@ -115,7 +115,7 @@ class BooleanValueSpec extends WordSpec with PropertyChecks with Matchers {
}
}

class BigDecimalValueSpec extends WordSpec with PropertyChecks with Matchers {
class BigDecimalValueSpec extends UnitSpec with PropertyChecks {

val generatedBigDecimals = arbitrary[BigDecimal]

Expand All @@ -138,7 +138,7 @@ class BigDecimalValueSpec extends WordSpec with PropertyChecks with Matchers {
}
}

class RoundedBigDecimalValueSpec extends WordSpec with PropertyChecks with Matchers {
class RoundedBigDecimalValueSpec extends UnitSpec with PropertyChecks {

case class AnotherRoundedBigDecimalValue(nonRoundedValue: BigDecimal) extends RoundedBigDecimalValue {

Expand Down Expand Up @@ -220,3 +220,31 @@ class RoundedBigDecimalValueSpec extends WordSpec with PropertyChecks with Match
}
}
}

private case class NotNestedType(value: String) extends StringValue {
val name = typeName
}

class TypeNameSpec extends UnitSpec {


"TypeName" should {

"provide type name" in {
case class NestedType(value: String) extends StringValue {
val name = typeName
}

NotNestedType("abc").name shouldBe "NotNestedType"
}

"provide type name without dollar sign for nested types" in {
case class NestedType(value: String) extends StringValue {
val name = typeName
}

NestedType("abc").name shouldBe "NestedType"
}

}
}
41 changes: 41 additions & 0 deletions src/test/scala/uk/gov/voa/valuetype/constraints/NonEmptySpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2016 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package uk.gov.voa.valuetype.constraints

import org.scalatest.prop.PropertyChecks
import uk.gov.voa.valuetype.StringValue
import uk.gov.voa.valuetype.tooling.UnitSpec
import uk.gov.voa.valuetype.tooling.generators.GeneratorOf.nonEmptyStrings

class NonEmptySpec extends UnitSpec with PropertyChecks {

private case class TestNonEmpty(value: String) extends StringValue with NonEmpty

"NonEmpty" should {

"allow instantiation with any non-empty String" in {
forAll(nonEmptyStrings()) { value =>
TestNonEmpty(value).value shouldBe value
}
}

"throw an IllegalArgumentException when instantiating with an empty String" in {
val exception = the[IllegalArgumentException] thrownBy TestNonEmpty("")
exception.getMessage shouldBe "requirement failed: TestNonEmpty cannot be empty"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2016 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package uk.gov.voa.valuetype.constraints

import org.scalatest.prop.PropertyChecks
import uk.gov.voa.valuetype.BigDecimalValue
import uk.gov.voa.valuetype.tooling.UnitSpec
import uk.gov.voa.valuetype.tooling.generators.GeneratorOf._

class PositiveBigDecimalSpec extends UnitSpec with PropertyChecks {

private case class TestPositiveBigDecimal(value: BigDecimal) extends BigDecimalValue with PositiveBigDecimal

"PositiveBigDecimal" should {

"allow instantiation with any positive number" in {
forAll(positiveBigDecimal) { value =>
TestPositiveBigDecimal(value).value shouldBe value
}
}

"throw an IllegalArgumentException when instantiating with zero" in {
val exception = the[IllegalArgumentException] thrownBy TestPositiveBigDecimal(0)
exception.getMessage shouldBe "requirement failed: TestPositiveBigDecimal's value has to be positive"
}

"throw an IllegalArgumentException when instantiating with a negative value" in {
val exception = the[IllegalArgumentException] thrownBy TestPositiveBigDecimal(-1)
exception.getMessage shouldBe "requirement failed: TestPositiveBigDecimal's value has to be positive"
}
}
}
Loading

0 comments on commit dd466cf

Please sign in to comment.