pqi-conformance 1.0.0.0 → 1.0.0.1
raw patch · 15 files changed
+37/−33 lines, 15 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- CHANGELOG.md +4/−0
- README.md +8/−8
- pqi-conformance.cabal +1/−1
- src/library/Pqi/Conformance/Observation.hs +3/−3
- src/library/Pqi/Conformance/Operation/Cancel.hs +1/−1
- src/library/Pqi/Conformance/Operation/Cancel/Cleanup.hs +3/−3
- src/library/Pqi/Conformance/Operation/Cancel/Stale.hs +1/−1
- src/library/Pqi/Conformance/Operation/ErrorMessage.hs +3/−3
- src/library/Pqi/Conformance/Operation/Finish/PostFinishSend.hs +4/−4
- src/library/Pqi/Conformance/Operation/GetNotice.hs +1/−1
- src/library/Pqi/Conformance/Operation/Notifies.hs +2/−2
- src/library/Pqi/Conformance/Operation/ResultErrorField.hs +1/−1
- src/library/Pqi/Conformance/Operation/ResultStatus.hs +1/−1
- src/library/Pqi/Conformance/Reference.hs +2/−2
- src/library/Pqi/Conformance/Scenario.hs +2/−2
CHANGELOG.md view
@@ -1,3 +1,7 @@+# v1.0.0.1++Doc corrections.+ # v1.0.0.0 ## Non-breaking
README.md view
@@ -10,7 +10,7 @@ ## Goal: full libpq fidelity -The purpose of this suite is to enforce **byte-identical output to `libpq`**+The purpose of this suite is to enforce **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.@@ -26,26 +26,26 @@ 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+- **`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+- **`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+- **`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+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`,+`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 connection-independent `Adapter` fields `Pqi.unescapeBytea` and@@ -56,7 +56,7 @@ ## Usage -An adapter's own test suite is a one-liner — it hands `specs` a `Proxy` of its+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):
pqi-conformance.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: pqi-conformance-version: 1.0.0.0+version: 1.0.0.1 category: Database, PostgreSQL, Testing synopsis: Differential conformance tests for pqi adapters description:
src/library/Pqi/Conformance/Observation.hs view
@@ -8,8 +8,8 @@ -- the comparison to protocol-derived information only. -- -- Only protocol-derived information is captured: both adapters parse the same--- wire bytes, so these fields genuinely agree. All result fields — including--- the flat error message text and all structured error fields — are captured+-- wire bytes, so these fields genuinely agree. All result fields - including+-- the flat error message text and all structured error fields - are captured -- in full and compared byte-identically. module Pqi.Conformance.Observation ( ResultObservation (..),@@ -31,7 +31,7 @@ -- | Every structured field of the error report, keyed by 'Lq.FieldCode'. -- All of them are carried by the wire error response. errorFields :: [(Lq.FieldCode, Maybe ByteString)],- -- | The flat formatted error message, byte-identical to libpq's+ -- | The flat formatted error message, identical to libpq's -- @PQresultErrorMessage@ at DEFAULT verbosity. errorMessage :: Maybe ByteString, ntuples :: Int32,
src/library/Pqi/Conformance/Operation/Cancel.hs view
@@ -2,7 +2,7 @@ -- both on an idle connection and against a running query (which then fails -- with SQLSTATE @57014@). ----- The full 'Either' value is compared — not just success\/failure — so that+-- The full 'Either' value is compared - not just success\/failure - so that -- any divergence in error text is caught. module Pqi.Conformance.Operation.Cancel ( spec,
src/library/Pqi/Conformance/Operation/Cancel/Cleanup.hs view
@@ -4,8 +4,8 @@ -- Root cause: in pqi-native, 'Pqi.getResult' returns 'Nothing' (the pipeline -- separator) __before__ reading the trailing 'ReadyForQuery' message, leaving -- @asyncPending = True@. If 'Pqi.cancel' is called while @asyncPending@ is--- still @True@ — as 'Hasql.Comms.Session.cleanUpAfterInterruption' does after--- draining results — a cancel request is sent to the server even though the+-- still @True@ - as 'Hasql.Comms.Session.cleanUpAfterInterruption' does after+-- draining results - a cancel request is sent to the server even though the -- query has already finished. The server receives the signal, sets -- @QueryCancelPending@, and the __next__ command (e.g. @ABORT@) is cancelled -- with SQLSTATE @57014@, leaving the connection unusable.@@ -35,7 +35,7 @@ -- a stale cancel. -- 3. drainResults reads the remaining ReadyForQuery. -- 4. The connection re-enters serial mode.- -- 5. exec runs a follow-up command — it must NOT be cancelled by the+ -- 5. exec runs a follow-up command - it must NOT be cancelled by the -- stale signal that arrived in step 2. it "does not corrupt subsequent commands when cancel is called after pipeline results are drained" \conninfo -> bracket (Lq.connectdb adapter conninfo) Lq.finish \connection -> do
src/library/Pqi/Conformance/Operation/Cancel/Stale.hs view
@@ -3,7 +3,7 @@ -- the connection. -- -- Root cause: 'Pqi.cancel' opens a TCP connection to the postmaster, sends the--- @CancelRequest@, and — in the buggy implementation — immediately closes its+-- @CancelRequest@, and - in the buggy implementation - immediately closes its -- end. The request can then sit unread in the postmaster's socket buffer while -- the client races ahead and issues its next query. The postmaster eventually -- reads the request and delivers @SIGINT@ to the backend, which by then is
src/library/Pqi/Conformance/Operation/ErrorMessage.hs view
@@ -1,8 +1,8 @@ -- | Coverage for 'Pqi.errorMessage': the connection-level error string. ----- The goal is byte-identical output to libpq's @PQerrorMessage@ in all--- documented scenarios. Error strings are compared in full — not just for--- presence — so formatting bugs are caught. Scenarios are chosen to avoid+-- The goal is identical output to libpq's @PQerrorMessage@ in all+-- documented scenarios. Error strings are compared in full - not just for+-- presence - so formatting bugs are caught. Scenarios are chosen to avoid -- statement-position fields (@'P'@), which depend on the client-stored query -- text and cannot be reproduced from wire fields alone. --
src/library/Pqi/Conformance/Operation/Finish/PostFinishSend.hs view
@@ -9,18 +9,18 @@ -- operations kept attempting to use the closed socket's file descriptor. The -- first such send after close typically failed fast with "Bad file -- descriptor", but in a process doing other concurrent socket I\/O (exactly--- the situation in a real test suite or application — e.g. testcontainers+-- the situation in a real test suite or application - e.g. testcontainers -- and a connection pool's own background threads keep other sockets open), -- that closed file descriptor number could already have been reused for an -- unrelated socket by the time of a second attempt. Writing to the (silently -- reused) fd then blocked waiting for buffer space that a completely--- unrelated peer would never free — which is exactly what @hasql@'s+-- unrelated peer would never free - which is exactly what @hasql@'s -- @cleanUpAfterInterruption@ flow does after a first send fails: it always -- re-attempts a further write to deallocate prepared statements. -- -- This mirrors hasql-pool's @Sessions.closeConn@ helper, which deliberately -- calls 'Pqi.finish' on a connection and then keeps using it (to test that--- the pool notices and evicts it) — a supported use of the low-level+-- the pool notices and evicts it) - a supported use of the low-level -- @onLibpqConnection@ escape hatch, and the code path in which the hang was -- first observed (hasql-pool's @Specs.UseSpec@, "Connection errors cause -- eviction of connection").@@ -72,7 +72,7 @@ _ <- Pqi.exec connection "select 1" Pqi.finish connection result <- timeout sendTimeoutMicros (try @SomeException (Pqi.exec connection "select 1"))- -- 'Nothing' means the send never completed within the bound — i.e.+ -- 'Nothing' means the send never completed within the bound - i.e. -- it hung. Succeeding or throwing are both acceptable outcomes here; -- only hanging is not. isJust result `shouldBe` True
src/library/Pqi/Conformance/Operation/GetNotice.hs view
@@ -1,7 +1,7 @@ -- | Coverage for 'Pqi.getNotice': retrieving accumulated notices, which -- are present only while reporting is enabled and drain after retrieval. ----- The notice text is compared byte-identically — both adapters must produce+-- The notice text is compared byte-identically - both adapters must produce -- the same formatted string as libpq's notice processor at DEFAULT verbosity. -- At that verbosity, context (@'W'@ field) is suppressed for NOTICE-level -- messages, so the formatted string is just @\"NOTICE: \<message\>\\n\"@.
src/library/Pqi/Conformance/Operation/Notifies.hs view
@@ -1,8 +1,8 @@ -- | Coverage for 'Pqi.notifies': @LISTEN@\/@NOTIFY@ delivery, queueing, -- and that @UNLISTEN@ stops delivery. ----- The backend PID carried by a notification is connection-specific — the--- candidate and the reference are distinct backends — so 'bePid' is omitted+-- The backend PID carried by a notification is connection-specific - the+-- candidate and the reference are distinct backends - so 'bePid' is omitted -- from the cross-adapter comparison. Each scenario that receives a -- notification instead asserts independently (per adapter) that -- @Pqi.bePid notification == backendPID connection@, verifying that the PID field
src/library/Pqi/Conformance/Operation/ResultErrorField.hs view
@@ -1,5 +1,5 @@ -- | Coverage for 'Pqi.resultErrorField': the structured fields carried by--- a wire error response — SQLSTATE, severity, primary message, detail, hint,+-- a wire error response - SQLSTATE, severity, primary message, detail, hint, -- positions, internal query, context, and source location. -- -- These all come from the wire error response and are compared in full
src/library/Pqi/Conformance/Operation/ResultStatus.hs view
@@ -1,5 +1,5 @@ -- | Coverage for 'Pqi.resultStatus': the status reported for each kind of--- result — tuples, a command, an empty query, and a failure.+-- result - tuples, a command, an empty query, and a failure. module Pqi.Conformance.Operation.ResultStatus ( spec, )
src/library/Pqi/Conformance/Reference.hs view
@@ -1,6 +1,6 @@ -- | The reference adapter: a direct @postgresql-libpq@ wrapper used as the -- ground truth in differential tests. It is intentionally independent of the--- @pqi-ffi@ package (despite producing byte-identical output) so that+-- @pqi-ffi@ package (despite producing identical output) so that -- adapter test suites can depend on @pqi-conformance@ without a circular -- dependency through @pqi-ffi@. module Pqi.Conformance.Reference@@ -150,7 +150,7 @@ -- so the C call escapes 'Foreign.ForeignPtr.withForeignPtr' as an unevaluated -- thunk over the raw @PGresult@ pointer. Should the handle become unreachable -- before that thunk is forced, its @PQclear@ finalizer has already freed the--- memory and the number read is garbage — which made the reference disagree+-- memory and the number read is garbage - which made the reference disagree -- with a correct candidate whenever a collection happened to land in the -- window. Every other accessor is imported in 'IO' and is unaffected. strictly :: LibPQ.Result -> IO Int32 -> IO Int32
src/library/Pqi/Conformance/Scenario.hs view
@@ -1,9 +1,9 @@ -- | Reusable scenario fragments shared by the per-operation spec modules. ----- These are the recurring building blocks — running a query and observing its+-- These are the recurring building blocks - running a query and observing its -- result, draining the asynchronous result stream, collecting a @COPY OUT@ -- stream, driving a polling loop to its terminal status, and the handful of--- well-known type OIDs — factored out so each operation module stays focused on+-- well-known type OIDs - factored out so each operation module stays focused on -- the one operation it covers. module Pqi.Conformance.Scenario ( -- * Running queries