From 8ba337f2ce2a1bc0c6808294e7cde0465f08b198 Mon Sep 17 00:00:00 2001 From: Diego Casella Date: Fri, 22 Mar 2024 23:44:57 +0100 Subject: [PATCH 1/2] fix(RE-5): remove obsolete tests --- .../polentino/redacted/RedactedSpec.scala | 61 +++++++------------ .../redacted/RedactionWithCompanionObj.scala | 9 --- .../RedactionWithoutCompanionObj.scala | 5 -- 3 files changed, 23 insertions(+), 52 deletions(-) delete mode 100644 tests/src/test/scala/io/github/polentino/redacted/RedactionWithCompanionObj.scala delete mode 100644 tests/src/test/scala/io/github/polentino/redacted/RedactionWithoutCompanionObj.scala diff --git a/tests/src/test/scala/io/github/polentino/redacted/RedactedSpec.scala b/tests/src/test/scala/io/github/polentino/redacted/RedactedSpec.scala index da92fc7..5ea91c2 100644 --- a/tests/src/test/scala/io/github/polentino/redacted/RedactedSpec.scala +++ b/tests/src/test/scala/io/github/polentino/redacted/RedactedSpec.scala @@ -9,42 +9,47 @@ class RedactedSpec extends AnyFlatSpec { behavior of "@redacted" - it should "work with case classes without user-defined companion object" in { - val name: String = "Berfu" - val age = 26 - val email: String = "berfu@gmail.com" - val expected = s"RedactionWithoutCompanionObj(***,$age,***)" + it should "work with a redacted case class of just one member" in { + case class OneMember(@redacted name: String) + val name = "berfu" + val expected = "OneMember(***)" - val testing = RedactionWithoutCompanionObj(name, age, email) + val testing = OneMember(name) val implicitToString = s"$testing" val explicitToString = testing.toString - val cp = new Checkpoint + val cp = new Checkpoint cp { assert(implicitToString == expected) } cp { assert(explicitToString == expected) } - cp { assert(testing.name == name && testing.age == age && testing.email == email) } + cp { assert(testing.name == name) } cp.reportAll() } - it should "work with case classes with user-defined companion object" in { - val name: String = "Berfu" - val age = 26 - val email: String = "berfu@gmail.com" - val expected = s"RedactionWithCompanionObj(***,$age,***)" + it should "work with a redacted case class with many members" in { + case class ManyMembers(field1: String, @redacted field2: String, @redacted field3: String, field4: String) + val field1 = "field-1" + val field2 = "field-2" + val field3 = "field-3" + val field4 = "field-4" + val expected = s"ManyMembers($field1,***,***,$field4)" - val testing = RedactionWithCompanionObj(name, age, email) + val testing = ManyMembers(field1, field2, field3, field4) val implicitToString = s"$testing" val explicitToString = testing.toString - val cp = new Checkpoint + val cp = new Checkpoint cp { assert(implicitToString == expected) } cp { assert(explicitToString == expected) } - cp { assert(testing.name == name && testing.age == age && testing.email == email) } - cp { assert(RedactionWithCompanionObj.something == 123) } + cp { + assert(testing.field1 == field1 && + testing.field2 == field2 && + testing.field3 == field3 && + testing.field4 == field4) + } cp.reportAll() } - it should "work with nested case classes in object" in { + it should "work with nested case classes" in { val id = "id-1" val name1 = "Diego" val age1 = 999 @@ -57,7 +62,6 @@ class RedactedSpec extends AnyFlatSpec { val explicitToString = testing.toString val cp = new Checkpoint - cp { assert(implicitToString == expected) } cp { assert(explicitToString == expected) } cp { @@ -73,7 +77,6 @@ class RedactedSpec extends AnyFlatSpec { it should "work with nested case classes in case class" in { case class Inner(userId: String, @redacted balance: Int) case class Outer(inner: Inner) - val userId = "user-123" val balance = 123_456_789 val expected = s"Outer(Inner($userId,***))" @@ -83,7 +86,6 @@ class RedactedSpec extends AnyFlatSpec { val explicitToString = testing.toString val cp = new Checkpoint - cp { assert(implicitToString == expected) } cp { assert(explicitToString == expected) } cp { @@ -94,21 +96,4 @@ class RedactedSpec extends AnyFlatSpec { } cp.reportAll() } - - it should "work with a redacted case class of just one member" in { - case class OneMember(@redacted name: String) - val name = "berfu" - val expected = "OneMember(***)" - - val testing = OneMember(name) - val implicitToString = s"$testing" - val explicitToString = testing.toString - - val cp = new Checkpoint - - cp { assert(implicitToString == expected) } - cp { assert(explicitToString == expected) } - cp { assert(testing.name == name) } - cp.reportAll() - } } diff --git a/tests/src/test/scala/io/github/polentino/redacted/RedactionWithCompanionObj.scala b/tests/src/test/scala/io/github/polentino/redacted/RedactionWithCompanionObj.scala deleted file mode 100644 index 8100ee2..0000000 --- a/tests/src/test/scala/io/github/polentino/redacted/RedactionWithCompanionObj.scala +++ /dev/null @@ -1,9 +0,0 @@ -package io.github.polentino.redacted - -import io.github.polentino.redacted.redacted - -case class RedactionWithCompanionObj(@redacted name: String, age: Int, @redacted email: String) - -object RedactionWithCompanionObj { - val something = 123 -} diff --git a/tests/src/test/scala/io/github/polentino/redacted/RedactionWithoutCompanionObj.scala b/tests/src/test/scala/io/github/polentino/redacted/RedactionWithoutCompanionObj.scala deleted file mode 100644 index 7e2ab0d..0000000 --- a/tests/src/test/scala/io/github/polentino/redacted/RedactionWithoutCompanionObj.scala +++ /dev/null @@ -1,5 +0,0 @@ -package io.github.polentino.redacted - -import io.github.polentino.redacted.redacted - -case class RedactionWithoutCompanionObj(@redacted name: String, age: Int, @redacted email: String) From a0e393caaf2c30f8a42764be2ed1c2331852399b Mon Sep 17 00:00:00 2001 From: Diego Casella Date: Sat, 23 Mar 2024 01:18:13 +0100 Subject: [PATCH 2/2] fix(RE-5): use property-based testing * ensure we don't tamper with `hashCode()` --- build.sbt | 5 +- .../polentino/redacted/RedactedSpec.scala | 155 ++++++++++-------- 2 files changed, 87 insertions(+), 73 deletions(-) diff --git a/build.sbt b/build.sbt index 3d637f3..f87e286 100644 --- a/build.sbt +++ b/build.sbt @@ -70,7 +70,10 @@ lazy val redactedTests = (project in file("tests")) .settings(scalafixSettings) .settings( publish / skip := true, - libraryDependencies ++= Seq("org.scalatest" %% "scalatest" % "3.2.17" % Test), + libraryDependencies ++= Seq( + "org.scalatest" %% "scalatest" % "3.2.17" % Test, + "org.scalatestplus" %% "scalacheck-1-17" % "3.2.17.0" % Test + ), scalacOptions ++= { val jar = (redactedCompilerPlugin / assembly).value val addPlugin = "-Xplugin:" + jar.getAbsolutePath diff --git a/tests/src/test/scala/io/github/polentino/redacted/RedactedSpec.scala b/tests/src/test/scala/io/github/polentino/redacted/RedactedSpec.scala index 5ea91c2..19bf6b5 100644 --- a/tests/src/test/scala/io/github/polentino/redacted/RedactedSpec.scala +++ b/tests/src/test/scala/io/github/polentino/redacted/RedactedSpec.scala @@ -2,98 +2,109 @@ package io.github.polentino.redacted import org.scalatest.Checkpoints.* import org.scalatest.flatspec.AnyFlatSpec - import io.github.polentino.redacted.RedactionWithNestedCaseClass.Inner +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks + +import java.util.UUID -class RedactedSpec extends AnyFlatSpec { +class RedactedSpec extends AnyFlatSpec with ScalaCheckPropertyChecks { behavior of "@redacted" it should "work with a redacted case class of just one member" in { case class OneMember(@redacted name: String) - val name = "berfu" - val expected = "OneMember(***)" - - val testing = OneMember(name) - val implicitToString = s"$testing" - val explicitToString = testing.toString - - val cp = new Checkpoint - cp { assert(implicitToString == expected) } - cp { assert(explicitToString == expected) } - cp { assert(testing.name == name) } - cp.reportAll() + + forAll { (name: String) => + val expected = "OneMember(***)" + val testing = OneMember(name) + val implicitToString = s"$testing" + val explicitToString = testing.toString + + val cp = new Checkpoint + cp { assert(implicitToString == expected) } + cp { assert(explicitToString == expected) } + cp { assert(testing.name == name) } + cp.reportAll() + } } it should "work with a redacted case class with many members" in { case class ManyMembers(field1: String, @redacted field2: String, @redacted field3: String, field4: String) - val field1 = "field-1" - val field2 = "field-2" - val field3 = "field-3" - val field4 = "field-4" - val expected = s"ManyMembers($field1,***,***,$field4)" - - val testing = ManyMembers(field1, field2, field3, field4) - val implicitToString = s"$testing" - val explicitToString = testing.toString - - val cp = new Checkpoint - cp { assert(implicitToString == expected) } - cp { assert(explicitToString == expected) } - cp { - assert(testing.field1 == field1 && - testing.field2 == field2 && - testing.field3 == field3 && - testing.field4 == field4) + + forAll { (field1: String, field2: String, field3: String, field4: String) => + val expected = s"ManyMembers($field1,***,***,$field4)" + val testing = ManyMembers(field1, field2, field3, field4) + val implicitToString = s"$testing" + val explicitToString = testing.toString + + val cp = new Checkpoint + cp { assert(implicitToString == expected) } + cp { assert(explicitToString == expected) } + cp { + assert(testing.field1 == field1 && + testing.field2 == field2 && + testing.field3 == field3 && + testing.field4 == field4) + } + cp.reportAll() } - cp.reportAll() } - it should "work with nested case classes" in { - val id = "id-1" - val name1 = "Diego" - val age1 = 999 - val name2 = "Berfu" - val age2 = 888 - val expected = s"RedactionWithNestedCaseClass($id,***,Inner(***,$age2))" - - val testing = RedactionWithNestedCaseClass(id, Inner(name1, age1), Inner(name2, age2)) - val implicitToString = s"$testing" - val explicitToString = testing.toString - - val cp = new Checkpoint - cp { assert(implicitToString == expected) } - cp { assert(explicitToString == expected) } - cp { - assert(testing.id == id && - testing.inner1.name == name1 && - testing.inner1.age == age1 && - testing.inner2.name == name2 && - testing.inner2.age == age2) + it should "work with case class nested in companion object" in { + forAll { (id: String, name1: String, age1: Int, name2: String, age2: Int) => + val expected = s"RedactionWithNestedCaseClass($id,***,Inner(***,$age2))" + val testing = RedactionWithNestedCaseClass(id, Inner(name1, age1), Inner(name2, age2)) + val implicitToString = s"$testing" + val explicitToString = testing.toString + + val cp = new Checkpoint + cp { assert(implicitToString == expected) } + cp { assert(explicitToString == expected) } + cp { + assert(testing.id == id && + testing.inner1.name == name1 && + testing.inner1.age == age1 && + testing.inner2.name == name2 && + testing.inner2.age == age2) + } + cp.reportAll() } - cp.reportAll() } it should "work with nested case classes in case class" in { case class Inner(userId: String, @redacted balance: Int) case class Outer(inner: Inner) - val userId = "user-123" - val balance = 123_456_789 - val expected = s"Outer(Inner($userId,***))" - - val testing = Outer(Inner(userId, balance)) - val implicitToString = s"$testing" - val explicitToString = testing.toString - - val cp = new Checkpoint - cp { assert(implicitToString == expected) } - cp { assert(explicitToString == expected) } - cp { - assert( - testing.inner.userId == userId && - testing.inner.balance == balance - ) + + forAll { (userId: String, balance: Int) => + val expected = s"Outer(Inner($userId,***))" + val testing = Outer(Inner(userId, balance)) + val implicitToString = s"$testing" + val explicitToString = testing.toString + + val cp = new Checkpoint + cp { assert(implicitToString == expected) } + cp { assert(explicitToString == expected) } + cp { + assert( + testing.inner.userId == userId && + testing.inner.balance == balance + ) + } + cp.reportAll() + } + } + + it must "not change the behavior of `hashCode`" in { + final case class TestClass(uuid: UUID, name: String, age: Int) + object RedactedTestClass { + final case class TestClass(uuid: UUID, @redacted name: String, @redacted age: Int) + } + + forAll { (uuid: UUID, name: String, age: Int) => + val testClass = TestClass(uuid, name, age) + val redactedTestClass = RedactedTestClass.TestClass(uuid, name, age) + + assert(testClass.hashCode() == redactedTestClass.hashCode()) } - cp.reportAll() } }