Skip to content

Instantly share code, notes, and snippets.

View supermanue's full-sized avatar

Manuel Rodríguez Pascual supermanue

View GitHub Profile
@supermanue
supermanue / sbt_extract.sbt
Last active April 4, 2022 06:43
SBT extract
val DoobieVersion = "0.12.1"
libraryDependencies ++= Seq(
"org.tpolecat" %% "doobie-core" % DoobieVersion,
"org.tpolecat" %% "doobie-h2" % DoobieVersion,
//needed for functional stuff
"dev.zio" %% "zio-interop-cats" % "2.0.0.0-RC12"
)
object UserServiceTest extends DefaultRunnableSpec with DomainFixtures {
def spec: ZSpec[TestEnvironment, Failure] =
suite("UserService unit test")(
testM("get a non existing user should fail") {
assertM(getUser(100).run)(fails(anything))
},
testM("create a user then get it should return the same user ") {
checkM(userGen) { user =>
@supermanue
supermanue / testDB.scala
Created March 26, 2022 18:30
testing DB
case class TestDB(users: Ref[Vector[User]]) extends StoragePort {
def get(id: Int): IO[AppError, User] =
users.get.flatMap(users => IO.require(UserNotFound(id))(Task.succeed(users.find(_.id.value == id))))
def create(user: User): IO[AppError, User] =
users.update(_ :+ user).map(_ => user)
def delete(id: Int): Task[Boolean] =
users.modify(users => true -> users.filterNot(_.id.value == id))
}
object TestDB {
@supermanue
supermanue / userService.scala
Created March 26, 2022 18:25
UserService class
object UserService {
def getUser(id: Int): ZIO[UserPersistence, AppError, User] = RIO.accessM(_.get.get(id))
def createUser(id: Int, name: String): ZIO[UserPersistence, AppError, User] =
for {
user <- ZIO.fromEither(User.build(id, name))
stored <- RIO.accessM[UserPersistence](_.get.create(user))
} yield stored
def deleteUser(id: Int): RIO[UserPersistence, Boolean] = RIO.accessM(_.get.delete(id))
}
object UserTest extends DefaultRunnableSpec with DomainFixtures {
def spec: ZSpec[TestEnvironment, Failure] =
suite("UserTest")(
testM("creating a user with valid input should succeed") {
check(positiveIntGen, nonemptyStringGen) { (positiveInt, nonemptyString) =>
val user = User.build(positiveInt, nonemptyString)
assert(user)(isRight)
}
},
testM("creating a user with id=0 should fail") {
@supermanue
supermanue / fixtureTesting.scala
Created March 26, 2022 17:33
Fixture testing
object FixturesTest extends DefaultRunnableSpec with DomainFixtures {
def spec: ZSpec[TestEnvironment, Failure] =
suite("Fixtures test")(
testM("positiveIntGen generates positive ints") {
check(positiveIntGen) { positiveInt =>
assert(positiveInt > 0)(isTrue)
}
},
testM("nonemptyStringGen generates nonempty strings") {
@supermanue
supermanue / typeGenerators.scala
Created March 26, 2022 17:31
type generators
trait DomainFixtures {
def positiveIntGen: Gen[Random, Int] = anyInt.map(num => math.abs(num) + 1)
def nonemptyStringGen: Gen[Random with Sized, String] = (anyASCIIString <*> anyChar).map(elems => elems._1 + elems._2)
def userGen: Gen[Random with Sized, User] =
(positiveIntGen <*> nonemptyStringGen).map(elems =>
User.build(elems._1, elems._2).getOrElse(throw new Exception("Exception in test building customer"))
)
}
@supermanue
supermanue / refinedTypesForUsers.scala
Created March 22, 2022 07:35
Refined Types for users
type Name = String Refined NameRestrictions
type Id = Int Refined IdRestrictions
//string between 1 and 1000 characters, not all of them white
type NameRestrictions = Size[Interval.Closed[W.`1`.T, W.`1000`.T]] And Not[Forall[Whitespace]]
type IdRestrictions = Positive
private def toRefinedId(id: Int): Either[RefinedTypeError, Id] =
refineV[IdRestrictions](id).left
.map(_ => RefinedTypeError("must be a positive int", id.toString))
@supermanue
supermanue / userWithRefinedTypes.scala
Created March 22, 2022 07:28
User with Refined types
object User {
sealed abstract case class User private (id: Id, name: Name)
object User {
def build(id: Int, name: String): Either[AppError, User] =
for {
refinedId <- toRefinedId(id)
refinedName <- toRefinedName(name)
} yield new User(refinedId, refinedName) {}
}
// previous GIST with refined type stuff
@supermanue
supermanue / usingUsers.scala
Last active March 22, 2022 07:37
Using users
//creating
val maybeUser: Either[RefinedTypeError, User] = User.build(input.id, input.name)
//accesing
val id = user.id.value
val name = user.name.value
//create user in the UserService needs a for comprehension
def createUser(id: Int, name: String): ZIO[UserPersistence, AppError, User] =