diff --git a/modules/refined4s-doobie-ce2/shared/src/main/scala/refined4s/modules/doobie/derivation/types/all.scala b/modules/refined4s-doobie-ce2/shared/src/main/scala/refined4s/modules/doobie/derivation/types/all.scala index 386c1a7..60bf5bd 100644 --- a/modules/refined4s-doobie-ce2/shared/src/main/scala/refined4s/modules/doobie/derivation/types/all.scala +++ b/modules/refined4s-doobie-ce2/shared/src/main/scala/refined4s/modules/doobie/derivation/types/all.scala @@ -109,6 +109,9 @@ trait all { inline given derivedNonEmptyStringGet(using Show[String]): Get[NonEmptyString] = Get[String].temap(NonEmptyString.from) inline given derivedNonEmptyStringPut: Put[NonEmptyString] = Put[String].contramap(_.value) + inline given derivedNonBlankStringGet(using Show[String]): Get[NonBlankString] = Get[String].temap(NonBlankString.from) + inline given derivedNonBlankStringPut: Put[NonBlankString] = Put[String].contramap(_.value) + inline given derivedUuidGet(using Show[String]): Get[Uuid] = Get[String].temap(Uuid.from) inline given derivedUuidPut: Put[Uuid] = Put[String].contramap(_.value) diff --git a/modules/refined4s-doobie-ce2/shared/src/test/scala/refined4s/modules/doobie/derivation/types/allSpec.scala b/modules/refined4s-doobie-ce2/shared/src/test/scala/refined4s/modules/doobie/derivation/types/allSpec.scala index fccecc1..9009125 100644 --- a/modules/refined4s-doobie-ce2/shared/src/test/scala/refined4s/modules/doobie/derivation/types/allSpec.scala +++ b/modules/refined4s-doobie-ce2/shared/src/test/scala/refined4s/modules/doobie/derivation/types/allSpec.scala @@ -2,17 +2,18 @@ package refined4s.modules.doobie.derivation.types import cats.effect.ContextShift import cats.syntax.all.* -import hedgehog.* -import hedgehog.runner.* -import doobie.{Get, Put} import doobie.syntax.all.* +import doobie.{Get, Put} +import extras.core.syntax.all.* import extras.doobie.RunWithDb import extras.doobie.ce2.DbTools import extras.runner.ce2.RunSyncCe2 +import hedgehog.* +import hedgehog.runner.* import refined4s.* +import refined4s.modules.doobie.derivation.types.all.given import refined4s.types.all.* import refined4s.types.networkGens -import refined4s.modules.doobie.derivation.types.all.given import java.util.UUID import java.util.concurrent.atomic.AtomicInteger @@ -103,6 +104,8 @@ object allSpec extends Properties, RunSyncCe2, RunWithDb { // propertyWithDb("test Get[NonEmptyString] and Put[NonEmptyString]", postgresPortNumber.getAndIncrement(), testGetAndPutNonEmptyString), // + propertyWithDb("test Get[NonBlankString] and Put[NonBlankString]", postgresPortNumber.getAndIncrement(), testGetAndPutNonBlankString), + // propertyWithDb("test Get[Uuid] and Put[Uuid]", postgresPortNumber.getAndIncrement(), testGetAndPutUuid), // propertyWithDb("test Get[Uri] and Put[Uri]", postgresPortNumber.getAndIncrement(), testGetAndPutUri), @@ -1815,6 +1818,77 @@ object allSpec extends Properties, RunSyncCe2, RunWithDb { /// + def testGetAndPutNonBlankString(testName: String, postgresPortNumber: Int): Property = + for { + nonWhitespaceString <- Gen + .string(hedgehog.extra.Gens.genNonWhitespaceChar, Range.linear(1, 10)) + .map(s => if s === "\u0000" then "blah" else s) + .log("nonWhitespaceString") + whitespaceString <- Gen + .string( + hedgehog.extra.Gens.genCharByRange(refined4s.types.strings.WhitespaceCharRange), + Range.linear(1, 10), + ) + .log("whitespaceString") + + s <- Gen.constant(scala.util.Random.shuffle((nonWhitespaceString + whitespaceString).toList).mkString).log("s") + } yield withDb[F]( + testName, + postgresPortNumber, + sql"""CREATE SCHEMA IF NOT EXISTS db_tools_test""", + sql""" + CREATE TABLE IF NOT EXISTS db_tools_test.example + ( + id SERIAL PRIMARY KEY, + value TEXT NOT NULL + ) + """, + ) { transactor => + + val expected = NonBlankString.unsafeFrom(s) + + val expectedFetchBefore = none[NonBlankString] + val expectedInsert = 1 + val expectedFetchAfter = expected.some + + val fetch = DbTools.fetchSingleRow[F][NonBlankString]( + sql""" + SELECT value + FROM db_tools_test.example + """ + )(transactor) + + val insert = DbTools.updateSingle[F]( + sql""" + INSERT INTO db_tools_test.example (value) VALUES ($expected) + """ + )(transactor) + + for { + fetchResultBefore <- fetch.map(_ ==== expectedFetchBefore) + insertResult <- insert.map(_ ==== expectedInsert) + fetchResultAfter <- fetch.map(actual => + (actual ==== expectedFetchAfter).log( + show""" actual: ${actual.map(_.value)} + |expectedFetchAfter: ${expectedFetchAfter.map(_.value)} + | + | actual (unicode): ${actual.map(_.value.encodeToUnicode)} + |expectedFetchAfter (unicode): ${expectedFetchAfter.map(_.value.encodeToUnicode)} + |""".stripMargin + ) + ) + } yield Result.all( + List( + fetchResultBefore.log("Failed: fetch before"), + insertResult.log("Failed: insert"), + fetchResultAfter.log("Failed: fetch after"), + ) + ) + + } + + /// + def testGetAndPutUuid(testName: String, postgresPortNumber: Int): Property = for { uuid <- Gen.constant(UUID.randomUUID()).log("uuid") diff --git a/modules/refined4s-doobie-ce3/shared/src/main/scala/refined4s/modules/doobie/derivation/types/all.scala b/modules/refined4s-doobie-ce3/shared/src/main/scala/refined4s/modules/doobie/derivation/types/all.scala index 386c1a7..60bf5bd 100644 --- a/modules/refined4s-doobie-ce3/shared/src/main/scala/refined4s/modules/doobie/derivation/types/all.scala +++ b/modules/refined4s-doobie-ce3/shared/src/main/scala/refined4s/modules/doobie/derivation/types/all.scala @@ -109,6 +109,9 @@ trait all { inline given derivedNonEmptyStringGet(using Show[String]): Get[NonEmptyString] = Get[String].temap(NonEmptyString.from) inline given derivedNonEmptyStringPut: Put[NonEmptyString] = Put[String].contramap(_.value) + inline given derivedNonBlankStringGet(using Show[String]): Get[NonBlankString] = Get[String].temap(NonBlankString.from) + inline given derivedNonBlankStringPut: Put[NonBlankString] = Put[String].contramap(_.value) + inline given derivedUuidGet(using Show[String]): Get[Uuid] = Get[String].temap(Uuid.from) inline given derivedUuidPut: Put[Uuid] = Put[String].contramap(_.value) diff --git a/modules/refined4s-doobie-ce3/shared/src/test/scala/refined4s/modules/doobie/derivation/types/allSpec.scala b/modules/refined4s-doobie-ce3/shared/src/test/scala/refined4s/modules/doobie/derivation/types/allSpec.scala index 84725d5..5507b85 100644 --- a/modules/refined4s-doobie-ce3/shared/src/test/scala/refined4s/modules/doobie/derivation/types/allSpec.scala +++ b/modules/refined4s-doobie-ce3/shared/src/test/scala/refined4s/modules/doobie/derivation/types/allSpec.scala @@ -4,6 +4,7 @@ import cats.effect.IO import cats.syntax.all.* import doobie.syntax.all.* import doobie.{Get, Put} +import extras.core.syntax.all.* import extras.doobie.RunWithDb import extras.doobie.ce3.DbTools import extras.hedgehog.ce3.CatsEffectRunner @@ -104,6 +105,8 @@ object allSpec extends Properties, CatsEffectRunner, RunWithDb { // propertyWithDb("test Get[NonEmptyString] and Put[NonEmptyString]", postgresPortNumber.getAndIncrement(), testGetAndPutNonEmptyString), // + propertyWithDb("test Get[NonBlankString] and Put[NonBlankString]", postgresPortNumber.getAndIncrement(), testGetAndPutNonBlankString), + // propertyWithDb("test Get[Uuid] and Put[Uuid]", postgresPortNumber.getAndIncrement(), testGetAndPutUuid), // propertyWithDb("test Get[Uri] and Put[Uri]", postgresPortNumber.getAndIncrement(), testGetAndPutUri), @@ -1882,6 +1885,79 @@ object allSpec extends Properties, CatsEffectRunner, RunWithDb { /// + def testGetAndPutNonBlankString(testName: String, postgresPortNumber: Int): Property = + for { + nonWhitespaceString <- Gen + .string(hedgehog.extra.Gens.genNonWhitespaceChar, Range.linear(1, 10)) + .map(s => if s === "\u0000" then "blah" else s) + .log("nonWhitespaceString") + whitespaceString <- Gen + .string( + hedgehog.extra.Gens.genCharByRange(refined4s.types.strings.WhitespaceCharRange), + Range.linear(1, 10), + ) + .log("whitespaceString") + + s <- Gen.constant(scala.util.Random.shuffle((nonWhitespaceString + whitespaceString).toList).mkString).log("s") + } yield runIO( + withDb[F]( + testName, + postgresPortNumber, + sql"""CREATE SCHEMA IF NOT EXISTS db_tools_test""", + sql""" + CREATE TABLE IF NOT EXISTS db_tools_test.example + ( + id SERIAL PRIMARY KEY, + value TEXT NOT NULL + ) + """, + ) { transactor => + + val expected = NonBlankString.unsafeFrom(s) + + val expectedFetchBefore = none[NonBlankString] + val expectedInsert = 1 + val expectedFetchAfter = expected.some + + val fetch = DbTools.fetchSingleRow[F][NonBlankString]( + sql""" + SELECT value + FROM db_tools_test.example + """ + )(transactor) + + val insert = DbTools.updateSingle[F]( + sql""" + INSERT INTO db_tools_test.example (value) VALUES ($expected) + """ + )(transactor) + + for { + fetchResultBefore <- fetch.map(_ ==== expectedFetchBefore) + insertResult <- insert.map(_ ==== expectedInsert) + fetchResultAfter <- fetch.map(actual => + (actual ==== expectedFetchAfter).log( + show""" actual: ${actual.map(_.value)} + |expectedFetchAfter: ${expectedFetchAfter.map(_.value)} + | + | actual (unicode): ${actual.map(_.value.encodeToUnicode)} + |expectedFetchAfter (unicode): ${expectedFetchAfter.map(_.value.encodeToUnicode)} + |""".stripMargin + ) + ) + } yield Result.all( + List( + fetchResultBefore.log("Failed: fetch before"), + insertResult.log("Failed: insert"), + fetchResultAfter.log("Failed: fetch after"), + ) + ) + + } + ) + + /// + def testGetAndPutUuid(testName: String, postgresPortNumber: Int): Property = for { uuid <- Gen.constant(UUID.randomUUID()).log("uuid")