Tika-Server REST UAT Script

A portable shell script that exercises the tika-server REST surface against an already-running server. The same script is used as the docker image smoke test, the e2e integration test, and as part of the source-release verification.

Where it lives

release-tools/uat/
├── run-uat.sh            # the script
└── test-files/
    ├── testPDF.pdf
    ├── testHTML.html
    ├── testOCR_spacing.png
    └── test_recursive_embedded.docx

All four files must be present — the script exits 2 if any is missing. Point TIKA_UAT_TEST_FILES at another directory to override the location.

What it covers

Roughly 30 REST endpoint checks across the default-mode endpoints, header behavior, and error handling — the same surface enumerated in the manual walkthrough at Tika-Server Integration Testing, translated to bash + curl assertions.

Coverage includes:

  • /version, /parsers, /detectors, /mime-types (introspection)

  • /detect (mime detection)

  • /tika/text, /tika/html, /tika/xml, /tika/json (parse)

  • /meta, /meta/{field} (metadata)

  • /rmeta, /rmeta/text (recursive metadata)

  • /unpack/all (embedded extraction; verifies the response is a valid zip)

  • /language

  • /meta/form, /rmeta/form (multipart variants)

  • allowPerRequestConfig=false gating, both enforcement points: the path-based filter (/meta/config, /rmeta/config, /tika/config, /unpack/all/config all return 403) and the content-based gate (a multipart config part on /unpack returns 403). Note there is no /unpack/config path — the config-variant is /unpack/all/config.

  • /status is not registered by default (returns 404 unless explicitly listed under endpoints)

  • A removed X-Tika-OCRskipOcr header is ignored rather than rejected (asserts HTTP 200 only; the X-Tika-OCR*/X-Tika-PDF* families no longer exist)

  • Handler selection: /tika/text is body-only (no <title> in the output), /tika/json with no handler named matches /tika/json/md (markdown is the default), and an unrecognized handler name returns 400 rather than silently falling back

  • /meta with Accept: application/rdf+xml returns XMP — content negotiation, not a distinct path. Worth a smoke check because this shipped broken in 4.0.0-alpha-1 and beta-1: the resource failed to load and the endpoint 406’d

  • Removed endpoints stay removed (/translate/* returns 404)

  • 404 / 405 error handling

  • Exception reporting on a corrupt document (TIKA-4848): /rmeta embeds tk:exception:container-exception at 200; raw /tika returns 422 with a content-only body; /unpack and /meta/{field} carry the container exception in their 422 bodies, asserted free of this server’s own frames (PipesParsingHelper, MetadataResource)

  • OCR (PUT /tika/text of an image whose text only exists as pixels): skipped when the server has no tesseract (the minimal image / a plain java -jar server), and a hard failure when TIKA_UAT_REQUIRE_OCR=1 is set. docker-tool.sh test-uat sets that env var for the -full image, so OCR is asserted there and skipped for the minimal image.

The inference pass

A second run, against a server started on release-tools/uat/uat-inference-config.json, exercises what the default-mode run cannot: the engine wiring between tika-server, its forked parse worker and an inference engine. Nothing real is called. MockInferenceServer.java in the same directory stands in for a hosted OCR model and an embedding model; it speaks the two OpenAI-shaped endpoints Tika’s engines use plus the health check, has no dependencies beyond the JDK, and runs from source (java release-tools/uat/MockInferenceServer.java 18080 --flaky). With --flaky it refuses every other request with a 429 first, the way a concurrency-capped engine answers, so the pass proves the client’s retry as well.

With TIKA_UAT_INFERENCE=1 the script adds:

  • T40/T41: the mock OCR model is the configured text recognizer; its markdown comes back as text, and the HTML table it emits (document-OCR models write tables that way) is parsed into a <table>, not escaped.

  • T42/T43: the IMAGES binding puts a vector on an image, the TEXT binding one on a document’s text, on /rmeta.

  • T44/T45: the per-request switches, text-recognizers.enabled: false and an inference.bindings subset, through /tika/config and /rmeta/config (allowPerRequestConfig is on in the inference config, so the T18 gating checks are skipped in this pass; the default-mode run covers them).

  • T46: the MEDIA bindings cut a 3 s clip into one segment per channel sharing a correlator. This needs ffmpeg on the server: a hard failure with TIKA_UAT_REQUIRE_MEDIA=1 (the -full image ships it), a skip otherwise.

  • T47: the mock’s /stats (reached at TIKA_UAT_MOCK_URL, default http://localhost:18080) shows refusals, so the checks above passed only because the client retried. A mock started without --flaky reports no refusals and T47 is a skip. docker-tool.sh and the e2e test start it flaky only when the server under test carries the retry (TIKA-4912), detected from its tika-http-jdk jar; TIKA_UAT_MOCK_FLAKY=1 or =0 overrides that in the tool.

The config names the engine key as ${env:TIKA_UAT_MOCK_KEY}, so the pass also proves environment interpolation reaches the forked workers. docker-tool.sh test-uat runs this pass against both images after the default-mode run, with the mock on the host and the container reaching it as host.docker.internal; the RunUatSmokeTest e2e test runs it against a forked java -jar server.

docker-tool.sh test-uat-snapshot builds the two images the docker-snapshot workflow publishes, from Dockerfile.snapshot over this checkout’s tika-server-standard zip, and runs both passes against each. No download and no signature check, so it works on any build, and what it tests is the image itself: that is how the full image’s ffmpeg is verified before a release, and it is the run that showed the snapshot Dockerfile had been missed when ffmpeg was added to the release one.

The allowPipes=false "refuse to start" gating and the stale-enableUnsecureFeatures upgrade behavior are start-time properties, so they are not in this script (which runs against an already-running server) — they are covered by the org.apache.tika.server.e2e.RunUatSmokeTest e2e test instead.

Running it

The script takes a URL pointing at a running tika-server. It does not start or stop the server itself.

release-tools/uat/run-uat.sh [base-url]
# default: http://localhost:9998

Exit code: 0 on all-pass, 1 on any failure. Failed checks print the expected pattern and a truncated response body.

Against the unpacked server distribution zip

unzip tika-server-standard-<VERSION>.zip -d /tmp/tika-server-dist
cd /tmp/tika-server-dist
java -jar tika-server-standard-<VERSION>.jar -p 9998 -h localhost &
sleep 12
<tika-checkout>/release-tools/uat/run-uat.sh

Against the Docker image

The docker-tool.sh test-uat subcommand wraps starting each container, waiting for /version, running the UAT, and stopping the container. It runs against both the minimal and the -full image:

cd tika-server/docker-build
./docker-tool.sh test-uat <DOCKER_VERSION>

As part of the e2e tests (CI)

The Maven module tika-e2e-tests/tika-server unpacks the distribution zip, forks java -jar tika-server-standard-<VERSION>.jar, and invokes this script via org.apache.tika.server.e2e.RunUatSmokeTest. The CI workflow .github/workflows/main-jdk17-build.yml runs it on pull requests to main (its e2e-tests job installs the full reactor, then runs mvn -pl :tika-e2e-tests-server,:tika-grpc-e2e-test clean verify -Pe2e). -pl must name the leaf modules: tika-e2e-tests is an aggregator pom, and Maven does not pull in a selected aggregator’s children.

When to use it

  • Pre-vote release verification. Unpack tika-server-standard-<VERSION>.zip from dist/dev and run the UAT against it. Catches packaging regressions before the vote thread starts.

  • Pre-publish docker verification. Run via docker-tool.sh test-uat after building a new image and before tagging it for release.

  • Local development sanity check. When changing anything in tika-server-core or the server assembly descriptor, run the UAT against the build output to confirm you didn’t regress endpoint behavior.

  • Adding new endpoints. When a new REST endpoint lands, add a corresponding check to the script so future regressions get caught.

Platform notes

The script is bash + curl + unzip. It’s skipped automatically on Windows by the e2e test (no bash). On Linux/macOS it runs as-is. No external dependencies beyond the standard tooling.