mirror of
https://github.com/saltbo/zpan.git
synced 2026-09-01 05:44:38 +08:00
82f5988d56
Keep object list responses limited to recorded creator identities and resolve full profiles through the creator subresource. Normalize list avatar sizing and avoid retrying terminal creator lookup errors.
272 lines
9.0 KiB
Gherkin
272 lines
9.0 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/creator-attribution @api
|
|
Scenario: Object creation records the authenticated actor
|
|
Given an authenticated user, API key, agent, or device
|
|
When that actor creates an object
|
|
Then the object stores and returns the stable actor identity
|
|
And its display profile is available from the creator subresource
|
|
|
|
@objects/legacy-creator @api
|
|
Scenario: Historical objects do not impersonate the workspace owner
|
|
Given an object created before actor attribution was recorded
|
|
When the object is listed or inspected
|
|
Then its creator is reported as not recorded
|
|
|
|
@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-capacity-offer @api
|
|
Scenario: Creating a file over quota offers an immediate capacity upgrade
|
|
Given a file would exceed the workspace quota and a larger published plan is available
|
|
When the file object is created
|
|
Then the API responds 402 with the eligible capacity offer and a purchase request hash
|
|
|
|
@objects/create-capacity-unavailable @api
|
|
Scenario: Creating a file over quota fails when no immediate capacity upgrade exists
|
|
Given a file would exceed the workspace quota and no published plan can close the gap
|
|
When the file object is created
|
|
Then the API responds 422 without a payment offer
|
|
|
|
@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
|