All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.io.kotest.assertions.json.JsonMatchers.kt Maven / Gradle / Ivy

package io.kotest.assertions.json

import io.kotest.matchers.ComparableMatcherResult
import io.kotest.matchers.Matcher
import io.kotest.matchers.MatcherResult
import io.kotest.matchers.should
import io.kotest.matchers.shouldNot
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import kotlin.reflect.KClass

@OptIn(ExperimentalSerializationApi::class)
internal val pretty by lazy { Json { prettyPrint = true; prettyPrintIndent = "  " } }

/**
 * Verifies that the [expected] string is valid json, and that it matches this string.
 *
 * This matcher will consider two json strings matched if they have the same key-values pairs,
 * regardless of order.
 *
 */
@Deprecated("Use shouldEqualJson. Deprecated since 5.6. Will be removed in 6.0")
infix fun String?.shouldMatchJson(expected: String?) =
   this should matchJson(expected)

@Deprecated("Use shouldNotEqualJson. Deprecated since 5.6. Will be removed in 6.0")
infix fun String?.shouldNotMatchJson(expected: String?) =
   this shouldNot matchJson(expected)

fun matchJson(expected: String?) = object : Matcher {
   override fun test(value: String?): MatcherResult {
      val actualJson = try {
         value?.let(pretty::parseToJsonElement)
      } catch (ex: Exception) {
         return MatcherResult(
            false,
            { "expected: actual json to be valid json: $value" },
            { "expected: actual json to be invalid json: $value" }
         )
      }

      val expectedJson = try {
         expected?.let(pretty::parseToJsonElement)
      } catch (ex: Exception) {
         return MatcherResult(
            false,
            { "expected: expected json to be valid json: $expected" },
            { "expected: expected json to be invalid json: $expected" }
         )
      }

      return ComparableMatcherResult(
         actualJson == expectedJson,
         { "expected json to match, but they differed\n" },
         { "expected not to match with: $expectedJson but match: $actualJson" },
         actualJson.toString(),
         expectedJson.toString()
      )
   }
}

fun beValidJson() = object : Matcher {
   override fun test(value: String?): MatcherResult {
      return try {
         value?.let(pretty::parseToJsonElement)
         MatcherResult(
            true,
            { "expected: actual json to be valid json: $value" },
            { "expected: actual json to be invalid json: $value" }
         )
      } catch (ex: Exception) {
         MatcherResult(
            false,
            { "expected: actual json to be valid json: $value" },
            { "expected: actual json to be invalid json: $value" }
         )
      }
   }
}

fun beJsonType(kClass: KClass<*>) = object : Matcher {

   override fun test(value: String?): MatcherResult {
      val element = try {
         value?.let(pretty::parseToJsonElement)
      } catch (ex: Exception) {
         return MatcherResult(
            false,
            { "expected: actual json to be valid json: $value" },
            { "expected: actual json to be invalid json: $value" }
         )
      }
      return MatcherResult(
         kClass.isInstance(element),
         { "expected: $value to be valid json of type: ${kClass.simpleName}" },
         { "expected: $value to not be of type: ${kClass.simpleName}" }
      )
   }
}

/**
 * Verifies that the [expected] string is valid json, and that it matches this string.
 *
 * This matcher will consider two json strings matched if they have the same key-values pairs,
 * regardless of order.
 *
 */
@Deprecated("Use shouldEqualJson which uses a lambda. Deprecated since 5.6. Will be removed in 6.0")
fun String.shouldEqualJson(expected: String, mode: CompareMode, order: CompareOrder) =
   this.shouldEqualJson(expected, legacyOptions(mode, order))

@Deprecated("Use shouldEqualJson which uses a lambda. Deprecated since 5.6. Will be removed in 6.0")
fun String.shouldEqualJson(expected: String, options: CompareJsonOptions) {
   this should equalJson(expected, options)
}

@Deprecated("Use shouldNotEqualJson which uses a lambda. Deprecated since 5.6. Will be removed in 6.0")
fun String.shouldNotEqualJson(expected: String, mode: CompareMode, order: CompareOrder) =
   this.shouldNotEqualJson(expected, legacyOptions(mode, order))

@Deprecated("Use shouldNotEqualJson which uses a lambda. Deprecated since 5.6. Will be removed in 6.0")
fun String.shouldNotEqualJson(expected: String, options: CompareJsonOptions) {
   this shouldNot equalJson(expected, options)
}

fun String.shouldBeEmptyJsonArray(): String {
   this should matchJson("[]")
   return this
}

fun String.shouldBeEmptyJsonObject(): String {
   this should matchJson("{}")
   return this
}

fun String.shouldBeJsonArray(): String {
   this should beJsonArray()
   return this
}

fun String.shouldNotBeJsonArray(): String {
   this shouldNot beJsonArray()
   return this
}

fun beJsonArray() = beJsonType(JsonArray::class)

fun String.shouldBeJsonObject(): String {
   this should beJsonObject()
   return this
}

fun String.shouldNotBeJsonObject(): String {
   this shouldNot beJsonObject()
   return this
}

fun beJsonObject() = beJsonType(JsonObject::class)

fun String.shouldBeValidJson(): String {
   this should beValidJson()
   return this
}

fun String.shouldNotBeValidJson(): String {
   this shouldNot beValidJson()
   return this
}

internal fun parse(expected: String, actual: String): Pair {
   val enode = pretty.parseToJsonElement(expected)
   val anode = pretty.parseToJsonElement(actual)
   val e = toJsonTree(enode)
   val a = toJsonTree(anode)
   return Pair(e, a)
}

internal fun toJsonTree(root: JsonElement) =
   with(root.toJsonNode()) {
      JsonTree(this, prettyPrint(this))
   }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy