spacetrouble

module
v0.0.0-...-95589b7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 28, 2024 License: MIT

README ΒΆ

SpaceTrouble πŸš€

SpaceTrouble is a Go-based REST API for booking space travel tickets. It enables users to create and manage bookings for space trips while ensuring launchpad availability and avoiding conflicts with SpaceX launches.

Features 🌟

  • Create and view space travel bookings
  • Real-time integration with SpaceX API to check launch conflicts
  • Validation of booking requests (age, destination, launchpad availability)
  • Support for multiple destinations (Mars, Moon, Pluto, etc.)
  • PostgreSQL database for persistent storage
  • JSON and XML response formats
  • Cursor-based pagination for booking listings
  • Health check endpoint with system metrics
  • Docker containerization for easy deployment

Prerequisites πŸ“‹

  • Docker and Docker Compose
  • Go 1.22 or higher (for local development)
  • Make (optional, but recommended)
  • PostgreSQL 16 (handled by Docker)

Running the Project πŸš€

Local Development
  1. Clone the repository:
git clone https://github.com/chrisdamba/spacetrouble.git
cd spacetrouble
  1. Copy and configure environment variables:
cp .env.example .env
# Edit .env with your preferred settings
  1. Start the services:
# Build the Docker images
make docker-build

# Start all services
make docker-up

# Run database migrations
make migrate-up
  1. Verify the setup:
# Check if services are running
docker-compose ps

# Check application logs
docker-compose logs -f app

# Test the health endpoint
curl http://localhost:5000/v1/health
Stopping the Project
# Stop all services
make docker-down

# Stop and remove all containers, networks, and volumes
docker-compose down -v
Rebuilding After Changes
# Rebuild the application
make docker-build

# Restart services
make docker-down
make docker-up

The API will be available at http://localhost:5000

API Endpoints πŸ› οΈ

Create Booking
POST /v1/bookings
Content-Type: application/json

{
    "first_name": "John",
    "last_name": "Doe",
    "gender": "male",
    "birthday": "1990-01-01T00:00:00Z",
    "launchpad_id": "5e9e4502f5090995de566f86",
    "destination_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
    "launch_date": "2025-01-01T00:00:00Z"
}

Response (201 Created):

{
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "user": {
        "id": "123e4567-e89b-12d3-a456-426614174001",
        "first_name": "John",
        "last_name": "Doe",
        "gender": "male",
        "birthday": "1990-01-01T00:00:00Z"
    },
    "flight": {
        "id": "123e4567-e89b-12d3-a456-426614174002",
        "launchpad_id": "5e9e4502f5090995de566f86",
        "destination": {
            "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
            "name": "Mars"
        },
        "launch_date": "2025-01-01T00:00:00Z"
    },
    "status": "ACTIVE",
    "created_at": "2024-01-01T00:00:00Z"
}
List Bookings
GET /v1/bookings?limit=10&cursor=<cursor_token>
Accept: application/json

Response (200 OK):

{
    "bookings": [
        {
            "id": "123e4567-e89b-12d3-a456-426614174000",
            "user": {
                "id": "123e4567-e89b-12d3-a456-426614174001",
                "first_name": "John",
                "last_name": "Doe",
                "gender": "male",
                "birthday": "1990-01-01T00:00:00Z"
            },
            "flight": {
                "id": "123e4567-e89b-12d3-a456-426614174002",
                "launchpad_id": "5e9e4502f5090995de566f86",
                "destination": {
                    "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
                    "name": "Mars"
                },
                "launch_date": "2025-01-01T00:00:00Z"
            },
            "status": "ACTIVE",
            "created_at": "2024-01-01T00:00:00Z"
        }
    ],
    "limit": 10,
    "cursor": "next_page_token"
}
Delete Booking
DELETE /v1/bookings?id=123e4567-e89b-12d3-a456-426614174000

Response (204 No Content)

Health Check
GET /v1/health

Response (200 OK):

{
    "status": "healthy",
    "timestamp": "2024-01-01T00:00:00Z",
    "version": "1.0.0",
    "uptime": "24h0m0s",
    "go_version": "go1.22",
    "memory": {
        "alloc": 1234567,
        "totalAlloc": 2345678,
        "sys": 3456789,
        "numGC": 10
    }
}
Available Destinations
Destination ID
Mars a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11
Moon b0eebc99-9c0b-4ef8-bb6d-6bb9bd380a22
Pluto c0eebc99-9c0b-4ef8-bb6d-6bb9bd380a33
Asteroid Belt d0eebc99-9c0b-4ef8-bb6d-6bb9bd380a44
Europa e0eebc99-9c0b-4ef8-bb6d-6bb9bd380a55
Titan f0eebc99-9c0b-4ef8-bb6d-6bb9bd380a66
Ganymede 70eebc99-9c0b-4ef8-bb6d-6bb9bd380a77
Error Responses
Status Code Description
400 Bad Request - Invalid input data
404 Not Found - Booking or destination not found
409 Conflict - Launchpad unavailable or SpaceX conflict
500 Internal Server Error

Example error response:

{
    "error": "launchpad is unavailable"
}
Request Validation Rules
  • first_name, last_name: Required, max 50 characters
  • gender: Must be "male", "female", or "other"
  • birthday: Must be between 18-75 years old
  • launchpad_id: Must be 24 characters
  • destination_id: Must be a valid UUID from available destinations
  • launch_date: Must be in the future

Environment Variables βš™οΈ

Variable Description Default
SERVER_ADDRESS Server listening address :5000
SERVER_READ_TIMEOUT HTTP read timeout 15s
SERVER_WRITE_TIMEOUT HTTP write timeout 15s
SERVER_IDLE_TIMEOUT HTTP idle timeout 30s
POSTGRES_HOST PostgreSQL host localhost
POSTGRES_PORT PostgreSQL port 5432
POSTGRES_DB PostgreSQL database name space
POSTGRES_USER PostgreSQL username postgres
POSTGRES_PASSWORD PostgreSQL password postgres
MAX_CONNS Max DB connections 99
SPACEX_URL SpaceX API base URL https://api.spacexdata.com/v4

Project Structure πŸ“

spacetrouble/
β”œβ”€β”€ cmd/
β”‚   └── api/ 
β”‚       β”œβ”€β”€β”€β”€main.go            # Application entry point
β”œβ”€β”€ internal/
β”‚   β”œβ”€β”€ api/                    # API handlers
β”‚   β”œβ”€β”€ models/                 # Domain models
β”‚   β”œβ”€β”€ repository/             # Database operations
β”‚   β”œβ”€β”€ service/                # Business logic
β”‚   └── validator/              # Request validation
β”œβ”€β”€ pkg/
β”‚   β”œβ”€β”€ config/                 # Configuration management
β”‚   β”œβ”€β”€ health/                 # Health check endpoint
β”‚   └── spacex/                 # SpaceX API client
β”œβ”€β”€ tests/                      # Tests
β”‚   β”œβ”€β”€ api/
β”‚   β”œβ”€β”€ mocks/
β”‚   β”œβ”€β”€ pkg/
β”‚   β”œβ”€β”€ repository/
β”‚   β”œβ”€β”€ service/
β”‚   β”œβ”€β”€ utils/
β”‚   └── validator/
β”œβ”€β”€ migrations/                 # Database migrations
β”œβ”€β”€ Dockerfile                  # Docker build instructions
β”œβ”€β”€ docker-compose.yml          # Docker compose configuration
β”œβ”€β”€ Makefile                    # Build and development commands
└── README.md                   # Project documentation

Available Make Commands πŸ› οΈ

  • make build: Build the application
  • make test: Run tests
  • make vet: Run Go vet
  • make docker-build: Build Docker image
  • make docker-up: Start Docker containers
  • make docker-down: Stop Docker containers
  • make migrate-up: Run database migrations
  • make migrate-down: Revert database migrations

Testing πŸ§ͺ

Run the test suite:

make test

Testing Structure πŸ§ͺ

Test Organization
tests/
β”œβ”€β”€ api/                            # API handler tests
β”‚   └── api_test.go                 # Tests for API endpoints
β”œβ”€β”€ mocks/                          # Mock implementations
β”‚   β”œβ”€β”€ booking_repository.go       # Mock repository
β”‚   └── spacex_client.go            # Mock SpaceX client
β”œβ”€β”€ pkg/                            # Package tests
β”‚   β”œβ”€β”€ config/                     # Configuration tests
β”‚   β”œβ”€β”€ health/                     # Health check tests
β”‚   └── spacex/                     # SpaceX client tests
β”œβ”€β”€ repository/                     # Repository layer tests
β”‚   └── booking_repository_test.go
β”œβ”€β”€ service/                        # Service layer tests
β”‚   └── booking_service_test.go
β”œβ”€β”€ utils/                          # Test utilities
β”‚   └── mock_data.go                # Test data generators
└── validator/                      # Validation tests
└── validator_test.go
Test Coverage
API Tests (tests/api/)
  • Tests HTTP endpoints
  • Validates request/response formats
  • Checks content type handling
  • Verifies error responses
  • Tests pagination
  • Validates HTTP method restrictions
Repository Tests (tests/repository/)
  • Database CRUD operations
  • Transaction handling
  • Error scenarios
  • Connection pooling
  • Query building
  • Cursor-based pagination
Service Tests (tests/service/)
  • Business logic validation
  • SpaceX integration
  • Booking workflow
  • Error handling
  • Data transformation
  • Business rules enforcement
Mock Implementations (tests/mocks/)
  • MockBookingRepository: Repository layer mocking

    • Simulates database operations
    • Provides predictable responses
    • Enables error scenario testing
  • MockSpaceXClient: SpaceX API mocking

    • Simulates API responses
    • Tests network failures
    • Validates integration points
Test Utilities (tests/utils/)
  • mock_data.go: Test data generation
    • Creates consistent test bookings
    • Generates valid UUIDs
    • Provides sample requests/responses
    • Helps compare complex objects
Running Tests
All Tests
make test
Specific Package Tests
go test ./tests/service/...
go test ./tests/repository/...
go test ./tests/api/...
With Coverage
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
Test Categories
Unit Tests
  • Individual component testing
  • Mock external dependencies
  • Fast execution
  • High coverage
Integration Tests
  • Database interaction
  • API endpoint behavior
  • External service integration
  • Real-world scenarios
Mock Tests

Tests using mock implementations to verify:

  • Error handling
  • Edge cases
  • Resource unavailability
  • Invalid data scenarios
Test Scenarios
Booking Creation Tests
  • Valid booking creation
  • Invalid input validation
  • Destination verification
  • Launch date conflicts
  • SpaceX availability
  • Database errors
  • Transaction rollback
Booking Listing Tests
  • Pagination functionality
  • Cursor handling
  • Empty results
  • Large result sets
  • Filter application
  • Sort ordering
Booking Deletion Tests
  • Successful deletion
  • Non-existent booking
  • Invalid UUID
  • Unauthorized deletion
  • Status validation
Error Handling Tests
  • Database connection failures
  • SpaceX API unavailability
  • Invalid input data
  • Business rule violations
  • Concurrent access
Test Design Principles
  1. Isolation

    • Tests run independently
    • No shared state
    • Clean setup/teardown
  2. Reproducibility

    • Consistent test data
    • Deterministic results
    • Clear failure messages
  3. Coverage

    • Happy path scenarios
    • Error conditions
    • Edge cases
    • Business rules
  4. Maintainability

    • Clear test names
    • Shared utilities
    • Common patterns
    • Documentation
Writing New Tests
  1. Create test file in appropriate directory
package repository_test

func TestNewFeature(t *testing.T) {
    t.Run("successful case", func(t *testing.T) {
        // Test setup
        // Test execution
        // Assertions
    })

    t.Run("error case", func(t *testing.T) {
        // Test setup
        // Test execution
        // Assertions
    })
}
  1. Use test utilities
booking := utils.CreateMockBooking(uuid.Nil)
bookings := utils.CreateMockBookings(5)
  1. Use mocks
mockRepo := new(mocks.MockBookingRepository)
mockSpaceX := new(mocks.MockSpaceXClient)
  1. Assert expectations
assert.NoError(t, err)
assert.Equal(t, expected, actual)
mockRepo.AssertExpectations(t)
Common Test Patterns
  1. Table-Driven Tests
tests := []struct {
    name     string
    input    string
    expected string
    wantErr  bool
}{
    {"valid case", "input", "expected", false},
    {"error case", "bad input", "", true},
}

for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
        // Test logic
    })
}
  1. Setup/Teardown
func setupTest(t *testing.T) (*mocks.MockBookingRepository, *service.BookingService) {
    mockRepo := new(mocks.MockBookingRepository)
    svc := service.NewBookingService(mockRepo)
    return mockRepo, svc
}
  1. Helper Functions
func assertBookingsEqual(t *testing.T, expected, actual *models.Booking) {
    t.Helper()
    assert.Equal(t, expected.ID, actual.ID)
    // Additional assertions
}

Deployment 🚒

The application is containerised and can be deployed using Docker:

  1. Build the image:
make docker-build
  1. Configure environment variables for your deployment environment

  2. Run the containers:

make docker-up

Troubleshooting πŸ”§

Common Docker Issues
  1. Port Conflicts
Error starting userland proxy: listen tcp 0.0.0.0:5000: bind: address already in use

Solution:

# Find the process using the port
sudo lsof -i :5000
# Kill the process
kill -9 <PID>
# Or change the port in docker-compose.yml
  1. Database Connection Issues
error: connect: connection refused

Solutions:

  • Check if the database container is running:
docker-compose ps
  • Verify database credentials in .env
  • Try restarting the services:
make docker-down
make docker-up
  1. Permission Issues
permission denied while trying to connect to the Docker daemon socket

Solution:

# Add your user to the docker group
sudo usermod -aG docker ${USER}
# Log out and back in or run:
newgrp docker
  1. Database Migration Failures
error: migration failed

Solutions:

  • Check migration logs:
docker-compose logs app
  • Reset migrations:
make migrate-down
make migrate-up
  • Verify database connection:
docker-compose exec db psql -U postgres -d space -c "\l"
  1. Container Not Starting
Error response from daemon: Container is not running

Solutions:

  • Check container logs:
docker-compose logs <service_name>
  • Verify container status:
docker-compose ps
  • Remove containers and volumes:
docker-compose down -v
docker-compose up -d
  1. Image Build Failures
failed to build: exit status 1

Solutions:

  • Clean Docker cache:
docker system prune -a
  • Rebuild with no cache:
docker-compose build --no-cache
Maintenance Commands
  1. Reset Everything
# Stop all containers and remove volumes
make docker-down
docker-compose down -v

# Remove all related images
docker rmi $(docker images | grep spacetrouble)

# Rebuild from scratch
make docker-build
make docker-up
make migrate-up
  1. View Logs
# All services
docker-compose logs -f

# Specific service
docker-compose logs -f app
docker-compose logs -f db
  1. Access Database
# Connect to PostgreSQL
docker-compose exec db psql -U postgres -d space

# Backup database
docker-compose exec db pg_dump -U postgres space > backup.sql

# Restore database
cat backup.sql | docker-compose exec -T db psql -U postgres -d space
  1. Check Container Status
# List containers
docker-compose ps

# Container details
docker inspect <container_id>

# Resource usage
docker stats
Performance Tuning

If you experience performance issues:

  1. Database Tuning
    • Adjust max_connections in PostgreSQL
    • Modify connection pool size in .env
    • Monitor query performance with:
docker-compose exec db psql -U postgres -d space -c "SELECT * FROM pg_stat_activity;"
  1. Application Tuning
    • Adjust timeouts in .env
    • Monitor memory usage:
docker stats spacetrouble_app_1
  1. System Resources
    • Increase Docker resources (CPU/Memory) in Docker Desktop settings
    • Monitor resource usage:
docker-compose top

Technical Details πŸ”§

  • Architecture: Clean Architecture pattern
  • API Design: RESTful with JSON/XML support
  • Database: PostgreSQL with migrations
  • External Integration: SpaceX API for launch checks
  • Validation: Custom validation rules for bookings
  • Error Handling: Structured error responses
  • Monitoring: Health check endpoint with metrics

Directories ΒΆ

Path Synopsis
cmd
api
api
pkg
tests

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL