mirror of
https://github.com/saltbo/zpan.git
synced 2026-08-29 00:01:42 +08:00
360237d069
* feat: add restish zpan upload plugin Agent-Profile: https://agent-kanban.dev/agents/f68cfbce6456edb5 * fix: restore single upload resume for restish plugin Agent-Profile: https://agent-kanban.dev/agents/f68cfbce6456edb5 * ci(release): publish restish-zpan plugin assets * fix(restish-zpan): accept restish operation aliases --------- Co-authored-by: Ethan Cole <ethan-cole@mails.agent-kanban.dev> Co-authored-by: saltbo <saltbo@foxmail.com>
247 lines
7.8 KiB
Gherkin
247 lines
7.8 KiB
Gherkin
Feature: Objects
|
|
Files and folders ("matters") are the core entity. Creating a file returns
|
|
size-decided upload instructions; the client PUTs each slice directly to S3,
|
|
reads the ETags, and completes the upload. Clients browse/rename/move, soft-delete
|
|
to trash and restore, permanently purge, copy, and transfer across spaces — with
|
|
name-conflict resolution and quota enforcement throughout.
|
|
|
|
@objects/auth-required @api
|
|
Scenario: Object access requires authentication
|
|
Given an unauthenticated request
|
|
When it calls the objects API
|
|
Then the API responds 401
|
|
|
|
@objects/list-empty @api
|
|
Scenario: A new drive lists nothing
|
|
Given a user with no objects
|
|
When they list objects
|
|
Then an empty list is returned
|
|
|
|
@objects/list-pagination @api
|
|
Scenario: Object listing paginates
|
|
Given many objects
|
|
When they are listed with pagination params
|
|
Then the requested page is returned
|
|
|
|
@objects/list-by-parent @api
|
|
Scenario: Objects filter by parent folder
|
|
Given nested folders
|
|
When listing filters by parent
|
|
Then only that parent's children are returned
|
|
|
|
@objects/list-live-only @api
|
|
Scenario: Listing returns live objects only
|
|
Given live and trashed objects
|
|
When objects are listed
|
|
Then only live (non-trashed) objects are returned
|
|
|
|
@objects/create-folder @api
|
|
Scenario: A folder is created
|
|
Given an authenticated user
|
|
When they create a folder
|
|
Then the folder is created
|
|
|
|
@objects/create-invalid @api
|
|
Scenario: Invalid create input is rejected
|
|
Given invalid object input
|
|
When it is posted
|
|
Then the API responds 400
|
|
|
|
@objects/create-no-storage @api
|
|
Scenario: Creating a file needs a storage
|
|
Given no storage is available
|
|
When a file object is created
|
|
Then the API responds 500
|
|
|
|
@objects/create-file-presign @api
|
|
Scenario: Creating a file returns upload instructions
|
|
Given a configured storage
|
|
When a file object is created
|
|
Then a draft with upload instructions (part size + presigned URLs) is returned
|
|
|
|
@objects/create-file-too-large @api
|
|
Scenario: Creating an oversized file is rejected
|
|
Given a file larger than the 5 TiB maximum
|
|
When the file object is created
|
|
Then the API responds 400
|
|
|
|
@objects/detail @api
|
|
Scenario: An object's detail is returned
|
|
Given an existing object
|
|
When its detail is requested
|
|
Then the detail is returned
|
|
|
|
@objects/detail-missing @api
|
|
Scenario: A missing object detail is 404
|
|
Given a missing object id
|
|
When its detail is requested
|
|
Then the API responds 404
|
|
|
|
@objects/rename @api
|
|
Scenario: An object is renamed
|
|
Given an existing object
|
|
When it is renamed
|
|
Then the new name is persisted
|
|
|
|
@objects/move @api
|
|
Scenario: An object is moved
|
|
Given an existing object
|
|
When it is moved to a new parent
|
|
Then its location is updated
|
|
|
|
@objects/complete-upload @api
|
|
Scenario: An upload is completed
|
|
Given a draft object whose slices were uploaded to S3
|
|
When the upload is completed with the part ETags
|
|
Then the object becomes live
|
|
|
|
@objects/complete-etag-mismatch @api
|
|
Scenario: Completing with a mismatched ETag is rejected
|
|
Given a draft single-PutObject upload
|
|
When it is completed with an ETag that does not match the stored object
|
|
Then the API responds 409
|
|
|
|
@objects/presign-upload-parts-single @api
|
|
Scenario: A single-PutObject upload session can re-sign its upload part
|
|
Given a draft single-PutObject upload
|
|
When the client requests fresh upload instructions for part 1
|
|
Then the API returns a single-mode part descriptor without a multipart upload id
|
|
|
|
@objects/abort-upload @api
|
|
Scenario: An upload is aborted
|
|
Given a draft object with an open upload session
|
|
When the upload is aborted
|
|
Then the draft is discarded and its S3 upload is cleaned up
|
|
|
|
@objects/trash @api
|
|
Scenario: A file is soft-deleted to trash
|
|
Given a live file
|
|
When it is deleted
|
|
Then it moves to trash (trashedAt is set)
|
|
|
|
@objects/trash-cascade @api
|
|
Scenario: Trashing a folder cascades to children
|
|
Given a folder with children
|
|
When it is trashed
|
|
Then its children are trashed too
|
|
|
|
@objects/restore @api
|
|
Scenario: A trashed file is restored
|
|
Given a trashed file
|
|
When it is restored
|
|
Then it becomes active again
|
|
|
|
@objects/list-trashed @api
|
|
Scenario: Trashed roots list under their active parents
|
|
Given trashed objects under active parents
|
|
When trashed objects are listed
|
|
Then trashed folder roots are returned nested under active parents
|
|
|
|
@objects/purge-folder @api
|
|
Scenario: A trashed folder is permanently deleted
|
|
Given a trashed folder
|
|
When it is permanently deleted
|
|
Then it is removed
|
|
|
|
@objects/purge-file-s3 @api
|
|
Scenario: Purging a trashed file cleans up S3
|
|
Given a trashed file
|
|
When it is permanently deleted
|
|
Then it is removed and its S3 object deleted
|
|
|
|
@objects/get-trashed @api
|
|
Scenario: A trashed object's detail is returned from the trash
|
|
Given a trashed object
|
|
When its trash detail is requested
|
|
Then the trashed object is returned
|
|
|
|
@objects/download-url @api
|
|
Scenario: A file detail returns a download URL
|
|
Given a file
|
|
When its detail is requested
|
|
Then a download URL is returned
|
|
|
|
@objects/download-traffic @api
|
|
Scenario: Downloads report Cloud traffic for bound instances
|
|
Given a bound instance
|
|
When a file download URL is requested
|
|
Then Cloud traffic is reported before returning the URL
|
|
|
|
@objects/copy-file @api
|
|
Scenario: A file is copied
|
|
Given a file
|
|
When it is copied
|
|
Then a new file is created from the source via S3
|
|
|
|
@objects/copy-folder @api
|
|
Scenario: A folder is copied
|
|
Given a folder
|
|
When it is copied
|
|
Then the folder tree is duplicated
|
|
|
|
@objects/create-conflict @api
|
|
Scenario: A duplicate folder name conflicts
|
|
Given an existing folder name
|
|
When a folder with the same name is created
|
|
Then the API responds 409 NAME_CONFLICT
|
|
|
|
@objects/create-conflict-rename @api
|
|
Scenario: onConflict=rename auto-renames on create
|
|
Given an existing folder name
|
|
When a folder is created with onConflict=rename
|
|
Then it succeeds with an auto-renamed folder
|
|
|
|
@objects/rename-conflict @api
|
|
Scenario: Renaming into a taken name conflicts
|
|
Given a sibling with the target name
|
|
When an object is renamed to it
|
|
Then the API responds 409 NAME_CONFLICT
|
|
|
|
@objects/move-conflict @api
|
|
Scenario: Moving into a collision conflicts
|
|
Given a destination with a colliding name
|
|
When an object is moved without onConflict
|
|
Then the API responds 409
|
|
|
|
@objects/restore-conflict @api
|
|
Scenario: Restoring into a taken name conflicts
|
|
Given a trashed object whose name is now taken
|
|
When it is restored
|
|
Then the API responds 409
|
|
|
|
@objects/transfer-copy @api
|
|
Scenario: A file copies into an editable team space
|
|
Given a team space the user can edit
|
|
When a file is copied into it
|
|
Then the copy is created there
|
|
|
|
@objects/transfer-move @api
|
|
Scenario: A file moves into a team space
|
|
Given a team space the user can edit
|
|
When a file is moved into it
|
|
Then the source is deleted and its quota released
|
|
|
|
@objects/transfer-folder @api
|
|
Scenario: A folder transfers recursively
|
|
Given a folder
|
|
When it is transferred into a target space
|
|
Then it is copied recursively
|
|
|
|
@objects/transfer-permission @api
|
|
Scenario: Transfer requires target membership
|
|
Given a team the user is not a member of
|
|
When a transfer is attempted
|
|
Then it is rejected
|
|
|
|
@objects/transfer-quota @api
|
|
Scenario: Transfer respects the target quota
|
|
Given a target space whose quota is exceeded
|
|
When a transfer is attempted
|
|
Then it is rejected
|
|
|
|
@objects/transfer-same-space @api
|
|
Scenario: Transfer to the same space is rejected
|
|
Given a source and target that are the same space
|
|
When a transfer is attempted
|
|
Then it is rejected
|