packages feed

pqi-conformance-0.0.1.0: README.md

# pqi-conformance

[![Hackage](https://img.shields.io/hackage/v/pqi-conformance.svg)](https://hackage.haskell.org/package/pqi-conformance)
[![Continuous Haddock](https://img.shields.io/badge/haddock-master-blue)](https://nikita-volkov.github.io/pqi-conformance/)

A reusable [`hspec`](https://hackage.haskell.org/package/hspec) toolkit that
checks any [`pqi`](https://github.com/nikita-volkov/pqi) adapter against the
battle-tested [`postgresql-libpq`](https://hackage.haskell.org/package/postgresql-libpq)
library as a reference.

## Goal: full libpq fidelity

The purpose of this suite is to enforce **byte-identical output to `libpq`**
for every protocol-derived value. This means error message strings, notice
text, result status, field metadata, cell data, and all structured error fields
must all match `libpq`'s output exactly, not just in shape or presence.

The suite runs the same operation on the candidate adapter and on a direct
[`postgresql-libpq`](https://hackage.haskell.org/package/postgresql-libpq)
reference connection (the fidelity reference, which delegates directly to the C
`libpq` library), connected to the same throwaway PostgreSQL container. It then
asserts that the protocol-derived observations are equal.

### Structurally incomparable values

A small number of values are **structurally incomparable across connections**
and are handled differently:

- **`backendPID`** — the OS process ID of the backend. Each connection gets a
  distinct backend, so the two PIDs will never match. The spec asserts `> 0`
  independently for each adapter.
- **`socket`** — the file descriptor of the client socket. Also
  connection-specific. Covered only by its own presence check.
- **`Notify.bePid`** — the PID of the notifying backend. Since each adapter's
  connection has its own backend, cross-adapter comparison would always fail.
  Instead, each adapter asserts independently that `notification.bePid ==
  backendPID connection` (a within-connection assertion that verifies the PID
  field is correctly populated).

These omissions are a structural constraint of the differential testing
approach, not an intentional leniency in the suite. All other values —
including error message text, notice text, and cancel error text — are compared
in full.

## Structure: one module per operation

The suite is organised as **one spec module per API operation**, under
`Pqi.Conformance.Operation.*` — `...Operation.Exec`, `...Operation.ExecParams`,
`...Operation.LoSeek`, `...Operation.Fnumber`, and so on, one for every public
method of `Pqi.IsConnection`, `Pqi.IsResult`, and `Pqi.IsCancel`,
plus the standalone `Pqi.unescapeBytea`. Each module holds the differential
scenarios that exercise that one operation (its happy paths, its edge cases,
and its error paths). Shared scenario fragments live in
`Pqi.Conformance.Scenario`.

## Usage

An adapter's own test suite is a one-liner — it hands `specs` a `Proxy` of its
connection type and `specs` takes care of booting the container and running the
whole battery (every operation, the coverage meta-test, and SCRAM):

```haskell
module Main (main) where

import Data.Proxy (Proxy (..))
import Pqi.Conformance (specs)
import MyAdapter (MyConnection)
import Test.Hspec (hspec)

main :: IO ()
main = hspec (specs (Proxy @MyConnection))
```

`MyConnection` only needs a `Pqi.IsConnection` instance; the candidate and
the `postgresql-libpq` reference are both driven through that interface.

## Contributing

Contributions extending the suite are very welcome. The more thoroughly we
cover the operations, the more confidence we can have in the adapters.