test(manifest-consistency): ensure hurl tests are ok

This commit is contained in:
Edouard Vanbelle
2026-08-23 20:44:46 +02:00
parent 8d696ccc60
commit a847e0fd2f
2 changed files with 117 additions and 46 deletions
+12 -6
View File
@@ -225,9 +225,10 @@ jsonpath "$.outcome.count" exists
# Step 4c — Trigger `consistency_batch`. Coordinator (plain # Step 4c — Trigger `consistency_batch`. Coordinator (plain
# JobHandler) — snapshots the registry, filters names # JobHandler) — snapshots the registry, filters names
# ending `_consistency`, sequentially triggers each. # ending `_consistency`, sequentially triggers each.
# `outcome.count` = number of children dispatched (5 as # `outcome.count` = number of children dispatched (6 as
# of Slice 10: drives + folders + files + blobs + # of the copy_folder_ref_count fix: drives + folders +
# backend). `extra.per_check` carries a per-child outcome # files + blobs + manifests + backend). `extra.per_check`
# carries a per-child outcome
# map. Batch itself always returns ok — child failures # map. Batch itself always returns ok — child failures
# live inside per_check. `?deep=true` propagates as # live inside per_check. `?deep=true` propagates as
# `extra.deep`. # `extra.deep`.
@@ -239,15 +240,20 @@ HTTP 200
[Asserts] [Asserts]
jsonpath "$.ok" == true jsonpath "$.ok" == true
jsonpath "$.outcome.outcome" == "ok" jsonpath "$.outcome.outcome" == "ok"
jsonpath "$.outcome.count" == 5 jsonpath "$.outcome.count" == 6
jsonpath "$.outcome.extra.deep" == true jsonpath "$.outcome.extra.deep" == true
jsonpath "$.outcome.extra.ok" == 5 jsonpath "$.outcome.extra.ok" == 6
jsonpath "$.outcome.extra.err" == 0 jsonpath "$.outcome.extra.err" == 0
# per_check is keyed by child job name. # per_check is keyed by child job name. `manifests_consistency` was added
# by the copy_folder_ref_count fix — see docs/plan/derived-blobs.md and
# `[[bug_dual_refcount_divergence]]` for why the second counter needed
# its own tenant. Auto-picked by `consistency_batch` via `.ends_with(
# "_consistency")` (no explicit list in the batch service).
jsonpath "$.outcome.extra.per_check.drives_consistency.outcome" == "ok" jsonpath "$.outcome.extra.per_check.drives_consistency.outcome" == "ok"
jsonpath "$.outcome.extra.per_check.folders_consistency.outcome" == "ok" jsonpath "$.outcome.extra.per_check.folders_consistency.outcome" == "ok"
jsonpath "$.outcome.extra.per_check.files_consistency.outcome" == "ok" jsonpath "$.outcome.extra.per_check.files_consistency.outcome" == "ok"
jsonpath "$.outcome.extra.per_check.blobs_consistency.outcome" == "ok" jsonpath "$.outcome.extra.per_check.blobs_consistency.outcome" == "ok"
jsonpath "$.outcome.extra.per_check.manifests_consistency.outcome" == "ok"
jsonpath "$.outcome.extra.per_check.backend_consistency.outcome" == "ok" jsonpath "$.outcome.extra.per_check.backend_consistency.outcome" == "ok"
+100 -35
View File
@@ -62,12 +62,23 @@ token: jsonpath "$.access_token"
# ───────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────
# Step 2 — Baseline sweep: run `blobs_consistency` before we # Step 2 — Baseline sweeps: run BOTH consistency tenants that
# touch anything and capture the finding count. Later # check ref_count invariants and capture their finding
# sweeps assert equality with this baseline instead of # counts. Later checkpoints assert equality with these
# `== 0` — so a stale finding from a previous test's # baselines instead of `== 0` — so stale findings from
# leftover state doesn't flunk this test, only NEW # previous tests don't flunk this one; only NEW drift
# drift introduced by our copy/delete does. # introduced by our copy/delete does.
#
# Two tenants because there are two counters (see
# `[[bug_dual_refcount_divergence]]`):
#
# - `blobs_consistency` — reconciles
# `storage.blobs.ref_count` against its auditor
# formula. Catches chunk-level drift.
# - `manifests_consistency` — reconciles
# `storage.chunk_manifests.ref_count` against its
# auditor formula. Catches whole-file drift (the
# path the FE + `/api/dedup/check` surface reads).
# #
# Trigger returns `outcome.count = stats.finding_count` # Trigger returns `outcome.count = stats.finding_count`
# for recoverable tenants (see # for recoverable tenants (see
@@ -81,7 +92,18 @@ Authorization: Bearer {{token}}
HTTP 200 HTTP 200
[Captures] [Captures]
baseline_findings: jsonpath "$.outcome.count" baseline_blobs_findings: jsonpath "$.outcome.count"
[Asserts]
jsonpath "$.ok" == true
jsonpath "$.outcome.outcome" == "ok"
POST {{base_url}}/api/admin/jobs/manifests_consistency/trigger
Authorization: Bearer {{token}}
HTTP 200
[Captures]
baseline_manifests_findings: jsonpath "$.outcome.count"
[Asserts] [Asserts]
jsonpath "$.ok" == true jsonpath "$.ok" == true
jsonpath "$.outcome.outcome" == "ok" jsonpath "$.outcome.outcome" == "ok"
@@ -184,12 +206,15 @@ jsonpath "$.ref_count" == 2
# ───────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────
# A5b — Sweep the ENTIRE blob table via `blobs_consistency`. # A5b — Sweep both consistency tenants that check ref_count.
# Complements the single-hash probe above: if the copy # Complements the single-hash probe above: if the copy
# path miscounted some OTHER blob shared by an unrelated # path miscounted some OTHER blob or manifest, the single-
# row (e.g. a global thumbnail blob, an OS icon dedup # hash probe wouldn't catch it. Delta vs the baselines
# hit), the single-hash probe wouldn't catch it. Delta # isolates NEW drift from ambient.
# vs `baseline_findings` isolates NEW drift from ambient. #
# Both tenants required — one counter each; see
# [[bug_dual_refcount_divergence]] for why the copy path
# must maintain both symmetrically.
# ───────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────
POST {{base_url}}/api/admin/jobs/blobs_consistency/trigger POST {{base_url}}/api/admin/jobs/blobs_consistency/trigger
Authorization: Bearer {{token}} Authorization: Bearer {{token}}
@@ -198,7 +223,17 @@ HTTP 200
[Asserts] [Asserts]
jsonpath "$.ok" == true jsonpath "$.ok" == true
jsonpath "$.outcome.outcome" == "ok" jsonpath "$.outcome.outcome" == "ok"
jsonpath "$.outcome.count" == {{baseline_findings}} jsonpath "$.outcome.count" == {{baseline_blobs_findings}}
POST {{base_url}}/api/admin/jobs/manifests_consistency/trigger
Authorization: Bearer {{token}}
HTTP 200
[Asserts]
jsonpath "$.ok" == true
jsonpath "$.outcome.outcome" == "ok"
jsonpath "$.outcome.count" == {{baseline_manifests_findings}}
# ───────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────
@@ -280,10 +315,10 @@ jsonpath "$.exists" == false
# ───────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────
# A9 — End-of-Scenario-A sweep. Scenario A introduced two file # A9 — End-of-Scenario-A sweep on BOTH tenants. Scenario A
# rows (source + copy), then deleted both. Net effect on # introduced two file rows (source + copy), then deleted
# the DB is zero — so the drift count must be exactly the # both. Net effect on the DB is zero — so the drift count
# baseline, no more, no less. # on each counter must be exactly the baseline.
# ───────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────
POST {{base_url}}/api/admin/jobs/blobs_consistency/trigger POST {{base_url}}/api/admin/jobs/blobs_consistency/trigger
Authorization: Bearer {{token}} Authorization: Bearer {{token}}
@@ -292,21 +327,29 @@ HTTP 200
[Asserts] [Asserts]
jsonpath "$.ok" == true jsonpath "$.ok" == true
jsonpath "$.outcome.outcome" == "ok" jsonpath "$.outcome.outcome" == "ok"
jsonpath "$.outcome.count" == {{baseline_findings}} jsonpath "$.outcome.count" == {{baseline_blobs_findings}}
POST {{base_url}}/api/admin/jobs/manifests_consistency/trigger
Authorization: Bearer {{token}}
HTTP 200
[Asserts]
jsonpath "$.ok" == true
jsonpath "$.outcome.outcome" == "ok"
jsonpath "$.outcome.count" == {{baseline_manifests_findings}}
# ============================================================= # =============================================================
# Scenario B — 2 MB multi-chunk file (CDC manifest path) # Scenario B — 2 MB multi-chunk file (CDC manifest path)
# #
# Coverage note: `blobs_consistency` iterates every blob row # Coverage: this scenario now sweeps BOTH `blobs_consistency`
# (whole-file AND per-chunk) and checks each ref_count against # (chunk-level ref_counts on `storage.blobs.ref_count`) and
# the auditor SQL, so a chunk-level under-count is caught by # `manifests_consistency` (whole-file ref_counts on
# the sweep in B5b. But the DEDICATED tenant for the CDC path # `storage.chunk_manifests.ref_count`). Same-shape assertions
# is `manifest_consistency` (queued on a separate branch as of # as Scenario A — see the baseline capture block near the top
# 2026-08-23) — when it lands, replace the `blobs_consistency` # of this file and [[bug_dual_refcount_divergence]] for why
# trigger in B5b/B9 with `manifest_consistency` (or run both # both are needed.
# in the batch) so the assertion is scoped to what actually
# describes the CDC invariant.
# ============================================================= # =============================================================
# ───────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────
@@ -403,11 +446,12 @@ jsonpath "$.ref_count" == 2
# ───────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────
# B5b — Full-DB sweep after CDC copy. Multi-chunk path # B5b — Full-DB sweep on BOTH tenants after CDC copy. Multi-
# exercises the manifest side of the ref-count invariant # chunk path exercises the manifest side of the invariant
# — a bug that skips one chunk out of N would leak that # — a bug that skips one chunk out of N would leak that
# chunk without touching the single whole-file assertion # chunk without touching the whole-file assertion above.
# above. Sweep catches it. # `blobs_consistency` catches chunk-level drift;
# `manifests_consistency` catches whole-file drift.
# ───────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────
POST {{base_url}}/api/admin/jobs/blobs_consistency/trigger POST {{base_url}}/api/admin/jobs/blobs_consistency/trigger
Authorization: Bearer {{token}} Authorization: Bearer {{token}}
@@ -416,7 +460,17 @@ HTTP 200
[Asserts] [Asserts]
jsonpath "$.ok" == true jsonpath "$.ok" == true
jsonpath "$.outcome.outcome" == "ok" jsonpath "$.outcome.outcome" == "ok"
jsonpath "$.outcome.count" == {{baseline_findings}} jsonpath "$.outcome.count" == {{baseline_blobs_findings}}
POST {{base_url}}/api/admin/jobs/manifests_consistency/trigger
Authorization: Bearer {{token}}
HTTP 200
[Asserts]
jsonpath "$.ok" == true
jsonpath "$.outcome.outcome" == "ok"
jsonpath "$.outcome.count" == {{baseline_manifests_findings}}
# ───────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────
@@ -478,8 +532,9 @@ jsonpath "$.exists" == false
# ───────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────
# B9 — End-of-Scenario-B sweep. All Scenario B rows gone; # B9 — End-of-Scenario-B sweep on BOTH tenants. All Scenario B
# finding count must be back at baseline. # rows gone; both counter drift counts must be back at
# baseline.
# ───────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────
POST {{base_url}}/api/admin/jobs/blobs_consistency/trigger POST {{base_url}}/api/admin/jobs/blobs_consistency/trigger
Authorization: Bearer {{token}} Authorization: Bearer {{token}}
@@ -488,7 +543,17 @@ HTTP 200
[Asserts] [Asserts]
jsonpath "$.ok" == true jsonpath "$.ok" == true
jsonpath "$.outcome.outcome" == "ok" jsonpath "$.outcome.outcome" == "ok"
jsonpath "$.outcome.count" == {{baseline_findings}} jsonpath "$.outcome.count" == {{baseline_blobs_findings}}
POST {{base_url}}/api/admin/jobs/manifests_consistency/trigger
Authorization: Bearer {{token}}
HTTP 200
[Asserts]
jsonpath "$.ok" == true
jsonpath "$.outcome.outcome" == "ok"
jsonpath "$.outcome.count" == {{baseline_manifests_findings}}
# ============================================================= # =============================================================