Files

149 lines
7.3 KiB
Plaintext
Raw Permalink Normal View History

# =============================================================
# OxiCloud – transcode_import: registration, contract, empty-tree run
#
# ## What this covers, and what it deliberately does not
#
# It covers the job's SURFACE: that it is registered, that it declares
# the metadata the admin panel switches on, and that a run against an
# already-drained tree completes cleanly with zeroed counters rather
# than erroring or reporting phantom work.
#
# It does NOT cover the re-keying — the part that matters most, where
# `{file_id}.webp` is resolved through `storage.files` to a content
# hash, and several entries naming the same content collapse into one
# row. That needs `.transcoded/webp/` entries on disk before the run,
# and nothing reachable over HTTP can create them: since the write path
# moved to the derived tier, only callers with no content hash
# (external mounts) still write there, and hurl cannot place files in
# the server's storage directory either.
#
# So the migration itself is validated by a snapshot restore against a
# populated `.transcoded/`, the same way the thumbnail migration was —
# an exercise that found three bugs the test suite did not. This file
# guards the contract that surrounds it; it is not a substitute.
#
# The empty-tree assertions are still worth having. A drained tree is
# the steady state of every install after the first boot, so this is
# what the job does on the overwhelming majority of its runs, and
# "does nothing, quietly, forever" is a real property — the thumbnail
# equivalent warned on every boot for exactly this case until it was
# caught by eye.
#
# Prerequisites: setup.hurl must have run (admin user exists).
#
# Run:
# hurl --variables-file tests/api/test.env --file-root tests \
# --test tests/api/transcode_import.hurl
# =============================================================
# ─────────────────────────────────────────────────────────────
# Step 1 – Login
# ─────────────────────────────────────────────────────────────
POST {{base_url}}/api/auth/login
Content-Type: application/json
{
"username": "{{username}}",
"password": "{{password}}"
}
HTTP 200
[Captures]
token: jsonpath "$.access_token"
# ─────────────────────────────────────────────────────────────
# Step 2 – The job is registered, and declares itself correctly.
#
# The admin panel keys its read-only badge and its repair toggle off
# these three fields, so a job that stops declaring them degrades the
# UI silently: `mutates` wrong means a destructive job renders as safe,
# and a missing `repair_description` removes the only way to reach the
# deletion from the interface.
#
# `always` because a plain run inserts rows and writes blobs — the
# repair flag adds deletion on top, which is why the two are
# independent rather than one implying the other.
# ─────────────────────────────────────────────────────────────
GET {{base_url}}/api/admin/jobs
Authorization: Bearer {{token}}
HTTP 200
[Asserts]
jsonpath "$[*].name" contains "transcode_import"
jsonpath "$[?(@.name=='transcode_import')].mutates" == "always"
jsonpath "$[?(@.name=='transcode_import')].recoverable" == true
# ─────────────────────────────────────────────────────────────
# Step 3 – A discovery-only run over a drained tree.
#
# No `?repair=true`: the default must import without deleting, which is
# what the daily-tick and the no-silent-auto-repair rule both depend
# on. Counters are asserted to exact zeros rather than "exists" — a
# non-zero here would mean the job found work in a directory the API
# cannot have put anything into, which is the shape a re-keying bug
# would take.
# ─────────────────────────────────────────────────────────────
POST {{base_url}}/api/admin/jobs/transcode_import/trigger
Authorization: Bearer {{token}}
HTTP 200
[Asserts]
jsonpath "$.ok" == true
jsonpath "$.outcome.outcome" == "ok"
jsonpath "$.outcome.extra.completed" == true
jsonpath "$.outcome.extra.extra_stats.imported" == 0
jsonpath "$.outcome.extra.extra_stats.negatives" == 0
jsonpath "$.outcome.extra.extra_stats.file_gone" == 0
jsonpath "$.outcome.extra.extra_stats.deleted" == 0
jsonpath "$.outcome.extra.extra_stats.failed" == 0
jsonpath "$.outcome.extra.extra_stats.unverified" == 0
# A clean run raises no findings. `file_gone` entries are recorded as
# anomalies, so a non-zero count here would contradict the zero above.
jsonpath "$.outcome.extra.finding_count" == 0
# ─────────────────────────────────────────────────────────────
# Step 4 – The same run under `?repair=true`.
#
# On a drained tree this must be indistinguishable from the run above:
# nothing to import, nothing to delete, and — the part worth pinning —
# no complaint about the directory being absent. The thumbnail teardown
# warned `could not be removed / No such file or directory` on every
# boot after its migration finished, warning about success forever.
# Absence is the end state, not a failure, and the job has to treat it
# that way because this is what every run after the first looks like.
# ─────────────────────────────────────────────────────────────
POST {{base_url}}/api/admin/jobs/transcode_import/trigger?repair=true
Authorization: Bearer {{token}}
HTTP 200
[Asserts]
jsonpath "$.ok" == true
jsonpath "$.outcome.outcome" == "ok"
jsonpath "$.outcome.extra.extra_stats.imported" == 0
jsonpath "$.outcome.extra.extra_stats.negatives" == 0
jsonpath "$.outcome.extra.extra_stats.deleted" == 0
jsonpath "$.outcome.extra.extra_stats.failed" == 0
jsonpath "$.outcome.extra.finding_count" == 0
# ─────────────────────────────────────────────────────────────
# Step 5 – Idempotence.
#
# The job is registered as a daily tick AND named in
# OXICLOUD_STARTUP_JOBS, so on a long-lived install it runs
# unattended, repeatedly, forever. Re-running must stay a no-op —
# anything that accumulated across runs would accumulate unattended.
# ─────────────────────────────────────────────────────────────
POST {{base_url}}/api/admin/jobs/transcode_import/trigger
Authorization: Bearer {{token}}
HTTP 200
[Asserts]
jsonpath "$.outcome.outcome" == "ok"
jsonpath "$.outcome.extra.extra_stats.imported" == 0
jsonpath "$.outcome.extra.extra_stats.negatives" == 0
jsonpath "$.outcome.extra.finding_count" == 0