packages feed

keiki-codec-json-test-0.1.0.0: README.md

# keiki-codec-json-test

Property-test toolkit for downstream consumers of
[`keiki-codec-json`](../keiki-codec-json/README.md).

Use this package in test suites that persist `RegFile rs` snapshots as
JSON and want release-time protection against schema drift. Production
code that only needs JSON encoding and decoding should depend on
`keiki-codec-json`, not this package.

## What this is for

The primary tool is the **case-#10 detector**: a per-slot-type golden-byte
test that catches a silent change to a slot type's `Aeson.ToJSON`
instance. This is the schema-evolution failure mode the shape hash
in [`Keiki.Shape`](../src/Keiki/Shape.hs) cannot detect by design —
the hash is over the slot type's *TypeRep*, not over its encoding.
If a consumer edits the `ToJSON` instance to change the on-the-wire
shape, the hash stays the same and old snapshots silently fail to
decode. `slotGoldenSpec` is the contract anchor that makes the drift
loud and obvious.

The secondary helpers expose the in-tree property disciplines as
library functions parameterised over the consumer's own slot list:
Value-path and Encoding-path round-trip, within-path determinism, and
structural sensitivity.

## Using

```haskell
import Data.Proxy (Proxy (..))
import qualified Data.Text as T
import Test.Hspec (describe, hspec)

import Keiki.Codec.JSON.Test
  ( regFileCodecProps
  , regFileShapeSensitivitySpec
  , someKnownShape
  )
import Keiki.Codec.JSON.Test.Golden
  ( SlotGolden (..)
  , slotGoldenSpec
  )

-- Your slot type and slot lists:
-- data Email = Email Text deriving (...)
-- type MySlots = '[ '("email", Email), '("count", Int) ]
-- type MySlotsRenamed = '[ '("emailAddress", Email), '("count", Int) ]

main :: IO ()
main = hspec $ do
  -- Case-#10 detector. Add one slotGoldenSpec per slot type whose
  -- ToJSON / FromJSON instances you want to pin.
  slotGoldenSpec "Email" (SlotGolden
    { sgInput = Email (T.pack "alice@example.com")
    , sgBytes = "\"alice@example.com\""
    })

  -- Round-trip + determinism over the snapshot's slot list.
  describe "props: MySlots" (regFileCodecProps @MySlots)

  -- Sensitivity: every named mutation must flip the shape hash.
  describe "sensitivity: MySlots" $
    regFileShapeSensitivitySpec
      (Proxy @MySlots)
      [ ("rename email", someKnownShape @MySlotsRenamed) ]
```

The toolkit assumes each slot type has `Aeson.ToJSON`,
`Aeson.FromJSON`, `Arbitrary` (for the property suite),
`CanonicalTypeName` (for the shape hash; default `Typeable`-based
instance is usually enough), `Eq`, and `Show`. If your slot type
lacks `Arbitrary`, write one — it is typically a one-liner via
`quickcheck-instances` for the standard library types.

## When you don't need this

`keiki-codec-json` alone is sufficient for production use. This
package is opt-in for test suites. Pulling it in adds `QuickCheck`,
`hspec`, and `quickcheck-instances` as transitive deps; if you only
want the codec in production, do not depend on this package.

## Running the self-test

```sh
cabal test keiki-codec-json-test:keiki-codec-json-test-test
```

The self-test exercises every public helper against a small toy
fixture (`Email`, `DemoSlots`, `DemoSlotsRenamed`). Expected
output: 7 examples, 0 failures.