Development

Testing

How to run and write tests for tileserver-rs

tileserver-rs has a comprehensive test suite covering unit tests, integration tests, and end-to-end rendering tests.

Running Tests

All Tests

cargo test

Specific Test Categories

# Unit tests (overlay parsing, color parsing, etc.)
cargo test render::overlay

# Integration tests (config loading, source validation)
cargo test --test integration

# E2E rendering tests (snapshot testing)
cargo test --test e2e_rendering

Test Structure

src/
├── render/
│   └── overlay.rs      # Unit tests for overlay parsing (52 tests)
│
tests/
├── config.test.toml    # Test configuration using data/ fixtures
├── integration.rs      # Integration tests (45 tests)
├── e2e_rendering.rs    # E2E rendering tests (13 tests)
└── snapshots/          # Snapshot artifacts
    ├── *.snap          # JSON snapshots (insta)
    └── *.png           # Generated test images

Test Fixtures

The test suite uses fixtures from the data/ directory:

data/
├── fonts/
│   ├── Noto Sans Medium/    # PBF glyph files
│   └── Noto Sans Regular/
├── styles/
│   └── protomaps-light/
│       └── style.json
└── tiles/
    ├── protomaps-sample.pmtiles
    └── zurich_switzerland.mbtiles

Snapshot Testing

We use insta for snapshot testing. Snapshots capture expected JSON output and rendered images for regression testing.

Workflow

# Run tests and see snapshot diffs
cargo insta test

# Interactive review of changes
cargo insta review

# Accept all new/changed snapshots
cargo insta test --accept

Installing cargo-insta

cargo install cargo-insta

Unit Tests

Unit tests are embedded in the source files using #[cfg(test)] modules.

Example: Overlay Parsing Tests

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_parse_hex_color_6digit() {
        let color = parse_hex_color("ff5500").unwrap();
        assert_eq!(color, [255, 85, 0, 255]);
    }

    #[test]
    fn test_parse_marker_with_label() {
        let marker = parse_marker("pin-s-a+ff0000(10,20)").unwrap();
        assert_eq!(marker.label, Some("a".to_string()));
        assert_eq!(marker.color, [255, 0, 0, 255]);
    }
}

Integration Tests

Integration tests verify the system works correctly with real tile sources and configurations.

Test Categories

  • Config Loading: Validates TOML configuration parsing
  • Source Loading: Tests PMTiles and MBTiles source initialization
  • Style Validation: Verifies style JSON structure and references
  • Security Tests: Path traversal prevention, SQL injection prevention
  • Input Validation: Coordinate bounds, zoom levels, tile formats

Example: Source Loading Test

#[tokio::test]
async fn test_load_sources_from_config() {
    let config = Config::load(Path::new("tests/config.test.toml")).unwrap();
    let manager = SourceManager::from_config(&config).await.unwrap();
    
    assert!(manager.get_source("protomaps").is_some());
    assert!(manager.get_source("zurich").is_some());
}

E2E Rendering Tests

E2E tests generate actual PNG images and compare them against stored snapshots.

What Gets Tested

  • Marker rendering (different sizes: s/m/l)
  • Path rendering (lines, polygons with fill)
  • Combined overlays (multiple markers + paths)
  • Google Polyline decoding and rendering
  • JSON API responses (TileJSON, style info)

Generated Artifacts

Test images are saved to tests/snapshots/:

FileDescription
marker_red_small.pngSmall red marker
marker_green_*.pngGreen markers (s/m/l sizes)
path_blue_diagonal.pngBlue diagonal path
path_magenta_diamond.pngDiamond shape with fill
polyline_google_example.pngDecoded Google polyline
combined_paths_markers.pngMultiple overlays

Writing New Tests

Adding Unit Tests

Add tests in the same file as the code being tested:

// src/render/overlay.rs

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_your_new_feature() {
        // Test implementation
    }
}

Adding Integration Tests

Add tests to tests/integration.rs:

#[tokio::test]
async fn test_your_integration_scenario() {
    let config = Config::load(Path::new("tests/config.test.toml")).unwrap();
    // Test against real fixtures
}

Adding E2E Rendering Tests

Add tests to tests/e2e_rendering.rs:

#[test]
fn test_render_new_overlay() {
    let overlays = vec![
        parse_marker("pin-s+ff0000(10,20)").unwrap(),
    ];
    
    let image = render_overlay_to_image(&overlays, 256, 256);
    save_test_image(&image, "new_overlay.png");
    
    // Verify image properties
    assert!(image.width() > 0);
}

CI Integration

Tests run automatically on every pull request via GitHub Actions. The CI workflow:

  1. Runs cargo test for all test categories
  2. Verifies snapshots match committed versions
  3. Fails if any test fails or snapshots differ

To update snapshots after intentional changes:

cargo insta test --accept
git add tests/snapshots/
git commit -m "test: update snapshots"