integration

package
v0.0.0-...-c157d14 Latest Latest
Warning

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

Go to latest
Published: Oct 27, 2025 License: MIT Imports: 21 Imported by: 0

README

Integration Tests

This directory contains end-to-end integration tests for the Engine application. These tests verify complete user workflows with real HTTP requests, database interactions, and middleware.

Overview

Integration tests differ from unit tests:

  • Unit Tests: Test individual functions in isolation with mocked dependencies
  • Integration Tests: Test complete workflows with real HTTP server, database, CSRF, sessions, and middleware

Test Structure

tests/integration/
├── README.md                      # This file
├── helper.go                      # Shared test infrastructure and utilities
├── admin_flow_test.go             # Admin user management integration tests (10 tests)
└── password_reset_flow_test.go    # Password reset flow integration tests (8 tests)

Additional Integration Tests (in package directories):

internal/auth/integration_test.go    # Auth flow tests (registration, login, logout, CSRF)
internal/twofa/integration_test.go   # 2FA flow tests (setup, login, recovery codes, disable)

Test Coverage

1. Admin Flow Tests (admin_flow_test.go)

Complete admin panel functionality with HTTP requests:

Test Description
TestAdminFlow_CompleteUserManagement Full admin workflow: dashboard, list users, search, view details, edit user, verify logging
TestAdminFlow_NonAdminAccessDenied Regular users cannot access admin routes (403 Forbidden)
TestAdminFlow_PaginationWorks User list pagination with page/limit parameters
TestAdminFlow_FilterByRole Filter users by role (admin/customer)
TestAdminFlow_SortByCreatedAt Sort users by creation date (asc/desc)
TestAdminFlow_DeactivateUser Deactivate user and verify they cannot login
TestAdminFlow_ViewUserSessions View all sessions for a user
TestAdminFlow_TerminateUserSession Admin terminates user session, verify access revoked
TestAdminFlow_AuditLogRetention Verify admin actions are logged with timestamp and details
TestAdminFlow_StatisticsAccuracy Admin dashboard displays correct user/session statistics

Total: 10 admin integration tests

2. Password Reset Flow Tests (password_reset_flow_test.go)

Complete password reset workflow via HTTP:

Test Description
TestPasswordResetFlow_Complete Full flow: request reset → receive token → reset password → login with new password
TestPasswordResetFlow_ExpiredToken Expired tokens are rejected with error message
TestPasswordResetFlow_UsedToken Used tokens cannot be reused
TestPasswordResetFlow_InvalidToken Invalid/non-existent tokens show error
TestPasswordResetFlow_TokenCleanup Cleanup job deletes expired tokens, keeps valid ones
TestPasswordResetFlow_NonexistentEmail Reset request for non-existent email succeeds (security: don't reveal user existence)
TestPasswordResetFlow_WeakPasswordRejected Password validation rejects weak passwords
TestPasswordResetFlow_PasswordMismatch Password confirmation must match

Total: 8 password reset integration tests

3. Auth Flow Tests (internal/auth/integration_test.go)

Authentication workflows:

  • Registration (valid, duplicate username/email, invalid data)
  • Login (username/email, invalid credentials, inactive user, remember me)
  • Logout (session cleanup)
  • Protected routes (access control, expired sessions)
  • CSRF protection (missing/invalid/valid tokens)

Total: ~15 auth integration tests

4. 2FA Flow Tests (internal/twofa/integration_test.go)

Two-factor authentication workflows:

  • 2FA setup (complete flow, invalid code, already enabled)
  • 2FA login (TOTP code, invalid code)
  • Recovery codes (login with code, single-use enforcement, regeneration)
  • 2FA disable (with password confirmation)

Total: ~12 2FA integration tests

Setup

Prerequisites
  1. PostgreSQL Database: Integration tests require a real PostgreSQL database
  2. Test Database: Create a separate database for testing (never use production!)
  3. Environment Variable: Set TEST_DATABASE_URL or DATABASE_URL
Database Setup
# Create test database
createdb engine_test

# Set environment variable
export TEST_DATABASE_URL="postgresql://postgres:password@localhost:5432/engine_test?sslmode=disable"

# Or use DATABASE_URL as fallback
export DATABASE_URL="postgresql://postgres:password@localhost:5432/engine_test?sslmode=disable"

# Run migrations
migrate -path ./migrations -database "$TEST_DATABASE_URL" up

Important: Integration tests will DELETE ALL DATA from the test database. Never point TEST_DATABASE_URL at a production or development database.

Running Tests

Run All Integration Tests
# Run all integration tests in tests/integration/
go test ./tests/integration/... -v

# Run with coverage
go test ./tests/integration/... -coverprofile=integration-coverage.out -v

# View coverage report
go tool cover -html=integration-coverage.out
Run Specific Test Files
# Admin flow tests only
go test ./tests/integration/admin_flow_test.go ./tests/integration/helper.go -v

# Password reset tests only
go test ./tests/integration/password_reset_flow_test.go ./tests/integration/helper.go -v
Run Specific Tests
# Single test
go test ./tests/integration/... -run TestAdminFlow_CompleteUserManagement -v

# Tests matching pattern
go test ./tests/integration/... -run "Admin.*Deactivate" -v
Run All Integration Tests (Including Package Tests)
# Run auth integration tests
go test ./internal/auth/... -run Integration -v

# Run 2FA integration tests
go test ./internal/twofa/... -run Integration -v

# Run all tests with "Integration" in name
go test ./... -run Integration -v
Run with Timeout

Integration tests may take longer than unit tests:

# Increase timeout to 5 minutes
go test ./tests/integration/... -v -timeout 5m

Test Infrastructure

helper.go

Provides shared utilities for integration tests:

Core Functions:

  • SetupTestServer(t) - Creates HTTP test server with all routes, middleware, and services
  • CleanupTestData(t, db) - Deletes all test data from database (sessions, users, logs, etc.)
  • CreateTestUser(t, db, username, email, password, role) - Creates user directly in DB
  • CreateTestUserWithSession(t, authService, sessionService, username, role) - Creates user and session
  • CreateHTTPClient() - HTTP client with cookie jar for session persistence

Helper Functions:

  • ExtractCSRFToken(html) - Extracts CSRF token from HTML forms
  • GetSessionCookie(resp) - Gets session cookie from HTTP response
  • ReadResponseBody(resp) - Reads response body as string

Adapters:

  • twofaServiceAdapter - Adapts twofa.Service to auth.TwofaService interface
  • authServiceAdapter - Adapts auth.Service for handlers
  • activityServiceAdapter - Adapts activity.Service for logging

Best Practices

1. Test Isolation

Each test should:

  • Call CleanupTestData(t, server.DB) in defer to ensure cleanup
  • Be independent and not rely on other tests
  • Create its own test data
func TestSomething(t *testing.T) {
    server := SetupTestServer(t)
    defer server.Close()
    defer CleanupTestData(t, server.DB)

    // Test implementation
}
2. HTTP Request Pattern
// Create client with cookie jar (persists sessions)
client := CreateHTTPClient()

// Make request
req, _ := http.NewRequest("GET", server.URL+"/admin/users", nil)
req.AddCookie(&http.Cookie{Name: auth.SessionCookieName, Value: sessionToken})

resp, err := client.Do(req)
require.NoError(t, err)
defer resp.Body.Close()

// Validate response
assert.Equal(t, http.StatusOK, resp.StatusCode)
body := ReadResponseBody(resp)
assert.Contains(t, body, "expected content")
3. CSRF Token Handling

All POST requests require CSRF tokens:

// 1. GET form to extract CSRF token
resp, _ := client.Get(server.URL + "/auth/login")
body := ReadResponseBody(resp)
csrfToken := ExtractCSRFToken(body)
resp.Body.Close()

// 2. POST with CSRF token
formData := url.Values{
    "csrf_token": {csrfToken},
    "username":   {"testuser"},
    "password":   {"password123"},
}

resp, _ = client.PostForm(server.URL+"/auth/login", formData)
4. Session Management
// Create user with session
userID, sessionToken := CreateTestUserWithSession(t, server.AuthService, server.SessionService, "testuser", "customer")

// Use session in requests
req.AddCookie(&http.Cookie{
    Name:  auth.SessionCookieName,
    Value: sessionToken,
})
5. Database Verification

Always verify database state after actions:

// Verify user is deactivated
var isActive bool
err := server.DB.QueryRow("SELECT is_active FROM users WHERE id = $1", userID).Scan(&isActive)
require.NoError(t, err)
assert.False(t, isActive, "User should be deactivated")

// Verify session is deleted
var count int
err = server.DB.QueryRow("SELECT COUNT(*) FROM sessions WHERE token = $1", token).Scan(&count)
require.NoError(t, err)
assert.Equal(t, 0, count, "Session should be deleted")

Troubleshooting

Tests Skip with "TEST_DATABASE_URL not set"

Solution: Set the environment variable:

export TEST_DATABASE_URL="postgresql://user:password@localhost:5432/engine_test?sslmode=disable"
go test ./tests/integration/... -v
Database Connection Fails

Possible Issues:

  1. PostgreSQL not running: brew services start postgresql (macOS)
  2. Wrong credentials: Check username/password in connection string
  3. Database doesn't exist: createdb engine_test
  4. Migrations not run: migrate -path ./migrations -database "$TEST_DATABASE_URL" up
CSRF Validation Fails

Cause: CSRF token not extracted or not included in POST request

Solution: Always GET form first, extract token, then POST with token:

resp, _ := client.Get(server.URL + "/form")
csrfToken := ExtractCSRFToken(ReadResponseBody(resp))
resp.Body.Close()

formData := url.Values{"csrf_token": {csrfToken}, ...}
client.PostForm(server.URL + "/submit", formData)
Session Not Persisted

Cause: Not using HTTP client with cookie jar

Solution: Use CreateHTTPClient() which includes cookie jar:

client := CreateHTTPClient()  // ✓ Correct - persists cookies
// NOT: client := &http.Client{}  ✗ Wrong - no cookie jar
Template Loading Fails

Cause: Template paths are relative to test execution directory

Note: Some tests may skip if templates fail to load. This is usually harmless for tests that don't render full HTML. Check logs for warnings:

Warning: Failed to load templates: <error> - some tests may fail
Foreign Key Constraint Violations During Cleanup

Cause: Wrong cleanup order (child records deleted before parent)

Solution: CleanupTestData deletes in correct order:

tables := []string{
    "sessions",                    // Child (references users)
    "two_factor_recovery_codes",   // Child (references users)
    "admin_logs",                  // Child (references users)
    "user_activity_log",           // Child (references users)
    "password_reset_tokens",       // Child (references users)
    "users",                       // Parent
}

CI/CD Integration

GitHub Actions Example
name: Integration Tests

on: [push, pull_request]

jobs:
  integration:
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: engine_test
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432

    steps:
      - uses: actions/checkout@v3

      - name: Set up Go
        uses: actions/setup-go@v4
        with:
          go-version: '1.21'

      - name: Run migrations
        run: |
          go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
          migrate -path ./migrations -database "postgresql://postgres:postgres@localhost:5432/engine_test?sslmode=disable" up
        env:
          TEST_DATABASE_URL: postgresql://postgres:postgres@localhost:5432/engine_test?sslmode=disable

      - name: Run integration tests
        run: go test ./tests/integration/... -v -timeout 5m
        env:
          TEST_DATABASE_URL: postgresql://postgres:postgres@localhost:5432/engine_test?sslmode=disable

      - name: Run package integration tests
        run: |
          go test ./internal/auth/... -run Integration -v
          go test ./internal/twofa/... -run Integration -v
        env:
          DATABASE_URL: postgresql://postgres:postgres@localhost:5432/engine_test?sslmode=disable

Summary

This integration test suite provides comprehensive coverage of:

  • Admin user management (10 tests) - User CRUD, sessions, access control, audit logging
  • Password reset flows (8 tests) - Complete reset workflow, token validation, security
  • Authentication flows (~15 tests) - Registration, login, logout, CSRF, session management
  • Two-factor authentication (~12 tests) - Setup, login, recovery codes, disable

Total: 45+ integration tests covering all major user journeys

All tests use real database, HTTP server, middleware (CSRF, auth, 2FA, admin), and session management to verify end-to-end functionality.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CleanupTestData

func CleanupTestData(t *testing.T, db *sql.DB)

CleanupTestData removes all test data from database

func CreateHTTPClient

func CreateHTTPClient() *http.Client

CreateHTTPClient creates an HTTP client with cookie jar for session persistence

func CreateTestUser

func CreateTestUser(t *testing.T, db *sql.DB, username, email, password string, role string) (string, string)

CreateTestUser creates a user in the database for testing

func CreateTestUserWithSession

func CreateTestUserWithSession(t *testing.T, authService *auth.Service, sessionService *auth.SessionService, username string, role string) (string, string)

CreateTestUserWithSession creates a user and a session, returns userID and session token

func ExtractCSRFToken

func ExtractCSRFToken(html string) string

ExtractCSRFToken extracts CSRF token from HTML

func GetSessionCookie

func GetSessionCookie(resp *http.Response) *http.Cookie

GetSessionCookie retrieves session cookie from response

func ReadResponseBody

func ReadResponseBody(resp *http.Response) string

ReadResponseBody reads response body into string

Types

type TestServer

type TestServer struct {
	*httptest.Server
	DB              *sql.DB
	Logger          *log.Logger
	AuthService     *auth.Service
	SessionService  *auth.SessionService
	AdminService    *admin.Service
	TwofaService    *twofa.Service
	ActivityService *activity.Service
}

TestServer wraps an httptest.Server with additional context for integration tests

func SetupTestServer

func SetupTestServer(t *testing.T) *TestServer

SetupTestServer creates a test HTTP server with real database and all middleware

func (*TestServer) Close

func (ts *TestServer) Close()

Close shuts down the test server and closes database connection

Jump to

Keyboard shortcuts

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