-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into add_zio_cache_quickstart
- Loading branch information
Showing
5 changed files
with
238 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
scalaVersion := "2.13.13" | ||
|
||
libraryDependencies ++= Seq( | ||
"dev.zio" %% "zio-prelude" % "1.0.0-RC23", | ||
"dev.zio" %% "zio-test" % "2.0.22" % Test, | ||
"dev.zio" %% "zio-test-sbt" % "2.0.22" % Test, | ||
"dev.zio" %% "zio-test-junit" % "2.0.22" % Test | ||
) |
54 changes: 54 additions & 0 deletions
54
zio-quickstart-prelude/src/test/scala/AssociativeSpec.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import zio._ | ||
import zio.test.assertTrue | ||
import zio.prelude._ | ||
import zio.prelude.newtypes.Max | ||
import zio.test.{Spec, TestEnvironment} | ||
import zio.test.junit.JUnitRunnableSpec | ||
|
||
object AssociativeSpec extends JUnitRunnableSpec { | ||
object Topic extends Newtype[String] | ||
type Topic = Topic.Type | ||
|
||
object Votes extends Subtype[Int] { | ||
implicit val Associative: Associative[Votes] = new Associative[Votes] { | ||
override def combine(l: => Votes, r: => Votes): Votes = Votes(l + r) | ||
} | ||
} | ||
type Votes = Votes.Type | ||
|
||
override def spec: Spec[TestEnvironment with Scope, Any] = | ||
suite("Associative")( | ||
test("combine custom class") { | ||
|
||
case class VoteMap(wrapped: Map[Topic, Votes]) | ||
object VoteMap { | ||
implicit val Associative: Associative[VoteMap] = | ||
new Associative[VoteMap] { | ||
override def combine(l: => VoteMap, r: => VoteMap): VoteMap = | ||
VoteMap(l.wrapped <> r.wrapped) | ||
} | ||
} | ||
|
||
val vm1 = VoteMap( | ||
Map(Topic("newType") -> Votes(3), Topic("associative") -> Votes(1)) | ||
) | ||
val vm2 = VoteMap( | ||
Map(Topic("associative") -> Votes(6), Topic("prelude") -> Votes(2)) | ||
) | ||
val resultVm = | ||
VoteMap( | ||
Map( | ||
Topic("newType") -> Votes(3), | ||
Topic("associative") -> Votes(7), | ||
Topic("prelude") -> Votes(2) | ||
) | ||
) | ||
assertTrue(vm1 <> vm2 == resultVm) | ||
}, | ||
test("combine as max using Max newtype") { | ||
val rawValues = Seq(100, 262, 131, 66) | ||
val maxValues: Seq[Max[Int]] = Max.wrapAll(rawValues) | ||
assertTrue(maxValues.reduce(_ <> _) === rawValues.max) | ||
} | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import zio._ | ||
import zio.test.assertTrue | ||
import zio.prelude._ | ||
import zio.prelude.newtypes._ | ||
import zio.test.junit.JUnitRunnableSpec | ||
|
||
object NewTypeSpec extends JUnitRunnableSpec { | ||
override def spec = suite("Newtype")( | ||
test("natural") { | ||
// new types to increase the type safety without compromising performance or ergonomics | ||
// Natural new type is an Int type with assertion on greater or equal to 0 | ||
val five: Validation[String, Int] = Natural.make(5) | ||
val notNatural: Validation[String, Natural] = Natural.make(-2) | ||
|
||
assertTrue(five.toOption.contains(5)) | ||
assertTrue( | ||
notNatural == Validation.failNonEmptyChunk( | ||
NonEmptyChunk("-2 did not satisfy greaterThanOrEqualTo(0)") | ||
) | ||
) | ||
assertTrue(Natural.zero - Natural.one == -1) // unsafe manipulations | ||
assertTrue( | ||
Natural.minus(Natural.zero, Natural.one) == Natural.zero | ||
) // safe manipulations | ||
}, | ||
test("custom assertion") { | ||
// you can define your own value type with assertion | ||
object MyType extends Subtype[String] { | ||
override def assertion: QuotedAssertion[String] = (value: String) => | ||
Either.cond( | ||
value.startsWith("!"), | ||
value, | ||
AssertionError.failure(s"must start with exclamation mark") | ||
) | ||
} | ||
|
||
val valid: Validation[String, String] = MyType.make("!Hello!") | ||
val notValid: Validation[String, String] = MyType.make("NotValidString") | ||
|
||
assertTrue(valid.toOption.contains("!Hello!")) | ||
assertTrue( | ||
notValid == Validation.failNonEmptyChunk( | ||
NonEmptyChunk.single( | ||
"NotValidString did not satisfy must start with exclamation mark" | ||
) | ||
) | ||
) | ||
} | ||
) | ||
} |
123 changes: 123 additions & 0 deletions
123
zio-quickstart-prelude/src/test/scala/ValidationSpec.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import zio._ | ||
import zio.prelude._ | ||
import zio.test.{Spec, TestEnvironment, assertTrue} | ||
import zio.test.junit.JUnitRunnableSpec | ||
|
||
object ValidationSpec extends JUnitRunnableSpec { | ||
override def spec: Spec[TestEnvironment with Scope, Any] = | ||
suite("Validation")( | ||
test("validate class") { | ||
case class Person(name: String, age: Int) | ||
|
||
def validateName(name: String): Validation[String, String] = | ||
if (name.isEmpty) Validation.fail("Name was empty") | ||
else Validation.succeed(name) | ||
|
||
def validateAge(age: Int): Validation[String, Int] = | ||
Validation.fromPredicateWith(s"Age $age was less than zero")(age)( | ||
_ >= 0 | ||
) | ||
|
||
def validatePerson(name: String, age: Int): Validation[String, Person] = | ||
Validation.validateWith(validateName(name), validateAge(age))( | ||
Person.apply | ||
) | ||
|
||
assertTrue( | ||
validatePerson("Grisha", 25).toOption.contains(Person("Grisha", 25)) | ||
) | ||
assertTrue( | ||
validatePerson("", -5) == Validation.failNonEmptyChunk( | ||
NonEmptyChunk("Name was empty", "Age -5 was less than zero") | ||
) | ||
) | ||
}, | ||
test("validate newtype") { | ||
object Name extends Subtype[String] { | ||
override def assertion = assert(!Assertion.isEmptyString) | ||
} | ||
type Name = Name.Type | ||
object Age extends Subtype[Int] { | ||
override def assertion = assert(Assertion.greaterThanOrEqualTo(0)) | ||
} | ||
type Age = Age.Type | ||
|
||
case class Person(name: Name, age: Age) | ||
|
||
def validatePerson(name: String, age: Int) = | ||
Validation.validateWith(Name.make(name), Age.make(age))(Person.apply) | ||
|
||
assertTrue( | ||
validatePerson("Grisha", 25).toOption.contains( | ||
Person(Name("Grisha"), Age(25)) | ||
) | ||
) | ||
assertTrue( | ||
validatePerson("", -5) == Validation.failNonEmptyChunk( | ||
NonEmptyChunk( | ||
" did not satisfy hasLength(notEqualTo(0))", | ||
"-5 did not satisfy greaterThanOrEqualTo(0)" | ||
) | ||
) | ||
) | ||
}, | ||
test("chaining validations") { | ||
object Age extends Subtype[Int] { | ||
override def assertion = assert(Assertion.greaterThanOrEqualTo(0)) | ||
} | ||
type Age = Age.Type | ||
|
||
def validateNonEmpty( | ||
s: String | ||
): Validation[String, NonEmptyList[String]] = | ||
Validation.fromOptionWith( | ||
"String must contain at least one value divided by space character" | ||
)(NonEmptyList.fromIterableOption(s.split(" "))) | ||
|
||
def validateInt(s: String): Validation[String, Int] = | ||
Validation.fromOptionWith(s"String must be int like, but got $s")( | ||
s.toIntOption | ||
) | ||
|
||
def validateAge(i: Int): Validation[String, Age] = Age.make(i) | ||
|
||
def calculateResult( | ||
line: String | ||
): Validation[String, NonEmptyList[Age]] = | ||
for { | ||
strAges <- validateNonEmpty(line) | ||
intAges <- Validation.validateAll(strAges.map(validateInt)) | ||
ages <- Validation.validateAll(intAges.map(validateAge)) | ||
} yield ages | ||
|
||
val result1 = calculateResult("12 10 5") | ||
val result2 = calculateResult("12 -5 -2") | ||
val result3 = calculateResult("") | ||
val result4 = calculateResult("12 _f") | ||
|
||
assertTrue( | ||
result1.toOption.get === NonEmptyList(Age(12), Age(10), Age(5)) | ||
) | ||
assertTrue( | ||
result2 == Validation.failNonEmptyChunk( | ||
NonEmptyChunk( | ||
"-5 did not satisfy greaterThanOrEqualTo(0)", | ||
"-2 did not satisfy greaterThanOrEqualTo(0)" | ||
) | ||
) | ||
) | ||
assertTrue( | ||
result3 == Validation.failNonEmptyChunk( | ||
NonEmptyChunk.single( | ||
"String must contain at least one value divided by space character" | ||
) | ||
) | ||
) | ||
assertTrue( | ||
result4 == Validation.failNonEmptyChunk( | ||
NonEmptyChunk.single("String must be int like, but got _f") | ||
) | ||
) | ||
} | ||
) | ||
} |