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
29 KiB
29 KiB
Drive CLI E2E Coverage
Metrics
- Denominator: 46 leaf commands
- Covered: 26
- Coverage: 56.5%
Summary
- TestDrive_FilesCreateFolderWorkflow: proves
drive files create_folderincreate_folder as bot; helper asserts the returned folder token and registers best-effort cleanup viadrive files delete. - TestDrive_StatusWorkflow: proves
drive +statusagainst a real Drive folder. Seeds the remote side viadrive +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 expectedrel_pathandfile_token. Cleans up uploaded files and the parent folder via best-effort cleanup hooks. - TestDrive_UploadWorkflow: proves
drive +uploadagainst 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-tokenagainst the returned token, asserts the overwrite keeps the token stable, downloads the file with explicit--outputto confirm the remote content changed, and downloads again without--outputto prove default filename resolution saves the remote name with matching bytes. - TestDriveDownloadDryRun_DefaultNamePlansMetadataBeforeDownload / TestDriveDownloadDryRun_ExplicitOutputSkipsMetadata: dry-run coverage for
drive +download; asserts omitted--outputplans metadata before download for default naming, while explicit--outputpreserves the direct download-only path. - TestDrive_ImportWorkflow: proves
drive +importagainst 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 +statusand defaultdrive +pullboth fail with a typed validation error for the duplicate rel_path, whiledrive +pull --on-duplicate-remote=renamesucceeds, downloads both files, and writes a hashed renamed sibling locally. The other subtest uploads duplicate remote files, runsdrive +push --on-duplicate-remote=newest --if-exists=overwrite --delete-remote --yes, and then re-runsdrive +statusto prove the mirror converged to a single unchangeddup.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--typepath, request method/URL/type-query/perm/remark body shape, optionalremarkomission when unset, and client-side rejection of invalid permissions, unsafe tokens, non-root URL markers, encoded path separators, and conflicting URL/--typeinputs. Runs without hitting the live API. - TestDriveAddCommentDryRun_File / TestDriveAddCommentDryRun_Base: dry-run coverage for
drive +add-commenton supported Drive file and Base targets; pins themetas.batch_query -> files/:token/new_commentsfile chain, Basefile_type=bitable, and Base anchor fields. - TestDriveCopyDryRun_DocxURL / TestDriveCopyDryRun_BareTokenBaseAlias / TestDriveCopyDryRun_MySpaceTarget / TestDriveCopyDryRun_WikiURLResolvesResourceThenCopiesToDrive: dry-run coverage for
drive +copy; asserts URL parsing tofiles/:token/copy, request body shape (name/type/folder_token,--extra key=value→extrakey/value array), folder-URL target parsing,base→bitablenormalization, the--folder-token my_spacetwo-step plan (root_folder/meta -> copywith a placeholder folder_token), the bot-modepost_copy_noteauto-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 typedfailed_preconditionrejection 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 againstvalidate.ResourceName. - TestDrive_CopyWorkflow: proves
drive +copyagainst 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_spaceand 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,--typemismatch (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 tofiles/:token/comments, apps/page/<token>URL parsing withfile_type=apps, defaultis_solved=false, default omittedis_wholeanduser_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, anddrive +react-reply; asserts URL→type inference (incl. Miaoda apps/page/<token>→file_type=appsand Base/base/→file_type=bitable),file_type/page_size/need_reaction/need_relation(docx-gated) query wiring,comment_ids/is_solved/ replycontent.elements[](text_run) / reactionaction+reaction_type+reply_idbody shapes, resolved:comment_id/:reply_idpath segments, the Wikiget_node -> batch_query/get_node -> replies list/get_node -> v2 reactionorchestration plans, and the batch_query wiki dry-run surfacingneed_relationas 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, the1069303 forbiddencross-identity update rejection, the server persisting arbitraryreaction_typestrings, 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 throughdrive +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-commentsfinds it by ID,+add-replyattaches a reply,+list-repliessurfaces it,+update-replyrewrites its content (confirmed by polling+list-repliesuntil the new text lands),+react-replyattaches 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-commentmarks it solved and+restore-commentreopens it (with polling reads between state flips to absorb rate limiting),+delete-reply --yesremoves the created reply, and cleanup deletes the file. - TestDrive_SecureLabelDryRun: dry-run coverage for
drive +secure-label-listanddrive +secure-label-update; asserts label-list query params and update URL→type inference, request method/URL/type query, andlabel-idbody 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 wikitokenget_node -> export_tasksplanning, markdown fetch request shape without docs fetchextra_param, local--file-name/--output-dirmetadata, andbitable.baseonly_schemarequest body without calling live APIs. - TestDriveDeleteDryRunAsyncParams / TestDrive_DeleteAsyncWorkflow: dry-run coverage for
drive +deletepinsDELETE /drive/v1/files/:file_tokenparams withtypeplusasync=trueand the follow-uptask_checkplan; 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-emptytask_id) are verified viadrive +task_result --scenario task_check, sync deletes (emptytask_id) assertdeleted=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 intoPATCH /drive/v1/files/:file_tokenwith thetypequery andnew_titlebody, bare-token--typehandling forbase→bitableandfolder, the--new-titlealias, the single-request wiki plan that keeps the node token (noget_nodeunwrap step), the--type filetwo-step plan (metas.batch_queryfor the extension guard, then the rename) collapsing to one request under--on-extension-mismatch=allow, rejection of the removederrorpolicy, the Miaoda apps redirect (both a/page/URL and--type appsfail locally with a hint pointing at the apps domain), the local rejection of the server-refuseddocandmindnotetypes (both--typeand URL spellings), and the local rejections for a whitespace-only title, a--typethat conflicts with the URL path type, and--on-extension-mismatchpaired with a non-file type. - TestDrive_UpdateTitleWorkflow: proves
drive +update-titleagainst the real backend. Uploads a file into a temporary Drive folder, renames it with an extension-preserving title, reads the title back throughdrive +inspect, then exercises both extension policies on the same file: defaultkeep(title without extension gets.txtappended,extension_appended/previous_titlereported, name verified) andallow(title submitted verbatim, extension dropped on purpose, noprevious_titlebecause 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 docxand asserts the endpoint answers981003while the name stays untouched. Live-probed manually beforehand as user before the assertions were encoded:type=docx/type=folder/type=file/type=wikirenames, thattype=wikineeds the wiki node token (the underlying document token returns981003), that wiki node and underlying docx titles stay in sync in both directions, that a--typemismatch and an unknown token share code981003, that a bot without edit permission gets981004, that an emptynew_titleis accepted by the server and blanks the title (hence the local--titleguard), and that atype=filetitle 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 returns981004 forbiddenfor both user and bot (a document-permission failure, not a scope one), a 25-request burst against the endpoint returns99991400 request trigger frequency limit(classifiedapi/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:99991679for a user token (envelope carriesmissing_scopesplus anauth login --scopehint) and99991672for an app scope that was never applied. Titles are stored verbatim — anew_titleof" spaced title "reads back with both pads intact — which is why the CLI trims--titlebefore sending it.docandmindnoteare listed by the platform metadata but refused by the backend: the service log for a real mindnote token recordsunsupported update file type=mindnoteand answers981002 params errorbefore any token lookup, and probes confirm the type is what fails — a real docx token renames fine under--type docxyet returns981002under--type docor--type mindnote, while unknown tokens under supported types return981003. Both are therefore refused locally. Miaoda apps were probed the same way:type=appsis rejected by gateway field validation (99992402, the same answer a nonsense type gets) while a supported type with an unknown token answers981003, 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|oldestby 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|oldestby the real CLI binary. - Cleanup note:
drive files deleteis 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_WikiTargetandTestDriveUploadDryRun_WithFileTokencover the wiki-target and overwrite request shapes fordrive +upload; live upload/status/duplicate workflows also use real+uploadagainst 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 |