ci: client-tests job runs all 3 driver suites against live PG#84
ci: client-tests job runs all 3 driver suites against live PG#84
Conversation
Temporary bridge until PR #84 lands and replaces `client-smoke` CI step with `client-tests` (vitest). Exits 0 if PGQUE_TEST_DSN is unset; runs minimal send→tick→receive→ack otherwise. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…, tests, packaging) (#83) * feat(clients/typescript): bring driver to parity with Go (API surface, tests, packaging) - Rewrite TS client around pg.Pool with feature parity to Go driver: connect, send, receive, ack, nack, subscribe, unsubscribe, newConsumer with handle/start. - Use JS bigint for msg_id, batch_id, and send() return — silent number-precision loss above 2^53 was a real correctness bug. Promote pg-types parser at module load time; documented as a side effect in README. - Add Consumer with AbortSignal-based cancellation, per-event-type dispatch, per-message nack on handler failure or unknown event type. - Error class hierarchy: PgqueError + Connection / QueueNotFound / ConsumerNotFound / Sql subclasses, mapped from raw pg errors. - Comprehensive vitest suite, env-gated via PGQUE_TEST_DSN: round-trip, default-type, validation, error mapping, payload edge cases, bigint preservation, subscribe idempotency, concurrent producers, nack routing to retry vs DLQ at the limit, custom retryAfter/reason, consumer dispatch + cancellation. - Publishable package: name 'pgque' (not 'pgque-ts-example'), version 0.2.0, Apache-2.0 LICENSE in module root, ESM exports map, engines >=20. - TSDoc on every exported symbol; strict + noUncheckedIndexedAccess. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ts): restore env-gated smoke.ts (B1) Temporary bridge until PR #84 lands and replaces `client-smoke` CI step with `client-tests` (vitest). Exits 0 if PGQUE_TEST_DSN is unset; runs minimal send→tick→receive→ack otherwise. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(ts): add Caveats section for global BIGINT parser (NB1) Documents that importing pgque mutates the process-global pg-types parser (oid 20 → JS bigint) and what that means for other pg-using code in the same process. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(ts): TSDoc on Consumer.start() re AbortSignal scope (NB2) Clarifies that aborting the signal cuts the inter-poll sleep immediately but does not cancel an in-flight receive() call. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(ts): document pool-size choice for 50-producer test (P1) Default pool size 10 is intentional: the test asserts uniqueness and count only, no elapsed-time bounds, so 5-deep queueing is acceptable. Comment added inline to the test. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(ts): TSDoc on send() payload-shape requirements (P2) Documents that undefined/circular refs cause JSON.stringify to throw or silently drop values, and that only plain JSON-compatible values are safe. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(ts): show JSON.parse(msg.payload) in quickstart (P3) payload is raw JSON text; consumer must call JSON.parse(). Added inline note and updated the handler example. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Nik Samokhvalov <nik@niks-mbp.lan> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
REV Review — PR #84CI:
Intentional Go failure (proof point — confirmed)This branch's That is the failure mode we wanted the new harness to surface. Not a setup misconfig — the harness is doing its job. Once PR #115 is on Status update on the band-aids (becomes important after this PR's PR-time CI ran)PR #84's last CI run was at 09:18 UTC. PR #82 merged at 09:30 UTC, PR #115 at 09:32 UTC, PR #83 at 09:34 UTC. So at run time, the Python
Recommend either rebasing this PR on top of current Blocking findingsNone. The Go failure is the proof point, and the band-aids are documented and defensible at the time the CI ran. Non-blocking findings
Potential issues to watch
Anti-leak scanClean. VerdictREADY FOR USER REVIEW — with a strong recommendation to either rebase + drop the now-stale |
b6838c1 to
7d6f061
Compare
7d6f061 to
fb84bd1
Compare
|
Rebase onto main complete ( Base SHA before/after rebase:
Conflicts: Band-aids check: Initial CI status (immediately post-push): Generated by Claude Code |
REV Review — PR #84 (post-rebase, head
|
| Job | Status |
|---|---|
verify |
PASS |
test (14)..test (18) |
PASS (5/5) |
Go client tests |
PASS |
Python client tests |
FAIL |
TypeScript client tests |
FAIL |
claude-review |
PASS |
So the per-client jobs did NOT all go green this round — Go yes, Python and TypeScript still red even with #117 on the base. Captured TS assertion fragments from the run:
Error: expected undefined to be defined
Error: expected [] to have a length of 1 but got +0
Error: expected '0' to be '1'
Error: expected 0 to be greater than or equal to 2
Error: expected [] to have a length of 3 but got +0
Error: expected +0 to be 50
Error: expected [] to have a length of 6 but got +0
These are the "I called receive() and got nothing back" / "I expected N rows, got 0" pattern. That's exactly the signature of the empty-batch / tick-not-firing bug class — except #117 is allegedly in the base. So either:
- There's a remaining empty-batch / dual-filter edge case the drivers hit (e.g.
force_ticknot called between send and receive, or a Subscriber/subscribe()ordering bug in the driver), or - The drivers themselves have bugs the now-running tests legitimately catch (which is the stated goal of this PR), or
- The tests don't drive
pgque.ticker()between send and receive and have always assumed someone else did.
This is workflow-PR territory, not driver-fix territory. The harness is doing its job — surfacing real failures. The fixes belong in the driver PRs, not here. Recommendation below.
1. Security
Clean.
PGQUE_TEST_DSNuses literalpgque_testpassword — CI-only, never leaves the runner, no secret-store coupling. Correct scoping (job-levelenv:, not workflow-level).- All actions on floating major tags (
@v4,@v5) — consistent with the existing workflow convention. Not a regression introduced by this PR. SHA pinning would be a separate workflow-wide hardening task. - No new permissions blocks added. Inherits repo defaults. No write tokens introduced.
- No secret echoing in
run:steps. DSN is exported as env var, not interpolated into shell withecho. npm ci/npm installstep usesif [ -f package-lock.json ]— sensible fallback. Watch supply chain on the install side; consider addingnpm ci --ignore-scriptsfor a future hardening pass. Non-blocking.
2. Bugs (workflow correctness)
Clean.
- Three independent jobs each spin their own postgres-in-docker on
localhost:5432of three different runners — no port conflict (separate VMs). Correct. needs: verifyon each — good gate; no parallel waste building broken sql.if: always()onCleanup— correct; ensures docker cleanup runs even on test failure.PGQUE_TEST_DSNdeclared at job scope withenv:, available to all steps including the env-gated test guards in all three drivers. This is the core fix the PR exists to deliver, and it works — Go'sgetDSN()-gated tests now run (and pass).- Wait loop
for i in $(seq 1 30); do … && break; sleep 1; done || exit 1— solid bounded wait. - One latent concern: nothing in any of the three jobs calls
pgque.ticker()or sets uppg_cron. If any test relies on the ticker firing in the background (it must, given snapshot-batching), it depends on the test framework callingforce_tick()itself. The Python and TS failures suggest some tests may not be doing this. Not a workflow bug — a test-suite contract issue surfaced by this PR.
3. Test analysis
This is the meat.
Does the new matrix catch what client-smoke missed? Yes, demonstrably. The old workflow ran one go test (TestSmoke), one python file (test_smoke.py), and one tsx script with a hardcoded DSN — and exported no PGQUE_TEST_DSN. The new workflow runs go test -race -v ./..., pytest -v clients/python/tests/, and npx vitest --run with PGQUE_TEST_DSN exported job-wide. The 19 prior Python failures and 6 prior TS failures (pre-#117) are direct evidence the new harness exercises code paths the old one couldn't.
Are tests still silently skipping? Two checks:
pytest -v clients/python/tests/— no--ignoreflags (post-rebase commitfb84bd1removed them). Good.npx vitest --run— no--passWithNoTests. Good. Ifclients/typescript/test/is empty, the job will red. Confirmedtest/directory exists post-feat(clients/typescript): bring driver to parity with Go (API surface, tests, packaging) #83.- However, individual env-gated tests (e.g.
if (!process.env.PGQUE_TEST_DSN) test.skip(...)) will still skip if the DSN env propagation fails. Withenv:at job level, propagation is correct. Tests that fail withexpected [] to have length 1are clearly running, not skipping.
Does -race matter? Yes — go test -race -v ./... is a meaningful tightening over the old go test -run TestSmoke. Watch runtime budget as the suite grows.
Coverage gap not fixed by this PR: the matrix tests against postgres:18 only for client tests. The SQL test (14)..test (18) matrix already covers PG version drift, so client-side drift is covered indirectly. Acceptable for v0.1 — non-blocking.
4. Guidelines
CLAUDE.md compliance check:
- Conventional commit prefix
ci:✓ (alsochore(ci):on the band-aid removal commit ✓) - Subject lines under 50 chars ✓
- No
pgq.references introduced (workflow usespgqueschema andpgque_testDB throughout) ✓ - 2-space indent in YAML and shell snippets ✓
- Multi-line
run:blocks do not setset -Eeuo pipefail. CLAUDE.md shell rules call for it, but this is consistent with all pre-existing steps in the same workflow file — not a regression. Non-blocking; suggest a workflow-wide hardening pass later.
Anti-leak scan — clean. Grepped diff, PR body, and the 5 commits for: gitlab|sahmed|@AR\b|hetzner|postgres-ai|round[ -]?[0-9]+|WI[ -]?#?[0-9]+|R8\b. No matches.
5. Docs
README.mdhas a CI badge pointing atci.yml— still works. Job renameclient-smoke→ three jobs is invisible to the badge (badge tracks workflow result, not job names).README.mdhas no per-job badge for client tests — neutral; the project hasn't promised one.- No
CONTRIBUTING.mdin the repo root, and no doc references toclient-smokejob name found in the README. Nothing to update. If this PR's intent is to make client-driver coverage visible, a follow-up could add per-driver badges; not required for this PR. Non-blocking.
Blocking findings
None. The workflow change itself is correct, well-scoped, and demonstrably catches bugs the prior client-smoke masked. The Python and TS failures are not workflow bugs — they are real driver/test issues that the new harness is supposed to surface.
Non-blocking findings
- [HIGH-info] Two of three new jobs are red post-rebase. Even with fix(pgque.receive): finish empty batch instead of stranding consumer #117 on the base, Python and TS fail with "expected [].length to be N, got 0" patterns. Recommend triaging these before merge: either (a) confirm they are real driver bugs and open driver-fix PRs to land alongside this one, or (b) confirm they are test-side ticker/force_tick contract issues and fix the tests. Do not merge with two red required jobs unless branch protection is configured to allow it and a tracking issue is open.
- [LOW] Workflow boilerplate triplication. ~25 lines of docker-up + checkout + build + install pgque are duplicated three times. Acceptable for v1 (parallelism beats matrix fail-fast), but a composite action under
.github/actions/setup-pgque-pg/would dedupe. Defer. - [LOW]
set -Eeuo pipefailmissing in multi-linerun:blocks. Pre-existing pattern in the file; addressing here would expand scope. Defer to a workflow-wide hardening PR. - [LOW] Action versions on floating majors.
@v4/@v5. Pre-existing convention. Move to SHA pinning project-wide if/when supply-chain hardening is done. Defer. - [LOW] Job ordering vs.
needs:. Client-tests jobs are defined above theverifyjob they depend on. YAML doesn't care; readers do. Cosmetic.
Verdict
REQUEST_CHANGES_NON_BLOCKING.
The workflow change is sound and lands a real coverage win — that part is approve-worthy. But two of the three new jobs are red on this PR's head, and merging with red required checks should be a deliberate decision (with a tracking issue for each driver), not the default path. Either:
- Land driver-side fixes in companion PRs and rebase this on top so all three turn green, or
- Open issues for the Python and TS failures, link them from this PR's body, and merge with a documented exception.
Either path is fine. The harness change itself is good to go once the failure-triage decision is made.
Generated by REV multi-perspective reviewer (Claude Opus 4.7, 1M context). Self-approval blocked; this is an advisory comment, not a formal review approval.
Generated by Claude Code
|
Investigated the Python and TypeScript failures locally against PG 16 with
Root causes
Local results
Once #129 and #130 land, rebasing this PR should turn all three driver jobs green. Generated by Claude Code |
fb84bd1 to
02bc199
Compare
|
Rebase onto main (post-#129, #130)
CI results (
Failure URL: https://github.com/NikolayS/pgque/actions/runs/25172193888/job/73794456363 Python (#129) and TypeScript (#130) driver fixes were both in main and took effect. The Go client test job still fails — no corresponding Go driver fix has landed on main yet. Generated by Claude Code |
client-smoke ran only TestSmoke-named tests and didn't set PGQUE_TEST_DSN, so env-gated integration tests silently skipped. That's how the Nack placeholder bug shipped green in PR #79. New client-tests job: - Sets PGQUE_TEST_DSN pointing at the docker PG. - Runs full Go suite: go test -race -v ./... - Runs full Python suite: pytest -v clients/python/tests/ - Runs full TypeScript suite: npx vitest --run (replaces tsx src/smoke.ts). Switching the TS step to vitest also unblocks PR #83, which deletes the hardcoded src/smoke.ts in favor of the vitest suite. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace single client-tests job with three sibling jobs: go-client-tests, python-client-tests, typescript-client-tests. Each shows as a separate named check in the PR checks UI. All three depend on verify and run against a live PG 18 service. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Drop noisy npm ci-then-fallback pattern in favor of an explicit lockfile check, and add --passWithNoTests to vitest so the job stays green on branches that don't yet ship tests under clients/typescript/. Once PR #83 lands its vitest suite, the same command runs the new specs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
02bc199 to
00eaa56
Compare
|
Closing as stale/conflicting. Replaced by #174, a clean current-main PR that splits client checks into per-driver jobs and runs full live-PG suites. |
Why
client-smokewas effectively a no-op for catching driver bugs:That's exactly how the Nack placeholder bug in PR #79 shipped green: `TestNack` is env-gated on `getDSN()`, CI never set the DSN, the test silently skipped, and the placeholder/arg mismatch slipped past review.
What changes
client-smoke→client-tests. Same job slot, same Postgres-in-docker setup, but now:The TypeScript step now uses vitest instead of `npx tsx src/smoke.ts`. This also unblocks PR #83 (TypeScript driver overhaul), whose smoke.ts deletion was incompatible with the old workflow.
What's preserved
Test plan
Coordination
🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com