Files
zpan/spec/objects.feature
T
jarvis 82f5988d56 fix(objects): load creator profiles on demand
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.
2026-08-16 21:06:09 -04:00

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