A Quarkus-based command-line application for automated compatibility testing of the docling-serve-api client library against multiple versions of the Docling Serve container image.
This application automatically tests docling-java client compatibility by:
- Fetching available Docker image tags from the Docling Serve container registry
- Starting Docling Serve containers for each version tag
- Executing conversion requests against each container
- Validating responses and health checks
- Generating detailed markdown reports
- Optionally creating GitHub issues for failures
This application leverages the following frameworks and libraries:
- Quarkus - Supersonic Subatomic Java framework for building cloud-native applications
| Extension | Purpose | Documentation |
|---|---|---|
| Picocli | Command-line interface framework with argument parsing and help generation | Quarkus Picocli Guide |
| Config YAML | YAML-based configuration support (application.yml) |
Quarkus YAML Configuration |
| ArC | Dependency injection (CDI implementation) | Quarkus CDI Reference |
| SmallRye Config | Type-safe configuration mapping (@ConfigMapping) |
Quarkus Configuration Guide |
| REST Client Jackson | Reactive REST client with Jackson serialization (for registry API calls) | Quarkus REST Client Guide |
| Qute | Server-side template engine (generates markdown reports) | Quarkus Qute Guide |
| JUnit 5 | Testing framework with Quarkus extensions | Quarkus Testing Guide |
| WireMock | HTTP mocking for integration tests | Quarkus WireMock Extension |
| GitHub API | GitHub REST API client (for automated issue creation) | Quarkus GitHub API Extension |
- AssertJ - Fluent assertions for validation and testing
- Docling Testcontainers - Spins up ephemeral Docling Serve Docker containers for each version test
- Semver4j - Semantic versioning parsing and filtering
- SmallRye Mutiny - Reactive programming for parallel test execution
docling-serve-client- Java HTTP client for Docling Serve APIdocling-testcontainers- Testcontainers wrapper for Docling Servedocling-core- Core Docling document model
The application starts via Quarkus Picocli integration. The VersionTestsCommand class is annotated with @Command and implements Runnable. It defines CLI options:
-p, --parallelism: Number of concurrent container tests (default: 1)-i, --image: Docker image to test (default:docling-project/docling-serve)-r, --registry: Container registry URL (default:ghcr.io)-o, --output: Results output directory (default:results/)-t, --tags: Explicit list of tags to test (fetches all if omitted)-e, --exclude-tags-regex: Regex pattern to exclude tags-g, --create-github-issue: Create GitHub issue on test failure-c, --cleanup-container-images: Remove Docker images after testing
If --tags is not provided, the application queries the container registry:
- Authentication: Calls the registry's token endpoint (
/token?scope=repository:{image}:pull) - Fetch Tags: Paginates through
/v2/{image}/tags/list(1000 tags per page) - Filter Versions: Extracts semantic version tags (e.g.,
v1.13.0) and excludes non-version tags - Apply Exclusions: Filters out tags matching
--exclude-tags-regex
The GHCRClient is a Quarkus REST Client interface (@RegisterRestClient) with declarative HTTP methods.
For each tag, the TagsTester service:
-
Spin Up Container: Creates a
DoclingServeContainer(Testcontainers wrapper) for the specific tagvar containerConfig = DoclingServeContainerConfig.builder() .image("{registry}/{image}:{tag}") .startupTimeout(Duration.ofMinutes(5)) .build(); var doclingContainer = new DoclingServeContainer(containerConfig); doclingContainer.start();
-
Health Check: Validates the
/healthendpoint returns{"status": "ok"} -
Execute Conversion: Sends a test conversion request
- Source:
https://docling.ai(viaHttpSource) - Output Formats: Markdown, JSON, Text
- Options:
abortOnError=true,includeImages=true
- Source:
-
Assertions: Uses AssertJ to validate:
- Response is not null
- No errors in response
- All output formats (markdown, text, JSON) are non-empty
- JSON content deserializes to
DoclingDocument
-
Capture Logs: Retrieves container logs via
doclingContainer.getLogs() -
Cleanup: Optionally removes the Docker image to save disk space
-
Record Result: Stores outcome as
TagTestResult(success or failure with stack trace)
Parallelization is achieved via SmallRye Mutiny (Multi.transformToUniAndMerge), distributing work across a fixed thread pool.
Results are processed by two handlers (run in parallel via WorkParallelizer):
Generates a comprehensive markdown report using Qute templates:
- Template:
results.md - Output:
{output-dir}/results-{timestamp}/results.md - Content:
- Summary table with ✅/❌ icons per tag
- Expandable detail sections for each tag:
- Success/failure message
- Full stack trace (if failed)
- Container logs
If --create-github-issue is enabled and at least one test fails:
- Reads
GITHUB_TOKENfrom environment - Creates an issue in the configured repository (default:
docling-project/docling-java) - Issue body contains the markdown report
- Labels:
automation,area:docling-serve
Configured via @ConfigMapping:
docling-version-tester:
github:
issue-creation:
issue-org: docling-project # default
issue-repo: docling-java # defaultThe application uses Quarkus Config with YAML support (application.yml):
- REST Client: Configures the
github-container-registryclient base URL (https://ghcr.io) - WireMock: Dev/test mode uses WireMock to mock registry responses
- Logging: Configures log levels for Testcontainers, Docker client, etc.
- Profiles:
%dev,test: Uses WireMock onlocalhost%no-wiremock: Disables WireMock for real registry calls
The application includes unit tests for:
- Command Options: Validates Picocli argument parsing (
VersionTestsCommandOptionTests) - Domain Models: Tests
TagTestResultserialization (TagTestResultTests) - GHCR Client: Mocks registry API with WireMock (
GHCRClientTests)
Tests use:
- Quarkus JUnit (
@QuarkusTest) - Quarkus WireMock for HTTP mocking
- AssertJ for fluent assertions
Run the CLI with specific arguments:
# Test specific tags
./gradlew :docling-version-tests:quarkusDev -Dquarkus.args="-t v1.13.0 v1.12.0 -p 2"
# Test all tags from GHCR
./gradlew :docling-version-tests:quarkusDev -Dquarkus.args="-p 4 --exclude-tags-regex '.*rc.*'"GitHub Actions workflow (version-tests.yml) runs this application on a schedule:
- name: Run version tests
run: |
./gradlew :docling-version-tests:build
java -jar docling-testing/docling-version-tests/build/quarkus-app/quarkus-run.jar \
--parallelism 2 \
--output results \
--create-github-issue
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}- Java 17+ (tested on 17, 21, 25)
- Docker or Podman (for Testcontainers)
- Gradle 8.5+
- Optional: GraalVM for native compilation
- Optional:
GITHUB_TOKENenvironment variable (for issue creation)
Results are written to {output-dir}/results-{timestamp}/:
results/
└── results-2026-03-02T14-30-00.123456Z/
└── results.md
Example results.md excerpt:
# Results for ghcr.io/docling-project/docling-serve as of 2026-03-02T14:30:00.123456Z
| Tag | Result | Details |
| --- | ------ | ------- |
| v1.13.0 | ✅ SUCCESS | [Click for run details](#v1.13.0-details) |
| v1.12.0 | ❌ FAILURE | [Click for run details](#v1.12.0-details) |View detailed coverage reports for this module on Codecov.
docling-version-tests/
├── src/
│ ├── main/
│ │ ├── java/ai/docling/client/tester/
│ │ │ ├── VersionTestsCommand.java # CLI entrypoint
│ │ │ ├── client/
│ │ │ │ ├── RegistryClient.java # Registry API interface
│ │ │ │ ├── RegistryClientFactory.java # Client factory
│ │ │ │ └── ghcr/
│ │ │ │ └── GHCRClient.java # GHCR REST client
│ │ │ ├── config/
│ │ │ │ └── Config.java # Type-safe config
│ │ │ ├── domain/
│ │ │ │ ├── Tags.java # Tag domain model
│ │ │ │ ├── TagsTestRequest.java # Test request
│ │ │ │ ├── TagsTestResults.java # Test results
│ │ │ │ └── TagTestResult.java # Single tag result
│ │ │ └── service/
│ │ │ ├── TagsTester.java # Core testing logic
│ │ │ ├── WorkParallelizer.java # Parallel execution
│ │ │ └── results/
│ │ │ ├── ResultsHandler.java # Handler interface
│ │ │ ├── ResultsHandlers.java # Handler aggregator
│ │ │ ├── MarkdownFileResultsHandler.java
│ │ │ └── GithubIssueResultsHandler.java
│ │ └── resources/
│ │ ├── application.yml # Quarkus config
│ │ └── templates/
│ │ └── results/
│ │ └── results.md # Qute template
│ └── test/
│ ├── java/ # Unit tests
│ └── resources/
│ └── wiremock/ # WireMock stubs
└── build.gradle.kts
- Create interface extending
RegistryClientinclient/ - Annotate with
@RegisterRestClient(configKey = "my-registry") - Implement
getTokenForImageandgetTagsmethods - Add configuration to
application.yml:quarkus: rest-client: my-registry: url: https://my-registry.io
- Update
RegistryClientFactoryto return your client
Edit the Qute template at src/main/resources/templates/results/results.md. Qute syntax:
{#each results.results}
Tag: {it.tag}, Status: {it.result.status.name()}
{/each}This application is part of the docling-java project. See LICENSE for details.
See CONTRIBUTING.md for contribution guidelines. All commits must follow Conventional Commits.