diff --git a/modules/refined4s-circe/shared/src/main/scala/refined4s/modules/circe/derivation/generic/auto.scala b/modules/refined4s-circe/shared/src/main/scala/refined4s/modules/circe/derivation/generic/auto.scala index d5f83c95..80c80f72 100644 --- a/modules/refined4s-circe/shared/src/main/scala/refined4s/modules/circe/derivation/generic/auto.scala +++ b/modules/refined4s-circe/shared/src/main/scala/refined4s/modules/circe/derivation/generic/auto.scala @@ -1,6 +1,6 @@ package refined4s.modules.circe.derivation.generic -import io.circe.{Decoder, Encoder} +import io.circe.{Decoder, Encoder, KeyDecoder, KeyEncoder} import refined4s.modules.cats.syntax.contraCoercible import refined4s.{Coercible, RefinedCtor} @@ -17,5 +17,34 @@ trait auto { inline given derivedRefinedDecoder[A, B](using refinedCtor: RefinedCtor[B, A], decoder: Decoder[A]): Decoder[B] = decoder.emap(refinedCtor.create) + + inline given derivedKeyEncoder[A, B](using coercible: Coercible[A, B], encoder: KeyEncoder[B]): KeyEncoder[A] = + contraCoercible(encoder) + + inline given derivedNewtypeKeyDecoder[A, B](using coercible: Coercible[A, B], decoder: KeyDecoder[A]): KeyDecoder[B] = + Coercible.unsafeWrapTC(decoder) + + inline given derivedRefinedKeyDecoder[A, B](using refinedCtor: RefinedCtor[B, A], decoder: KeyDecoder[A]): KeyDecoder[B] with { + override def apply(key: String): Option[B] = + decoder.apply(key).flatMap(refinedCtor.create(_).toOption) + } + + /** Without this, Circe's decode uses an incorrect Decoder for the value. + * @example + * {{{ + * // when + * decode[Map[PortNumber, Int]]("""{ "0": 65536 }""") + * + * // The expected result is + * Map(PortNumber(0), 65536) + * + * // But the actual result is + * // Either[DecodingFailure, Map[PortNumber, Int]] = + * // Left(DecodingFailure(Invalid value: [65536]. It has to be Int between 0 and 65535 (0 <= PortNumber <= 65535), List(DownField(0)))) + * // The KeyDecoder is fine here, but the value Decoder `decode` uses is not Decoder[Int] but Decoder[PortNumber]. + * }}} + */ + inline given derivedMapDecoder[A, B](using KeyDecoder[A], Decoder[B]): Decoder[Map[A, B]] = io.circe.Decoder.decodeMap + } object auto extends auto diff --git a/modules/refined4s-circe/shared/src/test/scala/refined4s/modules/circe/derivation/generic/CustomTypeSpec.scala b/modules/refined4s-circe/shared/src/test/scala/refined4s/modules/circe/derivation/generic/CustomTypeSpec.scala index f8d81a83..305d344b 100644 --- a/modules/refined4s-circe/shared/src/test/scala/refined4s/modules/circe/derivation/generic/CustomTypeSpec.scala +++ b/modules/refined4s-circe/shared/src/test/scala/refined4s/modules/circe/derivation/generic/CustomTypeSpec.scala @@ -18,20 +18,47 @@ object CustomTypeSpec { def allTests: List[Test] = List( property("test CirceEncoder for Newtype", testNewtypeEncoder), + property("test CirceKeyEncoder for Newtype", testNewtypeKeyEncoder), + // property("test CirceEncoder for Refined", testRefinedEncoder), + property("test CirceKeyEncoder for Refined", testRefinedKeyEncoder), + // property("test CirceEncoder for Newtype(Refined)", testRefinedNewtypeEncoder), + property("test CirceKeyEncoder for Newtype(Refined)", testRefinedNewtypeKeyEncoder), + // property("test CirceEncoder for InlinedRefined", testInlinedRefinedEncoder), + property("test CirceKeyEncoder for InlinedRefined", testInlinedRefinedKeyEncoder), + // property("test CirceEncoder for Newtype(InlinedRefined)", testInlinedRefinedNewtypeEncoder), + property("test CirceKeyEncoder for Newtype(InlinedRefined)", testInlinedRefinedNewtypeKeyEncoder), // property("test CirceDecoder for Newtype", testNewtypeDecoder), + property("test CirceKeyDecoder for Newtype", testNewtypeKeyDecoder), + // property("test CirceDecoder for Refined", testRefinedDecoder), + property("test CirceKeyDecoder for Refined", testRefinedKeyDecoder), + // example("test CirceDecoder for Refined with invalid value", testRefinedDecoderInvalid), + example("test CirceKeyDecoder for Refined with invalid value", testRefinedKeyDecoderInvalid), + // property("test CirceDecoder for Newtype(Refined)", testRefinedNewtypeDecoder), + property("test CirceKeyDecoder for Newtype(Refined)", testRefinedNewtypeKeyDecoder), + // example("test CirceDecoder for Newtype(Refined) with invalid value", testRefinedNewtypeDecoderInvalid), + example("test CirceKeyDecoder for Newtype(Refined) with invalid value", testRefinedNewtypeKeyDecoderInvalid), + // property("test CirceDecoder for InlinedRefined", testInlinedRefinedDecoder), + property("test CirceKeyDecoder for InlinedKeyRefined", testInlinedRefinedKeyDecoder), + // example("test CirceDecoder for InlinedRefined with invalid value", testInlinedRefinedDecoderInvalid), + example("test CirceKeyDecoder for InlinedRefined with invalid value", testInlinedRefinedKeyDecoderInvalid), + // property("test CirceDecoder for Newtype(InlinedRefined)", testInlinedRefinedNewtypeDecoder), + property("test CirceKeyDecoder for Newtype(InlinedRefined)", testInlinedRefinedNewtypeKeyDecoder), + // example("test CirceDecoder for Newtype(InlinedRefined) with invalid value", testInlinedRefinedNewtypeDecoderInvalid), + example("test CirceKeyDecoder for Newtype(InlinedRefined) with invalid value", testInlinedRefinedNewtypeKeyDecoderInvalid), + // ) def testNewtypeEncoder: Property = @@ -49,6 +76,24 @@ object CustomTypeSpec { ) } + def testNewtypeKeyEncoder: Property = + for { + ss <- Gen.string(Gen.unicode, Range.linear(1, 10)).list(Range.linear(1, 10)).log("ss") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ss.length)).log("ns2") + map <- Gen.constant(ss.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => MyNewtype(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key -> value.asJson }) + + val actual = input.asJson + Result.all( + List( + actual ==== expected, + actual.noSpaces ==== expected.noSpaces, + ) + ) + } + def testRefinedEncoder: Property = for { s <- Gen.string(Gen.unicode, Range.linear(1, 10)).log("s") @@ -64,6 +109,24 @@ object CustomTypeSpec { ) } + def testRefinedKeyEncoder: Property = + for { + ss <- Gen.string(Gen.unicode, Range.linear(1, 10)).list(Range.linear(1, 10)).log("ss") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ss.length)).log("ns2") + map <- Gen.constant(ss.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => MyRefinedType.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key -> value.asJson }) + + val actual = input.asJson + Result.all( + List( + actual ==== expected, + actual.noSpaces ==== expected.noSpaces, + ) + ) + } + def testRefinedNewtypeEncoder: Property = for { s <- Gen.string(Gen.unicode, Range.linear(1, 10)).log("s") @@ -79,6 +142,24 @@ object CustomTypeSpec { ) } + def testRefinedNewtypeKeyEncoder: Property = + for { + ss <- Gen.string(Gen.unicode, Range.linear(1, 10)).list(Range.linear(1, 10)).log("ss") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ss.length)).log("ns2") + map <- Gen.constant(ss.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => MyRefinedNewtype(MyRefinedType.unsafeFrom(key)) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key -> value.asJson }) + + val actual = input.asJson + Result.all( + List( + actual ==== expected, + actual.noSpaces ==== expected.noSpaces, + ) + ) + } + def testInlinedRefinedEncoder: Property = for { s <- Gen.string(Gen.unicode, Range.linear(1, 10)).log("s") @@ -94,6 +175,24 @@ object CustomTypeSpec { ) } + def testInlinedRefinedKeyEncoder: Property = + for { + ss <- Gen.string(Gen.unicode, Range.linear(1, 10)).list(Range.linear(1, 10)).log("ss") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ss.length)).log("ns2") + map <- Gen.constant(ss.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => MyInlinedRefinedType.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key -> value.asJson }) + + val actual = input.asJson + Result.all( + List( + actual ==== expected, + actual.noSpaces ==== expected.noSpaces, + ) + ) + } + def testInlinedRefinedNewtypeEncoder: Property = for { s <- Gen.string(Gen.unicode, Range.linear(1, 10)).log("s") @@ -109,6 +208,25 @@ object CustomTypeSpec { ) } + def testInlinedRefinedNewtypeKeyEncoder: Property = + for { + ss <- Gen.string(Gen.unicode, Range.linear(1, 10)).list(Range.linear(1, 10)).log("ss") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ss.length)).log("ns2") + map <- Gen.constant(ss.zip(ns2).toMap).log("map") + input <- + Gen.constant(map.map { case (key, value) => MyInlinedRefinedNewtype(MyInlinedRefinedType.unsafeFrom(key)) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key -> value.asJson }) + + val actual = input.asJson + Result.all( + List( + actual ==== expected, + actual.noSpaces ==== expected.noSpaces, + ) + ) + } + def testNewtypeDecoder: Property = for { s <- Gen.string(Gen.unicode, Range.linear(1, 10)).log("s") @@ -123,6 +241,23 @@ object CustomTypeSpec { ) } + def testNewtypeKeyDecoder: Property = + for { + ss <- Gen.string(Gen.unicode, Range.linear(1, 10)).list(Range.linear(1, 10)).log("ss") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ss.length)).log("ns2") + map <- Gen.constant(ss.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => MyNewtype(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key -> value.asJson }) + val actual = decode[Map[MyNewtype, Int]](input.noSpaces) + + Result.all( + List( + actual ==== expected.asRight + ) + ) + } + def testRefinedDecoder: Property = for { s <- Gen.string(Gen.unicode, Range.linear(1, 10)).log("s") @@ -137,6 +272,23 @@ object CustomTypeSpec { ) } + def testRefinedKeyDecoder: Property = + for { + ss <- Gen.string(Gen.unicode, Range.linear(1, 10)).list(Range.linear(1, 10)).log("ss") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ss.length)).log("ns2") + map <- Gen.constant(ss.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => MyRefinedType.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key -> value.asJson }) + val actual = decode[Map[MyRefinedType, Int]](input.noSpaces) + + Result.all( + List( + actual ==== expected.asRight + ) + ) + } + def testRefinedDecoderInvalid: Result = { val expected = MyRefinedType.from("").leftMap(io.circe.DecodingFailure(_, List.empty)) val input = "".asJson @@ -148,6 +300,17 @@ object CustomTypeSpec { ) } + def testRefinedKeyDecoderInvalid: Result = { + val expected = io.circe.DecodingFailure("Couldn't decode key.", List(CursorOp.DownField(""))).asLeft[Map[MyRefinedType, Int]] + val input = Json.fromFields(List("" -> 123.asJson)) + val actual = decode[Map[MyRefinedType, Int]](input.noSpaces) + Result.all( + List( + actual ==== expected + ) + ) + } + def testRefinedNewtypeDecoder: Property = for { s <- Gen.string(Gen.unicode, Range.linear(1, 10)).log("s") @@ -162,6 +325,23 @@ object CustomTypeSpec { ) } + def testRefinedNewtypeKeyDecoder: Property = + for { + ss <- Gen.string(Gen.unicode, Range.linear(1, 10)).list(Range.linear(1, 10)).log("ss") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ss.length)).log("ns2") + map <- Gen.constant(ss.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => MyRefinedNewtype(MyRefinedType.unsafeFrom(key)) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key -> value.asJson }) + val actual = decode[Map[MyRefinedNewtype, Int]](input.noSpaces) + + Result.all( + List( + actual ==== expected.asRight + ) + ) + } + def testRefinedNewtypeDecoderInvalid: Result = { val expected = MyRefinedType .from("") @@ -176,6 +356,17 @@ object CustomTypeSpec { ) } + def testRefinedNewtypeKeyDecoderInvalid: Result = { + val expected = io.circe.DecodingFailure("Couldn't decode key.", List(CursorOp.DownField(""))).asLeft[Map[MyRefinedType, Int]] + val input = Json.fromFields(List("" -> 123.asJson)) + val actual = decode[Map[MyRefinedNewtype, Int]](input.noSpaces) + Result.all( + List( + actual ==== expected + ) + ) + } + def testInlinedRefinedDecoder: Property = for { s <- Gen.string(Gen.unicode, Range.linear(1, 10)).log("s") @@ -190,6 +381,23 @@ object CustomTypeSpec { ) } + def testInlinedRefinedKeyDecoder: Property = + for { + ss <- Gen.string(Gen.unicode, Range.linear(1, 10)).list(Range.linear(1, 10)).log("ss") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ss.length)).log("ns2") + map <- Gen.constant(ss.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => MyInlinedRefinedType.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key -> value.asJson }) + val actual = decode[Map[MyInlinedRefinedType, Int]](input.noSpaces) + + Result.all( + List( + actual ==== expected.asRight + ) + ) + } + def testInlinedRefinedDecoderInvalid: Result = { val expected = MyInlinedRefinedType.from("").leftMap(io.circe.DecodingFailure(_, List.empty)) val input = "".asJson @@ -201,6 +409,18 @@ object CustomTypeSpec { ) } + def testInlinedRefinedKeyDecoderInvalid: Result = { + val expected = io.circe.DecodingFailure("Couldn't decode key.", List(CursorOp.DownField(""))).asLeft[Map[MyInlinedRefinedType, Int]] + val input = Json.fromFields(List("" -> 123.asJson)) + + val actual = decode[Map[MyInlinedRefinedType, Int]](input.noSpaces) + Result.all( + List( + actual ==== expected + ) + ) + } + def testInlinedRefinedNewtypeDecoder: Property = for { s <- Gen.string(Gen.unicode, Range.linear(1, 10)).log("s") @@ -215,6 +435,28 @@ object CustomTypeSpec { ) } + def testInlinedRefinedNewtypeKeyDecoder: Property = + for { + ss <- Gen.string(Gen.unicode, Range.linear(1, 10)).list(Range.linear(1, 10)).log("ss") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ss.length)).log("ns2") + map <- Gen.constant(ss.zip(ns2).toMap).log("map") + expected <- Gen + .constant(map.map { + case (key, value) => + MyInlinedRefinedNewtype(MyInlinedRefinedType.unsafeFrom(key)) -> value + }) + .log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key -> value.asJson }) + val actual = decode[Map[MyInlinedRefinedNewtype, Int]](input.noSpaces) + + Result.all( + List( + actual ==== expected.asRight + ) + ) + } + def testInlinedRefinedNewtypeDecoderInvalid: Result = { val expected = MyInlinedRefinedType .from("") @@ -229,6 +471,18 @@ object CustomTypeSpec { ) } + def testInlinedRefinedNewtypeKeyDecoderInvalid: Result = { + val expected = io.circe.DecodingFailure("Couldn't decode key.", List(CursorOp.DownField(""))).asLeft[Map[MyInlinedRefinedNewtype, Int]] + val input = Json.fromFields(List("" -> 123.asJson)) + + val actual = decode[Map[MyInlinedRefinedNewtype, Int]](input.noSpaces) + Result.all( + List( + actual ==== expected + ) + ) + } + type MyNewtype = MyNewtype.Type object MyNewtype extends Newtype[String] diff --git a/modules/refined4s-circe/shared/src/test/scala/refined4s/modules/circe/derivation/generic/networkSpec.scala b/modules/refined4s-circe/shared/src/test/scala/refined4s/modules/circe/derivation/generic/networkSpec.scala index be2c13bf..82a284af 100644 --- a/modules/refined4s-circe/shared/src/test/scala/refined4s/modules/circe/derivation/generic/networkSpec.scala +++ b/modules/refined4s-circe/shared/src/test/scala/refined4s/modules/circe/derivation/generic/networkSpec.scala @@ -1,5 +1,6 @@ package refined4s.modules.circe.derivation.generic +import cats.syntax.all.* import hedgehog.* import hedgehog.runner.* import io.circe.* @@ -9,7 +10,7 @@ import refined4s.types.all.* import refined4s.types.networkGens /** @author Kevin Lee - * @since 2024-08-24 + * @since 2024-08-23 */ object networkSpec { @@ -18,24 +19,38 @@ object networkSpec { def allTests: List[Test] = List( property("test Encoder[Uri]", testEncoderUri), property("test Decoder[Uri]", testDecoderUri), + property("test KeyEncoder[Uri]", testKeyEncoderUri), + property("test KeyDecoder[Uri]", testKeyDecoderUri), // property("test Encoder[Url]", testEncoderUrl), property("test Decoder[Url]", testDecoderUrl), + property("test KeyEncoder[Url]", testKeyEncoderUrl), + property("test KeyDecoder[Url]", testKeyDecoderUrl), // property("test Encoder[PortNumber]", testEncoderPortNumber), property("test Decoder[PortNumber]", testDecoderPortNumber), + property("test KeyEncoder[PortNumber]", testKeyEncoderPortNumber), + property("test KeyDecoder[PortNumber]", testKeyDecoderPortNumber), // property("test Encoder[SystemPortNumber]", testEncoderSystemPortNumber), property("test Decoder[SystemPortNumber]", testDecoderSystemPortNumber), + property("test KeyEncoder[SystemPortNumber]", testKeyEncoderSystemPortNumber), + property("test KeyDecoder[SystemPortNumber]", testKeyDecoderSystemPortNumber), // property("test Encoder[NonSystemPortNumber]", testEncoderNonSystemPortNumber), property("test Decoder[NonSystemPortNumber]", testDecoderNonSystemPortNumber), + property("test KeyEncoder[NonSystemPortNumber]", testKeyEncoderNonSystemPortNumber), + property("test KeyDecoder[NonSystemPortNumber]", testKeyDecoderNonSystemPortNumber), // property("test Encoder[UserPortNumber]", testEncoderUserPortNumber), property("test Decoder[UserPortNumber]", testDecoderUserPortNumber), + property("test KeyEncoder[UserPortNumber]", testKeyEncoderUserPortNumber), + property("test KeyDecoder[UserPortNumber]", testKeyDecoderUserPortNumber), // property("test Encoder[DynamicPortNumber]", testEncoderDynamicPortNumber), property("test Decoder[DynamicPortNumber]", testDecoderDynamicPortNumber), + property("test KeyEncoder[DynamicPortNumber]", testKeyEncoderDynamicPortNumber), + property("test KeyDecoder[DynamicPortNumber]", testKeyDecoderDynamicPortNumber), ) def testEncoderUri: Property = @@ -68,15 +83,47 @@ object networkSpec { actual ==== expected } + def testKeyEncoderUri: Property = + for { + uris <- networkGens.genUriString.list(Range.linear(1, 10)).log("uris") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(uris.length)).log("ns2") + map <- Gen.constant(uris.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => Uri.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key -> value.asJson }) + + val actual = input.asJson + + Result.all( + List( + actual ==== expected, + actual.noSpaces ==== expected.noSpaces, + ) + ) + } + + def testKeyDecoderUri: Property = + for { + uris <- networkGens.genUriString.list(Range.linear(1, 10)).log("uris") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(uris.length)).log("ns2") + map <- Gen.constant(uris.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => Uri.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key -> value.asJson }) + val actual = decode[Map[Uri, Int]](input.noSpaces) + + actual ==== expected.asRight + } + // def testEncoderUrl: Property = for { - uri <- networkGens.genUrlString.log("uri") + url <- networkGens.genUrlString.log("url") } yield { - val input = Url.unsafeFrom(uri) + val input = Url.unsafeFrom(url) - val expected = uri.asJson + val expected = url.asJson val actual = input.asJson Result.all( @@ -89,17 +136,49 @@ object networkSpec { def testDecoderUrl: Property = for { - uri <- networkGens.genUrlString.log("uri") + url <- networkGens.genUrlString.log("url") } yield { - val input = uri.asJson + val input = url.asJson - val expected = Url.from(uri) + val expected = Url.from(url) val actual = decode[Url](input.noSpaces) actual ==== expected } + def testKeyEncoderUrl: Property = + for { + urls <- networkGens.genUrlString.list(Range.linear(1, 10)).log("urls") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(urls.length)).log("ns2") + map <- Gen.constant(urls.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => Url.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key -> value.asJson }) + + val actual = input.asJson + + Result.all( + List( + actual ==== expected, + actual.noSpaces ==== expected.noSpaces, + ) + ) + } + + def testKeyDecoderUrl: Property = + for { + urls <- networkGens.genUrlString.list(Range.linear(1, 10)).log("urls") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(urls.length)).log("ns2") + map <- Gen.constant(urls.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => Url.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key -> value.asJson }) + val actual = decode[Map[Url, Int]](input.noSpaces) + + actual ==== expected.asRight + } + // def testEncoderPortNumber: Property = @@ -132,6 +211,41 @@ object networkSpec { actual ==== expected } + def testKeyEncoderPortNumber: Property = + for { + portNumbers <- networkGens.genPortNumberInt.list(Range.linear(1, 10)).log("portNumbers") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(portNumbers.length)).log("ns2") + map <- Gen.constant(portNumbers.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => PortNumber.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + + Result.all( + List( + actual ==== expected, + actual.noSpacesSortKeys ==== expected.noSpacesSortKeys, + ) + ) + } + + def testKeyDecoderPortNumber: Property = + for { + portNumbers <- networkGens.genPortNumberInt.list(Range.linear(1, 10)).log("portNumbers") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(portNumbers.length)).log("ns2") + map <- Gen.constant(portNumbers.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => PortNumber.unsafeFrom(key) -> value }).log("expected") + } yield { + + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[PortNumber, Int]](input.noSpaces) + + actual ==== expected.asRight + } + + // + def testEncoderSystemPortNumber: Property = for { systemPortNumber <- networkGens.genSystemPortNumberInt.log("systemPortNumber") @@ -162,6 +276,40 @@ object networkSpec { actual ==== expected } + def testKeyEncoderSystemPortNumber: Property = + for { + systemPortNumbers <- networkGens.genSystemPortNumberInt.list(Range.linear(1, 10)).log("systemPortNumbers") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(systemPortNumbers.length)).log("ns2") + map <- Gen.constant(systemPortNumbers.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => SystemPortNumber.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + + Result.all( + List( + actual ==== expected, + actual.noSpacesSortKeys ==== expected.noSpacesSortKeys, + ) + ) + } + + def testKeyDecoderSystemPortNumber: Property = + for { + systemPortNumbers <- networkGens.genSystemPortNumberInt.list(Range.linear(1, 10)).log("systemPortNumbers") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(systemPortNumbers.length)).log("ns2") + map <- Gen.constant(systemPortNumbers.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => SystemPortNumber.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[SystemPortNumber, Int]](input.noSpaces) + + actual ==== expected.asRight + } + + // + def testEncoderNonSystemPortNumber: Property = for { nonSystemPortNumber <- networkGens.genNonSystemPortNumberInt.log("nonSystemPortNumber") @@ -192,6 +340,40 @@ object networkSpec { actual ==== expected } + def testKeyEncoderNonSystemPortNumber: Property = + for { + nonSystemPortNumbers <- networkGens.genNonSystemPortNumberInt.list(Range.linear(1, 10)).log("nonSystemPortNumbers") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(nonSystemPortNumbers.length)).log("ns2") + map <- Gen.constant(nonSystemPortNumbers.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => NonSystemPortNumber.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + + Result.all( + List( + actual ==== expected, + actual.noSpacesSortKeys ==== expected.noSpacesSortKeys, + ) + ) + } + + def testKeyDecoderNonSystemPortNumber: Property = + for { + nonSystemPortNumbers <- networkGens.genNonSystemPortNumberInt.list(Range.linear(1, 10)).log("nonSystemPortNumbers") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(nonSystemPortNumbers.length)).log("ns2") + map <- Gen.constant(nonSystemPortNumbers.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => NonSystemPortNumber.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[NonSystemPortNumber, Int]](input.noSpaces) + + actual ==== expected.asRight + } + + // + def testEncoderUserPortNumber: Property = for { userPortNumber <- networkGens.genUserPortNumberInt.log("userPortNumber") @@ -222,6 +404,40 @@ object networkSpec { actual ==== expected } + def testKeyEncoderUserPortNumber: Property = + for { + userPortNumbers <- networkGens.genUserPortNumberInt.list(Range.linear(1, 10)).log("userPortNumbers") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(userPortNumbers.length)).log("ns2") + map <- Gen.constant(userPortNumbers.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => UserPortNumber.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + + Result.all( + List( + actual ==== expected, + actual.noSpacesSortKeys ==== expected.noSpacesSortKeys, + ) + ) + } + + def testKeyDecoderUserPortNumber: Property = + for { + userPortNumbers <- networkGens.genUserPortNumberInt.list(Range.linear(1, 10)).log("userPortNumbers") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(userPortNumbers.length)).log("ns2") + map <- Gen.constant(userPortNumbers.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => UserPortNumber.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[UserPortNumber, Int]](input.noSpaces) + + actual ==== expected.asRight + } + + // + def testEncoderDynamicPortNumber: Property = for { dynamicPortNumber <- networkGens.genDynamicPortNumberInt.log("dynamicPortNumber") @@ -252,4 +468,36 @@ object networkSpec { actual ==== expected } + def testKeyEncoderDynamicPortNumber: Property = + for { + dynamicPortNumbers <- networkGens.genDynamicPortNumberInt.list(Range.linear(1, 10)).log("dynamicPortNumbers") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(dynamicPortNumbers.length)).log("ns2") + map <- Gen.constant(dynamicPortNumbers.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => DynamicPortNumber.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + + Result.all( + List( + actual ==== expected, + actual.noSpacesSortKeys ==== expected.noSpacesSortKeys, + ) + ) + } + + def testKeyDecoderDynamicPortNumber: Property = + for { + dynamicPortNumbers <- networkGens.genDynamicPortNumberInt.list(Range.linear(1, 10)).log("dynamicPortNumbers") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(dynamicPortNumbers.length)).log("ns2") + map <- Gen.constant(dynamicPortNumbers.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => DynamicPortNumber.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[DynamicPortNumber, Int]](input.noSpaces) + + actual ==== expected.asRight + } + } diff --git a/modules/refined4s-circe/shared/src/test/scala/refined4s/modules/circe/derivation/generic/numericSpec.scala b/modules/refined4s-circe/shared/src/test/scala/refined4s/modules/circe/derivation/generic/numericSpec.scala index 0c05dacf..d80edbad 100644 --- a/modules/refined4s-circe/shared/src/test/scala/refined4s/modules/circe/derivation/generic/numericSpec.scala +++ b/modules/refined4s-circe/shared/src/test/scala/refined4s/modules/circe/derivation/generic/numericSpec.scala @@ -2,144 +2,163 @@ package refined4s.modules.circe.derivation.generic import hedgehog.* import hedgehog.runner.* +import cats.syntax.all.* import io.circe.* import io.circe.parser.* import io.circe.syntax.* import refined4s.types.all.* /** @author Kevin Lee - * @since 2024-08-24 + * @since 2024-08-23 */ object numericSpec { + import refined4s.modules.circe.derivation.generic.auto.given def allTests: List[Test] = List( property("test Encoder[NegInt]", testEncoderNegInt), - property("test Decoder[NegInt]", testEncoderNegInt), + property("test Decoder[NegInt]", testDecoderNegInt), + property("test KeyEncoder[NegInt]", testKeyEncoderNegInt), + property("test KeyDecoder[NegInt]", testKeyDecoderNegInt), // property("test Encoder[NonNegInt]", testEncoderNonNegInt), - property("test Decoder[NonNegInt]", testEncoderNonNegInt), + property("test Decoder[NonNegInt]", testDecoderNonNegInt), + property("test KeyEncoder[NonNegInt]", testKeyEncoderNonNegInt), + property("test KeyDecoder[NonNegInt]", testKeyDecoderNonNegInt), // property("test Encoder[PosInt]", testEncoderPosInt), property("test Decoder[PosInt]", testDecoderPosInt), - + property("test KeyEncoder[PosInt]", testKeyEncoderPosInt), + property("test KeyDecoder[PosInt]", testKeyDecoderPosInt), // property("test Encoder[NonPosInt]", testEncoderNonPosInt), property("test Decoder[NonPosInt]", testDecoderNonPosInt), - + property("test KeyEncoder[NonPosInt]", testKeyEncoderNonPosInt), + property("test KeyDecoder[NonPosInt]", testKeyDecoderNonPosInt), // property("test Encoder[NegLong]", testEncoderNegLong), property("test Decoder[NegLong]", testDecoderNegLong), - + property("test KeyEncoder[NegLong]", testKeyEncoderNegLong), + property("test KeyDecoder[NegLong]", testKeyDecoderNegLong), // property("test Encoder[NonNegLong]", testEncoderNonNegLong), property("test Decoder[NonNegLong]", testDecoderNonNegLong), - + property("test KeyEncoder[NonNegLong]", testKeyEncoderNonNegLong), + property("test KeyDecoder[NonNegLong]", testKeyDecoderNonNegLong), // property("test Encoder[PosLong]", testEncoderPosLong), property("test Decoder[PosLong]", testDecoderPosLong), - + property("test KeyEncoder[PosLong]", testKeyEncoderPosLong), + property("test KeyDecoder[PosLong]", testKeyDecoderPosLong), // property("test Encoder[NonPosLong]", testEncoderNonPosLong), property("test Decoder[NonPosLong]", testDecoderNonPosLong), - + property("test KeyEncoder[NonPosLong]", testKeyEncoderNonPosLong), + property("test KeyDecoder[NonPosLong]", testKeyDecoderNonPosLong), // property("test Encoder[NegShort]", testEncoderNegShort), property("test Decoder[NegShort]", testDecoderNegShort), - + property("test KeyEncoder[NegShort]", testKeyEncoderNegShort), + property("test KeyDecoder[NegShort]", testKeyDecoderNegShort), // property("test Encoder[NonNegShort]", testEncoderNonNegShort), property("test Decoder[NonNegShort]", testDecoderNonNegShort), - + property("test KeyEncoder[NonNegShort]", testKeyEncoderNonNegShort), + property("test KeyDecoder[NonNegShort]", testKeyDecoderNonNegShort), // property("test Encoder[PosShort]", testEncoderPosShort), property("test Decoder[PosShort]", testDecoderPosShort), - + property("test KeyEncoder[PosShort]", testKeyEncoderPosShort), + property("test KeyDecoder[PosShort]", testKeyDecoderPosShort), // property("test Encoder[NonPosShort]", testEncoderNonPosShort), property("test Decoder[NonPosShort]", testDecoderNonPosShort), - + property("test KeyEncoder[NonPosShort]", testKeyEncoderNonPosShort), + property("test KeyDecoder[NonPosShort]", testKeyDecoderNonPosShort), // property("test Encoder[NegByte]", testEncoderNegByte), property("test Decoder[NegByte]", testDecoderNegByte), - + property("test KeyEncoder[NegByte]", testKeyEncoderNegByte), + property("test KeyDecoder[NegByte]", testKeyDecoderNegByte), // property("test Encoder[NonNegByte]", testEncoderNonNegByte), property("test Decoder[NonNegByte]", testDecoderNonNegByte), - + property("test KeyEncoder[NonNegByte]", testKeyEncoderNonNegByte), + property("test KeyDecoder[NonNegByte]", testKeyDecoderNonNegByte), // property("test Encoder[PosByte]", testEncoderPosByte), property("test Decoder[PosByte]", testDecoderPosByte), - + property("test KeyEncoder[PosByte]", testKeyEncoderPosByte), + property("test KeyDecoder[PosByte]", testKeyDecoderPosByte), // property("test Encoder[NonPosByte]", testEncoderNonPosByte), property("test Decoder[NonPosByte]", testDecoderNonPosByte), - + property("test KeyEncoder[NonPosByte]", testKeyEncoderNonPosByte), + property("test KeyDecoder[NonPosByte]", testKeyDecoderNonPosByte), // property("test Encoder[NegFloat]", testEncoderNegFloat), property("test Decoder[NegFloat]", testDecoderNegFloat), - // property("test Encoder[NonNegFloat]", testEncoderNonNegFloat), property("test Decoder[NonNegFloat]", testDecoderNonNegFloat), - // property("test Encoder[PosFloat]", testEncoderPosFloat), property("test Decoder[PosFloat]", testDecoderPosFloat), - // property("test Encoder[NonPosFloat]", testEncoderNonPosFloat), property("test Decoder[NonPosFloat]", testDecoderNonPosFloat), - // property("test Encoder[NegDouble]", testEncoderNegDouble), property("test Decoder[NegDouble]", testDecoderNegDouble), - // property("test Encoder[NonNegDouble]", testEncoderNonNegDouble), property("test Decoder[NonNegDouble]", testDecoderNonNegDouble), - // property("test Encoder[PosDouble]", testEncoderPosDouble), property("test Decoder[PosDouble]", testDecoderPosDouble), - // property("test Encoder[NonPosDouble]", testEncoderNonPosDouble), property("test Decoder[NonPosDouble]", testDecoderNonPosDouble), - // property("test Encoder[NegBigInt]", testEncoderNegBigInt), property("test Decoder[NegBigInt]", testDecoderNegBigInt), - + property("test KeyEncoder[NegBigInt]", testKeyEncoderNegBigInt), + property("test KeyDecoder[NegBigInt]", testKeyDecoderNegBigInt), // property("test Encoder[NonNegBigInt]", testEncoderNonNegBigInt), property("test Decoder[NonNegBigInt]", testDecoderNonNegBigInt), - + property("test KeyEncoder[NonNegBigInt]", testKeyEncoderNonNegBigInt), + property("test KeyDecoder[NonNegBigInt]", testKeyDecoderNonNegBigInt), // property("test Encoder[PosBigInt]", testEncoderPosBigInt), property("test Decoder[PosBigInt]", testDecoderPosBigInt), - + property("test KeyEncoder[PosBigInt]", testKeyEncoderPosBigInt), + property("test KeyDecoder[PosBigInt]", testKeyDecoderPosBigInt), // property("test Encoder[NonPosBigInt]", testEncoderNonPosBigInt), property("test Decoder[NonPosBigInt]", testDecoderNonPosBigInt), - + property("test KeyEncoder[NonPosBigInt]", testKeyEncoderNonPosBigInt), + property("test KeyDecoder[NonPosBigInt]", testKeyDecoderNonPosBigInt), // property("test Encoder[NegBigDecimal]", testEncoderNegBigDecimal), property("test Decoder[NegBigDecimal]", testDecoderNegBigDecimal), - // property("test Encoder[NonNegBigDecimal]", testEncoderNonNegBigDecimal), property("test Decoder[NonNegBigDecimal]", testDecoderNonNegBigDecimal), - // property("test Encoder[PosBigDecimal]", testEncoderPosBigDecimal), property("test Decoder[PosBigDecimal]", testDecoderPosBigDecimal), - // property("test Encoder[NonPosBigDecimal]", testEncoderNonPosBigDecimal), property("test Decoder[NonPosBigDecimal]", testDecoderNonPosBigDecimal), + // ) + @SuppressWarnings(Array("org.wartremover.warts.ToString")) + given bigIntKeyEncoder: KeyEncoder[BigInt] = _.toString + + given bigIntKeyDecoder: KeyDecoder[BigInt] = s => scala.util.Try(BigInt(s)).toOption + def testEncoderNegInt: Property = for { n <- Gen.int(Range.linear(-1, Int.MinValue)).log("n") @@ -167,6 +186,37 @@ object numericSpec { actual ==== expected } + def testKeyEncoderNegInt: Property = + for { + ns1 <- Gen.int(Range.linear(-1, Int.MinValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => NegInt.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + Result.all( + List( + actual ==== expected, + actual.noSpacesSortKeys ==== expected.noSpacesSortKeys, + ) + ) + } + + def testKeyDecoderNegInt: Property = + for { + ns1 <- Gen.int(Range.linear(-1, Int.MinValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => NegInt.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[NegInt, Int]](input.noSpaces) + + actual ==== expected.asRight + } + // def testEncoderNonNegInt: Property = @@ -196,6 +246,37 @@ object numericSpec { actual ==== expected } + def testKeyEncoderNonNegInt: Property = + for { + ns1 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => NonNegInt.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + Result.all( + List( + actual ==== expected, + actual.noSpacesSortKeys ==== expected.noSpacesSortKeys, + ) + ) + } + + def testKeyDecoderNonNegInt: Property = + for { + ns1 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => NonNegInt.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[NonNegInt, Int]](input.noSpaces) + + actual ==== expected.asRight + } + // def testEncoderPosInt: Property = @@ -225,6 +306,37 @@ object numericSpec { actual ==== expected } + def testKeyEncoderPosInt: Property = + for { + ns1 <- Gen.int(Range.linear(1, Int.MaxValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => PosInt.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + Result.all( + List( + actual ==== expected, + actual.noSpacesSortKeys ==== expected.noSpacesSortKeys, + ) + ) + } + + def testKeyDecoderPosInt: Property = + for { + ns1 <- Gen.int(Range.linear(1, Int.MaxValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => PosInt.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[PosInt, Int]](input.noSpaces) + + actual ==== expected.asRight + } + // def testEncoderNonPosInt: Property = @@ -254,6 +366,37 @@ object numericSpec { actual ==== expected } + def testKeyEncoderNonPosInt: Property = + for { + ns1 <- Gen.int(Range.linear(0, Int.MinValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => NonPosInt.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + Result.all( + List( + actual ==== expected, + actual.noSpacesSortKeys ==== expected.noSpacesSortKeys, + ) + ) + } + + def testKeyDecoderNonPosInt: Property = + for { + ns1 <- Gen.int(Range.linear(0, Int.MinValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => NonPosInt.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[NonPosInt, Int]](input.noSpaces) + + actual ==== expected.asRight + } + // def testEncoderNegLong: Property = @@ -283,6 +426,37 @@ object numericSpec { actual ==== expected } + def testKeyEncoderNegLong: Property = + for { + ns1 <- Gen.long(Range.linear(-1L, Long.MinValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => NegLong.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + Result.all( + List( + actual ==== expected, + actual.noSpacesSortKeys ==== expected.noSpacesSortKeys, + ) + ) + } + + def testKeyDecoderNegLong: Property = + for { + ns1 <- Gen.long(Range.linear(-1L, Long.MinValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => NegLong.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[NegLong, Int]](input.noSpaces) + + actual ==== expected.asRight + } + // def testEncoderNonNegLong: Property = @@ -312,6 +486,37 @@ object numericSpec { actual ==== expected } + def testKeyEncoderNonNegLong: Property = + for { + ns1 <- Gen.long(Range.linear(0L, Long.MaxValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => NonNegLong.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + Result.all( + List( + actual ==== expected, + actual.noSpacesSortKeys ==== expected.noSpacesSortKeys, + ) + ) + } + + def testKeyDecoderNonNegLong: Property = + for { + ns1 <- Gen.long(Range.linear(0L, Long.MaxValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => NonNegLong.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[NonNegLong, Int]](input.noSpaces) + + actual ==== expected.asRight + } + // def testEncoderPosLong: Property = @@ -341,6 +546,37 @@ object numericSpec { actual ==== expected } + def testKeyEncoderPosLong: Property = + for { + ns1 <- Gen.long(Range.linear(1L, Long.MaxValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => PosLong.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + Result.all( + List( + actual ==== expected, + actual.noSpacesSortKeys ==== expected.noSpacesSortKeys, + ) + ) + } + + def testKeyDecoderPosLong: Property = + for { + ns1 <- Gen.long(Range.linear(1L, Long.MaxValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => PosLong.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[PosLong, Int]](input.noSpaces) + + actual ==== expected.asRight + } + // def testEncoderNonPosLong: Property = @@ -370,6 +606,37 @@ object numericSpec { actual ==== expected } + def testKeyEncoderNonPosLong: Property = + for { + ns1 <- Gen.long(Range.linear(0L, Long.MinValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => NonPosLong.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + Result.all( + List( + actual ==== expected, + actual.noSpacesSortKeys ==== expected.noSpacesSortKeys, + ) + ) + } + + def testKeyDecoderNonPosLong: Property = + for { + ns1 <- Gen.long(Range.linear(0L, Long.MinValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => NonPosLong.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[NonPosLong, Int]](input.noSpaces) + + actual ==== expected.asRight + } + // def testEncoderNegShort: Property = @@ -399,6 +666,37 @@ object numericSpec { actual ==== expected } + def testKeyEncoderNegShort: Property = + for { + ns1 <- Gen.short(Range.linear(-1, Short.MinValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => NegShort.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + Result.all( + List( + actual ==== expected, + actual.noSpacesSortKeys ==== expected.noSpacesSortKeys, + ) + ) + } + + def testKeyDecoderNegShort: Property = + for { + ns1 <- Gen.short(Range.linear(-1, Short.MinValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => NegShort.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[NegShort, Int]](input.noSpaces) + + actual ==== expected.asRight + } + // def testEncoderNonNegShort: Property = @@ -428,6 +726,37 @@ object numericSpec { actual ==== expected } + def testKeyEncoderNonNegShort: Property = + for { + ns1 <- Gen.short(Range.linear(0, Short.MaxValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => NonNegShort.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + Result.all( + List( + actual ==== expected, + actual.noSpacesSortKeys ==== expected.noSpacesSortKeys, + ) + ) + } + + def testKeyDecoderNonNegShort: Property = + for { + ns1 <- Gen.short(Range.linear(0, Short.MaxValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => NonNegShort.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[NonNegShort, Int]](input.noSpaces) + + actual ==== expected.asRight + } + // def testEncoderPosShort: Property = @@ -457,6 +786,37 @@ object numericSpec { actual ==== expected } + def testKeyEncoderPosShort: Property = + for { + ns1 <- Gen.short(Range.linear(1, Short.MaxValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => PosShort.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + Result.all( + List( + actual ==== expected, + actual.noSpacesSortKeys ==== expected.noSpacesSortKeys, + ) + ) + } + + def testKeyDecoderPosShort: Property = + for { + ns1 <- Gen.short(Range.linear(1, Short.MaxValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => PosShort.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[PosShort, Int]](input.noSpaces) + + actual ==== expected.asRight + } + // def testEncoderNonPosShort: Property = @@ -486,6 +846,37 @@ object numericSpec { actual ==== expected } + def testKeyEncoderNonPosShort: Property = + for { + ns1 <- Gen.short(Range.linear(0, Short.MinValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => NonPosShort.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + Result.all( + List( + actual ==== expected, + actual.noSpacesSortKeys ==== expected.noSpacesSortKeys, + ) + ) + } + + def testKeyDecoderNonPosShort: Property = + for { + ns1 <- Gen.short(Range.linear(0, Short.MinValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => NonPosShort.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[NonPosShort, Int]](input.noSpaces) + + actual ==== expected.asRight + } + // def testEncoderNegByte: Property = @@ -515,6 +906,37 @@ object numericSpec { actual ==== expected } + def testKeyEncoderNegByte: Property = + for { + ns1 <- Gen.byte(Range.linear(-1, Byte.MinValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => NegByte.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + Result.all( + List( + actual ==== expected, + actual.noSpacesSortKeys ==== expected.noSpacesSortKeys, + ) + ) + } + + def testKeyDecoderNegByte: Property = + for { + ns1 <- Gen.byte(Range.linear(-1, Byte.MinValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => NegByte.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[NegByte, Int]](input.noSpaces) + + actual ==== expected.asRight + } + // def testEncoderNonNegByte: Property = @@ -544,6 +966,37 @@ object numericSpec { actual ==== expected } + def testKeyEncoderNonNegByte: Property = + for { + ns1 <- Gen.byte(Range.linear(0, Byte.MaxValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => NonNegByte.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + Result.all( + List( + actual ==== expected, + actual.noSpacesSortKeys ==== expected.noSpacesSortKeys, + ) + ) + } + + def testKeyDecoderNonNegByte: Property = + for { + ns1 <- Gen.byte(Range.linear(0, Byte.MaxValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => NonNegByte.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[NonNegByte, Int]](input.noSpaces) + + actual ==== expected.asRight + } + // def testEncoderPosByte: Property = @@ -573,6 +1026,37 @@ object numericSpec { actual ==== expected } + def testKeyEncoderPosByte: Property = + for { + ns1 <- Gen.byte(Range.linear(1, Byte.MaxValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => PosByte.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + Result.all( + List( + actual ==== expected, + actual.noSpacesSortKeys ==== expected.noSpacesSortKeys, + ) + ) + } + + def testKeyDecoderPosByte: Property = + for { + ns1 <- Gen.byte(Range.linear(1, Byte.MaxValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => PosByte.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[PosByte, Int]](input.noSpaces) + + actual ==== expected.asRight + } + // def testEncoderNonPosByte: Property = @@ -602,6 +1086,37 @@ object numericSpec { actual ==== expected } + def testKeyEncoderNonPosByte: Property = + for { + ns1 <- Gen.byte(Range.linear(0, Byte.MinValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => NonPosByte.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + Result.all( + List( + actual ==== expected, + actual.noSpacesSortKeys ==== expected.noSpacesSortKeys, + ) + ) + } + + def testKeyDecoderNonPosByte: Property = + for { + ns1 <- Gen.byte(Range.linear(0, Byte.MinValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => NonPosByte.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[NonPosByte, Int]](input.noSpaces) + + actual ==== expected.asRight + } + // def testEncoderNegFloat: Property = @@ -863,6 +1378,38 @@ object numericSpec { actual ==== expected } + def testKeyEncoderNegBigInt: Property = + for { + ns1 <- Gen.long(Range.linear(-1L, Long.MinValue)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => NegBigInt.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + Result.all( + List( + actual ==== expected, + actual.noSpacesSortKeys ==== expected.noSpacesSortKeys, + ) + ) + } + + @SuppressWarnings(Array("org.wartremover.warts.ToString")) + def testKeyDecoderNegBigInt: Property = + for { + ns1 <- Gen.long(Range.linear(-1L, Long.MinValue)).map(BigInt(_)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => NegBigInt.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[NegBigInt, Int]](input.noSpaces) + + actual ==== expected.asRight + } + // def testEncoderNonNegBigInt: Property = @@ -892,6 +1439,39 @@ object numericSpec { actual ==== expected } + @SuppressWarnings(Array("org.wartremover.warts.ToString")) + def testKeyEncoderNonNegBigInt: Property = + for { + ns1 <- Gen.long(Range.linear(0L, Long.MaxValue)).map(BigInt(_)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => NonNegBigInt.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + Result.all( + List( + actual ==== expected, + actual.noSpacesSortKeys ==== expected.noSpacesSortKeys, + ) + ) + } + + @SuppressWarnings(Array("org.wartremover.warts.ToString")) + def testKeyDecoderNonNegBigInt: Property = + for { + ns1 <- Gen.long(Range.linear(0L, Long.MaxValue)).map(BigInt(_)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => NonNegBigInt.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[NonNegBigInt, Int]](input.noSpaces) + + actual ==== expected.asRight + } + // def testEncoderPosBigInt: Property = @@ -921,6 +1501,39 @@ object numericSpec { actual ==== expected } + @SuppressWarnings(Array("org.wartremover.warts.ToString")) + def testKeyEncoderPosBigInt: Property = + for { + ns1 <- Gen.long(Range.linear(1L, Long.MaxValue)).map(BigInt(_)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => PosBigInt.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + Result.all( + List( + actual ==== expected, + actual.noSpacesSortKeys ==== expected.noSpacesSortKeys, + ) + ) + } + + @SuppressWarnings(Array("org.wartremover.warts.ToString")) + def testKeyDecoderPosBigInt: Property = + for { + ns1 <- Gen.long(Range.linear(1L, Long.MaxValue)).map(BigInt(_)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => PosBigInt.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[PosBigInt, Int]](input.noSpaces) + + actual ==== expected.asRight + } + // def testEncoderNonPosBigInt: Property = @@ -950,6 +1563,39 @@ object numericSpec { actual ==== expected } + @SuppressWarnings(Array("org.wartremover.warts.ToString")) + def testKeyEncoderNonPosBigInt: Property = + for { + ns1 <- Gen.long(Range.linear(0L, Long.MinValue)).map(BigInt(_)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => NonPosBigInt.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + Result.all( + List( + actual ==== expected, + actual.noSpacesSortKeys ==== expected.noSpacesSortKeys, + ) + ) + } + + @SuppressWarnings(Array("org.wartremover.warts.ToString")) + def testKeyDecoderNonPosBigInt: Property = + for { + ns1 <- Gen.long(Range.linear(0L, Long.MinValue)).map(BigInt(_)).list(Range.linear(1, 10)).log("ns1") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ns1.length)).log("ns2") + map <- Gen.constant(ns1.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => NonPosBigInt.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[NonPosBigInt, Int]](input.noSpaces) + + actual ==== expected.asRight + } + // def testEncoderNegBigDecimal: Property = @@ -1066,4 +1712,6 @@ object numericSpec { actual ==== expected } + // + } diff --git a/modules/refined4s-circe/shared/src/test/scala/refined4s/modules/circe/derivation/generic/stringsSpec.scala b/modules/refined4s-circe/shared/src/test/scala/refined4s/modules/circe/derivation/generic/stringsSpec.scala index e8d57d7d..ad87633a 100644 --- a/modules/refined4s-circe/shared/src/test/scala/refined4s/modules/circe/derivation/generic/stringsSpec.scala +++ b/modules/refined4s-circe/shared/src/test/scala/refined4s/modules/circe/derivation/generic/stringsSpec.scala @@ -1,5 +1,6 @@ package refined4s.modules.circe.derivation.generic +import cats.syntax.all.* import hedgehog.* import hedgehog.runner.* import io.circe.* @@ -10,7 +11,7 @@ import refined4s.types.all.* import java.util.UUID /** @author Kevin Lee - * @since 2024-08-24 + * @since 2024-08-23 */ object stringsSpec { @@ -19,14 +20,19 @@ object stringsSpec { def allTests: List[Test] = List( property("test Encoder[NonEmptyString]", testEncoderNonEmptyString), property("test Decoder[NonEmptyString]", testDecoderNonEmptyString), - + property("test KeyEncoder[NonEmptyString]", testKeyEncoderNonEmptyString), + property("test KeyDecoder[NonEmptyString]", testKeyDecoderNonEmptyString), // property("test Encoder[NonBlankString]", testEncoderNonBlankString), property("test Decoder[NonBlankString]", testDecoderNonBlankString), - + property("test KeyEncoder[NonBlankString]", testKeyEncoderNonBlankString), + property("test KeyDecoder[NonBlankString]", testKeyDecoderNonBlankString), // property("test Encoder[Uuid]", testEncoderUuid), property("test Decoder[Uuid]", testDecoderUuid), + property("test KeyEncoder[Uuid]", testKeyEncoderUuid), + property("test KeyDecoder[Uuid]", testKeyDecoderUuid), + // ) def testEncoderNonEmptyString: Property = @@ -58,6 +64,38 @@ object stringsSpec { actual ==== expected } + def testKeyEncoderNonEmptyString: Property = + for { + ss <- Gen.string(Gen.unicode, Range.linear(1, 10)).list(Range.linear(1, 10)).log("ss") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ss.length)).log("ns2") + map <- Gen.constant(ss.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => NonEmptyString.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key -> value.asJson }) + + val actual = input.asJson + + Result.all( + List( + actual ==== expected, + actual.noSpaces ==== expected.noSpaces, + ) + ) + } + + def testKeyDecoderNonEmptyString: Property = + for { + ss <- Gen.string(Gen.unicode, Range.linear(1, 10)).list(Range.linear(1, 10)).log("ss") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ss.length)).log("ns2") + map <- Gen.constant(ss.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => NonEmptyString.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key -> value.asJson }) + val actual = decode[Map[NonEmptyString, Int]](input.noSpaces) + + actual ==== expected.asRight + } + // def testEncoderNonBlankString: Property = @@ -105,6 +143,60 @@ object stringsSpec { actual ==== expected } + def testKeyEncoderNonBlankString: Property = + for { + nonWhitespaceString <- Gen.string(hedgehog.extra.Gens.genNonWhitespaceChar, Range.linear(1, 10)).log("nonWhitespaceString") + whitespaceString <- Gen + .string( + hedgehog.extra.Gens.genCharByRange(refined4s.types.strings.WhitespaceCharRange), + Range.linear(1, 10), + ) + .log("whitespaceString") + + ss <- Gen + .constant(scala.util.Random.shuffle((nonWhitespaceString + whitespaceString).toList).mkString) + .list(Range.linear(1, 10)) + .log("ss") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ss.length)).log("ns2") + map <- Gen.constant(ss.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => NonBlankString.unsafeFrom(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key -> value.asJson }) + + val actual = input.asJson + + Result.all( + List( + actual ==== expected, + actual.noSpaces ==== expected.noSpaces, + ) + ) + } + + def testKeyDecoderNonBlankString: Property = + for { + nonWhitespaceString <- Gen.string(hedgehog.extra.Gens.genNonWhitespaceChar, Range.linear(1, 10)).log("nonWhitespaceString") + whitespaceString <- Gen + .string( + hedgehog.extra.Gens.genCharByRange(refined4s.types.strings.WhitespaceCharRange), + Range.linear(1, 10), + ) + .log("whitespaceString") + + ss <- Gen + .constant(scala.util.Random.shuffle((nonWhitespaceString + whitespaceString).toList).mkString) + .list(Range.linear(1, 10)) + .log("ss") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(ss.length)).log("ns2") + map <- Gen.constant(ss.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => NonBlankString.unsafeFrom(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key -> value.asJson }) + val actual = decode[Map[NonBlankString, Int]](input.noSpaces) + + actual ==== expected.asRight + } + // @SuppressWarnings(Array("org.wartremover.warts.ToString")) @@ -140,4 +232,39 @@ object stringsSpec { actual ==== expected } + @SuppressWarnings(Array("org.wartremover.warts.ToString")) + def testKeyEncoderUuid: Property = + for { + uuids <- Gen.constant(UUID.randomUUID()).list(Range.linear(1, 10)).log("uuids") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(uuids.length)).log("ns2") + map <- Gen.constant(uuids.zip(ns2).toMap).log("map") + input <- Gen.constant(map.map { case (key, value) => Uuid(key) -> value }).log("input") + } yield { + val expected = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + + val actual = input.asJson + + Result.all( + List( + actual ==== expected, + actual.noSpaces ==== expected.noSpaces, + ) + ) + } + + @SuppressWarnings(Array("org.wartremover.warts.ToString")) + def testKeyDecoderUuid: Property = + for { + uuids <- Gen.constant(UUID.randomUUID()).list(Range.linear(1, 10)).log("uuids") + ns2 <- Gen.int(Range.linear(0, Int.MaxValue)).list(Range.singleton(uuids.length)).log("ns2") + map <- Gen.constant(uuids.zip(ns2).toMap).log("map") + expected <- Gen.constant(map.map { case (key, value) => Uuid(key) -> value }).log("expected") + } yield { + val input = Json.fromFields(map.map { case (key, value) => key.toString -> value.asJson }) + val actual = decode[Map[Uuid, Int]](input.noSpaces) + + actual ==== expected.asRight + } + + // }