Adding some tests for predicates.

This commit is contained in:
Pat Garrity 2025-11-24 21:17:27 -06:00
parent fae37780e9
commit 16f8356e2c
Signed by: pfm
GPG key ID: 5CA5D21BAB7F3A76
5 changed files with 331 additions and 0 deletions

View file

@ -0,0 +1,49 @@
package gs.predicate.v0.json
import cats.Applicative
import cats.syntax.all.*
import gs.predicate.v0.api.Predicate
import gs.predicate.v0.json.query.JsonQuery
import io.circe.Json
/** Predicate that matches if the JSON provider contains a JSON blob with the
* given key, and that blob contains the given query, and the result of the
* query matches one of the given values.
*
* @param key
* The name of the JSON value that must satisfy the given query.
* @param query
* The [[JsonQuery]] that must be satisfied.
* @param values
* The set of JSON values, such that one value should match the input.
*/
final class JsonQueryIn[F[_]: Applicative: JsonProvider](
val key: String,
val query: JsonQuery,
val values: Set[Json]
) extends Predicate[F]:
/** @inheritDocs
*/
final override val predicateType: String = JsonQueryIn.PredicateType
/** @inheritDocs
*/
override def eval(): F[Predicate.Result] =
JsonProvider[F].get(key).map {
case None => Predicate.Result.missed()
case Some(json) => Predicate.Result(query.eval(json, values.contains))
}
object JsonQueryIn:
final val PredicateType: String = "json-query-in"
def apply[F[_]: Applicative: JsonProvider](
key: String,
query: JsonQuery,
values: Set[Json]
): JsonQueryIn[F] =
new JsonQueryIn[F](key, query, values)
end JsonQueryIn

View file

@ -0,0 +1,20 @@
package gs.predicate.v0.json
import cats.effect.std.MapRef
import io.circe.Json
/** Provides keys and JSON values, given some in-memory map.
*
* @param map
* The underlying map.
*/
final class MemoryMapJsonProvider[F[_]](
private val map: MapRef[F, String, Option[Json]]
) extends JsonProvider[F]:
/** @inheritDocs
*/
override def get(key: String): F[Option[Json]] =
map.apply(key).get
end MemoryMapJsonProvider

View file

@ -0,0 +1,128 @@
package gs.predicate.v0.json
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.json.query.JsonQuery
import io.circe.Json
import support.IOSuite
class JsonQueryEqualsTests extends IOSuite:
import JsonQueryEqualsTests.Data
import JsonQueryEqualsTests.newProvider
iotest("should find an exact match against some string value") {
val key = Data.keyGen.gen()
val value = Data.strValGen.gen()
val query = key
val blob = Json.obj(
key -> value
)
newProvider(Map(key -> blob)).flatMap { provider =>
given JsonProvider[IO] = provider
val p = JsonQueryEquals[IO](key, JsonQuery.compile(query), value)
p.eval().map(result => assertEquals(result, Predicate.Result.matched()))
}
}
iotest("should fail to match against some non-equal string value") {
val key = Data.keyGen.gen()
val value = Data.strValGen.gen()
val searchValue = Data.strValGen.gen()
val query = key
val blob = Json.obj(
key -> value
)
newProvider(Map(key -> blob)).flatMap { provider =>
given JsonProvider[IO] = provider
val p = JsonQueryEquals[IO](key, JsonQuery.compile(query), searchValue)
p.eval().map(result => assertEquals(result, Predicate.Result.missed()))
}
}
iotest("should find an exact match against some integer value") {
val key = Data.keyGen.gen()
val value = Data.intValGen.gen()
val query = key
val blob = Json.obj(
key -> value
)
newProvider(Map(key -> blob)).flatMap { provider =>
given JsonProvider[IO] = provider
val p = JsonQueryEquals[IO](key, JsonQuery.compile(query), value)
p.eval().map(result => assertEquals(result, Predicate.Result.matched()))
}
}
iotest("should fail to match against some non-equal integer value") {
val key = Data.keyGen.gen()
val value = Data.intValGen.gen()
val searchValue = Data.intValGen.gen()
val query = key
val blob = Json.obj(
key -> value
)
newProvider(Map(key -> blob)).flatMap { provider =>
given JsonProvider[IO] = provider
val p = JsonQueryEquals[IO](key, JsonQuery.compile(query), searchValue)
p.eval().map(result => assertEquals(result, Predicate.Result.missed()))
}
}
iotest("should find an exact match against some boolean value") {
val key = Data.keyGen.gen()
val value = Data.boolValGen.gen()
val query = key
val blob = Json.obj(
key -> value
)
newProvider(Map(key -> blob)).flatMap { provider =>
given JsonProvider[IO] = provider
val p = JsonQueryEquals[IO](key, JsonQuery.compile(query), value)
p.eval().map(result => assertEquals(result, Predicate.Result.matched()))
}
}
iotest("should fail to match against some non-equal boolean value") {
val key = Data.keyGen.gen()
val value = Json.fromBoolean(true)
val searchValue = Json.fromBoolean(false)
val query = key
val blob = Json.obj(
key -> value
)
newProvider(Map(key -> blob)).flatMap { provider =>
given JsonProvider[IO] = provider
val p = JsonQueryEquals[IO](key, JsonQuery.compile(query), searchValue)
p.eval().map(result => assertEquals(result, Predicate.Result.missed()))
}
}
object JsonQueryEqualsTests:
object Data:
val keyGen: Gen[String] = Gen.string.alphaNumeric(Size.between(4, 16))
val strValGen: Gen[Json] =
Gen.string.uppercaseAlpha(Size.fixed(8)).map(Json.fromString)
val intValGen: Gen[Json] = Gen.integer.inRange(0, 1000).map(Json.fromInt)
val boolValGen: Gen[Json] = Gen.boolean().map(Json.fromBoolean)
end Data
def newProvider(data: Map[String, Json]): IO[JsonProvider[IO]] =
for map <- MapRef.ofSingleImmutableMap[IO, String, Json](data)
yield new MemoryMapJsonProvider(map)
end JsonQueryEqualsTests

View file

@ -0,0 +1,68 @@
package gs.predicate.v0.json
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.json.query.JsonQuery
import io.circe.Json
import support.IOSuite
class JsonQueryInTests extends IOSuite:
import JsonQueryInTests.Data
import JsonQueryInTests.newProvider
iotest("should find an exact match against some set of values") {
val key = Data.keyGen.gen()
val value = Data.strValGen.gen()
val values = Set(value, Data.strValGen.gen(), Data.intValGen.gen())
val query = key
val blob = Json.obj(
key -> value
)
newProvider(Map(key -> blob)).flatMap { provider =>
given JsonProvider[IO] = provider
val p = JsonQueryIn[IO](key, JsonQuery.compile(query), values)
p.eval().map(result => assertEquals(result, Predicate.Result.matched()))
}
}
iotest("should fail to match anything within some set of values") {
val key = Data.keyGen.gen()
val value = Data.strValGen.gen()
val searchValues = Set(Data.strValGen.gen())
val query = key
val blob = Json.obj(
key -> value
)
newProvider(Map(key -> blob)).flatMap { provider =>
given JsonProvider[IO] = provider
val p = JsonQueryIn[IO](key, JsonQuery.compile(query), searchValues)
p.eval().map(result => assertEquals(result, Predicate.Result.missed()))
}
}
end JsonQueryInTests
object JsonQueryInTests:
object Data:
val keyGen: Gen[String] = Gen.string.alphaNumeric(Size.between(4, 16))
val strValGen: Gen[Json] =
Gen.string.uppercaseAlpha(Size.fixed(8)).map(Json.fromString)
val intValGen: Gen[Json] = Gen.integer.inRange(0, 1000).map(Json.fromInt)
end Data
def newProvider(data: Map[String, Json]): IO[JsonProvider[IO]] =
for map <- MapRef.ofSingleImmutableMap[IO, String, Json](data)
yield new MemoryMapJsonProvider(map)
end JsonQueryInTests

View file

@ -0,0 +1,66 @@
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 ValueInTests extends IOSuite:
import ValueInTests.Data
iotest("should find an exact match against some value") {
ValueInTests.newProvider(Data.KeyValues).flatMap { provider =>
given KeyValueProvider[IO] = provider
val p =
ValueIn[IO](
Data.ExistingKey,
Set(
Data.ExistingValue,
"",
Gen.string.alphaNumeric(Size.Fixed(8)).gen()
)
)
for result <- p.eval()
yield assertEquals(result, Predicate.Result.matched())
}
}
iotest("should not find a value if it is not associated to a key") {
ValueInTests.newProvider(Data.KeyValues).flatMap { provider =>
given KeyValueProvider[IO] = provider
val p =
ValueIn[IO](Data.ExistingKey, Set(Data.NotExistingValue))
for result <- p.eval()
yield assertEquals(result, Predicate.Result.missed())
}
}
iotest("should not find a key that does not exist within some provider") {
ValueInTests.newProvider(Data.KeyValues).flatMap { provider =>
given KeyValueProvider[IO] = provider
val p = ValueIn[IO](Data.NotExistingKey, Set(""))
for result <- p.eval()
yield assertEquals(result, Predicate.Result.missed())
}
}
object ValueInTests:
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[KeyValueProvider[IO]] =
for map <- MapRef.ofSingleImmutableMap[IO, String, String](data)
yield new MemoryMapKeyValueProvider(map)
end ValueInTests