Nomad changes
All checks were successful
Deploy fil (kreuzberg) / deploy (push) Successful in 49s

This commit is contained in:
Henrik Jess Nielsen
2026-06-01 23:40:55 +02:00
parent 72b1a0a6ed
commit b4c07d3693
5723 changed files with 1130655 additions and 0 deletions

264
scripts/test/README.md Normal file
View File

@@ -0,0 +1,264 @@
# Docker Configuration Testing Scripts
This directory contains comprehensive testing scripts for validating Docker configuration scenarios.
## Scripts
### test-docker-config-local.sh
A comprehensive local Docker testing script that validates all configuration volume mount scenarios.
#### Purpose
Tests Docker configuration in various scenarios:
- Volume mounts to `/etc/kreuzberg/kreuzberg.toml` (recommended system path)
- Volume mounts to `/app/.config/kreuzberg/config.toml` (user path)
- Custom paths with `--config` flag
- Environment variable overrides with config files
- All config formats (TOML, YAML, JSON)
- Read-only mounts (`:ro` flag)
#### Requirements
- Docker installed and running
- Docker images pre-built (`kreuzberg:core` and/or `kreuzberg:full`)
- Port range 18100-18199 available for testing
#### Usage
```bash
./test-docker-config-local.sh [OPTIONS]
```
#### Options
| Option | Description | Default |
| ------------------- | ----------------------------------------------- | -------- |
| `--variant VARIANT` | Test specific variant: `core`, `full`, or `all` | `all` |
| `--verbose` | Enable verbose debugging output | Disabled |
| `--keep-containers` | Preserve containers after tests for inspection | Clean up |
| `--help` | Display help message | - |
#### Examples
Test both core and full variants:
```bash
./test-docker-config-local.sh
```
Test only the full variant with verbose output:
```bash
./test-docker-config-local.sh --variant full --verbose
```
Test core variant and keep containers for inspection:
```bash
./test-docker-config-local.sh --variant core --keep-containers
```
#### Test Cases
The script runs 8 test cases for each variant:
1. **Volume mount to /etc/kreuzberg/kreuzberg.toml**
- Tests the recommended system-wide configuration path
- Validates read-only mount functionality
2. **Volume mount to /app/.config/kreuzberg/config.toml**
- Tests the user-level configuration path
- Validates alternative mount location
3. **Custom path with --config flag**
- Tests custom configuration file paths
- Validates explicit path specification via CLI flag
4. **Environment variable overrides with config file**
- Tests that environment variables can override config file settings
- Validates configuration precedence
5. **TOML config format**
- Tests TOML configuration file format support
- Validates parsing of TOML syntax
6. **YAML config format**
- Tests YAML configuration file format support
- Validates parsing of YAML syntax
7. **JSON config format**
- Tests JSON configuration file format support
- Validates parsing of JSON syntax
8. **Read-only mount**
- Tests that containers work correctly with read-only mounts
- Validates security of mounted volumes
#### Validation Method
For each test, the script:
1. Creates a temporary configuration file in the specified format
2. Starts a Docker container with the configuration mounted
3. Waits for the service to become healthy (up to 30 seconds)
4. Verifies the health endpoint responds successfully
5. Stops and removes the container
6. Reports pass/fail status
#### Output
The script provides clear, color-coded output:
- `[PASS]` - Test passed (green)
- `[FAIL]` - Test failed (red)
- `[INFO]` - Informational messages (blue)
- `[WARN]` - Warnings (yellow)
- `[DEBUG]` - Debug information (yellow, with `--verbose`)
Example output:
```text
╔════════════════════════════════════════════════════════╗
║ Docker Configuration Volume Mount Test Suite ║
╚════════════════════════════════════════════════════════╝
[INFO] Configuration:
[INFO] Variant: all
[INFO] Verbose: false
[INFO] Keep Containers: false
[INFO] Port Range: 18100-18199
[INFO] Docker is available
Test 01: Volume mount to /etc/kreuzberg/kreuzberg.toml (variant: core)
[PASS] Test passed
Test 02: Volume mount to /app/.config/kreuzberg/config.toml (variant: core)
[PASS] Test passed
...
╔════════════════════════════════════════════════════════╗
║ Test Summary ║
╚════════════════════════════════════════════════════════╝
Total Tests: 16
Passed Tests: 16
Failed Tests: 0
Pass Rate: 100%
Tested Variants:
- kreuzberg:core
- kreuzberg:full
```
#### Troubleshooting
**Error: Docker is not installed or not in PATH**
- Install Docker from <https://www.docker.com/products/docker-desktop>
- Ensure Docker is in your system PATH
**Error: Docker daemon is not running**
- Start Docker Desktop or the Docker daemon
- On Linux: `sudo systemctl start docker`
**Error: Docker image does not exist**
- Build the required image(s):
```bash
cd /path/to/kreuzberg
docker build -f docker/Dockerfile.core -t kreuzberg:core .
docker build -f docker/Dockerfile.full -t kreuzberg:full .
```
**Tests timing out**
- Check system resources (CPU, memory)
- Increase timeout: Modify `TIMEOUT_SECONDS=30` in the script
- Check Docker logs: `docker logs <container-name>`
**Port conflicts**
- Ensure ports 18100-18199 are available
- Check for existing containers: `docker ps -a`
- Kill conflicting containers: `docker kill <container-name>`
#### Environment Variables
The script respects these environment variables:
| Variable | Description | Default |
| ----------------- | ------------------------------------- | ------- |
| `TEST_VARIANT` | Override variant via environment | Unset |
| `VERBOSE` | Enable verbose output via environment | `false` |
| `KEEP_CONTAINERS` | Keep containers via environment | `false` |
Example:
```bash
VERBOSE=true ./test-docker-config-local.sh --variant core
```
#### Temporary Files
The script creates temporary configuration files in `/tmp/kreuzberg-config-test-$PID/`:
- `kreuzberg.toml` - TOML format test config
- `config.yaml` - YAML format test config
- `config.json` - JSON format test config
These are automatically cleaned up after tests complete (unless `--keep-containers` is used).
#### Exit Codes
- `0` - All tests passed
- `1` - One or more tests failed, or Docker is not available
#### Performance Notes
- Each test takes approximately 2-5 seconds
- Total test suite runtime: 1-2 minutes for all variants
- Network latency may affect health check timing
- Container startup time depends on system resources
#### CI/CD Integration
The script can be integrated into CI/CD pipelines:
```bash
#!/bin/bash
set -e
# Build images
docker build -f docker/Dockerfile.core -t kreuzberg:core .
docker build -f docker/Dockerfile.full -t kreuzberg:full .
# Run tests
./scripts/test/test-docker-config-local.sh --variant all
echo "Configuration tests passed!"
```
#### Limitations
- Requires Docker to be installed and running
- Tests only configuration volume mounts (not other volume types)
- Tests only health endpoint (basic connectivity validation)
- Assumes `kreuzberg:*` image naming convention
- Tests run sequentially (not parallelized)
#### Future Enhancements
Potential improvements:
- Parallel test execution for faster results
- Additional validation endpoints (beyond `/health`)
- Configuration value verification (test that config was actually loaded)
- Performance benchmarking
- Multi-architecture testing (arm64, amd64)
- Docker Compose integration tests

528
scripts/test/USAGE.md Normal file
View File

@@ -0,0 +1,528 @@
# Docker Configuration Testing - Quick Start Guide
## Overview
The `test-docker-config-local.sh` script provides comprehensive testing for Docker configuration volume mounts and environment variable overrides.
## Prerequisites
1. **Docker**: Installed and running
2. **Images**: Pre-built Docker images for testing
3. **Ports**: 18100-18199 available for test containers
4. **Utilities**: `bash`, `curl`, `docker` command-line tools
## Building Test Images
Before running tests, build the Docker images:
```bash
cd .
# Build core variant
docker build -f docker/Dockerfile.core -t kreuzberg:core .
# Build full variant
docker build -f docker/Dockerfile.full -t kreuzberg:full .
# Or build both
docker build -f docker/Dockerfile.core -t kreuzberg:core . && \
docker build -f docker/Dockerfile.full -t kreuzberg:full .
```
## Running Tests
### Basic Usage
Test all variants with default settings:
```bash
./scripts/test/test-docker-config-local.sh
```
### Common Commands
**Test only core variant:**
```bash
./scripts/test/test-docker-config-local.sh --variant core
```
**Test only full variant:**
```bash
./scripts/test/test-docker-config-local.sh --variant full
```
**Enable verbose output:**
```bash
./scripts/test/test-docker-config-local.sh --verbose
```
**Keep containers after testing:**
```bash
./scripts/test/test-docker-config-local.sh --keep-containers
```
**Combine multiple options:**
```bash
./scripts/test/test-docker-config-local.sh --variant full --verbose --keep-containers
```
## Test Cases Explained
### 1. Volume Mount to /etc/kreuzberg/kreuzberg.toml
**What it tests**: System-wide configuration path (recommended)
**Docker command**:
```bash
docker run -v /local/config.toml:/etc/kreuzberg/kreuzberg.toml:ro kreuzberg:full
```
**Expected**: Container reads config from standard system location
---
### 2. Volume Mount to /app/.config/kreuzberg/config.toml
**What it tests**: User-level configuration path (alternative location)
**Docker command**:
```bash
docker run -v /local/config.toml:/app/.config/kreuzberg/config.toml:ro kreuzberg:full
```
**Expected**: Container reads config from user application directory
---
### 3. Custom Path with --config Flag
**What it tests**: Explicit configuration path specification
**Docker command**:
```bash
docker run \
-v /local/config.toml:/app/custom-config.toml:ro \
--entrypoint "/app/kreuzberg" \
kreuzberg:full \
--config /app/custom-config.toml
```
**Expected**: Container uses specified custom path
---
### 4. Environment Variable Overrides
**What it tests**: Environment variables override config file settings
**Docker command**:
```bash
docker run \
-v /local/config.toml:/etc/kreuzberg/kreuzberg.toml:ro \
-e KREUZBERG_SERVER_PORT=8000 \
kreuzberg:full
```
**Expected**: Environment variable takes precedence over config file
---
### 5. TOML Format Support
**What it tests**: Configuration in TOML format
**Config file**:
```toml
[server]
host = "0.0.0.0"
port = 8000
max_upload_mb = 100
[ocr]
backend = "tesseract"
language = "eng"
```
**Expected**: Container parses TOML correctly
---
### 6. YAML Format Support
**What it tests**: Configuration in YAML format
**Config file**:
```yaml
server:
host: "0.0.0.0"
port: 8000
max_upload_mb: 100
ocr:
backend: "tesseract"
language: "eng"
```
**Expected**: Container parses YAML correctly
---
### 7. JSON Format Support
**What it tests**: Configuration in JSON format
**Config file**:
```json
{
"server": {
"host": "0.0.0.0",
"port": 8000,
"max_upload_mb": 100
},
"ocr": {
"backend": "tesseract",
"language": "eng"
}
}
```
**Expected**: Container parses JSON correctly
---
### 8. Read-Only Mount
**What it tests**: Security of read-only mounted volumes
**Docker command**:
```bash
docker run -v /local/config.toml:/etc/kreuzberg/kreuzberg.toml:ro kreuzberg:full
```
**Expected**: Container works with read-only volumes, application doesn't attempt to modify config
---
## Understanding Output
### Success Output
```text
╔════════════════════════════════════════════════════════╗
║ Docker Configuration Volume Mount Test Suite ║
╚════════════════════════════════════════════════════════╝
[INFO] Configuration:
[INFO] Variant: all
[INFO] Verbose: false
[INFO] Keep Containers: false
[INFO] Port Range: 18100-18199
[INFO] Docker is available
Test 01: Volume mount to /etc/kreuzberg/kreuzberg.toml (variant: core)
[PASS] Test passed
```
### Failure Output
```text
Test 02: Custom path with --config flag (variant: core)
[FAIL] Test failed: Failed to start container with custom --config flag
[FAIL] Details: Container logs:
/app/kreuzberg: line 123: syntax error: unexpected token
```
### Summary
```text
╔════════════════════════════════════════════════════════╗
║ Test Summary ║
╚════════════════════════════════════════════════════════╝
Total Tests: 16
Passed Tests: 16
Failed Tests: 0
Pass Rate: 100%
Tested Variants:
- kreuzberg:core
- kreuzberg:full
```
## Debugging Failed Tests
### Enable Verbose Output
```bash
./scripts/test/test-docker-config-local.sh --variant core --verbose
```
Verbose output shows:
- Container IDs
- Docker arguments
- Service startup timing
- Health check attempts
### Keep Containers for Inspection
```bash
./scripts/test/test-docker-config-local.sh --keep-containers
```
Then inspect containers manually:
```bash
# List test containers
docker ps -a | grep kreuzberg-config-test
# View specific container logs
docker logs kreuzberg-config-test-etc-core-12345
# Execute command in running container
docker exec kreuzberg-config-test-etc-core-12345 cat /etc/kreuzberg/kreuzberg.toml
# Stop container manually
docker stop kreuzberg-config-test-etc-core-12345
docker rm kreuzberg-config-test-etc-core-12345
```
### Check Health Endpoint Manually
```bash
# Start container manually
docker run -d \
--name test-container \
-p 8000:8000 \
-v /path/to/config.toml:/etc/kreuzberg/kreuzberg.toml:ro \
kreuzberg:full
# Wait for startup
sleep 3
# Test health endpoint
curl -v http://localhost:8000/health
# View logs
docker logs test-container
# Cleanup
docker stop test-container
docker rm test-container
```
## Troubleshooting
### Docker Not Found
```text
[ERROR] Docker is not installed or not in PATH
```
**Solution**: Install Docker or ensure it's in your PATH
```bash
which docker
export PATH=$PATH:/usr/local/bin # or wherever docker is installed
```
### Docker Daemon Not Running
```text
[ERROR] Docker daemon is not running or you don't have permissions
```
**Solution**: Start Docker
```bash
# macOS
open -a Docker
# Linux
sudo systemctl start docker
# Check status
docker ps
```
### Image Not Found
```text
[WARN] Skipping tests for variant: full (image not found)
```
**Solution**: Build the image
```bash
docker build -f docker/Dockerfile.full -t kreuzberg:full .
```
### Port Already in Use
```text
[FAIL] Test failed: Failed to start container
[FAIL] Details: port is already allocated
```
**Solution**: Free the ports or wait for existing tests to finish
```bash
# Find what's using the ports
lsof -i :18100-18199
# Or just stop all test containers
docker ps -a --filter "name=kreuzberg-config-test" --format "{{.Names}}" | \
xargs -r docker stop
```
### Health Check Timeout
```text
[FAIL] Test failed: Service failed to start (health check timeout)
```
**Debugging**:
1. Check container is still running:
```bash
docker ps | grep kreuzberg-config-test
```
2. View container logs:
```bash
docker logs <container-name>
```
3. Check if service is binding to port:
```bash
docker exec <container-name> netstat -tuln | grep 8000
```
4. Increase timeout (edit script):
```bash
TIMEOUT_SECONDS=60 # Change from 30
```
## CI/CD Integration
### GitHub Actions
```yaml
name: Docker Config Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build Docker images
run: |
docker build -f docker/Dockerfile.core -t kreuzberg:core .
docker build -f docker/Dockerfile.full -t kreuzberg:full .
- name: Run configuration tests
run: ./scripts/test/test-docker-config-local.sh --variant all
```
### GitLab CI
```yaml
docker-config-tests:
stage: test
image: docker:latest
services:
- docker:dind
script:
- docker build -f docker/Dockerfile.core -t kreuzberg:core .
- docker build -f docker/Dockerfile.full -t kreuzberg:full .
- ./scripts/test/test-docker-config-local.sh --variant all
```
## Performance Expectations
| Metric | Time |
| ------------------------- | -------------- |
| Single test | 2-5 seconds |
| All 8 tests (1 variant) | 30-45 seconds |
| All 16 tests (2 variants) | 60-90 seconds |
| With verbose output | +10-20 seconds |
## Exit Codes
| Code | Meaning |
| ---- | ---------------------------------------------- |
| 0 | All tests passed |
| 1 | One or more tests failed OR Docker unavailable |
## Advanced Usage
### Custom Environment Variables
```bash
# Override variant via environment
TEST_VARIANT=core ./scripts/test/test-docker-config-local.sh
# Override verbose via environment
VERBOSE=true ./scripts/test/test-docker-config-local.sh
```
### Modify Timeout
Edit the script to change timeout:
```bash
TIMEOUT_SECONDS=60 # Line ~43, change from 30
```
### Test Specific Scenarios
To test only one specific scenario, modify the `run_test_suite()` call in `main()`:
```bash
# Comment out unwanted tests
# test_etc_kreuzberg_mount "$variant"
test_app_config_mount "$variant"
# test_custom_path_with_flag "$variant"
# ... etc
```
## Getting Help
```bash
./scripts/test/test-docker-config-local.sh --help
```
For detailed documentation:
```bash
cat ./scripts/test/README.md
```
## Related Files
- **Script**: `./scripts/test/test-docker-config-local.sh`
- **Documentation**: `./scripts/test/README.md`
- **This Guide**: `./scripts/test/USAGE.md`
- **Docker Files**: `./docker/Dockerfile.core`
- **Docker Files**: `./docker/Dockerfile.full`

View File

@@ -0,0 +1,800 @@
#!/bin/bash
################################################################################
# Docker Configuration Volume Mount Testing Script
#
# This script validates all Docker configuration scenarios locally:
# - Volume mounts to /etc/kreuzberg/kreuzberg.toml (recommended)
# - Volume mounts to /app/.config/kreuzberg/config.toml (user path)
# - Custom paths with --config flag
# - Environment variable overrides with config files
# - All config formats (TOML, YAML, JSON)
# - Read-only mounts
#
# Usage: ./test-docker-config-local.sh [OPTIONS]
# Options:
# --variant core|full|all Test specific variant (default: all)
# --verbose Enable verbose output
# --keep-containers Don't cleanup containers after tests
################################################################################
set -o pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DOCKER_DIR="$(cd "$SCRIPT_DIR/../../docker" && pwd)"
# Color codes
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
# Test configuration
TEST_VARIANT="${TEST_VARIANT:-all}"
IMAGE_NAME="${IMAGE_NAME:-}" # Empty means build from Dockerfile
VERBOSE="${VERBOSE:-false}"
KEEP_CONTAINERS="${KEEP_CONTAINERS:-false}"
TIMEOUT_SECONDS=30
PORT_BASE=18100
TEST_TEMP_DIR="/tmp/kreuzberg-config-test-$$"
# Test tracking
TOTAL_TESTS=0
PASSED_TESTS=0
FAILED_TESTS=0
declare -a FAILED_TEST_NAMES=()
declare -a TESTED_VARIANTS=()
################################################################################
# Helper Functions
################################################################################
log_header() {
echo -e "\n${CYAN}╔════════════════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}$1${NC}"
echo -e "${CYAN}╚════════════════════════════════════════════════════════╝${NC}\n"
}
log_info() {
echo -e "${BLUE}[INFO]${NC} $*"
}
log_success() {
echo -e "${GREEN}[PASS]${NC} $*"
}
log_warning() {
echo -e "${YELLOW}[WARN]${NC} $*"
}
log_error() {
echo -e "${RED}[FAIL]${NC} $*"
}
log_debug() {
if [ "$VERBOSE" = "true" ]; then
echo -e "${YELLOW}[DEBUG]${NC} $*"
fi
}
start_test() {
TOTAL_TESTS=$((TOTAL_TESTS + 1))
local test_num
test_num=$(printf "%02d" $TOTAL_TESTS)
echo ""
echo -e "${CYAN}Test $test_num:${NC} $*"
}
pass_test() {
PASSED_TESTS=$((PASSED_TESTS + 1))
log_success "Test passed"
}
fail_test() {
FAILED_TESTS=$((FAILED_TESTS + 1))
FAILED_TEST_NAMES+=("$1")
log_error "Test failed: $1"
if [ -n "${2:-}" ]; then
log_error " Details: $2"
fi
}
# shellcheck disable=SC2317,SC2329 # Function is invoked via trap EXIT
cleanup() {
log_info "Cleaning up test environment..."
if [ "$KEEP_CONTAINERS" != "true" ]; then
# Stop and remove test containers
docker ps -a --filter "name=kreuzberg-config-test-" --format "{{.Names}}" | while read -r container; do
log_debug "Stopping container: $container"
docker stop "$container" 2>/dev/null || true
docker rm "$container" 2>/dev/null || true
done
else
log_warning "Keeping containers for inspection (use 'docker ps -a' to view)"
fi
# Remove temporary test files
if [ -d "$TEST_TEMP_DIR" ]; then
log_debug "Removing temporary directory: $TEST_TEMP_DIR"
rm -rf "$TEST_TEMP_DIR"
fi
}
trap cleanup EXIT
################################################################################
# Setup Functions
################################################################################
setup_test_environment() {
log_info "Setting up test environment..."
if ! mkdir -p "$TEST_TEMP_DIR"; then
log_error "Failed to create temporary directory"
exit 1
fi
log_debug "Test temp directory: $TEST_TEMP_DIR"
}
verify_docker_available() {
if ! command -v docker &>/dev/null; then
log_error "Docker is not installed or not in PATH"
exit 1
fi
if ! docker ps &>/dev/null; then
log_error "Docker daemon is not running or you don't have permissions"
exit 1
fi
log_info "Docker is available"
}
check_image_exists() {
local image="$1"
if ! docker image inspect "$image" &>/dev/null; then
log_error "Docker image does not exist: $image"
log_error "Please build the image first with: docker build -f $DOCKER_DIR/Dockerfile.${image##*:} -t $image ."
return 1
fi
return 0
}
get_image_name() {
local variant="$1"
if [ -n "$IMAGE_NAME" ]; then
# Use provided image name (CI mode)
echo "$IMAGE_NAME"
else
# Use default naming convention (local mode)
echo "kreuzberg:$variant"
fi
}
################################################################################
# Config File Creation Functions
################################################################################
create_toml_config() {
local file_path="$1"
local port="${2:-8000}"
# Config must be valid ExtractionConfig (deny_unknown_fields).
# Server settings use defaults; ports are mapped via docker -p flag.
cat >"$file_path" <<EOF
use_cache = true
enable_quality_processing = true
[ocr]
backend = "tesseract"
language = "eng"
EOF
log_debug "Created TOML config: $file_path"
}
create_yaml_config() {
local file_path="$1"
local port="${2:-8000}"
# Config must be valid ExtractionConfig (deny_unknown_fields).
# Server settings use defaults; ports are mapped via docker -p flag.
cat >"$file_path" <<EOF
use_cache: true
enable_quality_processing: true
ocr:
backend: "tesseract"
language: "eng"
EOF
log_debug "Created YAML config: $file_path"
}
create_json_config() {
local file_path="$1"
local port="${2:-8000}"
# Config must be valid ExtractionConfig (deny_unknown_fields).
# Server settings use defaults; ports are mapped via docker -p flag.
cat >"$file_path" <<EOF
{
"use_cache": true,
"enable_quality_processing": true,
"ocr": {
"backend": "tesseract",
"language": "eng"
}
}
EOF
log_debug "Created JSON config: $file_path"
}
################################################################################
# Container Testing Functions
################################################################################
run_container() {
local container_name="$1"
local image="$2"
local port="$3"
shift 3
# Separate docker options from command arguments
local docker_opts=()
local cmd_args=()
local after_separator=false
while [ $# -gt 0 ]; do
if [ "$1" = "--" ]; then
after_separator=true
shift
continue
fi
if [ "$after_separator" = true ]; then
cmd_args+=("$1")
else
docker_opts+=("$1")
fi
shift
done
log_debug "Running container: $container_name"
log_debug "Docker opts: ${docker_opts[*]}"
log_debug "Command args: ${cmd_args[*]}"
if ! docker run -d \
--name "$container_name" \
-p "$port:8000" \
"${docker_opts[@]}" \
"$image" \
"${cmd_args[@]}" >/dev/null 2>&1; then
return 1
fi
return 0
}
wait_for_health() {
local port="$1"
local max_wait="${2:-$TIMEOUT_SECONDS}"
local elapsed=0
local interval=1
log_debug "Waiting for service on port $port (timeout: ${max_wait}s)"
while [ "$elapsed" -lt "$max_wait" ]; do
if curl -sf "http://localhost:$port/health" &>/dev/null; then
log_debug "Service became healthy after ${elapsed}s"
return 0
fi
sleep $interval
elapsed=$((elapsed + interval))
done
log_debug "Service did not become healthy within ${max_wait}s"
return 1
}
check_container_running() {
local container_name="$1"
if docker inspect "$container_name" --format='{{.State.Running}}' 2>/dev/null | grep -q "true"; then
return 0
fi
return 1
}
get_container_logs() {
local container_name="$1"
docker logs "$container_name" 2>&1 | tail -20
}
################################################################################
# Test Cases
################################################################################
test_etc_kreuzberg_mount() {
local variant="$1"
start_test "Volume mount to /etc/kreuzberg/kreuzberg.toml (variant: $variant)"
local image
image="$(get_image_name "$variant")"
local port=$((PORT_BASE + TOTAL_TESTS))
local container_name="kreuzberg-config-test-etc-${variant}-$$"
local config_file="$TEST_TEMP_DIR/kreuzberg.toml"
# Create config file
create_toml_config "$config_file" "$port"
# Run container with mount
if ! run_container "$container_name" "$image" "$port" \
--volume "$config_file:/etc/kreuzberg/kreuzberg.toml:ro"; then
fail_test "Failed to start container with /etc/kreuzberg mount"
log_error " Container logs:\n$(get_container_logs "$container_name" 2>/dev/null || echo 'N/A')"
return 1
fi
sleep 2
# Check if container is still running
if ! check_container_running "$container_name"; then
fail_test "Container exited unexpectedly"
log_error " Container logs:\n$(get_container_logs "$container_name")"
return 1
fi
# Wait for service to be healthy
if ! wait_for_health "$port"; then
fail_test "Service failed to start (health check timeout)"
log_error " Container logs:\n$(get_container_logs "$container_name")"
docker stop "$container_name" 2>/dev/null || true
return 1
fi
# Test the health endpoint
if ! curl -sf "http://localhost:$port/health" >/dev/null; then
fail_test "Health endpoint returned non-success status"
docker stop "$container_name" 2>/dev/null || true
return 1
fi
log_success "Service is running and healthy"
docker stop "$container_name" 2>/dev/null || true
pass_test
}
test_app_config_mount() {
local variant="$1"
start_test "Volume mount to /app/.config/kreuzberg/config.toml (variant: $variant)"
local image
image="$(get_image_name "$variant")"
local port=$((PORT_BASE + TOTAL_TESTS))
local container_name="kreuzberg-config-test-app-config-${variant}-$$"
local config_file="$TEST_TEMP_DIR/config.toml"
# Create config file
create_toml_config "$config_file" "$port"
# Run container with mount
if ! run_container "$container_name" "$image" "$port" \
--volume "$config_file:/app/.config/kreuzberg/config.toml:ro"; then
fail_test "Failed to start container with /app/.config mount"
log_error " Container logs:\n$(get_container_logs "$container_name" 2>/dev/null || echo 'N/A')"
return 1
fi
sleep 2
if ! check_container_running "$container_name"; then
fail_test "Container exited unexpectedly"
log_error " Container logs:\n$(get_container_logs "$container_name")"
return 1
fi
if ! wait_for_health "$port"; then
fail_test "Service failed to start (health check timeout)"
log_error " Container logs:\n$(get_container_logs "$container_name")"
docker stop "$container_name" 2>/dev/null || true
return 1
fi
if ! curl -sf "http://localhost:$port/health" >/dev/null; then
fail_test "Health endpoint returned non-success status"
docker stop "$container_name" 2>/dev/null || true
return 1
fi
log_success "Service is running and healthy"
docker stop "$container_name" 2>/dev/null || true
pass_test
}
test_custom_path_with_flag() {
local variant="$1"
start_test "Custom path with --config flag (variant: $variant)"
local image
image="$(get_image_name "$variant")"
local port=$((PORT_BASE + TOTAL_TESTS))
local container_name="kreuzberg-config-test-custom-${variant}-$$"
local config_file="$TEST_TEMP_DIR/custom-config.toml"
local container_path="/app/custom-config.toml"
# Create config file
create_toml_config "$config_file" "$port"
# Run container with custom config path
if ! run_container "$container_name" "$image" "$port" \
--volume "$config_file:$container_path:ro" \
--entrypoint "/usr/local/bin/kreuzberg" \
-- "serve" "--config" "$container_path" "--host" "0.0.0.0"; then
fail_test "Failed to start container with custom --config flag"
log_error " Container logs:\n$(get_container_logs "$container_name" 2>/dev/null || echo 'N/A')"
return 1
fi
sleep 2
if ! check_container_running "$container_name"; then
fail_test "Container exited unexpectedly"
log_error " Container logs:\n$(get_container_logs "$container_name")"
return 1
fi
if ! wait_for_health "$port"; then
fail_test "Service failed to start (health check timeout)"
log_error " Container logs:\n$(get_container_logs "$container_name")"
docker stop "$container_name" 2>/dev/null || true
return 1
fi
if ! curl -sf "http://localhost:$port/health" >/dev/null; then
fail_test "Health endpoint returned non-success status"
docker stop "$container_name" 2>/dev/null || true
return 1
fi
log_success "Service is running and healthy with custom config path"
docker stop "$container_name" 2>/dev/null || true
pass_test
}
test_env_var_overrides() {
local variant="$1"
start_test "Environment variable overrides with config file (variant: $variant)"
local image
image="$(get_image_name "$variant")"
local port=$((PORT_BASE + TOTAL_TESTS))
local container_name="kreuzberg-config-test-env-${variant}-$$"
local config_file="$TEST_TEMP_DIR/env-config.toml"
# Create config file with port 8000
create_toml_config "$config_file" "8000"
# Run container with config mount and environment variable override
if ! run_container "$container_name" "$image" "$port" \
--volume "$config_file:/etc/kreuzberg/kreuzberg.toml:ro" \
--env "KREUZBERG_SERVER_PORT=$port"; then
fail_test "Failed to start container with env var override"
log_error " Container logs:\n$(get_container_logs "$container_name" 2>/dev/null || echo 'N/A')"
return 1
fi
sleep 2
if ! check_container_running "$container_name"; then
fail_test "Container exited unexpectedly"
log_error " Container logs:\n$(get_container_logs "$container_name")"
return 1
fi
if ! wait_for_health "$port"; then
fail_test "Service failed to start (health check timeout)"
log_error " Container logs:\n$(get_container_logs "$container_name")"
docker stop "$container_name" 2>/dev/null || true
return 1
fi
if ! curl -sf "http://localhost:$port/health" >/dev/null; then
fail_test "Health endpoint returned non-success status"
docker stop "$container_name" 2>/dev/null || true
return 1
fi
log_success "Service is running with environment variable overrides"
docker stop "$container_name" 2>/dev/null || true
pass_test
}
test_toml_format() {
local variant="$1"
start_test "TOML config format (variant: $variant)"
local image
image="$(get_image_name "$variant")"
local port=$((PORT_BASE + TOTAL_TESTS))
local container_name="kreuzberg-config-test-toml-${variant}-$$"
local config_file="$TEST_TEMP_DIR/config.toml"
create_toml_config "$config_file" "$port"
if ! run_container "$container_name" "$image" "$port" \
--volume "$config_file:/etc/kreuzberg/kreuzberg.toml:ro"; then
fail_test "Failed to start container with TOML config"
return 1
fi
sleep 2
if ! wait_for_health "$port"; then
fail_test "Service failed to start with TOML config"
docker stop "$container_name" 2>/dev/null || true
return 1
fi
log_success "TOML config format works correctly"
docker stop "$container_name" 2>/dev/null || true
pass_test
}
test_yaml_format() {
local variant="$1"
start_test "YAML config format (variant: $variant)"
local image
image="$(get_image_name "$variant")"
local port=$((PORT_BASE + TOTAL_TESTS))
local container_name="kreuzberg-config-test-yaml-${variant}-$$"
local config_file="$TEST_TEMP_DIR/config.yaml"
create_yaml_config "$config_file" "$port"
if ! run_container "$container_name" "$image" "$port" \
--volume "$config_file:/etc/kreuzberg/kreuzberg.yaml:ro"; then
fail_test "Failed to start container with YAML config"
return 1
fi
sleep 2
if ! wait_for_health "$port"; then
fail_test "Service failed to start with YAML config"
docker stop "$container_name" 2>/dev/null || true
return 1
fi
log_success "YAML config format works correctly"
docker stop "$container_name" 2>/dev/null || true
pass_test
}
test_json_format() {
local variant="$1"
start_test "JSON config format (variant: $variant)"
local image
image="$(get_image_name "$variant")"
local port=$((PORT_BASE + TOTAL_TESTS))
local container_name="kreuzberg-config-test-json-${variant}-$$"
local config_file="$TEST_TEMP_DIR/config.json"
create_json_config "$config_file" "$port"
if ! run_container "$container_name" "$image" "$port" \
--volume "$config_file:/etc/kreuzberg/kreuzberg.json:ro"; then
fail_test "Failed to start container with JSON config"
return 1
fi
sleep 2
if ! wait_for_health "$port"; then
fail_test "Service failed to start with JSON config"
docker stop "$container_name" 2>/dev/null || true
return 1
fi
log_success "JSON config format works correctly"
docker stop "$container_name" 2>/dev/null || true
pass_test
}
test_readonly_mount() {
local variant="$1"
start_test "Read-only mount (variant: $variant)"
local image
image="$(get_image_name "$variant")"
local port=$((PORT_BASE + TOTAL_TESTS))
local container_name="kreuzberg-config-test-readonly-${variant}-$$"
local config_file="$TEST_TEMP_DIR/readonly-config.toml"
create_toml_config "$config_file" "$port"
# Run with read-only mount (explicitly :ro)
if ! run_container "$container_name" "$image" "$port" \
--volume "$config_file:/etc/kreuzberg/kreuzberg.toml:ro"; then
fail_test "Failed to start container with read-only mount"
return 1
fi
sleep 2
if ! check_container_running "$container_name"; then
fail_test "Container exited unexpectedly with read-only mount"
return 1
fi
if ! wait_for_health "$port"; then
fail_test "Service failed to start with read-only mount"
docker stop "$container_name" 2>/dev/null || true
return 1
fi
log_success "Read-only mount works correctly"
docker stop "$container_name" 2>/dev/null || true
pass_test
}
################################################################################
# Test Execution
################################################################################
run_test_suite() {
local variant="$1"
log_header "Testing variant: $(get_image_name "$variant")"
# Check if image exists
if ! check_image_exists "$(get_image_name "$variant")"; then
log_warning "Skipping tests for variant: $variant (image not found)"
return
fi
TESTED_VARIANTS+=("$variant")
# Run all test cases
test_etc_kreuzberg_mount "$variant"
test_app_config_mount "$variant"
test_custom_path_with_flag "$variant"
test_env_var_overrides "$variant"
test_toml_format "$variant"
test_yaml_format "$variant"
test_json_format "$variant"
test_readonly_mount "$variant"
}
print_summary() {
log_header "Test Summary"
local pass_rate=0
if [ $TOTAL_TESTS -gt 0 ]; then
pass_rate=$((PASSED_TESTS * 100 / TOTAL_TESTS))
fi
echo -e "Total Tests: ${CYAN}$TOTAL_TESTS${NC}"
echo -e "Passed Tests: ${GREEN}$PASSED_TESTS${NC}"
echo -e "Failed Tests: ${RED}$FAILED_TESTS${NC}"
echo -e "Pass Rate: ${BLUE}${pass_rate}%${NC}"
echo ""
if [ $FAILED_TESTS -gt 0 ]; then
echo -e "${RED}Failed Tests:${NC}"
for test_name in "${FAILED_TEST_NAMES[@]}"; do
echo " - $test_name"
done
echo ""
fi
if [ ${#TESTED_VARIANTS[@]} -gt 0 ]; then
echo -e "${CYAN}Tested Variants:${NC}"
for variant in "${TESTED_VARIANTS[@]}"; do
echo " - $(get_image_name "$variant")"
done
echo ""
fi
}
################################################################################
# Main Entry Point
################################################################################
main() {
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
--variant)
TEST_VARIANT="$2"
shift 2
;;
--image)
IMAGE_NAME="$2"
shift 2
;;
--verbose)
VERBOSE=true
shift
;;
--keep-containers)
KEEP_CONTAINERS=true
shift
;;
--help)
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Options:"
echo " --variant VARIANT Test specific variant (core, full, or all) [default: all]"
echo " --image IMAGE Use pre-built image instead of building [default: build from Dockerfile]"
echo " --verbose Enable verbose output"
echo " --keep-containers Don't cleanup containers after tests"
echo " --help Show this help message"
exit 0
;;
*)
log_error "Unknown option: $1"
exit 1
;;
esac
done
log_header "Docker Configuration Volume Mount Test Suite"
log_info "Configuration:"
log_info " Variant: $TEST_VARIANT"
log_info " Verbose: $VERBOSE"
log_info " Keep Containers: $KEEP_CONTAINERS"
log_info " Port Range: $PORT_BASE-$((PORT_BASE + 99))"
log_info ""
# Verify Docker is available
verify_docker_available
# Setup test environment
setup_test_environment
# Run tests based on variant selection
case "$TEST_VARIANT" in
core)
run_test_suite "core"
;;
full)
run_test_suite "full"
;;
all)
run_test_suite "core"
run_test_suite "full"
;;
*)
log_error "Invalid variant: $TEST_VARIANT (must be 'core', 'full', or 'all')"
exit 1
;;
esac
# Print summary
print_summary
# Exit with appropriate code
if [ $FAILED_TESTS -eq 0 ]; then
log_success "All tests passed!"
exit 0
else
log_error "Some tests failed"
exit 1
fi
}
main "$@"