> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/TecharoHq/Anubis/llms.txt
> Use this file to discover all available pages before exploring further.

# Testing

> Run tests and validate changes in Anubis

This guide covers running unit tests, integration tests, and smoke tests.

## Prerequisites

<Warning>
  **Assets must be built first**: All tests require frontend assets to be built before running. Tests will fail if assets are missing or stale.
</Warning>

Build assets before testing:

```bash theme={null}
npm run assets
```

## Unit Tests

Run all unit tests:

```bash theme={null}
npm run test
```

This runs:

```bash theme={null}
npm run assets && SKIP_INTEGRATION=1 go test ./...
```

The `SKIP_INTEGRATION=1` flag skips integration tests that require Docker/Podman.

### Run Tests Manually

Run tests with `go test` directly:

```bash theme={null}
# All tests (after building assets)
go test ./...

# Run a specific test by name
go test -run TestClampIP ./internal/

# Run all tests in a specific package
go test ./lib/config/

# Verbose output
go test -v -run TestBotValid ./lib/config/
```

### Test Coverage

Generate coverage report:

```bash theme={null}
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
```

## Integration Tests

Integration tests use Playwright to test Anubis in realistic browser scenarios.

### Run All Integration Tests

```bash theme={null}
npm run test:integration
```

This runs:

```bash theme={null}
npm run assets && go test -v ./internal/test
```

### Run with Specific Container Runtime

Use Podman:

```bash theme={null}
npm run test:integration:podman
```

Use Docker:

```bash theme={null}
npm run test:integration:docker
```

<Note>
  Integration tests require Docker or Podman to be installed and running.
</Note>

## Smoke Tests

Smoke tests validate Anubis against production-adjacent infrastructure configurations.

### What Are Smoke Tests?

The `test/` directory contains smoke tests - each is a folder with a `test.sh` script that:

1. Sets up infrastructure (Caddy, nginx, Docker registry, etc.)
2. Runs Anubis in that configuration
3. Validates expected behavior
4. Tears down infrastructure

### Available Smoke Tests

```bash theme={null}
test/
├── caddy/                    # Caddy reverse proxy integration
├── default-config-macro/     # Default configuration validation
├── docker-registry/          # Docker registry protection
├── double_slash/             # URL path handling
├── forced-language/          # i18n language forcing
├── git-clone/                # Git clone over HTTP
├── git-push/                 # Git push over HTTP
├── healthcheck/              # Health check endpoint
├── i18n/                     # Internationalization
├── log-file/                 # File logging
├── nginx/                    # nginx reverse proxy
├── nginx-external-auth/      # nginx external auth module
├── palemoon/                 # Pale Moon browser compatibility
├── robots_txt/               # robots.txt handling
├── unix-socket-xff/          # Unix socket with X-Forwarded-For
```

### Run Smoke Tests

Smoke tests run automatically in GitHub Actions using `.github/workflows/smoke-tests.yaml`.

Run a specific smoke test manually:

```bash theme={null}
cd test/caddy
./test.sh
```

<Warning>
  Smoke tests modify system state (start containers, bind ports, etc.). Run them in a clean environment or CI.
</Warning>

### Pale Moon Tests

Pale Moon browser has historically exposed bugs in Anubis's JavaScript challenge code. The Pale Moon smoke tests ensure compatibility:

```bash theme={null}
test/palemoon/amd64/test.sh
test/palemoon/i386/test.sh
```

These tests run a VNC-based Pale Moon instance and validate challenge solving.

## Test Conventions

From `CONTRIBUTING.md`, tests should follow these patterns:

### Table-Driven Tests

Use table-driven test patterns:

```go theme={null}
func TestExample(t *testing.T) {
    tests := []struct {
        name string
        input string
        want string
    }{
        {"case 1", "input1", "output1"},
        {"case 2", "input2", "output2"},
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got := ExampleFunc(tt.input)
            if got != tt.want {
                t.Errorf("got %v, want %v", got, tt.want)
            }
        )
    }
}
```

### Helper Functions

Use `t.Helper()` in test helpers:

```go theme={null}
func setupTest(t *testing.T) *TestContext {
    t.Helper()
    // setup code
    return ctx
}
```

### Cleanup

Use `t.Cleanup()` for teardown:

```go theme={null}
func TestWithResources(t *testing.T) {
    db := setupDatabase(t)
    t.Cleanup(func() {
        db.Close()
    )
    // test code
}
```

### Error Assertions

Use `errors.Is` for sentinel error validation:

```go theme={null}
err := SomeFunc()
if !errors.Is(err, ErrExpected) {
    t.Errorf("expected ErrExpected, got %v", err)
}
```

## Linting

Run static analysis tools:

```bash theme={null}
npm run lint
```

Or run individually:

```bash theme={null}
go vet ./...
go tool staticcheck ./...
go tool govulncheck ./...
```

## Continuous Integration

All tests run automatically in GitHub Actions on:

* Every push
* Every pull request
* Scheduled nightly builds

Smoke tests run in `.github/workflows/smoke-tests.yaml`.
