Skip to content

ci: run the suite against PostgreSQL, and fix what that found - #105

Merged
GauranshMathur merged 2 commits into
mainfrom
ci/postgresql-suite
Sep 2, 2026
Merged

ci: run the suite against PostgreSQL, and fix what that found#105
GauranshMathur merged 2 commits into
mainfrom
ci/postgresql-suite

Conversation

@GauranshMathur

Copy link
Copy Markdown
Owner

DATABASE_URL has been the design since the beginning, docs/database.md says the switch is one environment variable, and no job had ever run a line of the suite on PostgreSQL. F-6.3 — "search behaves identically on SQLite and PostgreSQL" — rested on the adapter that hides the difference.

So I ran it.

The first run failed 310 of 393 examples

And not one of them was application code.

A spec hook issued PRAGMA case_sensitive_like = ON — SQLite's way of making LIKE case-sensitive, which is what let #97's specs reproduce the PostgreSQL behaviour before the switch. On PostgreSQL that's a syntax error, and in PostgreSQL a failed statement aborts the entire transaction. So one adapter-blind hook didn't fail one example; it failed nearly everything that ran after it.

The hook now asks the adapter first. Both adapters run the same 393 examples, green.

That's the finding worth keeping: the app was portable and the test suite wasn't — and that's exactly the sort of thing that stays invisible until something actually runs it. The same shape as the libvips gap in #100, where the weak assertion existed because CI couldn't do better.

What the job proves that nothing did before

Claim Before Now
The suite passes on PostgreSQL never run 393 green
F-6.3 search parity true by simulation true on the real adapter
LOWER(…) LIKE … ESCAPE works on both argued run
The DATABASE_URL switch works design exercised every PR

Post.search's case folding and the ESCAPE clause from #97 both hold up on the adapter they were written for — which is the first time that's been more than an argument.

Two decisions in the job

  • A separate job, not a matrix. The SQLite run stays the fast one every PR waits on, and a PostgreSQL-only failure names itself on the checks list instead of hiding in a matrix leg.
  • db:schema:load, not db:prepare. db:prepare seeds a database it creates, and seeded rows aren't rolled back by the per-example transaction, so every expectation about which posts exist fails. That cost me two debugging rounds locally — the comment in the workflow says so, so it costs no one else.

Verified locally against a real PostgreSQL 16 before writing the workflow: 393 green on PostgreSQL, 393 green on SQLite, RuboCop clean.

F-6.3 no longer says "still unproven against a real PostgreSQL", because it no longer is.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KDUtHLJxUmEKXVbBJmcbAh


Generated by Claude Code

DATABASE_URL has been the design since ADR 0003 and no job had ever run
a line of the suite on PostgreSQL, so F-6.3's claim of adapter parity
rested on the adapter that hides the difference.

The first run failed 310 of 393 examples and not one was application
code: a spec hook issued PRAGMA case_sensitive_like, which PostgreSQL
rejects as a syntax error, and a rejected statement there aborts the
whole transaction — so one adapter-blind hook failed nearly everything
after it. The hook now asks the adapter first.

Both adapters run the same 393 examples green. The job loads the schema
rather than db:prepare, which seeds a database it creates, and seeded
rows survive the per-example transaction.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDUtHLJxUmEKXVbBJmcbAh
@GauranshMathur
GauranshMathur merged commit 31ab9c6 into main Sep 2, 2026
8 checks passed
@GauranshMathur
GauranshMathur deleted the ci/postgresql-suite branch September 2, 2026 01:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants