mirror of
https://github.com/larksuite/cli.git
synced 2026-09-14 18:42:53 +08:00
5b734238d7
* feat: report upload file events * test(drive): skip import workflow without tenant token * docs: document upload report helpers * docs: improve function documentation coverage * docs: complete incremental function documentation * docs: complete function documentation coverage * fix: report every upload file event * refactor: move file event reporting to internal package * fix: harden upload file event reporting * fix: omit upload mode from file event reports * test: fix workbook import dry-run token assertion
84 lines
29 KiB
Markdown
84 lines
29 KiB
Markdown
# Drive CLI E2E Coverage
|
|
|
|
## Metrics
|
|
- Denominator: 46 leaf commands
|
|
- Covered: 26
|
|
- Coverage: 56.5%
|
|
|
|
## Summary
|
|
- TestDrive_FilesCreateFolderWorkflow: proves `drive files create_folder` in `create_folder as bot`; helper asserts the returned folder token and registers best-effort cleanup via `drive files delete`.
|
|
- TestDrive_StatusWorkflow: proves `drive +status` against a real Drive folder. Seeds the remote side via `drive +upload` (`unchanged.txt`, `modified.txt`, `remote-only.txt`), seeds local files with the matching/diverging contents, and asserts every output bucket (`unchanged`, `modified`, `new_local`, `new_remote`) holds exactly the expected `rel_path` and `file_token`. Cleans up uploaded files and the parent folder via best-effort cleanup hooks.
|
|
- TestDrive_UploadWorkflow: proves `drive +upload` against the real backend in both create and overwrite modes. First uploads a fresh file into a temporary Drive folder, then re-uploads new bytes with `--file-token` against the returned token, asserts the overwrite keeps the token stable, downloads the file with explicit `--output` to confirm the remote content changed, and downloads again without `--output` to prove default filename resolution saves the remote name with matching bytes.
|
|
- TestDriveDownloadDryRun_DefaultNamePlansMetadataBeforeDownload / TestDriveDownloadDryRun_ExplicitOutputSkipsMetadata: dry-run coverage for `drive +download`; asserts omitted `--output` plans metadata before download for default naming, while explicit `--output` preserves the direct download-only path.
|
|
- TestDrive_ImportWorkflow: proves `drive +import` against the real backend. It imports a temporary Markdown file as docx, waits for the async task when needed, verifies the returned document token, and deletes the imported document during cleanup.
|
|
- TestDrive_DuplicateRemoteWorkflow: proves the duplicate-remote workflows against the real backend. One subtest uploads two same-name files into the same Drive folder and asserts `drive +status` and default `drive +pull` both fail with a typed validation error for the duplicate rel_path, while `drive +pull --on-duplicate-remote=rename` succeeds, downloads both files, and writes a hashed renamed sibling locally. The other subtest uploads duplicate remote files, runs `drive +push --on-duplicate-remote=newest --if-exists=overwrite --delete-remote --yes`, and then re-runs `drive +status` to prove the mirror converged to a single unchanged `dup.txt`.
|
|
- TestDrive_ApplyPermissionDryRun / TestDrive_ApplyPermissionDryRunRejectsFullAccess / TestDrive_ApplyPermissionDryRunRejectsUnsafeTargets: dry-run coverage for `drive +apply-permission`; asserts URL→type inference for docx/sheet/slides, bare-token + explicit `--type` path, request method/URL/type-query/perm/remark body shape, optional `remark` omission when unset, and client-side rejection of invalid permissions, unsafe tokens, non-root URL markers, encoded path separators, and conflicting URL/`--type` inputs. Runs without hitting the live API.
|
|
- TestDriveAddCommentDryRun_File / TestDriveAddCommentDryRun_Base: dry-run coverage for `drive +add-comment` on supported Drive file and Base targets; pins the `metas.batch_query -> files/:token/new_comments` file chain, Base `file_type=bitable`, and Base anchor fields.
|
|
- TestDriveCopyDryRun_DocxURL / TestDriveCopyDryRun_BareTokenBaseAlias / TestDriveCopyDryRun_MySpaceTarget / TestDriveCopyDryRun_WikiURLResolvesResourceThenCopiesToDrive: dry-run coverage for `drive +copy`; asserts URL parsing to `files/:token/copy`, request body shape (`name`/`type`/`folder_token`, `--extra key=value` → `extra` key/value array), folder-URL target parsing, `base`→`bitable` normalization, the `--folder-token my_space` two-step plan (`root_folder/meta -> copy` with a placeholder folder_token), the bot-mode `post_copy_note` auto-grant notice, and the wiki URL plan (`wiki get_node -> supported Drive type -> Drive copy`). Unit tests pin wiki URL and `--token + --type wiki`, successful Sheet unwrapping, the full supported resolved-type set, and typed `failed_precondition` rejection for unsupported Wiki node types. Bot-mode auto-grant of the current CLI user (success, and failure staying non-fatal) is covered by unit tests via the shared permission-grant harness; source-token injection contracts (`..`, `%2e%2e`, control characters, dangerous Unicode, traversal via URL path) are pinned by unit tests against `validate.ResourceName`.
|
|
- TestDrive_CopyWorkflow: proves `drive +copy` against the real backend. Uploads a source file into a temporary Drive folder, copies it with a new name via bare token + `--type file`, asserts the copy returns a distinct token with the requested name, downloads the copy, and verifies the content matches the source; a second copy targets `--folder-token my_space` and asserts the output carries the resolved root token instead of the sentinel. All copies and the folder are removed via best-effort cleanup hooks. The root-folder-meta resolution endpoint (absent from platform metadata) was live-verified for both user and bot identities. Direct Drive URL input and output shape were manually live-probed; wiki document unwrapping is currently covered by mocked unit tests plus dry-run orchestration, not a live fixture. Error codes in the skill reference were also live-probed: nonexistent source token → 1061003, `--type` mismatch (docx declared as sheet) → 1061003 (not 1061002 — the server looks the token up under the declared type).
|
|
- TestDriveListCommentsDryRun_DocxDefaults / TestDriveListCommentsDryRun_AppsPageURL / TestDriveListCommentsDryRun_WikiToken: dry-run coverage for `drive +list-comments`; asserts URL parsing to `files/:token/comments`, apps `/page/<token>` URL parsing with `file_type=apps`, default `is_solved=false`, default omitted `is_whole` and `user_id_type`, and Wiki token orchestration (`get_node -> comments.list`) without live API calls.
|
|
- TestDrive_CommentOpsDryRun: dry-run coverage for `drive +batch-query-comments`, `drive +resolve-comment`, `drive +restore-comment`, `drive +add-reply`, `drive +list-replies`, `drive +update-reply`, `drive +delete-reply`, and `drive +react-reply`; asserts URL→type inference (incl. Miaoda apps `/page/<token>` → `file_type=apps` and Base `/base/` → `file_type=bitable`), `file_type`/`page_size`/`need_reaction`/`need_relation` (docx-gated) query wiring, `comment_ids` / `is_solved` / reply `content.elements[]` (text_run) / reaction `action`+`reaction_type`+`reply_id` body shapes, resolved `:comment_id`/`:reply_id` path segments, the Wiki `get_node -> batch_query` / `get_node -> replies list` / `get_node -> v2 reaction` orchestration plans, and the batch_query wiki dry-run surfacing `need_relation` as the `<sent only when obj_type is docx>` placeholder, without live API calls. All eight verified manually against live documents (list → batch-query → add-reply → list-replies → update-reply → react add/delete → resolve/restore → delete-reply round trip; root-reply update rewriting the comment body, the `1069303 forbidden` cross-identity update rejection, the server persisting arbitrary `reaction_type` strings, and count=0 reaction tombstones were probed live as well).
|
|
- TestDriveAddCommentMarkdownFileWorkflow: opt-in live workflow skeleton for comment write/read, gated by `LARK_DRIVE_MD_COMMENT_E2E=1`; creates a Markdown file, adds a file comment, lists it back through `drive +list-comments`, and cleans up.
|
|
- TestDriveCommentOpsWorkflow: opt-in self-contained live workflow for the comment operation shortcuts, gated by `LARK_DRIVE_MD_COMMENT_E2E=1`; creates a Markdown file + file comment fixture, then `+batch-query-comments` finds it by ID, `+add-reply` attaches a reply, `+list-replies` surfaces it, `+update-reply` rewrites its content (confirmed by polling `+list-replies` until the new text lands), `+react-reply` attaches then removes a THUMBSUP reaction (confirmed by polling `+list-replies --need-reaction`, judging presence by count>0 because deleted reactions linger as count=0 entries), `+resolve-comment` marks it solved and `+restore-comment` reopens it (with polling reads between state flips to absorb rate limiting), `+delete-reply --yes` removes the created reply, and cleanup deletes the file.
|
|
- TestDrive_SecureLabelDryRun: dry-run coverage for `drive +secure-label-list` and `drive +secure-label-update`; asserts label-list query params and update URL→type inference, request method/URL/type query, and `label-id` body shape. Runs without hitting live APIs because update can trigger document-level security approval flows.
|
|
- TestDriveExportDryRun_FileNameMetadata / TestDriveExportDryRun_WikiURLPlansResolveBeforeExportTask / TestDriveExportDryRun_WikiTokenTypePlansResolveBeforeExportTask / TestDriveExportDryRun_MarkdownFetchAPI / TestDriveExportDryRun_BitableBaseOnlySchema: dry-run coverage for `drive +export`; asserts export task request shape, Wiki URL and `--doc-type wiki` token `get_node -> export_tasks` planning, markdown fetch request shape without docs fetch `extra_param`, local `--file-name` / `--output-dir` metadata, and `bitable` `.base` `only_schema` request body without calling live APIs.
|
|
- TestDriveDeleteDryRunAsyncParams / TestDrive_DeleteAsyncWorkflow: dry-run coverage for `drive +delete` pins `DELETE /drive/v1/files/:file_token` params with `type` plus `async=true` and the follow-up `task_check` plan; live workflow creates and deletes a docx, an empty folder, and a non-empty folder, converging every delete outcome to the resource-gone terminal state: async deletes (non-empty `task_id`) are verified via `drive +task_result --scenario task_check`, sync deletes (empty `task_id`) assert `deleted=true`, and the one verified backend transient (`server_error: "drive task failed"`) passes once the target is confirmed gone (retried up to 3 times otherwise); any other delete failure stays fatal.
|
|
- TestDriveUpdateTitleDryRun_DocxURL / TestDriveUpdateTitleDryRun_BareTokenTypes / TestDriveUpdateTitleDryRun_FileExtensionGuardPlan / TestDriveUpdateTitleDryRun_RejectsRemovedErrorPolicy / TestDriveUpdateTitleDryRun_RejectsExtensionPolicyOnNonFileType / TestDriveUpdateTitleDryRun_WikiURLKeepsNodeToken / TestDriveUpdateTitleDryRun_RejectsEmptyTitle / TestDriveUpdateTitleDryRun_RejectsTypeConflict: dry-run coverage for `drive +update-title`; asserts URL→type inference into `PATCH /drive/v1/files/:file_token` with the `type` query and `new_title` body, bare-token `--type` handling for `base`→`bitable` and `folder`, the `--new-title` alias, the single-request wiki plan that keeps the node token (no `get_node` unwrap step), the `--type file` two-step plan (`metas.batch_query` for the extension guard, then the rename) collapsing to one request under `--on-extension-mismatch=allow`, rejection of the removed `error` policy, the Miaoda apps redirect (both a `/page/` URL and `--type apps` fail locally with a hint pointing at the apps domain), the local rejection of the server-refused `doc` and `mindnote` types (both `--type` and URL spellings), and the local rejections for a whitespace-only title, a `--type` that conflicts with the URL path type, and `--on-extension-mismatch` paired with a non-file type.
|
|
- TestDrive_UpdateTitleWorkflow: proves `drive +update-title` against the real backend. Uploads a file into a temporary Drive folder, renames it with an extension-preserving title, reads the title back through `drive +inspect`, then exercises both extension policies on the same file: default `keep` (title without extension gets `.txt` appended, `extension_appended`/`previous_title` reported, name verified) and `allow` (title submitted verbatim, extension dropped on purpose, no `previous_title` because the guard skips the read). Then renames the containing folder (token stays stable, so the cleanup hook still resolves it) and re-reads that title too. A final negative leg renames the file with `--type docx` and asserts the endpoint answers `981003` while the name stays untouched. Live-probed manually beforehand as user before the assertions were encoded: `type=docx` / `type=folder` / `type=file` / `type=wiki` renames, that `type=wiki` needs the wiki node token (the underlying document token returns `981003`), that wiki node and underlying docx titles stay in sync in both directions, that a `--type` mismatch and an unknown token share code `981003`, that a bot without edit permission gets `981004`, that an empty `new_title` is accepted by the server and blanks the title (hence the local `--title` guard), and that a `type=file` title without an extension drops the extension server-side (hence the extension guard). Error-scenario guidance was probed the same way rather than assumed: renaming a document the identity cannot edit returns `981004 forbidden` for both user and bot (a document-permission failure, not a scope one), a 25-request burst against the endpoint returns `99991400 request trigger frequency limit` (classified `api/rate_limit`, retryable) for the requests over the limit, and the platform's missing-scope shapes were read off a scope the app does not hold: `99991679` for a user token (envelope carries `missing_scopes` plus an `auth login --scope` hint) and `99991672` for an app scope that was never applied. Titles are stored verbatim — a `new_title` of `" spaced title "` reads back with both pads intact — which is why the CLI trims `--title` before sending it. `doc` and `mindnote` are listed by the platform metadata but refused by the backend: the service log for a real mindnote token records `unsupported update file type=mindnote` and answers `981002 params error` before any token lookup, and probes confirm the type is what fails — a real docx token renames fine under `--type docx` yet returns `981002` under `--type doc` or `--type mindnote`, while unknown tokens under supported types return `981003`. Both are therefore refused locally. Miaoda apps were probed the same way: `type=apps` is rejected by gateway field validation (`99992402`, the same answer a nonsense type gets) while a supported type with an unknown token answers `981003`, which is why the CLI rejects apps inputs and points at the apps domain instead of forwarding them. Rate-limit and scope failures are not asserted in the live workflow — one would need a deliberate burst, the other a second app — so their guidance is unit-tested against the codes instead.
|
|
- TestDrive_PullDryRun / TestDrive_PullDryRunAcceptsDuplicateRemoteStrategies: dry-run coverage for `drive +pull`; asserts the list-files request shape, Validate-stage safety guards, and acceptance of `--on-duplicate-remote=rename|newest|oldest` by the real CLI binary.
|
|
- TestDrive_PushDryRun / TestDrive_PushDryRunAcceptsDuplicateRemoteStrategies: dry-run coverage for `drive +push`; asserts the list-files request shape, Validate-stage safety guards, conditional delete preflight, and acceptance of `--on-duplicate-remote=newest|oldest` by the real CLI binary.
|
|
- Cleanup note: `drive files delete` is only exercised in cleanup and is intentionally left uncovered.
|
|
- Blocked area: live export, permission, subscription, reply, and file comment API flows still need deterministic remote fixtures and filesystem setup.
|
|
- Dry-run note: `drive_upload_dryrun_test.go::TestDriveUploadDryRun_WikiTarget` and `TestDriveUploadDryRun_WithFileToken` cover the wiki-target and overwrite request shapes for `drive +upload`; live upload/status/duplicate workflows also use real `+upload` against the backend.
|
|
|
|
## Command Table
|
|
|
|
| Status | Cmd | Type | Testcase | Key parameter shapes | Notes / uncovered reason |
|
|
| --- | --- | --- | --- | --- | --- |
|
|
| ✓ | drive +add-comment | shortcut | drive_add_comment_dryrun_test.go::TestDriveAddCommentDryRun_File; drive_add_comment_dryrun_test.go::TestDriveAddCommentDryRun_Base | `--doc` file URL vs bare token + `--type file`; supported-extension metadata gate; placeholder `anchor.block_id`; Base URL with `--block-id <table-id>!<record-id>!<view-id>` | dry-run coverage in place; opt-in live file workflow exists behind `LARK_DRIVE_MD_COMMENT_E2E=1` |
|
|
| ✓ | drive +list-comments | shortcut | drive_list_comments_dryrun_test.go::TestDriveListCommentsDryRun_DocxDefaults; drive_list_comments_dryrun_test.go::TestDriveListCommentsDryRun_AppsPageURL; drive_list_comments_dryrun_test.go::TestDriveListCommentsDryRun_WikiToken; drive_add_comment_workflow_test.go::TestDriveAddCommentMarkdownFileWorkflow | `--url`; apps `/page/<token>` URL; `--token + --type wiki`; `--solved-status=false\|all`; `--comment-scope=all\|partial`; `--need-relation`; `--page-size` | dry-run locks URL/token parsing, apps `file_type=apps`, default unresolved filter, omitted all-scope filter, omitted `user_id_type`, and Wiki unwrap request shape; opt-in live workflow verifies a created file comment can be listed back |
|
|
| ✓ | drive +batch-query-comments | shortcut | drive_comment_ops_dryrun_test.go::TestDrive_CommentOpsDryRun; drive_comment_ops_workflow_test.go::TestDriveCommentOpsWorkflow | `--url` docx; base `/base/` URL (`file_type=bitable`); apps `/page/<token>` URL (`file_type=apps`); `--token + --type wiki`; `--comment-ids` CSV; `--need-reaction` body flag; `--need-relation` body flag (docx only, omitted otherwise) | dry-run pins request shape and wiki resolve plan; opt-in live workflow covers the full comment-ops round trip; need_relation is metadata-absent but live-verified (returns relation with block position) |
|
|
| ✓ | drive +resolve-comment | shortcut | drive_comment_ops_dryrun_test.go::TestDrive_CommentOpsDryRun; drive_comment_ops_workflow_test.go::TestDriveCommentOpsWorkflow | `--url` sheet; `--comment-id` path segment; fixed `is_solved=true` body | dry-run pins request shape; opt-in live workflow marks the fixture comment solved against the real backend |
|
|
| ✓ | drive +restore-comment | shortcut | drive_comment_ops_dryrun_test.go::TestDrive_CommentOpsDryRun; drive_comment_ops_workflow_test.go::TestDriveCommentOpsWorkflow | `--url` docx; `--comment-id` path segment; fixed `is_solved=false` body (same patch endpoint as `+resolve-comment`) | dry-run pins request shape; opt-in live workflow reopens the solved fixture comment against the real backend |
|
|
| ✓ | drive +add-reply | shortcut | drive_comment_ops_dryrun_test.go::TestDrive_CommentOpsDryRun; drive_comment_ops_workflow_test.go::TestDriveCommentOpsWorkflow | `--url` docx; `--comment-id` path segment (`.../comments/:comment_id/replies`); `--content` simplified elements → `content.elements[]` `text_run` | dry-run pins reply body mapping; opt-in live workflow attaches and verifies a real reply (body-comment_id create variant does NOT reply — creates a standalone comment) |
|
|
| ✓ | drive +list-replies | shortcut | drive_comment_ops_dryrun_test.go::TestDrive_CommentOpsDryRun; drive_comment_ops_workflow_test.go::TestDriveCommentOpsWorkflow | `--url` docx; `--token + --type wiki` resolve plan; `--comment-id` path segment (`.../comments/:comment_id/replies` GET); `--page-size`/`--page-token`/`--need-reaction` query | dry-run pins request shape and wiki resolve plan; opt-in live workflow lists the created reply back (the first-page items[0] is the root reply carrying the comment body) |
|
|
| ✓ | drive +update-reply | shortcut | drive_comment_ops_dryrun_test.go::TestDrive_CommentOpsDryRun; drive_comment_ops_workflow_test.go::TestDriveCommentOpsWorkflow | base `/base/` URL (`file_type=bitable`); `--comment-id` + `--reply-id` path segments (PUT); `--content` simplified elements → `content.elements[]` `text_run` | dry-run pins request/body shape; opt-in live workflow rewrites the created reply and polls `+list-replies` until the new text is visible; only the creator identity may update (live-probed `1069303 forbidden`) |
|
|
| ✓ | drive +delete-reply | shortcut | drive_comment_ops_dryrun_test.go::TestDrive_CommentOpsDryRun; drive_comment_ops_workflow_test.go::TestDriveCommentOpsWorkflow | `--url` docx; `--comment-id` + `--reply-id` path segments; `--dry-run` bypasses the `--yes` gate | dry-run pins request shape; opt-in live workflow deletes the created reply and verifies the count returns to baseline |
|
|
| ✓ | drive +react-reply | shortcut | drive_comment_ops_dryrun_test.go::TestDrive_CommentOpsDryRun; drive_comment_ops_workflow_test.go::TestDriveCommentOpsWorkflow | `--url` docx; `--token + --type wiki` resolve plan; v2 `.../comments/reaction` POST; `--reply-id`/`--emoji`/`--action add\|delete` → body `reply_id`/`reaction_type`/`action` | dry-run pins request/body shape and wiki resolve plan; opt-in live workflow adds then removes a reaction, polling `+list-replies --need-reaction` with count>0 presence checks; local `--emoji` enum validation guards the unvalidated server field |
|
|
| ✓ | drive +apply-permission | shortcut | drive_apply_permission_dryrun_test.go::TestDrive_ApplyPermissionDryRun | `--token` URL vs bare; `--type` (enum) with URL inference; `--perm view\|edit`; `--remark` optional | dry-run only; no live-apply E2E because a real request pushes a card to the owner |
|
|
| ✓ | drive +copy | shortcut | drive_copy_dryrun_test.go::TestDriveCopyDryRun_DocxURL; drive_copy_dryrun_test.go::TestDriveCopyDryRun_BareTokenBaseAlias; drive_copy_dryrun_test.go::TestDriveCopyDryRun_WikiURLResolvesResourceThenCopiesToDrive; drive_copy_workflow_test.go::TestDrive_CopyWorkflow | `--url` doc/wiki URL vs bare `--token + --type`; `--type base` alias; `--name` body; `--folder-token` URL vs bare vs `my_space` sentinel; `--extra key=value` passthrough; wiki URL/token unwrap to any Drive Copy-supported source type | dry-run locks request shape, the my_space plan, and wiki get_node -> Drive copy orchestration; unit tests pin supported-type passthrough and unsupported-type rejection; live workflow copies an uploaded file, verifies content via download, and copies into the resolved My Space root; `--extra` body shape confirmed against the live API (docx copy with `target_type=docx`) |
|
|
| ✓ | drive +delete | shortcut | drive_delete_dryrun_test.go::TestDriveDeleteDryRunAsyncParams + drive_delete_workflow_test.go::TestDrive_DeleteAsyncWorkflow | `--file-token`; `--type`; fixed query `async=true`; `task_check` follow-up | dry-run locks async request shape; live workflow covers docx, empty folder, and non-empty folder deletion with async/sync/transient-failure convergence |
|
|
| ✓ | drive +download | shortcut | drive_download_dryrun_test.go::TestDriveDownloadDryRun_DefaultNamePlansMetadataBeforeDownload; drive_download_dryrun_test.go::TestDriveDownloadDryRun_ExplicitOutputSkipsMetadata; drive_upload_workflow_test.go::TestDrive_UploadWorkflow | omitted `--output` plans `metas.batch_query` before file download; explicit `--output` skips metadata; live workflow downloads an uploaded file both with explicit output and with default remote-name output | dry-run plus live fixture coverage |
|
|
| ✓ | drive +export | shortcut | drive_export_dryrun_test.go::TestDriveExportDryRun_FileNameMetadata + TestDriveExportDryRun_WikiURLPlansResolveBeforeExportTask + TestDriveExportDryRun_WikiTokenTypePlansResolveBeforeExportTask + TestDriveExportDryRun_MarkdownFetchAPI + TestDriveExportDryRun_BitableBaseOnlySchema | `--url`; `--token`; `--doc-type`; `--file-extension`; `--file-name`; `--output-dir`; `--only-schema`; Wiki URL / `--doc-type wiki` resolve step; markdown fetch omits docs fetch `extra_param` | dry-run only; no live export workflow yet |
|
|
| ✕ | drive +export-download | shortcut | | none | no export-download workflow yet |
|
|
| ✓ | drive +import | shortcut | drive_import_dryrun_test.go::TestDriveImportDryRunFolderTokenWikiProbe + drive_import_workflow_test.go::TestDrive_ImportWorkflow | `--file`; `--type docx`; upload report request shape; async ticket polling; imported token cleanup | dry-run pins the upload/report/import request chain; live workflow imports a real Markdown fixture and deletes the resulting docx |
|
|
| ✕ | drive +move | shortcut | | none | no move workflow yet |
|
|
| ✓ | drive +pull | shortcut | drive_pull_dryrun_test.go::TestDrive_PullDryRun + drive_duplicate_sync_workflow_test.go::TestDrive_DuplicateRemoteWorkflow | `--local-dir`; `--folder-token`; `--on-duplicate-remote=rename\|newest\|oldest`; `--delete-local --yes` guard | dry-run locks flag/validate shape; live workflow proves duplicate fail-fast and rename recovery |
|
|
| ✓ | drive +push | shortcut | drive_push_dryrun_test.go::TestDrive_PushDryRun + drive_duplicate_sync_workflow_test.go::TestDrive_DuplicateRemoteWorkflow | `--local-dir`; `--folder-token`; `--if-exists`; `--on-duplicate-remote=newest\|oldest`; `--delete-remote --yes` | dry-run locks flag/validate shape; live workflow proves overwrite + duplicate cleanup converges status |
|
|
| ✓ | drive +secure-label-list | shortcut | drive_secure_label_dryrun_test.go::TestDrive_SecureLabelDryRun | `--page-size`; `--page-token`; `--lang` | dry-run only; live label availability depends on tenant security-label configuration |
|
|
| ✓ | drive +secure-label-update | shortcut | drive_secure_label_dryrun_test.go::TestDrive_SecureLabelDryRun | `--token` URL inference; `--type`; `--label-id` body | dry-run only; live update can require document-level approval or mutate a fixture document's security level |
|
|
| ✓ | drive +status | shortcut | drive_status_workflow_test.go::TestDrive_StatusWorkflow + drive_status_dryrun_test.go::TestDrive_StatusDryRun + drive_duplicate_sync_workflow_test.go::TestDrive_DuplicateRemoteWorkflow | `--local-dir`; `--folder-token`; bucketed `new_local` / `new_remote` / `modified` / `unchanged` outputs | dry-run pins request shape; live workflows cover both normal hashing buckets and duplicate-remote failure |
|
|
| ✓ | drive +sync | shortcut | drive_sync_dryrun_test.go::TestDrive_SyncDryRun + drive_sync_workflow_test.go::TestDrive_SyncWorkflow + drive_sync_workflow_test.go::TestDrive_SyncEmptyDirWorkflow | `--local-dir`; `--folder-token`; `--on-conflict=remote-wins\|local-wins\|keep-both\|ask`; `--on-duplicate-remote=fail\|newest\|oldest`; `--quick` | dry-run validates request shape, flag acceptance, and path safety guards; live workflow proves new_remote→pull, new_local→push, remote-wins/local-wins/keep-both conflict resolution, empty directory creation, and post-sync convergence |
|
|
| ✓ | drive +task_result | shortcut | drive_delete_workflow_test.go::TestDrive_DeleteAsyncWorkflow | `--scenario task_check`; `--task-id` | live delete workflow verifies task polling command can read returned delete tasks |
|
|
| ✓ | drive +update-title | shortcut | drive_update_title_dryrun_test.go::TestDriveUpdateTitleDryRun_DocxURL; drive_update_title_dryrun_test.go::TestDriveUpdateTitleDryRun_BareTokenTypes; drive_update_title_dryrun_test.go::TestDriveUpdateTitleDryRun_FileExtensionGuardPlan; drive_update_title_dryrun_test.go::TestDriveUpdateTitleDryRun_RejectsRemovedErrorPolicy; drive_update_title_dryrun_test.go::TestDriveUpdateTitleDryRun_WikiURLKeepsNodeToken; drive_update_title_workflow_test.go::TestDrive_UpdateTitleWorkflow | `--url` vs bare `--token + --type`; `--type base` alias → `bitable`; `--type folder` / `file` / `wiki`; `type` query param; `--title` (alias `--new-title`) → body `new_title`; `--on-extension-mismatch=keep\|allow` (file only, `metas.batch_query` pre-read); removed `error` policy, whitespace-only title, URL/`--type` conflict and non-file extension policy rejected locally | dry-run locks request shape, the wiki single-request plan and the file two-step guard plan; live workflow renames a file and its folder, walks both extension policies, verifies titles via `drive +inspect`, and pins the `981003` type-mismatch failure |
|
|
| ✓ | drive +upload | shortcut | drive_upload_dryrun_test.go::TestDriveUploadDryRun_WikiTarget + drive_upload_dryrun_test.go::TestDriveUploadDryRun_WithFileToken + drive_upload_workflow_test.go::TestDrive_UploadWorkflow + drive_status_workflow_test.go::TestDrive_StatusWorkflow + drive_duplicate_sync_workflow_test.go::TestDrive_DuplicateRemoteWorkflow | `--wiki-token`; `--file-token`; `parent_type=wiki`; `parent_node`; named uploads into Drive folders; in-place overwrite uploads | dry-run covers wiki-target and overwrite request shapes; live workflows assert returned file tokens, token-stable overwrite behavior, and that uploaded fixtures are consumable by downstream commands |
|
|
| ✕ | drive file.comment.replys create | api | | none | no reply workflow yet |
|
|
| ✕ | drive file.comment.replys delete | api | | none | no reply workflow yet |
|
|
| ✕ | drive file.comment.replys list | api | | none | no reply workflow yet |
|
|
| ✕ | drive file.comment.replys update | api | | none | no reply workflow yet |
|
|
| ✕ | drive file.comments create_v2 | api | | none | no file comment workflow yet |
|
|
| ✕ | drive file.comments list | api | | none | no file comment workflow yet |
|
|
| ✕ | drive file.comments patch | api | | none | no file comment workflow yet |
|
|
| ✕ | drive file.statistics get | api | | none | no statistics workflow yet |
|
|
| ✕ | drive file.view_records list | api | | none | no view-record workflow yet |
|
|
| ✕ | drive files copy | api | | none | endpoint exercised live through `drive +copy` (TestDrive_CopyWorkflow); the raw service command itself has no workflow |
|
|
| ✓ | drive files create_folder | api | drive_files_workflow_test.go::TestDrive_FilesCreateFolderWorkflow/create_folder as bot | `name`; empty `folder_token` in `--data` | |
|
|
| ✕ | drive files list | api | | none | no list workflow yet |
|
|
| ✕ | drive metas batch_query | api | | none | no metadata workflow yet |
|
|
| ✕ | drive permission.members auth | api | | none | permission workflows not covered |
|
|
| ✕ | drive permission.members create | api | | none | permission workflows not covered |
|
|
| ✕ | drive permission.members transfer_owner | api | | none | permission workflows not covered |
|
|
| ✕ | drive user remove_subscription | api | | none | subscription workflows not covered |
|
|
| ✕ | drive user subscription | api | | none | subscription workflows not covered |
|
|
| ✕ | drive user subscription_status | api | | none | subscription workflows not covered |
|