Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ else
gotestsum --junitfile $(ARTIFACT_DIR)/integration-junit.xml ./test/integration/... -count 1 -p 1
endif

.PHONY: ci-integration
ci-integration:
./scripts/integration.sh

images:
$(DOCKER) build .

Expand Down
87 changes: 87 additions & 0 deletions scripts/integration.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#!/bin/bash
# Run integration tests with automatic PostgreSQL setup.
#
# In a devcontainer (or CI with a sidecar Postgres), uses the existing
# PostgreSQL service. On a bare host, spins up a temporary Postgres
# container with Podman and tears it down on exit.
#
# Set SIPPY_INTEGRATION_DSN to skip container management and connect
# to an already-running Postgres instance directly.

set -euo pipefail

DOCKER="podman"
PSQL_CONTAINER="sippy-integration-test-postgresql"
PSQL_PORT="23434"

EXIT_CODE=0

# ---------------------------------------------------------------------------
# If the caller already provided a DSN, just run the tests.
# ---------------------------------------------------------------------------
if [ -n "${SIPPY_INTEGRATION_DSN:-}" ]; then
echo "Using caller-provided SIPPY_INTEGRATION_DSN"
make integration
exit $?
fi

# ---------------------------------------------------------------------------
# Devcontainer vs host setup
# ---------------------------------------------------------------------------
if [ -f /run/.containerenv ]; then
# Inside devcontainer: reuse the existing PostgreSQL service.
ADMIN_DSN="${SIPPY_DATABASE_DSN:-postgresql://postgres:password@sippy-postgres:5432/postgres}"
export SIPPY_INTEGRATION_DSN="$ADMIN_DSN"
echo "Detected devcontainer — using existing PostgreSQL"
echo " DSN: ${SIPPY_INTEGRATION_DSN%%@*}@***"

clean_up() {
ARG=$?
[ $ARG -ne 0 ] && EXIT_CODE=$ARG
exit $EXIT_CODE
}
trap clean_up EXIT
else
# On host: start a temporary Postgres container.
clean_up() {
ARG=$?
[ $ARG -ne 0 ] && EXIT_CODE=$ARG
echo "Tearing down container $PSQL_CONTAINER"
$DOCKER stop -i $PSQL_CONTAINER 2>/dev/null || true
$DOCKER rm -i $PSQL_CONTAINER 2>/dev/null || true
exit $EXIT_CODE
}
trap clean_up EXIT

echo "Cleaning up old integration test container if present"
$DOCKER stop -i $PSQL_CONTAINER 2>/dev/null || true
$DOCKER rm -i $PSQL_CONTAINER 2>/dev/null || true

echo "Starting PostgreSQL container: $PSQL_CONTAINER"
$DOCKER run --name $PSQL_CONTAINER \
-e POSTGRES_PASSWORD=password \
-p $PSQL_PORT:5432 \
-d quay.io/enterprisedb/postgresql

echo "Waiting for PostgreSQL to start..."
TIMEOUT=30
ELAPSED=0
while [ $ELAPSED -lt $TIMEOUT ]; do
if $DOCKER exec $PSQL_CONTAINER pg_isready -U postgres > /dev/null 2>&1; then
echo "PostgreSQL is ready after ${ELAPSED}s"
break
fi
sleep 1
ELAPSED=$((ELAPSED + 1))
done
if [ $ELAPSED -ge $TIMEOUT ]; then
echo "Timeout waiting for PostgreSQL to start"
exit 1
fi

export SIPPY_INTEGRATION_DSN="postgresql://postgres:password@localhost:$PSQL_PORT/postgres"
echo " DSN: ${SIPPY_INTEGRATION_DSN%%@*}@***"
fi

make integration
EXIT_CODE=$?
8 changes: 6 additions & 2 deletions test/integration/jobs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,13 @@ func runTests(m *testing.M) int {
ctx := context.Background()

var err error
pgContainer, err = intutil.StartPostgresContainer(ctx)
if dsn := os.Getenv("SIPPY_INTEGRATION_DSN"); dsn != "" {
pgContainer, err = intutil.ConnectToExternalPostgres(ctx, dsn)
} else {
pgContainer, err = intutil.StartPostgresContainer(ctx)
}
if err != nil {
panic("failed to start postgres container: " + err.Error())
panic("failed to start postgres: " + err.Error())
}
defer func() {
if err := pgContainer.Terminate(ctx); err != nil {
Expand Down
25 changes: 23 additions & 2 deletions test/integration/util/testdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,26 @@ func StartPostgresContainer(ctx context.Context) (*PostgresContainer, error) {
}, nil
}

// Terminate stops and removes the container.
// ConnectToExternalPostgres connects to an existing Postgres instance and
// creates a template database with the integration schema applied. Use this
// in CI environments where a Postgres service is provided externally and
// testcontainers cannot be used (no container runtime available).
func ConnectToExternalPostgres(ctx context.Context, dsn string) (*PostgresContainer, error) {
if err := createTemplateDB(dsn); err != nil {
return nil, fmt.Errorf("creating template database: %w", err)
}
return &PostgresContainer{
baseDSN: dsn,
}, nil
}

// Terminate stops and removes the container. When using an external
// Postgres (container is nil), this is a no-op.
func (pc *PostgresContainer) Terminate(ctx context.Context) error {
return pc.container.Terminate(ctx)
if pc.container != nil {
return pc.container.Terminate(ctx)
}
return nil
}

// createTemplateDB creates a fresh database, applies the integration schema,
Expand All @@ -140,6 +157,10 @@ func createTemplateDB(baseDSN string) error {
}
defer adminDB.Close()

// Unmark as template first (required before dropping a template database),
// then drop if it exists. Ignore errors here because the database may not
// exist yet on a fresh Postgres instance.
_, _ = adminDB.Exec(fmt.Sprintf("ALTER DATABASE %s IS_TEMPLATE = false", templateDB))
if _, err := adminDB.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s", templateDB)); err != nil {
return fmt.Errorf("dropping old template: %w", err)
}
Expand Down