A Rust application to showcase badge awards in the AT Protocol ecosystem.

Development Guide#

This guide provides instructions for setting up the development environment and running tests for the Showcase application.

Prerequisites#

PostgreSQL Development Setup#

The application supports both SQLite (default) and PostgreSQL storage backends. For integration testing with PostgreSQL, you'll need to run a PostgreSQL instance locally.

Create a docker-compose.yml file in the project root:

version: '3.8'

services:
  postgres:
    image: postgres:17-alpine
    container_name: showcase_postgres
    environment:
      POSTGRES_DB: showcase_test
      POSTGRES_USER: showcase
      POSTGRES_PASSWORD: showcase_dev_password
      POSTGRES_INITDB_ARGS: "--encoding=UTF8 --locale=C"
    ports:
      - "5433:5432"  # Using 5433 to avoid conflicts with system PostgreSQL
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init-db.sql:/docker-entrypoint-initdb.d/init-db.sql:ro
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U showcase -d showcase_test"]
      interval: 5s
      timeout: 5s
      retries: 5
    restart: unless-stopped

volumes:
  postgres_data:

Start the PostgreSQL container:

# Start PostgreSQL in background
docker-compose up -d postgres

# Check logs to ensure it's running properly
docker-compose logs postgres

# Stop when done
docker-compose down

Option 2: Docker Run Command#

If you prefer to use Docker directly without Docker Compose:

# Create a Docker network (optional, for better container management)
docker network create showcase-dev

# Run PostgreSQL 17 container
docker run -d \
  --name showcase_postgres \
  --network showcase-dev \
  -e POSTGRES_DB=showcase_test \
  -e POSTGRES_USER=showcase \
  -e POSTGRES_PASSWORD=showcase_dev_password \
  -e POSTGRES_INITDB_ARGS="--encoding=UTF8 --locale=C" \
  -p 5433:5432 \
  -v showcase_postgres_data:/var/lib/postgresql/data \
  postgres:17-alpine

# Verify the container is running
docker ps | grep showcase_postgres

# Check logs
docker logs showcase_postgres

# Stop and remove container when done
docker stop showcase_postgres
docker rm showcase_postgres

Database Initialization#

Create an optional init-db.sql file for additional database setup:

-- init-db.sql
-- Additional database setup if needed

-- Create extensions that might be useful for testing
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pgcrypto";

-- Set timezone
SET timezone TO 'UTC';

-- Create additional test database for parallel testing
CREATE DATABASE showcase_test_parallel;
GRANT ALL PRIVILEGES ON DATABASE showcase_test_parallel TO showcase;

MinIO Object Storage Development Setup#

The application supports S3-compatible object storage for badge images using MinIO. This is useful for testing S3 functionality locally and for production deployments that use object storage instead of local file storage.

Setting up MinIO with Docker Compose#

Add MinIO service to your docker-compose.yml:

version: '3.8'

services:
  postgres:
    image: postgres:17-alpine
    container_name: showcase_postgres
    environment:
      POSTGRES_DB: showcase_test
      POSTGRES_USER: showcase
      POSTGRES_PASSWORD: showcase_dev_password
      POSTGRES_INITDB_ARGS: "--encoding=UTF8 --locale=C"
    ports:
      - "5433:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init-db.sql:/docker-entrypoint-initdb.d/init-db.sql:ro
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U showcase -d showcase_test"]
      interval: 5s
      timeout: 5s
      retries: 5
    restart: unless-stopped

  minio:
    image: minio/minio:latest
    container_name: showcase_minio
    command: server /data --console-address ":9001"
    environment:
      MINIO_ROOT_USER: showcase_minio
      MINIO_ROOT_PASSWORD: showcase_dev_secret
    ports:
      - "9000:9000"  # MinIO API
      - "9001:9001"  # MinIO Console
    volumes:
      - minio_data:/data
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 30s
      timeout: 20s
      retries: 3
    restart: unless-stopped

volumes:
  postgres_data:
  minio_data:

Starting MinIO#

# Start MinIO and PostgreSQL
docker-compose up -d

# Check if MinIO is running
docker-compose logs minio

# Access MinIO Console at http://localhost:9001
# Username: showcase_minio
# Password: showcase_dev_secret

MinIO Bucket Setup#

  1. Via MinIO Console (Web UI):

    • Open http://localhost:9001 in your browser
    • Login with showcase_minio / showcase_dev_secret
    • Click "Buckets" → "Create Bucket"
    • Create a bucket named showcase-badges
    • Set bucket policy to allow public read access (for testing)
  2. Via MinIO Client (mc):

    # Install MinIO client
    curl https://dl.min.io/client/mc/release/linux-amd64/mc \
      --create-dirs -o $HOME/minio-binaries/mc
    chmod +x $HOME/minio-binaries/mc
    export PATH=$PATH:$HOME/minio-binaries/
    
    # Configure MinIO client
    mc alias set local http://localhost:9000 showcase_minio showcase_dev_secret
    
    # Create bucket
    mc mb local/showcase-badges
    
    # Set bucket policy for public read (optional, for testing)
    mc policy set download local/showcase-badges
    
    # List buckets
    mc ls local
    
  3. Via Docker:

    # Create bucket using MinIO container
    docker-compose exec minio mc mb /data/showcase-badges
    
    # Set bucket policy
    docker-compose exec minio mc policy set download /data/showcase-badges
    

Alternative MinIO Setup (Docker Run)#

If you prefer not to use Docker Compose:

# Run MinIO container
docker run -d \
  --name showcase_minio \
  -p 9000:9000 \
  -p 9001:9001 \
  -e MINIO_ROOT_USER=showcase_minio \
  -e MINIO_ROOT_PASSWORD=showcase_dev_secret \
  -v showcase_minio_data:/data \
  minio/minio:latest server /data --console-address ":9001"

# Verify MinIO is running
docker logs showcase_minio

# Create bucket (after MinIO is started)
docker exec showcase_minio mc mb /data/showcase-badges

# Stop MinIO when done
docker stop showcase_minio
docker rm showcase_minio

Environment Configuration#

For PostgreSQL Integration Tests#

Create a .env.test file for test-specific environment variables:

# .env.test - PostgreSQL test configuration
DATABASE_URL=postgresql://showcase:showcase_dev_password@localhost:5433/showcase_test
EXTERNAL_BASE=http://localhost:8080
BADGE_ISSUERS=did:plc:test1;did:plc:test2
HTTP_PORT=8080
BADGE_IMAGE_STORAGE=./test_badges
PLC_HOSTNAME=plc.directory
HTTP_CLIENT_TIMEOUT=10s
RUST_LOG=showcase=debug,info

For SQLite Development (Default)#

Create a .env.dev file for SQLite development:

# .env.dev - SQLite development configuration
DATABASE_URL=sqlite://showcase_dev.db
EXTERNAL_BASE=http://localhost:8080
BADGE_ISSUERS=did:plc:test1;did:plc:test2
HTTP_PORT=8080
BADGE_IMAGE_STORAGE=./badges
PLC_HOSTNAME=plc.directory
HTTP_CLIENT_TIMEOUT=10s
RUST_LOG=showcase=info,debug

For S3/MinIO Object Storage#

Create environment files for S3 object storage:

For MinIO Local Development (.env.minio):

# .env.minio - MinIO development configuration
DATABASE_URL=sqlite://showcase_dev.db
EXTERNAL_BASE=http://localhost:8080
BADGE_ISSUERS=did:plc:test1;did:plc:test2
HTTP_PORT=8080
BADGE_IMAGE_STORAGE=s3://showcase_minio:showcase_dev_secret@localhost:9000/showcase-badges
PLC_HOSTNAME=plc.directory
HTTP_CLIENT_TIMEOUT=10s
RUST_LOG=showcase=info,debug

For AWS S3 Production (.env.s3):

# .env.s3 - AWS S3 configuration
DATABASE_URL=postgresql://username:password@hostname:5432/database
EXTERNAL_BASE=https://your-domain.com
BADGE_ISSUERS=did:plc:production1;did:plc:production2
HTTP_PORT=8080
BADGE_IMAGE_STORAGE=s3://access_key:secret_key@s3.amazonaws.com/your-bucket/badges
PLC_HOSTNAME=plc.directory
HTTP_CLIENT_TIMEOUT=10s
RUST_LOG=showcase=info,warn

S3 URL Format: The BADGE_IMAGE_STORAGE environment variable supports the following format for S3-compatible storage:

s3://[access_key]:[secret_key]@[hostname]/[bucket][/optional_prefix]

Examples:

  • MinIO: s3://minio_user:minio_pass@localhost:9000/my-bucket/badges
  • AWS S3: s3://AKIAIOSFODNN7EXAMPLE:wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY@s3.amazonaws.com/my-bucket
  • DigitalOcean Spaces: s3://access_key:secret_key@nyc3.digitaloceanspaces.com/my-space/images

Running Tests#

Unit Tests (SQLite - Default)#

# Run all tests with SQLite (default)
cargo test

# Run specific test module
cargo test storage::tests

# Run tests with output
cargo test -- --nocapture

# Run tests in single thread (useful for database tests)
cargo test -- --test-threads=1

Integration Tests with PostgreSQL#

  1. Start PostgreSQL container:

    docker-compose up -d postgres
    
    # Wait for PostgreSQL to be ready
    docker-compose exec postgres pg_isready -U showcase -d showcase_test
    
  2. Set environment variables:

    # Load PostgreSQL test environment
    export $(cat .env.test | xargs)
    
    # Or use direnv (if installed)
    echo "dotenv .env.test" > .envrc
    direnv allow
    
  3. Run tests with PostgreSQL:

    # Run all tests against PostgreSQL
    cargo test
    
    # Run specific storage tests
    cargo test --lib storage
    
    # Run with verbose output
    RUST_LOG=debug cargo test -- --nocapture
    
  4. Clean up:

    # Stop PostgreSQL container
    docker-compose down
    
    # Remove test data (optional)
    docker-compose down -v
    

S3/MinIO Integration Tests#

Test the S3 file storage functionality:

  1. Start MinIO container:

    docker-compose up -d minio
    
    # Wait for MinIO to be ready
    timeout 30s bash -c 'until curl -f http://localhost:9000/minio/health/live; do sleep 2; done'
    
  2. Create test bucket:

    # Using MinIO client
    docker-compose exec minio mc mb /data/showcase-badges
    
    # Or via API (requires mc client installed)
    mc alias set local http://localhost:9000 showcase_minio showcase_dev_secret
    mc mb local/showcase-badges
    
  3. Set environment and run tests:

    # Load MinIO test environment
    export $(cat .env.minio | xargs)
    
    # Build with S3 feature
    cargo build --features s3
    
    # Run tests with S3 feature
    cargo test --features s3
    
    # Run specific file storage tests
    cargo test --features s3 file_storage
    
  4. Clean up:

    # Stop MinIO container
    docker-compose down
    
    # Remove MinIO data (optional)
    docker-compose down -v
    

Testing S3 Feature without MinIO#

If you want to test the S3 implementation without running MinIO:

# Build with S3 feature to check compilation
cargo build --features s3

# Run unit tests that don't require actual S3 connection
cargo test --features s3 test_s3_file_storage_object_key_generation

# Check that the feature flag works correctly
cargo test --features s3 --lib

Integration Test Scripts#

Create helper scripts for common test scenarios:

scripts/test-postgres.sh:

#!/bin/bash
set -e

echo "Starting PostgreSQL container..."
docker-compose up -d postgres

echo "Waiting for PostgreSQL to be ready..."
timeout 30s bash -c 'until docker-compose exec postgres pg_isready -U showcase -d showcase_test; do sleep 1; done'

echo "Loading test environment..."
export $(cat .env.test | xargs)

echo "Running tests with PostgreSQL..."
cargo test

echo "Cleaning up..."
docker-compose down

scripts/test-sqlite.sh:

#!/bin/bash
set -e

echo "Loading SQLite test environment..."
export $(cat .env.dev | xargs)

echo "Running tests with SQLite..."
cargo test

echo "Cleaning up test databases..."
rm -f showcase_dev.db showcase_test.db

scripts/test-minio.sh:

#!/bin/bash
set -e

echo "Starting MinIO container..."
docker-compose up -d minio

echo "Waiting for MinIO to be ready..."
timeout 60s bash -c 'until curl -f http://localhost:9000/minio/health/live; do sleep 2; done'

echo "Creating test bucket..."
docker-compose exec minio mc mb /data/showcase-badges || echo "Bucket may already exist"

echo "Loading MinIO test environment..."
export $(cat .env.minio | xargs)

echo "Building with S3 features..."
cargo build --features s3

echo "Running tests with S3 features..."
cargo test --features s3

echo "Cleaning up..."
docker-compose down

scripts/test-all.sh:

#!/bin/bash
set -e

echo "Running all test suites..."

echo "=== SQLite Tests ==="
./scripts/test-sqlite.sh

echo "=== PostgreSQL Tests ==="
./scripts/test-postgres.sh

echo "=== S3/MinIO Tests ==="
./scripts/test-minio.sh

echo "All tests completed successfully!"

Make scripts executable:

chmod +x scripts/test-postgres.sh scripts/test-sqlite.sh scripts/test-minio.sh scripts/test-all.sh

Development Workflow#

1. Setting up for Development#

# Clone the repository
git clone <repository-url>
cd showcase

# Install Rust dependencies
cargo build

# Start PostgreSQL for development
docker-compose up -d postgres

# Load environment variables
export $(cat .env.test | xargs)

# Run database migrations
cargo run --bin showcase migrate  # If migration command exists

2. Running the Application Locally#

With SQLite (Default):

export $(cat .env.dev | xargs)
cargo run --bin showcase

With PostgreSQL:

# Ensure PostgreSQL is running
docker-compose up -d postgres

# Load PostgreSQL environment
export $(cat .env.test | xargs)

# Run the application
cargo run --bin showcase

With MinIO Object Storage:

# Ensure MinIO is running
docker-compose up -d minio

# Wait for MinIO to be ready
timeout 30s bash -c 'until curl -f http://localhost:9000/minio/health/live; do sleep 2; done'

# Create bucket if it doesn't exist
docker-compose exec minio mc mb /data/showcase-badges || true

# Load MinIO environment
export $(cat .env.minio | xargs)

# Run the application with S3 features
cargo run --features s3 --bin showcase

3. Database Management#

View PostgreSQL logs:

docker-compose logs postgres -f

Connect to PostgreSQL for debugging:

# Using psql in container
docker-compose exec postgres psql -U showcase -d showcase_test

# Or using psql from host (if installed)
psql postgresql://showcase:showcase_dev_password@localhost:5433/showcase_test

Reset PostgreSQL database:

# Stop and remove with volumes
docker-compose down -v

# Start fresh
docker-compose up -d postgres

View SQLite database:

# Install sqlite3 if needed
sqlite3 showcase_dev.db

# View tables
.tables

# View schema
.schema

Continuous Integration#

For CI environments, use these commands:

# CI script for PostgreSQL tests
docker run -d \
  --name ci_postgres \
  -e POSTGRES_DB=showcase_test \
  -e POSTGRES_USER=showcase \
  -e POSTGRES_PASSWORD=ci_password \
  -p 5432:5432 \
  postgres:17-alpine

# Wait for PostgreSQL
timeout 60s bash -c 'until pg_isready -h localhost -p 5432 -U showcase; do sleep 2; done'

# Set CI environment
export DATABASE_URL=postgresql://showcase:ci_password@localhost:5432/showcase_test
export BADGE_ISSUERS=did:plc:ci1;did:plc:ci2
export EXTERNAL_BASE=http://localhost:8080

# Run tests
cargo test --all-features

# Cleanup
docker stop ci_postgres
docker rm ci_postgres

Troubleshooting#

PostgreSQL Connection Issues#

  1. Port conflicts:

    # Check if port 5433 is in use
    lsof -i :5433
    
    # Use different port in docker-compose.yml
    ports:
      - "5434:5432"
    
  2. Container not starting:

    # Check Docker logs
    docker-compose logs postgres
    
    # Remove and recreate container
    docker-compose down -v
    docker-compose up -d postgres
    
  3. Permission issues:

    # Fix Docker volume permissions (Linux)
    sudo chown -R $(id -u):$(id -g) postgres_data/
    

Test Failures#

  1. Database schema issues:

    # Drop and recreate test database
    docker-compose exec postgres psql -U showcase -c "DROP DATABASE IF EXISTS showcase_test;"
    docker-compose exec postgres psql -U showcase -c "CREATE DATABASE showcase_test;"
    
  2. Environment variable issues:

    # Verify environment variables are loaded
    env | grep DATABASE_URL
    env | grep BADGE_ISSUERS
    
  3. Dependency issues:

    # Clean and rebuild
    cargo clean
    cargo build
    

Performance Issues#

  1. Slow PostgreSQL startup:

    # Use smaller PostgreSQL image for faster startup
    # In docker-compose.yml, change to:
    image: postgres:17-alpine
    
  2. Test timeouts:

    # Increase test timeout
    cargo test -- --timeout 60
    
    # Run tests with single thread for database tests
    cargo test -- --test-threads=1
    

MinIO/S3 Issues#

  1. MinIO container not starting:

    # Check MinIO logs
    docker-compose logs minio
    
    # Remove and recreate container
    docker-compose down -v
    docker-compose up -d minio
    
  2. S3 connection issues:

    # Verify MinIO is accessible
    curl http://localhost:9000/minio/health/live
    
    # Check MinIO console
    open http://localhost:9001
    
    # Test bucket access
    docker-compose exec minio mc ls /data/
    
  3. Bucket permission issues:

    # Set bucket policy to public read (for testing)
    docker-compose exec minio mc policy set download /data/showcase-badges
    
    # List bucket contents
    docker-compose exec minio mc ls /data/showcase-badges
    
  4. S3 URL parsing errors:

    # Verify environment variable format
    echo $BADGE_IMAGE_STORAGE
    
    # Should match: s3://key:secret@hostname/bucket[/prefix]
    # Example: s3://showcase_minio:showcase_dev_secret@localhost:9000/showcase-badges
    
  5. Feature compilation issues:

    # Ensure S3 feature is enabled when needed
    cargo build --features s3
    
    # Check feature flags in code
    cargo expand --features s3 | grep -A5 -B5 "cfg.*s3"
    
  6. Credential issues:

    # Test credentials manually
    mc alias set test http://localhost:9000 showcase_minio showcase_dev_secret
    mc ls test
    
    # Check if credentials are correctly parsed
    echo "URL: s3://showcase_minio:showcase_dev_secret@localhost:9000/showcase-badges"
    echo "Should parse to:"
    echo "  Endpoint: http://localhost:9000"
    echo "  Access Key: showcase_minio"
    echo "  Secret Key: showcase_dev_secret"
    echo "  Bucket: showcase-badges"
    

Additional Resources#