Does a new store work for every consumer, before and after it is registered? This package
checks the store itself, titiler-eopf on both deployments (/rstaging, 0.12 code; /raster,
0.11.1), and the registered STAC item. It sits beside the GDAL suite in this repo and
doesn't touch it.
Slice 1 of the plan. Benchmarks and the OpenLayers browser runner come in later slices.
cd acceptance
uv sync # core
uv sync --extra reader # + titiler-eopf 0.12.4's own reader, for TR01 and the integration tests# 1. Dry run: what would run, the URL forms per endpoint, and the request bound. Sends nothing.
uv run eopf-accept plan --collection sentinel-3-olci-l1-efr-staging --stage registered \
--store https://s3.explorer.eopf.copernicus.eu/esa-zarr-sentinel-explorer-fra/tests-output/sentinel-3-olci-l1-efr-staging/<item>.zarr \
--item <item> --endpoint rstaging --endpoint raster
# 2. Scratch store, before registration (AWS_* env for the -tests bucket):
uv run eopf-accept run --collection sentinel-3-olci-l1-efr-staging --stage scratch \
--store s3://esa-zarr-sentinel-explorer-tests/<run>/sentinel-3-olci-l1-efr-staging/<item>.zarr --groups store,reader
# 3. Registered item, read-only against production (prefer the s3:// origin: the gateway caches for 1 h):
uv run eopf-accept run …same as plan…
# A local or ad-hoc endpoint: NAME=URL@REPORTED_VERSION:API (API 0.11 or 0.12)
uv run eopf-accept run … --endpoint local=http://127.0.0.1:[email protected]:0.12Reports go to ~/DevDS/EOPF/acceptance_runs/<UTC>_<collection>_<stage>_<label>/ (report.md,
run.json, requests.jsonl), outside this repo. Exit codes: 0 no FAIL, 1 FAIL, 2 usage or
budget refusal, 3 VOID (a cold request was a cache HIT, or a check crashed: no verdict).
Branch products published to a public bucket (e.g. EODC's continuous-integration bucket,
one prefix per data-model PR branch) are scratch stores, read without credentials:
B=https://objects.eodc.eu/continuous-integration/eopf-geozarr/branches/<branch>
uv run eopf-accept plan --collection sentinel-2-l2a --stage scratch --store $B/<product>.zarr --groups store
uv run eopf-accept run --collection sentinel-2-l2a --stage scratch --store $B/<product>.zarr --groups store --label <branch>-<product>- The https form reads unsigned and ignores every
AWS_*variable. Ans3://store followsAWS_ENDPOINT_URL[_S3]instead, which a shell may still hold from another run:planand the report say where reads go (store reads:). Unsigned s3:// needsAWS_SKIP_SIGNATURE=true. - At
--stage scratch, an unsigned reader counts a 403 as a missing key: a bucket without list rights answers 403, not 404. The report lists each such key, because a key refused for another reason looks the same. A signed reader, and any registered run, still fails on 403. --collectiononly picks the config (configs/).sentinel-1-l1-grdis forgeneric_rechunkeroutput (a draft): it expects theoverviewsdata-model#249 lists, which the #292 products don't have yet, so it FAILs on them. A check with nothing to examine reports SKIP, never PASS.
- GET only. No code path writes to a store, a STAC API or a cache.
- The request bound is a feature of the tool. Each check declares an upper bound.
runrefuses to start if the sum exceeds--max-requests(default 300), and every request (HTTP or store) spends the budget before it is sent, so request N+1 never leaves the process.tests/test_budget_and_cli.pyproves both against a local server. - Every titiler request carries a fresh
cb=<run nonce>-<seq>, so nothing is served from the tile cache. A cache HIT on a cold request makes the runVOID. - Physical requests are what's counted: obstore runs with
max_retries=0and httpx follows no redirects (tested against a server answering 503 and 302). - Titiler checks run only against endpoints named with
--endpoint.--stage scratchrefuses production hosts. TI00 refuses an endpoint whose reported version or route fingerprint (per-asset routes ⇔ the 0.12 API) differs from the config (fail-closed). - Side effect at the registered stage: each cache-busted render makes titiler write one tile
entry to the shared Redis/S3 cache.
plansays so.
Each ID's prefix says what it exercises, and --groups picks them:
- ST store and HT host (
store): the store's own metadata and data, and how its bucket serves it - GR generic rechunker (
store, for configs with a[generic]table): thegeneric_rechunkerlayout and CF packing - TR titiler reader (
reader): titiler-eopf's ownGeoZarrReader, in-process - TI titiler (
titiler): each titiler-eopf endpoint over HTTP - RG registration (
registration): the registered STAC item
Numbers are IDs, not an order; gaps are unused IDs.
| id | checks | catches |
|---|---|---|
| ST01 | consolidated metadata on the root (or each sub-root, consolidation = "subroot", data-model#291 Ex.2), the opened, multiscales and consolidated_groups; levels listed. A level group without its own consolidated metadata is listed, not warned (data-model#335, issue #8) |
unconsolidated groups → 0.12 500 (data-pipeline#446); the validator blind spot |
| ST03 | what titiler 0.12 reads without fallback: conventions, bbox, CRS, spatial:dimensions, shape+transform for min/max zoom, a transform per level; coarsening order |
OLCI tilejson 500 (data-model#303); level tiles 500 |
| ST04 | opened groups visible to titiler-eopf (_get_groups rule); no undeclared spatial:/proj: keys |
invisible groups (scl) |
| ST11 | zarr_conventions declarations equal the v0.1 schema consts (WARN, or FAIL with strict_declarations) |
stale names/URLs (inspect.geozarr.org) |
| ST12 | spatial/proj/multiscales attribute contents against geozarr-toolkit's models (WARN); spatial:dimensions required on arrays only, as spatial v0.1 says |
invalid convention attributes |
| ST07 | chunk/shard layout; tile size ol/source/GeoZarr will pick, per consumer ol version |
the ≤10.10 64 px fallback: WARN naming the upgrade when ol 10.11 avoids it, FAIL when 10.11 (the newest modelled release) still has it |
| ST08 | dtype allow-list; compression ratio of the centre chunk | heavy float64 stores |
| ST09 | the finest level has data where the coarsest level does | empty conversions |
| HT02 | each opened group, rooted where titiler roots it, opens with listing forbidden | PROPFIND 405 over HTTP |
| TR01 | titiler-eopf 0.12's own GeoZarrReader: open, zooms, tiles (scratch only) |
anything the server would 500 on |
| TI00–TI10 | per endpoint: version + route fingerprint, /info, tilejson without zoom params, decoded tiles per store level scaled by footprint coverage (a flat tile compared with others in the footprint), RGB channels, cold cache, viewer, URL-form contract, fit-bounds vs minzoom, and where a config names a second stretch, the same levels under it (WARN if flat) | C1, C8, C13, C14 |
| RG04/05/07/08 | the item's links render (xyz as PNG, the thumbnail as any image type the suite can decode, issue #8); do they also work on 0.12 (flip readiness); item newer than its store; asset hrefs point exactly at this store's groups | broken links, the /raster flip, stale registrations |
| GR02 | generic_rechunker layout: each dimension chunked to min(spatial_chunk, size), one shard per array (FAIL); 2-D coordinates left uncompressed, and arrays written like coordinates that nothing declares as one (WARN) |
rechunking or sharding that drifts from data-model#292's rule |
| GR03 | CF packing: CF scale_factor/add_offset and the scale_offset codec never both; _FillValue fits the dtype (FAIL); a valid range the dtype can't hold (WARN, named first in the summary, issue #8); zarr fill_value ≠ CF _FillValue (WARN) |
double decoding; nodata that readers mask differently |
Per-collection settings live in configs/*.toml; all but OLCI are drafts. For products whose
sub-root name differs per product (S1), subroot_asset names the asset of the root's own
stac_discovery that points into it, and {subroot} in open_groups, multiscales_groups and
consolidated_groups is replaced with it. GR02/GR03 run only for configs with a [generic]
table (spatial_chunk, sharding).
uv run pytest # fixtures only; tests/test_real_titiler.py also runs the real 0.12 app when the reader extra is installed