mirror of
https://github.com/saltbo/zpan.git
synced 2026-09-17 16:39:27 +08:00
* fix: harden legacy downloader bootstrap Agent-Profile: https://agent-kanban.dev/agents/f68cfbce6456edb5 * fix: cover downloader bootstrap hardening Agent-Profile: https://agent-kanban.dev/agents/f68cfbce6456edb5 * fix: document downloader bootstrap auth policy Agent-Profile: https://agent-kanban.dev/agents/f68cfbce6456edb5 --------- Co-authored-by: Ethan Cole <ethan-cole@mails.agent-kanban.dev>
215 lines
8.3 KiB
Gherkin
215 lines
8.3 KiB
Gherkin
Feature: Remote download tasks
|
|
Users queue remote downloads (HTTP/magnet) that external downloader agents claim
|
|
via device login, run, and upload back through the standard object-upload API.
|
|
Tasks are assigned by heartbeat/capacity; the free plan caps downloader count.
|
|
|
|
@download-tasks/register-downloader @api
|
|
Scenario: A downloader registers via device login
|
|
Given a device-login flow
|
|
When a downloader registers
|
|
Then it is registered through BetterAuth device login
|
|
|
|
@download-tasks/device-bootstrap-scope @api
|
|
Scenario: The legacy downloader device flow requires its exact client and scope
|
|
Given a legacy device-code request
|
|
When the client id or scope differs from zpan-cli downloader registration
|
|
Then the request is rejected
|
|
|
|
@download-tasks/device-bootstrap-single-use @api
|
|
Scenario: A downloader bootstrap token is single-use
|
|
Given an approved legacy downloader bootstrap token
|
|
When downloader registration succeeds
|
|
Then replaying the same bootstrap token is rejected
|
|
|
|
@download-tasks/device-bootstrap-rollback @api
|
|
Scenario: Failed downloader registration does not consume the bootstrap token
|
|
Given an approved legacy downloader bootstrap token
|
|
When downloader registration fails inside the database transaction
|
|
Then the same bootstrap token can be retried successfully
|
|
|
|
@download-tasks/device-bootstrap-silo @api
|
|
Scenario: Downloader bootstrap tokens cannot call non-registration APIs
|
|
Given an approved legacy downloader bootstrap token
|
|
When it is used on APIs other than downloader registration
|
|
Then those APIs reject it
|
|
|
|
@download-tasks/list-status-multi @api
|
|
Scenario: Task listing accepts multiple status values
|
|
Given tasks in several statuses
|
|
When they are listed with a comma-separated status query
|
|
Then matching statuses are returned and invalid statuses are rejected
|
|
|
|
@download-tasks/ssrf-guard @api
|
|
Scenario: Internal-host source URLs are rejected
|
|
Given a source URL targeting an internal host
|
|
When a download task is created
|
|
Then it is rejected
|
|
|
|
@download-tasks/magnet-validation @api
|
|
Scenario: Non-magnet magnet tasks are rejected
|
|
Given a magnet task whose URI is not a magnet link
|
|
When it is created
|
|
Then it is rejected
|
|
|
|
@download-tasks/delete-downloader-requeues @api
|
|
Scenario: Deleting a downloader requeues its tasks
|
|
Given a downloader with unfinished tasks
|
|
When it is deleted
|
|
Then its unfinished tasks return to the queue
|
|
|
|
@download-tasks/stale-no-assign @api
|
|
Scenario: Stale downloaders get no new tasks
|
|
Given a downloader with a stale heartbeat
|
|
When tasks are assigned
|
|
Then it receives no new tasks
|
|
|
|
@download-tasks/capacity-queue @api
|
|
Scenario: Tasks stay queued when downloaders are full
|
|
Given matching downloaders at capacity
|
|
When tasks await assignment
|
|
Then they remain queued
|
|
|
|
@download-tasks/stale-offline @api
|
|
Scenario: Stale downloaders show offline
|
|
Given a downloader with a stale heartbeat
|
|
When an admin lists downloaders
|
|
Then it is reported offline
|
|
|
|
@download-tasks/reassign-on-heartbeat @api
|
|
Scenario: Live heartbeat reassigns stale tasks
|
|
Given unfinished tasks on a stale downloader
|
|
When a live heartbeat arrives
|
|
Then the tasks are reassigned
|
|
|
|
@download-tasks/stale-resolves-canceling @api
|
|
Scenario: Stale downloader's canceling task settles to canceled
|
|
Given a canceling task on a stale downloader
|
|
When stale recovery runs
|
|
Then the task settles to canceled
|
|
|
|
@download-tasks/billing-suspend-gate @api
|
|
Scenario: A task with no credits is suspended before downloading
|
|
Given a downloader with credit billing and no remaining credits
|
|
When it marks an assigned task downloading
|
|
Then the task is suspended with a reason and no bytes are pulled
|
|
|
|
@download-tasks/billing-recover @api
|
|
Scenario: A suspended task recovers once credits are restored
|
|
Given a task suspended for insufficient credits
|
|
When the credits are restored and the unit is retried
|
|
Then the charge succeeds and the task is no longer suspended
|
|
|
|
@download-tasks/stale-clears-seeding @api
|
|
Scenario: Unreachable downloader's completed task stops showing seeding
|
|
Given a completed task still flagged seeding on a stale downloader
|
|
When stale recovery runs
|
|
Then its stale seeding runtime is cleared
|
|
|
|
@download-tasks/upload-flow @api
|
|
Scenario: A completed remote download uploads via the object API
|
|
Given an assigned task
|
|
When the downloader uploads the result
|
|
Then it flows through the standard object upload API
|
|
|
|
@download-tasks/cloud-usage-idempotency @api
|
|
Scenario: Cloud usage ids differ from local idempotency keys
|
|
Given a remote download usage event
|
|
When it is recorded
|
|
Then Cloud usage event ids may differ from local idempotency keys
|
|
|
|
@download-tasks/runtime-reports @api
|
|
Scenario: Runtime reports snapshot while progress stays patchable
|
|
Given a running downloader
|
|
When it reports runtime state
|
|
Then reports are stored as snapshots and progress remains patchable
|
|
|
|
@download-tasks/list-detail @api
|
|
Scenario: Browser lists return only list fields
|
|
Given a task with runtime files, peers, and trackers
|
|
When a user lists tasks and then opens one task
|
|
Then the list returns only its required fields and the detail returns the full runtime
|
|
|
|
@download-tasks/events @api
|
|
Scenario: Task events expose lifecycle and audit history
|
|
Given a task with lifecycle timestamps and audit events
|
|
When its events are requested
|
|
Then the API returns a sorted task timeline
|
|
|
|
@download-tasks/upload-session-failure @api
|
|
Scenario: Upload session creation failures are surfaced
|
|
Given multipart upload session creation fails
|
|
When the downloader uploads
|
|
Then the storage failure details are returned
|
|
|
|
@download-tasks/upload-completion-failure @api
|
|
Scenario: Upload completion failures are surfaced
|
|
Given multipart upload completion fails
|
|
When the downloader completes upload
|
|
Then the storage failure details are returned
|
|
|
|
@download-tasks/normalize-target @api
|
|
Scenario: Target folder paths are normalized
|
|
Given a download task with a target folder
|
|
When it is created
|
|
Then the target folder path is normalized
|
|
|
|
@download-tasks/user-actions @api
|
|
Scenario: User actions reach the downloader via polling
|
|
Given a running task
|
|
When the user submits an action
|
|
Then it is delivered through the downloader polling state
|
|
|
|
@download-tasks/recover-interrupted @api
|
|
Scenario: Downloaders recover interrupted tasks but not user-paused ones
|
|
Given interrupted and user-paused tasks
|
|
When the assigned downloader recovers
|
|
Then interrupted tasks resume but user-paused tasks do not
|
|
|
|
@download-tasks/checkpoint-on-retry @api
|
|
Scenario: Upload retries preserve the download checkpoint
|
|
Given an upload failure after download completed
|
|
When the upload is retried
|
|
Then the completed download checkpoint is preserved
|
|
|
|
@download-tasks/transitional-actions @api
|
|
Scenario: Pause and cancel use transitional states
|
|
Given a downloading task
|
|
When it is paused or cancelled
|
|
Then transitional states are used
|
|
|
|
@download-tasks/reject-invalid-pause @api
|
|
Scenario: Pause is rejected for billing-paused and uploading tasks
|
|
Given a billing-paused or uploading task
|
|
When pause is requested
|
|
Then it is rejected
|
|
|
|
@download-tasks/reject-invalid-action @api
|
|
Scenario: Invalid task actions are rejected
|
|
Given a task
|
|
When an invalid action is requested
|
|
Then it is rejected
|
|
|
|
@download-tasks/sort-filter @api
|
|
Scenario: Tasks sort and filter server-side
|
|
Given many tasks
|
|
When they are listed with sort and filter
|
|
Then the server returns them sorted and filtered
|
|
|
|
@download-tasks/free-limit @api
|
|
Scenario: The free plan caps downloaders
|
|
Given one downloader on the free plan
|
|
When a second registers
|
|
Then the API responds 402
|
|
|
|
@download-tasks/unlimited-entitlement @api
|
|
Scenario: The unlimited entitlement lifts the downloader cap
|
|
Given the downloaders_unlimited entitlement
|
|
When additional downloaders register
|
|
Then they are allowed
|
|
|
|
@downloaders/credit-billing @api
|
|
Scenario: Admins configure downloader credits separately
|
|
Given an existing downloader
|
|
When an admin updates downloader credit billing
|
|
Then the billing fields are persisted through the dedicated route
|