Running Integration Tests with a Database in Edgenivo CI
In this tutorial, you'll run integration tests against a real Postgres database in Edgenivo CI. You'll use Turborepo's with key to start the database next to your tests, so both run in the same compute instance and Turborepo stops the database when your tests finish.
You'll configure:
- A task that runs Postgres in a Docker container.
- A test task that starts the database with
with, waits until it's ready, and runs your tests.
- Edgenivo CI enabled for your repository.
- An app in your monorepo at
apps/apithat you want to test, with the package nameapi. - An empty package for the database at
packages/db, with the package namedb.
Add a script that runs the official Postgres image to
packages/db/package.json:packages/db/package.json{ "name": "db", "private": true, "scripts": { "postgres": "docker run --rm --name ci-postgres -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres:17" } }Then mark the task as persistent, because it runs until something stops it:
packages/db/turbo.json{ "extends": ["//"], "tasks": { "postgres": { "persistent": true, "cache": false } } }Edgenivo CI starts Docker before your task runs, so the task can use
docker runwithout any setup. To learn more, see Running containers in your tasks.Add a
test:integrationtask to your app'sturbo.json, and list the database task inwith:apps/api/turbo.json{ "extends": ["//"], "tasks": { "test:integration": { "with": ["db#postgres"], "tags": ["ci", "ci:cpus:4"] } } }withstartsdb#postgresat the same time astest:integration, in the same compute instance. When the tests finish, Turborepo stops the database, and the container removes itself.db#postgresdoesn't need acitag. Edgenivo CI only runs it as part oftest:integration.ci:cpus:4gives the database and your tests more CPUs than the default of 2, because they share the instance.
Postgres takes a few seconds to start, and Docker might need to download the image first. Add a script that waits until Postgres accepts connections:
apps/api/scripts/wait-for-postgres.sh#!/bin/sh set -eu for _ in $(seq 1 120); do if docker exec ci-postgres pg_isready -h 127.0.0.1 -U postgres >/dev/null 2>&1; then exit 0 fi sleep 1 done echo "Postgres didn't start within 120 seconds." >&2 exit 1The
-h 127.0.0.1flag checks the TCP port that your tests connect to. Without it,pg_isreadycan report that Postgres is ready while the image is still initializing the database.Install a Postgres client in your app:
npm i -D pgThen add a test that connects to the database:
apps/api/test/database.test.jsimport assert from 'node:assert/strict'; import { test } from 'node:test'; import pg from 'pg'; test('runs a query against Postgres', async () => { const client = new pg.Client({ connectionString: process.env.DATABASE_URL }); await client.connect(); try { const { rows } = await client.query('SELECT 1 + 1 AS sum'); assert.equal(rows[0].sum, 2); } finally { await client.end(); } });Add the
test:integrationscript to your app'spackage.json. It waits for the database, then runs the tests with the database's connection string:apps/api/package.json{ "name": "api", "type": "module", "scripts": { "test:integration": "sh scripts/wait-for-postgres.sh && DATABASE_URL=postgres://postgres:postgres@localhost:5432/postgres node --test" } }The example sets
DATABASE_URLinside the script because the database only exists for this task. To use a value from Edgenivo CI instead, see Make variables available to your tasks.Commit the files and push them to GitHub. When your changes affect
api, Edgenivo CI runsapi#test:integration, which starts Postgres, runs your tests, and stops the database.You can run the same task locally with Docker installed:
terminalturbo run api#test:integration
Was this helpful?