Reusable property-based test scaffolding for CPPB roles.
These helpers consume the :cppb/role metadata stamped onto vars by
defcollector / defpromoter / defpipeline / defboundary (see
hive-cppb.core) and produce clojure.test assertions that
exercise the canonical laws each role is expected to obey.
Helpers are designed to be called from a caller's own test ns:
(require '[hive-cppb.test :as cppb-test]) (require '[clojure.test.check.generators :as gen])
(deftest add-totals-laws (cppb-test/promoter-laws #'add-totals (gen/hash-map :fetch-numbers (gen/vector gen/small-integer))))
Each helper is a regular function that runs clojure.test/is assertions
inline, so it can be dropped into any deftest. They throw via the
underlying assertion machinery if a law is violated.
Laws codify the CPPB principle: pure data flow with effects pushed to the boundary.
Reusable property-based test scaffolding for CPPB roles.
These helpers consume the `:cppb/role` metadata stamped onto vars by
`defcollector` / `defpromoter` / `defpipeline` / `defboundary` (see
`hive-cppb.core`) and produce `clojure.test` assertions that
exercise the canonical laws each role is expected to obey.
Helpers are designed to be called from a caller's own test ns:
(require '[hive-cppb.test :as cppb-test])
(require '[clojure.test.check.generators :as gen])
(deftest add-totals-laws
(cppb-test/promoter-laws #'add-totals
(gen/hash-map :fetch-numbers (gen/vector gen/small-integer))))
Each helper is a regular function that runs `clojure.test/is` assertions
inline, so it can be dropped into any `deftest`. They throw via the
underlying assertion machinery if a law is violated.
Laws codify the CPPB principle: pure data flow with effects pushed to
the boundary.(boundary-contract boundary-var
&
{:keys [ok-gen err-gen num-tests]
:or {ok-gen gen/any-printable-equatable
err-gen (gen/map gen/keyword gen/small-integer)
num-tests default-num-tests}})Assert the contract of a CPPB boundary.
Args: boundary-var — a var #'foo where foo was defined via defboundary
Options: :ok-gen — generator producing values to wrap in (r/ok ...) (default any) :err-gen — generator producing data maps for (r/err :test/x data) (default a small map) :num-tests — how many random inputs to sample (default 50)
Contract asserted:
(r/ok value) without throwing.(r/err category data) without throwing.ex-info for non-Result inputs (defboundary's :else
branch). We assert the throw and that the ex-data carries
:cppb/role :boundary so callers can recognise it.Assert the contract of a CPPB boundary.
Args:
boundary-var — a var #'foo where foo was defined via defboundary
Options:
:ok-gen — generator producing values to wrap in (r/ok ...) (default any)
:err-gen — generator producing data maps for (r/err :test/x data)
(default a small map)
:num-tests — how many random inputs to sample (default 50)
Contract asserted:
1. Boundary handles `(r/ok value)` without throwing.
2. Boundary handles `(r/err category data)` without throwing.
3. Boundary throws `ex-info` for non-Result inputs (defboundary's :else
branch). We assert the throw and that the ex-data carries
`:cppb/role :boundary` so callers can recognise it.(pipeline-shape pipeline-var
seed-gen
&
{:keys [num-tests] :or {num-tests default-num-tests}})Assert structural laws of a CPPB pipeline.
Args: pipeline-var — a var #'foo where foo was defined via defpipeline seed-gen — a test.check generator producing seed inputs
Options: :num-tests — how many random seeds to sample (default 50)
Laws asserted:
Short-circuit-on-error law (NOT asserted here — needs DI):
Asserting that the pipeline aborts on the first failing collector
requires injecting a stub collector into the pipeline definition, which
the current defpipeline does not support (collectors are resolved as
closed-over symbols at macro-expansion time). The smoke test in
core_test.clj covers this with hand-written failing fixtures.
Assert structural laws of a CPPB pipeline. Args: pipeline-var — a var #'foo where foo was defined via defpipeline seed-gen — a test.check generator producing seed inputs Options: :num-tests — how many random seeds to sample (default 50) Laws asserted: 1. Result-shape: pipeline returns a hive-dsl Result for any seed. 2. Determinism: same seed → same Result (assumes underlying collectors and promoters are pure, per CPPB invariant). Short-circuit-on-error law (NOT asserted here — needs DI): Asserting that the pipeline aborts on the *first* failing collector requires injecting a stub collector into the pipeline definition, which the current `defpipeline` does not support (collectors are resolved as closed-over symbols at macro-expansion time). The smoke test in `core_test.clj` covers this with hand-written failing fixtures.
(promoter-laws promoter-var
input-gen
&
{:keys [num-tests idempotent?]
:or {num-tests default-num-tests}})Assert the canonical laws of a CPPB promoter via property-based tests.
Args:
promoter-var — a var #'foo where foo was defined via defpromoter
input-gen — a test.check generator producing data values that the
promoter accepts (i.e. the unwrapped accumulator map)
Options (kw-args, all optional):
:num-tests — how many random inputs to sample (default 50)
:idempotent? — if truthy (or if the var carries :cppb/idempotent
metadata), assert (promoter (promoter data)) ≡
(promoter data) when both calls are :ok
Laws asserted:
Identity-on-err law (caller responsibility): when the pipeline sees an
err Result it short-circuits before reaching the next promoter. Promoters
themselves never receive err input, so we deliberately do not test that
here — it is enforced by r/bind in defpipeline's expansion.
Assert the canonical laws of a CPPB promoter via property-based tests.
Args:
promoter-var — a var #'foo where foo was defined via defpromoter
input-gen — a test.check generator producing `data` values that the
promoter accepts (i.e. the unwrapped accumulator map)
Options (kw-args, all optional):
:num-tests — how many random inputs to sample (default 50)
:idempotent? — if truthy (or if the var carries `:cppb/idempotent`
metadata), assert (promoter (promoter data)) ≡
(promoter data) when both calls are :ok
Laws asserted:
1. Result-shape: promoter always returns a hive-dsl Result.
2. Determinism (purity): same input → same output across two invocations.
Promoters are required by CPPB to be pure, so this is unconditional.
3. Idempotence (opt-in): when enabled, applying the promoter to its own
output yields the same Result.
Identity-on-err law (caller responsibility): when the *pipeline* sees an
err Result it short-circuits before reaching the next promoter. Promoters
themselves never receive err input, so we deliberately do not test that
here — it is enforced by `r/bind` in `defpipeline`'s expansion.cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |