Skip to content

test(integration): add PostgreSQL real-database case#377

Merged
nao1215 merged 1 commit intomainfrom
test/issue-151-postgresql-integration
Apr 17, 2026
Merged

test(integration): add PostgreSQL real-database case#377
nao1215 merged 1 commit intomainfrom
test/issue-151-postgresql-integration

Conversation

@nao1215
Copy link
Copy Markdown
Owner

@nao1215 nao1215 commented Apr 17, 2026

Summary

Add an end-to-end integration test that drives the generated pog adapter against a real PostgreSQL server, closing the gap between "the generated code compiles" (existing ShellSpec build test) and "the generated code actually works" for PostgreSQL. CI provisions a postgres:16 service container and runs the new case after the existing gleam / ShellSpec steps. Closes #151.

Changes

  • test/fixtures/postgresql_schema.sql / postgresql_crud_query.sql: small authors schema plus CRUD + count queries covering :one, :many, :exec, :execlastid, and an aggregate :one.
  • integration_test/fixtures/postgresql_real_test.gleam: gleeunit test module that drives the generated pog_adapter functions end-to-end (create + get, ordered list_authors, null-bio round-trip, non-existent get returns None, delete round-trip, count row). The module checks DATABASE_URL at startup and prints a skip message if it is not set so local run.sh case_postgresql_real without Postgres still exits cleanly.
  • integration_test/cases.sh: register case_postgresql_real. Intentionally not added to ALL_INTEGRATION_CASES because most local checkouts do not have Postgres running; invoke it explicitly with DATABASE_URL=... sh integration_test/run.sh case_postgresql_real.
  • integration_test/lib.sh: add a gleeunit+envoy dev-deps alias so the new case can depend on envoy for DATABASE_URL lookup without passing a multi-line TOML through the existing key=value runner interface.
  • .github/workflows/ci.yml: add a postgres:16 service container with a health-check and a new CI step that sets DATABASE_URL and runs sh integration_test/run.sh case_postgresql_real.

Out-of-scope bug fix caught by the new coverage

  • render_pog_exec_last_id emitted |> pog.returning(decode.int), which tries to decode an entire row as an integer. pog 4 returns rows as positional arrays, so the decode failed with UnexpectedResultType([DecodeError("Int", "Array", [])]). Changed the returning decoder to read the first column instead, matching how :one results already decode. Verified against a local postgres:16 container (all six new tests pass after the fix).

Design Decisions

  • Skip at main() when DATABASE_URL is missing. The alternative — moving the check into every test function — would repeat eight lines of boilerplate per test without preventing anyone from forgetting it on a future test. Short-circuiting from main keeps every test in the file free to assume the connection works.
  • Opt-in case, not a default. Most local contributors do not have Postgres handy. Adding the case to the default list would force them to either run Docker or wait for a skip message for every run.sh. An explicit case_postgresql_real argument keeps run.sh a one-shot happy path locally while still letting CI provision the service and run it.
  • Service container on the same job, not a separate job. The Gleam build artefacts are already warm by the time the Postgres step runs, so keeping everything on one job avoids re-downloading dependencies and re-building the integration temp tree. Only this step needs the DB; skipping the DB service on other steps would complicate the workflow for no benefit.
  • envoy for env-var access instead of a custom FFI shim. The Gleam envoy package is the community default for os.getenv, and adding it as an integration-only dev dependency via the new gleeunit+envoy alias keeps the core sqlode package dependency surface unchanged. A custom Erlang getenv shim would have lived in src/ and forced a separate module to handle a one-line capability.

Limitations

  • The test suite only exercises the six adapter functions that :one, :many, :exec, :execlastid, and aggregate :one cover. RETURNING clauses, PostgreSQL-specific types (ENUMs, UUIDs, arrays), and slice parameters are not yet covered; those can be added as follow-up cases by registering more functions in cases.sh.
  • The skip path is observational only — the gleeunit main exits 0 without reporting that zero tests ran, which is indistinguishable from "successfully ran nothing". CI always exports DATABASE_URL, so the skip path exists primarily for local convenience.
  • The shared pool name is a fixed sqlode_pg_integration_pool atom. Running two instances of this test in parallel on the same node would conflict; the case keeps things serial inside one process, and CI runs it inside a fresh Erlang VM per job.

Summary by CodeRabbit

  • Tests

    • Added PostgreSQL integration test suite validating create, read, update, and delete operations against a live database instance.
    • Tests verify schema management, query execution, null value handling, and row count accuracy.
  • Bug Fixes

    • Fixed result decoding in the database adapter to correctly extract individual field values instead of misinterpreting entire rows.

Add a `case_postgresql_real` integration scenario that exercises the
generated pog adapter against a live PostgreSQL server.

- `test/fixtures/postgresql_schema.sql` / `postgresql_crud_query.sql`:
  a small `authors` schema plus CRUD / count queries covering `:one`,
  `:many`, `:exec`, `:execlastid`, and an aggregate `:one`.
- `integration_test/fixtures/postgresql_real_test.gleam`: gleeunit
  tests that drive the generated `pog_adapter` functions end-to-end
  (create + get, ordered list_authors, null bio round-trip,
  non-existent get returns None, delete round-trip, count row). The
  test module checks `DATABASE_URL` at startup and prints a skip
  message if it is not set so local `run.sh case_postgresql_real`
  invocations without Postgres still exit cleanly.
- `integration_test/cases.sh`: register the case. It is intentionally
  excluded from `ALL_INTEGRATION_CASES` because most local checkouts
  do not have a Postgres running; callers pass
  `sh integration_test/run.sh case_postgresql_real` with a
  `DATABASE_URL` exported.
- `integration_test/lib.sh`: extend `_integration_dev_deps_block`
  with a `gleeunit+envoy` alias so the new case can depend on
  `envoy` for `DATABASE_URL` lookup without passing a multi-line
  dev-deps TOML through the existing key=value runner interface.
- `.github/workflows/ci.yml`: provision a `postgres:16` service
  container and run the new case with the container URL after the
  existing gleam / ShellSpec steps.

Fix a pre-existing pog-adapter codegen bug that the new end-to-end
coverage surfaced:

- `render_pog_exec_last_id` emitted `|> pog.returning(decode.int)`,
  which tries to decode an entire row as an integer. pog 4 rows are
  positional arrays, so the decode failed with
  `UnexpectedResultType([DecodeError("Int", "Array", [])])`. Changed
  the returning decoder to read the first column instead, matching
  how `:one` results already decode. Verified against a local
  postgres:16 container (all six tests pass).

`just all` (format-check + check + build + `gleam test` +
ShellSpec) continues to pass end-to-end.

Closes #151
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 17, 2026

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Free

Run ID: 2e26c7f3-2073-4167-a090-377a1a47a191

📥 Commits

Reviewing files that changed from the base of the PR and between 50d6a85 and 4bf7386.

📒 Files selected for processing (7)
  • .github/workflows/ci.yml
  • integration_test/cases.sh
  • integration_test/fixtures/postgresql_real_test.gleam
  • integration_test/lib.sh
  • src/sqlode/codegen/adapter.gleam
  • test/fixtures/postgresql_crud_query.sql
  • test/fixtures/postgresql_schema.sql

📝 Walkthrough

Walkthrough

This pull request introduces PostgreSQL integration testing infrastructure by adding a PostgreSQL 16 service container to CI, implementing integration test cases with database fixtures, and updating code generation to properly decode pog adapter results.

Changes

Cohort / File(s) Summary
CI Infrastructure
.github/workflows/ci.yml
Added PostgreSQL 16 service container with health checks and a new CI step to execute PostgreSQL integration tests via integration_test/run.sh case_postgresql_real with DATABASE_URL configured.
Integration Test Framework
integration_test/cases.sh, integration_test/lib.sh
Registered new case_postgresql_real() integration test case; added gleeunit+envoy dev-dependencies block to manifest generation.
Integration Test Implementation
integration_test/fixtures/postgresql_real_test.gleam
Implemented PostgreSQL integration tests with database connection setup (connect_or_fail), schema reset (reset_authors), and test functions exercising CRUD operations against a real PostgreSQL database when DATABASE_URL is available.
Test Fixtures
test/fixtures/postgresql_schema.sql, test/fixtures/postgresql_crud_query.sql
Added PostgreSQL schema defining the authors table and five parameterized SQL queries (GetAuthor, ListAuthors, CreateAuthor, DeleteAuthor, CountAuthors) for CRUD operations.
Code Generation
src/sqlode/codegen/adapter.gleam
Updated render_pog_exec_last_id to decode the returned ID as a field from the result row instead of interpreting the entire row as an integer.

Sequence Diagram

sequenceDiagram
    participant CI as CI Runner
    participant PG as PostgreSQL Service
    participant Test as Test Executor (gleeunit)
    participant Adapter as pog_adapter
    
    CI->>PG: Start service on port 5432
    activate PG
    CI->>Test: Run case_postgresql_real
    activate Test
    Test->>Test: Read DATABASE_URL from env
    Test->>Adapter: connect_or_fail (create pool)
    Adapter->>PG: Establish connection
    PG-->>Adapter: Connection ready
    Adapter-->>Test: pog.Connection
    Test->>Test: reset_authors (drop/recreate table)
    Test->>PG: Execute schema reset
    PG-->>Test: Schema ready
    Test->>Test: Execute test functions
    loop Test Cases
        Test->>Adapter: Execute SQL query (create, read, list, delete, count)
        Adapter->>PG: Query execution
        PG-->>Adapter: Row results
        Adapter-->>Test: Decoded result
    end
    Test-->>CI: Test results
    deactivate Test
    deactivate PG
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 PostgreSQL integration hops into sight,
Real database tests running day and night,
Schema and queries dance in delight,
Code generation adapters spin just right,
Integration testing reaches new height! 🐇✨


Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

Comment @coderabbitai help to get the list of available commands and usage tips.

@nao1215 nao1215 merged commit 181e4b1 into main Apr 17, 2026
2 checks passed
@nao1215 nao1215 deleted the test/issue-151-postgresql-integration branch April 17, 2026 13:17
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.

test: add PostgreSQL integration test against real database

1 participant