Full coverage for key-value.
This commit is contained in:
parent
ec880797f1
commit
163957ad27
9 changed files with 395 additions and 11 deletions
|
|
@ -1,6 +1,6 @@
|
|||
package gs.predicate.v0.kv
|
||||
|
||||
import cats.effect.Sync
|
||||
import cats.Functor
|
||||
import cats.syntax.all.*
|
||||
import gs.predicate.v0.api.Predicate
|
||||
import gs.uuid.v0.UUID
|
||||
|
|
@ -16,7 +16,7 @@ import gs.uuid.v0.UUID
|
|||
* @param containedValue
|
||||
* The substring that must be contained in the value.
|
||||
*/
|
||||
final class StringContains[F[_]: Sync, K](
|
||||
final class StringContains[F[_]: Functor, K](
|
||||
val id: UUID,
|
||||
val key: K,
|
||||
val containedValue: String
|
||||
|
|
@ -30,3 +30,13 @@ final class StringContains[F[_]: Sync, K](
|
|||
case Some(value) => Predicate.Result(value.contains(containedValue))
|
||||
case _ => Predicate.Result.missed()
|
||||
}
|
||||
|
||||
object StringContains:
|
||||
|
||||
def apply[F[_]: Functor, K](
|
||||
key: K,
|
||||
containedValue: String
|
||||
): StringContains[F, K] =
|
||||
new StringContains[F, K](UUID.v7(), key, containedValue)
|
||||
|
||||
end StringContains
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package gs.predicate.v0.kv
|
||||
|
||||
import cats.effect.Sync
|
||||
import cats.Functor
|
||||
import cats.syntax.all.*
|
||||
import gs.predicate.v0.api.Predicate
|
||||
import gs.uuid.v0.UUID
|
||||
|
|
@ -16,7 +16,7 @@ import gs.uuid.v0.UUID
|
|||
* @param suffix
|
||||
* The substring that must be the suffix of the value.
|
||||
*/
|
||||
final class StringEndsWith[F[_]: Sync, K](
|
||||
final class StringEndsWith[F[_]: Functor, K](
|
||||
val id: UUID,
|
||||
val key: K,
|
||||
val suffix: String
|
||||
|
|
@ -30,3 +30,13 @@ final class StringEndsWith[F[_]: Sync, K](
|
|||
case Some(value) => Predicate.Result(value.endsWith(suffix))
|
||||
case _ => Predicate.Result.missed()
|
||||
}
|
||||
|
||||
object StringEndsWith:
|
||||
|
||||
def apply[F[_]: Functor, K](
|
||||
key: K,
|
||||
suffix: String
|
||||
): StringEndsWith[F, K] =
|
||||
new StringEndsWith[F, K](UUID.v7(), key, suffix)
|
||||
|
||||
end StringEndsWith
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package gs.predicate.v0.kv
|
||||
|
||||
import cats.effect.Sync
|
||||
import cats.Functor
|
||||
import cats.syntax.all.*
|
||||
import gs.predicate.v0.api.Predicate
|
||||
import gs.uuid.v0.UUID
|
||||
|
|
@ -16,7 +16,7 @@ import gs.uuid.v0.UUID
|
|||
* @param prefix
|
||||
* The substring that must be the prefix of the value.
|
||||
*/
|
||||
final class StringStartsWith[F[_]: Sync, K](
|
||||
final class StringStartsWith[F[_]: Functor, K](
|
||||
val id: UUID,
|
||||
val key: K,
|
||||
val prefix: String
|
||||
|
|
@ -30,3 +30,13 @@ final class StringStartsWith[F[_]: Sync, K](
|
|||
case Some(value) => Predicate.Result(value.startsWith(prefix))
|
||||
case _ => Predicate.Result.missed()
|
||||
}
|
||||
|
||||
object StringStartsWith:
|
||||
|
||||
def apply[F[_]: Functor, K](
|
||||
key: K,
|
||||
prefix: String
|
||||
): StringStartsWith[F, K] =
|
||||
new StringStartsWith[F, K](UUID.v7(), key, prefix)
|
||||
|
||||
end StringStartsWith
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package gs.predicate.v0.kv
|
||||
|
||||
import cats.effect.Sync
|
||||
import cats.Functor
|
||||
import cats.syntax.all.*
|
||||
import gs.predicate.v0.api.Predicate
|
||||
import gs.uuid.v0.UUID
|
||||
|
|
@ -15,10 +15,10 @@ import gs.uuid.v0.UUID
|
|||
* @param value
|
||||
* The value that must not be associated with the key.
|
||||
*/
|
||||
final class ValueNotEquals[F[_]: Sync, K, V](
|
||||
final class ValueNotEquals[F[_]: Functor, K, V](
|
||||
val id: UUID,
|
||||
val key: K,
|
||||
val expectedValue: V
|
||||
val value: V
|
||||
)(
|
||||
using
|
||||
CanEqual[V, V]
|
||||
|
|
@ -28,6 +28,19 @@ final class ValueNotEquals[F[_]: Sync, K, V](
|
|||
*/
|
||||
override def eval(input: KeyValueProvider[F, K, V]): F[Predicate.Result] =
|
||||
input.get(key).map {
|
||||
case Some(value) => Predicate.Result(value != expectedValue)
|
||||
case Some(v) => Predicate.Result(v != value)
|
||||
case _ => Predicate.Result.missed()
|
||||
}
|
||||
|
||||
object ValueNotEquals:
|
||||
|
||||
def apply[F[_]: Functor, K, V](
|
||||
key: K,
|
||||
value: V
|
||||
)(
|
||||
using
|
||||
CanEqual[V, V]
|
||||
): ValueNotEquals[F, K, V] =
|
||||
new ValueNotEquals[F, K, V](UUID.v7(), key, value)
|
||||
|
||||
end ValueNotEquals
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
package gs.predicate.v0.kv
|
||||
|
||||
import cats.Id
|
||||
|
||||
class KeyValueProviderTests extends munit.FunSuite:
|
||||
|
||||
test("should provide a no-op implementation") {
|
||||
val kvp = KeyValueProvider.noop[Id]
|
||||
assertEquals(kvp.exists("something"), false)
|
||||
assertEquals(kvp.get("something"), None)
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
package gs.predicate.v0.kv
|
||||
|
||||
import cats.effect.IO
|
||||
import cats.effect.std.MapRef
|
||||
import gs.datagen.v0.Gen
|
||||
import gs.datagen.v0.generators.Size
|
||||
import gs.predicate.v0.api.Predicate
|
||||
import gs.predicate.v0.kv.StringContainsTests.Data.Substring1
|
||||
import support.IOSuite
|
||||
|
||||
class StringContainsTests extends IOSuite:
|
||||
|
||||
import StringContainsTests.Data
|
||||
|
||||
iotest("should find a contained string in any position") {
|
||||
val p1 = StringContains[IO, String](Data.PassingKey, Data.Substring1)
|
||||
val p2 = StringContains[IO, String](Data.PassingKey, Data.Substring2)
|
||||
val p3 = StringContains[IO, String](Data.PassingKey, Data.Substring3)
|
||||
for
|
||||
provider <- StringContainsTests.newProvider(Data.KeyValues)
|
||||
r1 <- p1.eval(provider)
|
||||
r2 <- p2.eval(provider)
|
||||
r3 <- p3.eval(provider)
|
||||
yield
|
||||
assertEquals(r1, Predicate.Result.matched())
|
||||
assertEquals(r2, Predicate.Result.matched())
|
||||
assertEquals(r3, Predicate.Result.matched())
|
||||
}
|
||||
|
||||
iotest("should not find a key that does not exist within some provider") {
|
||||
val p = StringContains[IO, String](Data.NotExistingKey, "")
|
||||
for
|
||||
provider <- StringContainsTests.newProvider(Data.KeyValues)
|
||||
result <- p.eval(provider)
|
||||
yield assertEquals(result, Predicate.Result.missed())
|
||||
}
|
||||
|
||||
iotest("should match if an empty substring is provided") {
|
||||
val p1 = StringContains[IO, String](Data.EmptyStringKey, "")
|
||||
val p2 = StringContains[IO, String](Data.PassingKey, "")
|
||||
for
|
||||
provider <- StringContainsTests.newProvider(Data.KeyValues)
|
||||
r1 <- p1.eval(provider)
|
||||
r2 <- p2.eval(provider)
|
||||
yield
|
||||
assertEquals(r1, Predicate.Result.matched())
|
||||
assertEquals(r2, Predicate.Result.matched())
|
||||
}
|
||||
|
||||
iotest("should not match if the target value is not contained in the input") {
|
||||
val p = StringContains[IO, String](Data.PassingKey, Substring1.reverse)
|
||||
for
|
||||
provider <- StringContainsTests.newProvider(Data.KeyValues)
|
||||
result <- p.eval(provider)
|
||||
yield assertEquals(result, Predicate.Result.missed())
|
||||
}
|
||||
|
||||
object StringContainsTests:
|
||||
|
||||
object Data:
|
||||
|
||||
val PassingKey: String = Gen.string.alphaNumeric(Size.Fixed(8)).gen()
|
||||
val PassingValue: String = "abcdefhij"
|
||||
val Substring1: String = "abc"
|
||||
val Substring2: String = "def"
|
||||
val Substring3: String = "hij"
|
||||
|
||||
val NotExistingKey: String = Gen.string.alphaNumeric(Size.Fixed(6)).gen()
|
||||
val NotExistingValue: String = Gen.string.alphaNumeric(Size.Fixed(4)).gen()
|
||||
|
||||
val EmptyStringKey: String = "empty"
|
||||
|
||||
val KeyValues: Map[String, String] = Map(
|
||||
PassingKey -> PassingValue,
|
||||
EmptyStringKey -> ""
|
||||
)
|
||||
|
||||
end Data
|
||||
|
||||
def newProvider(data: Map[String, String]): IO[KeyValueStringProvider[IO]] =
|
||||
for map <- MapRef.ofSingleImmutableMap[IO, String, String](data)
|
||||
yield new MemoryMapStringProvider(map)
|
||||
|
||||
end StringContainsTests
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
package gs.predicate.v0.kv
|
||||
|
||||
import cats.effect.IO
|
||||
import cats.effect.std.MapRef
|
||||
import gs.datagen.v0.Gen
|
||||
import gs.datagen.v0.generators.Size
|
||||
import gs.predicate.v0.api.Predicate
|
||||
import support.IOSuite
|
||||
|
||||
class StringEndsWithTests extends IOSuite:
|
||||
|
||||
import StringEndsWithTests.Data
|
||||
|
||||
iotest("should find a string as a prefix of the input") {
|
||||
val p1 = StringEndsWith[IO, String](Data.PassingKey, Data.Substring1)
|
||||
val p2 = StringEndsWith[IO, String](Data.PassingKey, Data.Substring2)
|
||||
val p3 = StringEndsWith[IO, String](Data.PassingKey, Data.Substring3)
|
||||
for
|
||||
provider <- StringEndsWithTests.newProvider(Data.KeyValues)
|
||||
r1 <- p1.eval(provider)
|
||||
r2 <- p2.eval(provider)
|
||||
r3 <- p3.eval(provider)
|
||||
yield
|
||||
assertEquals(r1, Predicate.Result.matched())
|
||||
assertEquals(r2, Predicate.Result.matched())
|
||||
assertEquals(r3, Predicate.Result.matched())
|
||||
}
|
||||
|
||||
iotest("should not find a key that does not exist within some provider") {
|
||||
val p = StringEndsWith[IO, String](Data.NotExistingKey, "")
|
||||
for
|
||||
provider <- StringEndsWithTests.newProvider(Data.KeyValues)
|
||||
result <- p.eval(provider)
|
||||
yield assertEquals(result, Predicate.Result.missed())
|
||||
}
|
||||
|
||||
iotest("should match if an empty substring is provided") {
|
||||
val p1 = StringEndsWith[IO, String](Data.EmptyStringKey, "")
|
||||
val p2 = StringEndsWith[IO, String](Data.PassingKey, "")
|
||||
for
|
||||
provider <- StringEndsWithTests.newProvider(Data.KeyValues)
|
||||
r1 <- p1.eval(provider)
|
||||
r2 <- p2.eval(provider)
|
||||
yield
|
||||
assertEquals(r1, Predicate.Result.matched())
|
||||
assertEquals(r2, Predicate.Result.matched())
|
||||
}
|
||||
|
||||
iotest(
|
||||
"should not match if the target value is not the suffix of the input"
|
||||
) {
|
||||
val p1 = StringEndsWith[IO, String](Data.PassingKey, Data.Substring3 + "z")
|
||||
val p2 =
|
||||
StringEndsWith[IO, String](Data.PassingKey, Data.Substring3.reverse)
|
||||
val p3 =
|
||||
StringEndsWith[IO, String](Data.PassingKey, Data.Substring2.reverse)
|
||||
for
|
||||
provider <- StringEndsWithTests.newProvider(Data.KeyValues)
|
||||
r1 <- p1.eval(provider)
|
||||
r2 <- p2.eval(provider)
|
||||
r3 <- p3.eval(provider)
|
||||
yield
|
||||
assertEquals(r1, Predicate.Result.missed())
|
||||
assertEquals(r2, Predicate.Result.missed())
|
||||
assertEquals(r3, Predicate.Result.missed())
|
||||
}
|
||||
|
||||
object StringEndsWithTests:
|
||||
|
||||
object Data:
|
||||
|
||||
val PassingKey: String = Gen.string.alphaNumeric(Size.Fixed(8)).gen()
|
||||
val PassingValue: String = "abcdefghi"
|
||||
val Substring1: String = "i"
|
||||
val Substring2: String = "hi"
|
||||
val Substring3: String = "abcdefghi"
|
||||
|
||||
val NotExistingKey: String = Gen.string.alphaNumeric(Size.Fixed(6)).gen()
|
||||
|
||||
val EmptyStringKey: String = "empty"
|
||||
|
||||
val KeyValues: Map[String, String] = Map(
|
||||
PassingKey -> PassingValue,
|
||||
EmptyStringKey -> ""
|
||||
)
|
||||
|
||||
end Data
|
||||
|
||||
def newProvider(data: Map[String, String]): IO[KeyValueStringProvider[IO]] =
|
||||
for map <- MapRef.ofSingleImmutableMap[IO, String, String](data)
|
||||
yield new MemoryMapStringProvider(map)
|
||||
|
||||
end StringEndsWithTests
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
package gs.predicate.v0.kv
|
||||
|
||||
import cats.effect.IO
|
||||
import cats.effect.std.MapRef
|
||||
import gs.datagen.v0.Gen
|
||||
import gs.datagen.v0.generators.Size
|
||||
import gs.predicate.v0.api.Predicate
|
||||
import support.IOSuite
|
||||
|
||||
class StringStartsWithTests extends IOSuite:
|
||||
|
||||
import StringStartsWithTests.Data
|
||||
|
||||
iotest("should find a string as a prefix of the input") {
|
||||
val p1 = StringStartsWith[IO, String](Data.PassingKey, Data.Substring1)
|
||||
val p2 = StringStartsWith[IO, String](Data.PassingKey, Data.Substring2)
|
||||
val p3 = StringStartsWith[IO, String](Data.PassingKey, Data.Substring3)
|
||||
for
|
||||
provider <- StringStartsWithTests.newProvider(Data.KeyValues)
|
||||
r1 <- p1.eval(provider)
|
||||
r2 <- p2.eval(provider)
|
||||
r3 <- p3.eval(provider)
|
||||
yield
|
||||
assertEquals(r1, Predicate.Result.matched())
|
||||
assertEquals(r2, Predicate.Result.matched())
|
||||
assertEquals(r3, Predicate.Result.matched())
|
||||
}
|
||||
|
||||
iotest("should not find a key that does not exist within some provider") {
|
||||
val p = StringStartsWith[IO, String](Data.NotExistingKey, "")
|
||||
for
|
||||
provider <- StringStartsWithTests.newProvider(Data.KeyValues)
|
||||
result <- p.eval(provider)
|
||||
yield assertEquals(result, Predicate.Result.missed())
|
||||
}
|
||||
|
||||
iotest("should match if an empty substring is provided") {
|
||||
val p1 = StringStartsWith[IO, String](Data.EmptyStringKey, "")
|
||||
val p2 = StringStartsWith[IO, String](Data.PassingKey, "")
|
||||
for
|
||||
provider <- StringStartsWithTests.newProvider(Data.KeyValues)
|
||||
r1 <- p1.eval(provider)
|
||||
r2 <- p2.eval(provider)
|
||||
yield
|
||||
assertEquals(r1, Predicate.Result.matched())
|
||||
assertEquals(r2, Predicate.Result.matched())
|
||||
}
|
||||
|
||||
iotest(
|
||||
"should not match if the target value is not the prefix of the input"
|
||||
) {
|
||||
val p1 =
|
||||
StringStartsWith[IO, String](Data.PassingKey, Data.Substring3 + "k")
|
||||
val p2 =
|
||||
StringStartsWith[IO, String](Data.PassingKey, Data.Substring3.reverse)
|
||||
val p3 =
|
||||
StringStartsWith[IO, String](Data.PassingKey, Data.Substring2.reverse)
|
||||
for
|
||||
provider <- StringStartsWithTests.newProvider(Data.KeyValues)
|
||||
r1 <- p1.eval(provider)
|
||||
r2 <- p2.eval(provider)
|
||||
r3 <- p3.eval(provider)
|
||||
yield
|
||||
assertEquals(r1, Predicate.Result.missed())
|
||||
assertEquals(r2, Predicate.Result.missed())
|
||||
assertEquals(r3, Predicate.Result.missed())
|
||||
}
|
||||
|
||||
object StringStartsWithTests:
|
||||
|
||||
object Data:
|
||||
|
||||
val PassingKey: String = Gen.string.alphaNumeric(Size.Fixed(8)).gen()
|
||||
val PassingValue: String = "abcdefghi"
|
||||
val Substring1: String = "a"
|
||||
val Substring2: String = "ab"
|
||||
val Substring3: String = "abcdefghi"
|
||||
|
||||
val NotExistingKey: String = Gen.string.alphaNumeric(Size.Fixed(6)).gen()
|
||||
|
||||
val EmptyStringKey: String = "empty"
|
||||
|
||||
val KeyValues: Map[String, String] = Map(
|
||||
PassingKey -> PassingValue,
|
||||
EmptyStringKey -> ""
|
||||
)
|
||||
|
||||
end Data
|
||||
|
||||
def newProvider(data: Map[String, String]): IO[KeyValueStringProvider[IO]] =
|
||||
for map <- MapRef.ofSingleImmutableMap[IO, String, String](data)
|
||||
yield new MemoryMapStringProvider(map)
|
||||
|
||||
end StringStartsWithTests
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
package gs.predicate.v0.kv
|
||||
|
||||
import cats.effect.IO
|
||||
import cats.effect.std.MapRef
|
||||
import gs.datagen.v0.Gen
|
||||
import gs.datagen.v0.generators.Size
|
||||
import gs.predicate.v0.api.Predicate
|
||||
import support.IOSuite
|
||||
|
||||
class ValueNotEqualsTests extends IOSuite:
|
||||
|
||||
import ValueNotEqualsTests.Data
|
||||
|
||||
iotest("should NOT find an exact match against some value") {
|
||||
val p =
|
||||
ValueNotEquals[IO, String, String](Data.ExistingKey, Data.ExistingValue)
|
||||
for
|
||||
provider <- ValueNotEqualsTests.newProvider(Data.KeyValues)
|
||||
result <- p.eval(provider)
|
||||
yield assertEquals(result, Predicate.Result.missed())
|
||||
}
|
||||
|
||||
iotest("should match a value if that value is not equal to the target") {
|
||||
val p =
|
||||
ValueNotEquals[IO, String, String](
|
||||
Data.ExistingKey,
|
||||
Data.NotExistingValue
|
||||
)
|
||||
for
|
||||
provider <- ValueNotEqualsTests.newProvider(Data.KeyValues)
|
||||
result <- p.eval(provider)
|
||||
yield assertEquals(result, Predicate.Result.matched())
|
||||
}
|
||||
|
||||
iotest("should not find a key that does not exist within some provider") {
|
||||
val p = ValueNotEquals[IO, String, String](Data.NotExistingKey, "")
|
||||
for
|
||||
provider <- ValueNotEqualsTests.newProvider(Data.KeyValues)
|
||||
result <- p.eval(provider)
|
||||
yield assertEquals(result, Predicate.Result.missed())
|
||||
}
|
||||
|
||||
object ValueNotEqualsTests:
|
||||
|
||||
object Data:
|
||||
|
||||
val ExistingKey: String = Gen.string.alphaNumeric(Size.Fixed(8)).gen()
|
||||
val NotExistingKey: String = Gen.string.alphaNumeric(Size.Fixed(6)).gen()
|
||||
val ExistingValue: String = Gen.string.alphaNumeric(Size.Fixed(10)).gen()
|
||||
val NotExistingValue: String = Gen.string.alphaNumeric(Size.Fixed(4)).gen()
|
||||
val KeyValues: Map[String, String] = Map(ExistingKey -> ExistingValue)
|
||||
|
||||
end Data
|
||||
|
||||
def newProvider(data: Map[String, String]): IO[KeyValueStringProvider[IO]] =
|
||||
for map <- MapRef.ofSingleImmutableMap[IO, String, String](data)
|
||||
yield new MemoryMapStringProvider(map)
|
||||
|
||||
end ValueNotEqualsTests
|
||||
Loading…
Add table
Reference in a new issue