nubank / matcher-combinators

Library for creating matcher combinator to compare nested data structures
Other
467 stars 23 forks source link

Implementing Matcher protocol only matters if used as expected value #224

Open NoahTheDuke opened 6 months ago

NoahTheDuke commented 6 months ago

Problem statement

I use jsonb columns in Postgres to store arbitrary data. I don't automatically convert these columns when I query, preferring to wait until it's necessary. I can convert those columns before passing the results to match?, but it would be nice to perform that automatically within the matcher.

However, implementing Matcher for the PGObject or PGArray doesn't work if I use a normal sequence or object in the expected position, as the built-in matcher implementations check the type of actual to short-circuit an otherwise lengthy check. For example, here's sequence-match as used by EqualSeq.

https://github.com/nubank/matcher-combinators/blob/afdb64012757719bb699c2a7a3d9d8c2f06a9d32/src/cljc/matcher_combinators/core.cljc#L262-L265

Proposed solution

I'm not sure the right way to go about this, as there's a step of transformation that must happen before the actual can be checked, which I'm seeking to automate. Maybe that's outside the purview of matcher-combinators?

Thanks so much.

Repro:

(ns postgres-mc-example
  (:require
   [cheshire.core :as json]
   [clojure.test :refer [deftest is]]
   [matcher-combinators.core :as mc]
   [matcher-combinators.result :as-alias mcr]
   [matcher-combinators.test :refer [match?]])
  (:import
   [org.postgresql.util PGobject]))

(defn from-pg-json
  [^PGobject obj & cheshire-options]
  (when obj (apply json/parse-string (.getValue obj) cheshire-options)))

(defn to-pg-json
  [data]
  (doto (PGobject.)
    (.setType "jsonb")
    (.setValue (json/generate-string data))))

(extend-protocol mc/Matcher
  PGobject
  (-matcher-for [this] this)
  (-matcher-for [this _t->m] this)
  (-name [this] 'PGobjectEquals)
  (-match [this actual] (mc/match (from-pg-json this) actual)))

(deftest matcher-smoke-test
  (is (match? [1 2 3] (to-pg-json [1 2 3]))))