Skip to content

Commit

Permalink
Close #190 - [refined4s-cats] Add validateNelAs in refined4s.modules.…
Browse files Browse the repository at this point in the history
…cats.syntax to validate and return ValidatedNel
  • Loading branch information
kevin-lee committed Dec 29, 2023
1 parent 2b954a8 commit af5a557
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ trait syntax {
.toValidatedNec
.map(coercible(_))

inline def validateNelAs[N](
using coercible: Coercible[T, N],
refinedCtor: RefinedCtor[T, A],
): ValidatedNel[String, N] =
a.refinedTo[T]
.leftMap(err => s"Failed to create ${getTypeName[N]}: $err")
.toValidatedNel
.map(coercible(_))

}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ object syntaxSpec extends Properties {
RefinedNewtypeNec.tests ++
RefinedNewtypeNel.tests ++
ValidateAsSpec.tests ++
ValidateNecAsSpec.tests
ValidateNecAsSpec.tests ++
ValidateNelAsSpec.tests

object RefinedNewtypeNec {

Expand Down Expand Up @@ -575,6 +576,145 @@ object syntaxSpec extends Properties {
}
}

object ValidateNelAsSpec {

def tests: List[Test] = List(
///
property(
"For type T = Refined[A] and type N = NewType[T], a.validateNelAs[N] with a valid `a` should return EitherNel[String, N] = Right(N)",
testAValidateNelAsT,
),
example(
"For type T = Refined[A] and type N = NewType[T], a.validateNelAs[N] with an invalid `a` should return EitherNel[String, N] = Left(String)",
testAValidateNelAsTInvalid,
),
property(
"For type T = Refined[A] and type N = NewType[T], validateNelAs(a)[N] with a valid `a` should return EitherNel[String, N] = Right(N)",
testValidateNelAsTA,
),
example(
"For type T = Refined[A] and type N = NewType[T], validateNelAs(a)[N] with an invalid `a` should return EitherNel[String, N] = Left(String)",
testValidateNelAsTAInvalid,
),
///
property(
"For type T = InlinedRefined[A] and type N = NewType[T], a.validateNelAs[N] with a valid `a` should return EitherNel[String, N] = Right(N)",
testInlinedRefined_AValidateNelAsT,
),
property(
"For type T = InlinedRefined[A] and type N = NewType[T], a.validateNelAs[N] with an invalid `a` should return EitherNel[String, N] = Left(String)",
testInlinedRefined_AValidateNelAsTInvalid,
),
property(
"For type T = InlinedRefined[A] and type N = NewType[T], validateNelAs(a)[N] with a valid `a` should return EitherNel[String, N] = Right(N)",
testInlinedRefined_ValidateNelAsTA,
),
property(
"For type T = InlinedRefined[A] and type N = NewType[T], validateNelAs(a)[N] with an invalid `a` should return EitherNel[String, N] = Left(String)",
testInlinedRefined_ValidateNelAsTAInvalid,
),
)

def testAValidateNelAsT: Property =
for {
s <- Gen.string(Gen.unicode, Range.linear(1, 10)).log("s")
} yield {

val expected = NewMyType(MyType.unsafeFrom(s)).validNel[String]
val actual = s.validateNelAs[NewMyType]
actual ==== expected
}

def testAValidateNelAsTInvalid: Result = {
val expected =
"Failed to create refined4s.modules.cats.syntaxSpec.NewMyType: Invalid value: []. It has to be a non-empty String but got \"\""
.invalidNel[NewMyType]
val actual = "".validateNelAs[NewMyType]
actual ==== expected
}

def testValidateNelAsTA: Property =
for {
s <- Gen.string(Gen.unicode, Range.linear(1, 10)).log("s")
} yield {

val expected = NewMyType(MyType.unsafeFrom(s)).validNel[String]
val actual = validateNelAs(s)[NewMyType]
actual ==== expected
}

def testValidateNelAsTAInvalid: Result = {
val expected =
"Failed to create refined4s.modules.cats.syntaxSpec.NewMyType: Invalid value: []. It has to be a non-empty String but got \"\""
.invalidNel[NewMyType]
val actual = validateNelAs("")[NewMyType]
actual ==== expected
}

def testInlinedRefined_AValidateNelAsT: Property =
for {
s <- Gen.string(Gen.unicode, Range.linear(3, 10)).log("s")
} yield {

val expected = NewMoreThan2CharsString(MoreThan2CharsString.unsafeFrom(s)).validNel[String]
val actual = s.validateNelAs[NewMoreThan2CharsString]
(actual ==== expected).log(
raw""" s: ${s.encodeToUnicode}
| actual: ${actual.leftMap(_.map(_.encodeToUnicode))}
|expected: ${expected.leftMap(_.map(_.encodeToUnicode))}
|""".stripMargin
)
}

def testInlinedRefined_AValidateNelAsTInvalid: Property =
for {
s <- Gen.string(Gen.unicode, Range.linear(0, 2)).log("s")
} yield {
val expected =
s"Failed to create refined4s.modules.cats.syntaxSpec.NewMoreThan2CharsString: Invalid value: [$s]. The String should have more than 2 chars but got $s instead"
.invalidNel[NewMoreThan2CharsString]

val actual = s.validateNelAs[NewMoreThan2CharsString]
(actual ==== expected).log(
raw""" s: ${s.encodeToUnicode}
| actual: ${actual.leftMap(_.map(_.encodeToUnicode))}
|expected: ${expected.leftMap(_.map(_.encodeToUnicode))}
|""".stripMargin
)
}

def testInlinedRefined_ValidateNelAsTA: Property =
for {
s <- Gen.string(Gen.unicode, Range.linear(3, 10)).log("s")
} yield {

val expected = NewMoreThan2CharsString(MoreThan2CharsString.unsafeFrom(s)).validNel[String]
val actual = validateNelAs(s)[NewMoreThan2CharsString]
(actual ==== expected).log(
raw""" s: ${s.encodeToUnicode}
| actual: ${actual.leftMap(_.map(_.encodeToUnicode))}
|expected: ${expected.leftMap(_.map(_.encodeToUnicode))}
|""".stripMargin
)
}

def testInlinedRefined_ValidateNelAsTAInvalid: Property =
for {
s <- Gen.string(Gen.unicode, Range.linear(0, 2)).log("s")
} yield {
val expected =
s"Failed to create refined4s.modules.cats.syntaxSpec.NewMoreThan2CharsString: Invalid value: [$s]. The String should have more than 2 chars but got $s instead"
.invalidNel[NewMoreThan2CharsString]
val actual = validateNelAs(s)[NewMoreThan2CharsString]
(actual ==== expected).log(
raw""" s: ${s.encodeToUnicode}
| actual: ${actual.leftMap(_.map(_.encodeToUnicode))}
|expected: ${expected.leftMap(_.map(_.encodeToUnicode))}
|""".stripMargin
)
}
}

///

type MyType = MyType.Type
Expand Down

0 comments on commit af5a557

Please sign in to comment.