An open API service indexing awesome lists of open source software.

https://github.com/cybertec-postgresql/pgcov

PostgreSQL test runner and coverage tool
https://github.com/cybertec-postgresql/pgcov

Last synced: about 2 months ago
JSON representation

PostgreSQL test runner and coverage tool

Awesome Lists containing this project

README

          

[![Coverage Status](https://coveralls.io/repos/github/cybertec-postgresql/pgcov/badge.svg)](https://coveralls.io/github/cybertec-postgresql/pgcov)

# pgcov

PostgreSQL test runner and coverage tool

## Overview

pgcov is a Go-based CLI tool that discovers `*_test.sql` files, instruments SQL/PL/pgSQL source code for coverage tracking, executes tests in isolated temporary databases, and generates coverage reports in JSON and LCOV formats.

## Features

- ๐Ÿงช **Automatic Test Discovery**: Finds `*_test.sql` files and co-located source files
- ๐Ÿ”’ **Complete Test Isolation**: Each test runs in a temporary database
- ๐Ÿ“Š **Coverage Tracking**: Statement-level coverage via SQL instrumentation
- ๐Ÿ“ˆ **Multiple Report Formats**: JSON and LCOV output for CI/CD integration
- โšก **Parallel Execution**: Optional concurrent test execution with `--parallel` flag
- ๐ŸŽฏ **PostgreSQL Native**: Direct protocol access via pgx, no external dependencies

## Prerequisites

- **Go**: 1.21 or later (for building)
- **C Compiler**: Required for CGO (GCC on Linux, MinGW-w64 on Windows)
- **PostgreSQL**: 13 or later (running and accessible)
- **Permissions**: CREATEDB privilege for test isolation

### C Compiler Setup

**Linux/macOS**:

```bash
# Ubuntu/Debian
sudo apt-get install build-essential

# macOS (Xcode Command Line Tools)
xcode-select --install

# Fedora/RHEL
sudo dnf install gcc
```

**Windows**:

- Install [MSYS2](https://www.msys2.org/)
- Open MSYS2 terminal and run:

```bash
pacman -S mingw-w64-x86_64-gcc
```

- Add `C:\msys64\mingw64\bin` to your PATH

## Installation

### Building from Source

**Linux/macOS**:

```bash
# Clone repository
git clone https://github.com/cybertec-postgresql/pgcov.git
cd pgcov

# Enable CGO and build
export CGO_ENABLED=1
go build -o pgcov ./cmd/pgcov

# (Optional) Install to PATH
go install ./cmd/pgcov
```

**Windows (PowerShell) - Manual Build**:

```powershell
# Clone repository
git clone https://github.com/cybertec-postgresql/pgcov.git
cd pgcov

# Enable CGO and set compiler
$env:CGO_ENABLED = "1"
$env:CC = "C:\msys64\mingw64\bin\gcc.exe"
$env:PATH = "$env:PATH;C:\msys64\mingw64\bin"

# Build
go build -o pgcov.exe .\cmd\pgcov
```

### Why CGO is Required

pgcov uses `pg_query_go` which wraps the PostgreSQL query parser (libpg_query) written in C. This provides native PostgreSQL SQL parsing capabilities but requires CGO to be enabled during compilation.

## Quick Start

### 1. Configure PostgreSQL Connection

```bash
export PGHOST=localhost
export PGPORT=5432
export PGUSER=postgres
export PGPASSWORD=yourpassword
export PGDATABASE=postgres
```

### 2. Create Test Files

Test files must match `*_test.sql` pattern and be co-located with source files:

```
myproject/
โ”œโ”€โ”€ auth/
โ”‚ โ”œโ”€โ”€ authenticate.sql # Source (will be instrumented)
โ”‚ โ””โ”€โ”€ auth_test.sql # Test
```

### 3. Run Tests

```bash
# Current directory
pgcov run .

# Recursive (Go-style)
pgcov run ./...

# Specific directory
pgcov run ./tests/
```

### 4. Generate Coverage Reports

```bash
# HTML format (human-readable)
pgcov report --format=html -o coverage.html

# LCOV format (for CI)
pgcov report --format=lcov -o coverage.lcov
```

## Usage

### Commands

```bash
# Run tests and collect coverage
pgcov run [path]

# Generate coverage report
pgcov report [--format=json|lcov|html] [-o output-file]

# Show help
pgcov help [command]

# Show version
pgcov --version
```

### Configuration Flags

**Connection**:

- `--host`: PostgreSQL host (default: `localhost`)
- `--port`: PostgreSQL port (default: `5432`, valid range: 1-65535)
- `--user`: PostgreSQL user (default: current user)
- `--password`: PostgreSQL password
- `--database`: Template database (default: `postgres`)

**Execution**:

- `--timeout`: Per-test timeout (default: `30s`, format: `10s`, `1m`, `90s`)
- `--parallel`: Concurrent tests (default: `1`, valid range: 1-100)
- `--verbose`: Enable debug output

**Output**:

- `--coverage-file`: Coverage data path (default: `.pgcov/coverage.json`)

### Environment Variables

pgcov respects standard PostgreSQL environment variables:

- `PGHOST`, `PGPORT`, `PGUSER`, `PGPASSWORD`, `PGDATABASE`

**Configuration Priority** (highest to lowest):

1. Command-line flags (e.g., `--host`)
2. Environment variables (e.g., `PGHOST`)
3. Default values

### Configuration Validation

pgcov validates all configuration values and provides helpful error messages:

```bash
# Invalid port
$ pgcov run --port=99999 .
Error: configuration error for port: invalid port number: 99999

Suggestion: Port must be between 1 and 65535. Default PostgreSQL port is 5432.
Set via --port flag or PGPORT environment variable.

# Invalid parallelism
$ pgcov run --parallel=0 .
Error: configuration error for parallel: parallelism must be at least 1, got: 0

Suggestion: Use --parallel=N where N is number of tests to run concurrently.
Use 1 for sequential execution.

# Invalid timeout
$ pgcov run --timeout=-5s .
Error: configuration error for timeout: timeout must be positive

Suggestion: Use --timeout flag with format like '30s', '1m', '90s'. Default is 30s.
```

## Writing Tests

### Test File Structure

```sql
-- auth_test.sql

-- Setup: Create schema and test data
CREATE TABLE users (id INT PRIMARY KEY, name TEXT);
INSERT INTO users VALUES (1, 'Alice'), (2, 'Bob');

-- Test: Verify behavior
DO $$
BEGIN
IF NOT authenticate(1) THEN
RAISE EXCEPTION 'Test failed: User 1 should authenticate';
END IF;

IF authenticate(999) THEN
RAISE EXCEPTION 'Test failed: Invalid user should not authenticate';
END IF;

RAISE NOTICE 'All tests passed';
END;
$$;
```

### Source File Structure

Source files in the same directory as test files will be automatically instrumented:

```sql
-- authenticate.sql

CREATE OR REPLACE FUNCTION authenticate(user_id INT) RETURNS BOOLEAN AS $$
BEGIN
RETURN EXISTS(SELECT 1 FROM users WHERE id = user_id);
END;
$$ LANGUAGE plpgsql;
```

## CI/CD Integration

### GitHub Actions Example

```yaml
name: Tests

on: [push, pull_request]

jobs:
test:
runs-on: ubuntu-latest

services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
ports:
- 5432:5432

steps:
- uses: actions/checkout@v3

- uses: actions/setup-go@v4
with:
go-version: '1.21'

- name: Install pgcov
run: go install github.com/cybertec-postgresql/pgcov/cmd/pgcov@latest

- name: Run tests
env:
PGHOST: localhost
PGPORT: 5432
PGUSER: postgres
PGPASSWORD: postgres
run: pgcov run ./...

- name: Generate LCOV report
run: pgcov report --format=lcov -o coverage.lcov

- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: coverage.lcov
```

## Architecture

- **CLI Layer**: Command routing and user interface (`urfave/cli/v3`)
- **Discovery Layer**: Test and source file discovery (filesystem traversal)
- **Parser Layer**: SQL parsing and AST access (`pg_query_go`)
- **Instrumentation Layer**: AST rewriting with coverage injection
- **Database Layer**: PostgreSQL connections and temporary databases (`pgx/v5`)
- **Runner Layer**: Test execution orchestration and isolation
- **Coverage Layer**: Signal collection and aggregation (LISTEN/NOTIFY)
- **Reporter Layer**: Output formatting (HTML, JSON, LCOV)

## Development

### Running Tests

The project includes comprehensive integration tests that use testcontainers to spin up a PostgreSQL instance.

**Linux/macOS**:

```bash
# Enable CGO
export CGO_ENABLED=1

# Run all tests
go test ./...

# Run with verbose output
go test -v ./...

# Run specific test
go test -v ./internal -run TestEndToEndWithTestcontainers

# Run with timeout (useful for integration tests)
go test -timeout 5m ./...

# Run tests with coverage
go test -cover ./...
```

**Windows (PowerShell)**:

```powershell
# Enable CGO and set compiler
$env:CGO_ENABLED = "1"
$env:CC = "C:\msys64\mingw64\bin\gcc.exe"
$env:PATH = "$env:PATH;C:\msys64\mingw64\bin"

# Run all tests
go test .\...

# Run with verbose output
go test -v .\...

# Run specific test
go test -v .\internal -run TestEndToEndWithTestcontainers

# Run with timeout
go test -timeout 5m .\...

# Run tests with coverage
go test -cover .\...
```

### Building

**Linux/macOS**:

```bash
# Development build
export CGO_ENABLED=1
go build -o pgcov ./cmd/pgcov

# Release build with optimizations
go build -ldflags="-s -w" -o pgcov ./cmd/pgcov

# Format code
go fmt ./...

# Lint
go vet ./...

# Clean build cache
go clean -cache
```

**Windows (PowerShell)**:

```powershell
# Development build
$env:CGO_ENABLED = "1"
$env:CC = "C:\msys64\mingw64\bin\gcc.exe"
$env:PATH = "$env:PATH;C:\msys64\mingw64\bin"
go build -o pgcov.exe .\cmd\pgcov

# Release build with optimizations
go build -ldflags="-s -w" -o pgcov.exe .\cmd\pgcov

# Format code
go fmt .\...

# Lint
go vet .\...

# Clean build cache
go clean -cache
```

### Test Requirements

**Docker**: Integration tests use testcontainers-go which requires Docker to be running:

- Linux: Docker Engine
- macOS: Docker Desktop
- Windows: Docker Desktop with WSL2 backend

**PostgreSQL Version**: Tests verify PostgreSQL 13+ compatibility using the `postgres:16-alpine` image.

### Troubleshooting Build Issues

**CGO errors on Linux**:

```bash
# Install build tools
sudo apt-get update
sudo apt-get install build-essential

# Verify GCC is available
gcc --version
```

**CGO errors on Windows**:

```powershell
# Verify GCC is in PATH
gcc --version

# If not found, ensure MSYS2 MinGW64 is in PATH:
$env:PATH = "$env:PATH;C:\msys64\mingw64\bin"
```

**Missing DLL errors on Windows**:
Ensure `C:\msys64\mingw64\bin` is in your PATH to access required MinGW DLLs.

**Test container startup failures**:

```bash
# Verify Docker is running
docker ps

# Pull PostgreSQL image manually
docker pull postgres:16-alpine
```

## VS Code Integration

This project includes complete VS Code configuration for CGO development.

### Features

- โœ… **Automatic CGO Environment** - No manual env var setup required
- โœ… **IntelliSense Support** - Full code completion for CGO code
- โœ… **Debug Configurations** - F5 to debug, with 5 pre-configured scenarios
- โœ… **Build Tasks** - Ctrl+Shift+B to build, plus 9 other tasks
- โœ… **Integrated Terminal** - CGO variables automatically set
- โœ… **Cross-Platform** - Windows, Linux, and macOS configurations

### Quick Start

1. **Open workspace in VS Code**

```bash
code .
```

2. **Reload window** (if already open)
- Press `Ctrl+Shift+P` (or `Cmd+Shift+P` on macOS)
- Type "Reload Window"
- Press Enter

3. **Verify gopls is working**
- Check bottom-right status bar
- Should show "gopls" without errors

4. **Build the project**
- Press `Ctrl+Shift+B` (or `Cmd+Shift+B` on macOS)
- Or: Terminal โ†’ Run Build Task

5. **Debug the project**
- Open Run and Debug sidebar (`Ctrl+Shift+D`)
- Select "Launch pgcov"
- Press `F5`

### Configuration Files

See [.vscode/README.md](.vscode/README.md) for detailed documentation:

- **settings.json** - CGO environment for Go tools and terminal
- **launch.json** - Debug configurations
- **tasks.json** - Build and test tasks

## License

MIT

## Contributing

Contributions welcome! Please open an issue or pull request.

## Support

- **Documentation**: [Full docs](https://github.com/cybertec-postgresql/pgcov/docs)
- **Issues**: [GitHub Issues](https://github.com/cybertec-postgresql/pgcov/issues)
- **Examples**: [Examples directory](./examples)