Files
larksuite__cli/tests/cli_e2e/sheets/coverage.md
xiongyuanwen-byted c6c040c2c5 refactor(sheets)!: remove legacy sheets command surface (#2572)
* refactor(sheets)!: remove legacy sheets command surface

The `shortcuts/sheets/backward` package kept 42 pre-refactor command names
(`+create`, `+read`, `+write`, `+create-sheet`, `+media-upload`, ...) alive
alongside the refactored ones. Monitoring puts their combined share below 5%,
so they are dropped along with the machinery that carried them.

Removed with the package:
- the `sheetsAliasReplacement` map and `wrapSheetsBackwardDeprecation`, which
  tagged each alias with a `_notice` deprecation envelope on execution;
- the deprecated cobra group and the custom `sheets --help` usage template that
  existed only to hide it. `applySheetsCompatGroups` becomes
  `applySheetsCommandGroups`: it still groups the `+`-shortcuts so the OpenAPI
  metaapi subcommands keep filing under cobra's stock "Additional Commands".

The deleted package owned no shared logic. Its `parent_type` mapping for image
uploads was, by its own header, a deliberate mirror of the canonical one in
`shortcuts/sheets/helpers.go`; `common.IsLocalOfficeToken` and the drive upload
helpers are untouched and keep their other callers.

E2E tests still drove the removed commands and are ported to the refactored
surface: `+workbook-create` / `+workbook-info` / `+cells-set` / `+cells-get` /
`+cells-search` / `+sheet-*`. The sub-sheet dry-run assertions had to be
rewritten rather than renamed, because the old commands posted to
`sheets/v2/sheets_batch_update` while the new ones invoke
`modify_workbook_structure` over `sheet_ai/v2`. `+update-sheet` fanned out to
`+sheet-rename` + `+sheet-hide` + `+dim-freeze`. Every migrated command was
verified against a live workbook, which is where the assertions come from:
rename and hide answer with a bare revision counter, so their effect is read
back from `+workbook-info` (`sheet_name`, `is_hidden`).

Also updated, since these referenced the removed surface:
- three `skills/lark-drive` reference docs that instructed agents to run
  `sheets +read` / `sheets +find`; these ship embedded in the binary, so the
  instructions would have produced unknown-subcommand errors;
- `skill-template/domains/sheets.md`, deleted: every sheets command it named
  was removed and the cell payload shape it taught
  (`{"type":"formula","text":...}`) is rejected by `+cells-set`;
- stale comments naming `backward.uploadSheetMediaFile` and
  `backward/helpers.go`.

`Shortcut.OnInvoke` and `internal/deprecation` now have no producers. Both are
generic framework plumbing wired into the `_notice` envelope in `cmd/root.go`,
so they are left in place; the `OnInvoke` doc comment no longer claims a caller.

BREAKING CHANGE: removes the 42 pre-refactor sheets commands (`+create`,
`+read`, `+write`, `+append`, `+find`, `+set-style`, `+create-sheet`,
`+update-sheet`, `+add-dimension`, `+set-dropdown`, `+media-upload`,
`+create-filter-view`, ...). They now fail with `unknown subcommand` and carry
no deprecation notice, so a caller still on the old names gets no migration
pointer at runtime.

Replacements for all 42, plus the differences that are not simple renames — the
cell payload vocabulary (`{"type":"formula","text":...}` is now rejected),
response field paths, and `+update-sheet` / `+update-dimension` fanning out to
several commands — are documented in
skills/lark-sheets/references/lark-sheets-legacy-command-migration.md, reachable
at runtime via:

    lark-cli skills read lark-sheets references/lark-sheets-legacy-command-migration.md

* test(sheets): close the assertion gaps found in review

- The append subtest asserted only the ok envelope, so a +cells-set that
  reported success without persisting would pass; the later +cells-search
  covers row 2 only. Read A4:C4 back and assert the row landed. Verified
  non-vacuous against a live sheet: an unwritten row returns cells carrying
  no value, so the read-back fails if the write does not persist.
- Cover the omitted-title +sheet-copy path. The comment on the empty-title
  suite states an omitted --title means "let the server name the copy", but
  nothing exercised it; the new case pins that new_name is absent from the
  payload rather than sent empty.
- Assert tool_name in the shared dry-run loop instead of only in the create
  case, so copy / delete / rename / move cannot pass by selecting a different
  tool on the same /tools/invoke_write endpoint.

* docs(sheets): fix the migration guide examples found in review

- `+table-put --sheets` requires the `{"sheets":[…]}` envelope; the `+append`
  row showed a bare array, which the flag rejects outright ("top level must be
  the object {\"sheets\":[…]}, got a bare JSON array"). Verified both forms
  against a dry-run before and after.
- Tag the diagnostic fence as `text` (markdownlint MD040).
2026-09-01 19:47:18 +08:00

57 lines
10 KiB
Markdown

# Sheets CLI E2E Coverage
## Metrics
- Denominator: 30 leaf commands
- Covered: 23
- Coverage: 76.7%
- Scope note: this table tracks the commands these E2E tests touch plus the gaps
carried over from the pre-refactor surface. It is not a census of the whole
sheets shortcut surface, which is far larger; re-auditing it against the
current command list is a separate follow-up.
## Summary
- TestSheets_CRUDE2EWorkflow: proves `+workbook-create`, `+workbook-info`, `+cells-set`, `+cells-get`, `+cells-search`, and `+workbook-export`; key `t.Run(...)` proof points are `create spreadsheet with +workbook-create as bot`, `read data with +cells-get as bot`, `find cells with +cells-search as bot`, and `export spreadsheet with +workbook-export as bot`. `+cells-search` asserts both halves — one match at `A2` for a term on the sheet, zero for one that is not — so a search that silently matched everything would still fail.
- TestSheets_CreateWorkflowAsUser: proves the UAT path for `sheets +workbook-create` and `sheets +workbook-info` through `create spreadsheet with +workbook-create as user` and `get spreadsheet info with +workbook-info as user`.
- TestSheets_SpreadsheetsResource: proves direct `spreadsheets create`, `spreadsheets get`, and `spreadsheets patch`.
- TestSheets_FilterWorkflow: proves `spreadsheet.sheet.filters create`, `get`, `update`, and `delete`, with supporting sheet setup through `+workbook-create`, `+workbook-info`, and `+cells-set`.
- TestSheets_SheetShortcutsDryRun: proves request shapes for `+sheet-create`, `+sheet-copy`, `+sheet-delete`, `+sheet-rename`, and `+sheet-move` without hitting live APIs — all five reach the backend through `modify_workbook_structure`, so the pinned distinction is the `operation` and the fields packed beside it.
- TestSheets_SheetShortcutsWorkflow: proves live `+sheet-create`, `+sheet-copy`, `+sheet-rename`, `+sheet-hide`, `+dim-freeze`, and `+sheet-delete` flows against a real spreadsheet, verified by reading the workbook back through `+workbook-info` rather than by matching a field in each command's own response. Rename and hide answer with nothing but a revision counter, so the listing is the only place their effect can be observed (`sheet_name`, `is_hidden`); `+dim-freeze` reports `frozen_rows` / `frozen_columns` in its own result.
- TestSheets_ImageUploadDryRunParentType: dry-run coverage for the drive `parent_type` an image upload carries, across every surface a local file enters through — `+cells-set-image` and `+float-image-create`. A native spreadsheet must upload as `sheet_image` and one backed by an imported office file as `office_sheet_file`; a `/wiki/` ref must stay native, because a preview cannot resolve the node and so must not read the split out of the node_token. The negative half matters most: the backend does not validate `parent_node` against `parent_type`, so a wrong value uploads successfully and only surfaces later as an image that will not render.
- TestSheets_ImageUploadDryRunChunked: dry-run coverage for the 20 MB branch in `+cells-set-image`. Under the ceiling the preview shows one `upload_all`; one byte past it, the `upload_prepare` / `upload_part` / `upload_finish` trio Execute actually sends. Before that dispatch existed, an oversized image failed on `upload_all` with a bare `1061002` naming neither the size nor the limit.
- Cleanup note: workflow-created spreadsheets are cleaned up via `drive +delete --type sheet`; those cleanup-only executions are not counted as command coverage because no testcase asserts delete behavior as the primary proof surface.
## Command Table
| Status | Cmd | Type | Testcase | Key parameter shapes | Notes / uncovered reason |
| --- | --- | --- | --- | --- | --- |
| ✓ | sheets +cells-get | shortcut | sheets_crud_workflow_test.go::TestSheets_CRUDE2EWorkflow/read data with +cells-get as bot | `--spreadsheet-token`; `--sheet-id`; `--range` | values are collected out of the decoded payload, not matched against a fixed path: `get_cell_ranges`' response nesting is the backend's |
| ✕ | sheets +cells-merge | shortcut | | none | no merge workflow yet |
| ✕ | sheets +cells-replace | shortcut | | none | no replace workflow yet |
| ✓ | sheets +cells-search | shortcut | sheets_crud_workflow_test.go::TestSheets_CRUDE2EWorkflow/find cells with +cells-search as bot | `--spreadsheet-token`; `--sheet-id`; `--find`; `--range` | asserts the hit (`total_matches` 1, address `A2`) and the miss (`total_matches` 0) |
| ✓ | sheets +cells-set | shortcut | sheets_crud_workflow_test.go::TestSheets_CRUDE2EWorkflow/write data with +cells-set as bot; sheets_crud_workflow_test.go::TestSheets_CRUDE2EWorkflow/append a row with +cells-set as bot; sheets_filter_workflow_test.go::TestSheets_FilterWorkflow/write test data for filtering as bot | `--spreadsheet-token`; `--sheet-id`; `--range`; `--cells` | the append subtest writes past the existing block, which is the auto-expand path the pre-refactor `+append` relied on |
| ✕ | sheets +cells-set-image | shortcut | | dry-run only | live image workflow still missing; parent_type shape covered by sheets_image_upload_dryrun_test.go |
| ✓ | sheets +cells-set-style | shortcut | sheets_call_compat_workflow_test.go::TestSheets_CallCompatWorkflow/style it with an openpyxl border weight | `--spreadsheet-token`; `--range`; `--border-styles` | |
| ✕ | sheets +cells-unmerge | shortcut | | none | no merge workflow yet |
| ✓ | sheets +dim-delete | shortcut | sheets_dim_workflow_test.go::TestSheets_DimShortcutsWorkflow | `--spreadsheet-token`; `--sheet-id`; `--ranges` | |
| ✓ | sheets +dim-freeze | shortcut | sheets_sheet_shortcuts_workflow_test.go::TestSheets_SheetShortcutsWorkflow/freeze rows and columns with +dim-freeze as bot | `--spreadsheet-token`; `--sheet-id`; `--rows`; `--cols` | asserts the reported `frozen_rows` / `frozen_columns` |
| ✓ | sheets +dim-insert | shortcut | sheets_dim_workflow_test.go::TestSheets_DimShortcutsWorkflow | `--spreadsheet-token`; `--sheet-id`; `--position`; `--count` | dry-run inherit-style shape also covered by sheets_sheet_shortcuts_dryrun_test.go |
| ✕ | sheets +dim-move | shortcut | | none | no dimension move workflow yet |
| ✓ | sheets +sheet-copy | shortcut | sheets_sheet_shortcuts_workflow_test.go::TestSheets_SheetShortcutsWorkflow/copy sheet with +sheet-copy as bot | `--spreadsheet-token`; `--sheet-id`; optional `--title`; optional `--index` | dry-run shape also covered by sheets_sheet_shortcuts_dryrun_test.go |
| ✓ | sheets +sheet-create | shortcut | sheets_sheet_shortcuts_workflow_test.go::TestSheets_SheetShortcutsWorkflow/create sheet with +sheet-create as bot; sheets_sheet_list_workflow_test.go::TestSheets_SheetListWorkflow | `--spreadsheet-token`; `--title`; optional `--index` | dry-run shape also covered by sheets_sheet_shortcuts_dryrun_test.go |
| ✓ | sheets +sheet-delete | shortcut | sheets_sheet_shortcuts_workflow_test.go::TestSheets_SheetShortcutsWorkflow/delete sheet with +sheet-delete as bot | `--spreadsheet-token`; `--sheet-id`; `--yes` | dry-run shape also covered by sheets_sheet_shortcuts_dryrun_test.go |
| ✓ | sheets +sheet-hide | shortcut | sheets_sheet_shortcuts_workflow_test.go::TestSheets_SheetShortcutsWorkflow/hide sheet with +sheet-hide as bot | `--spreadsheet-token`; `--sheet-id` | the command answers with a revision only, so `is_hidden` is read back from +workbook-info |
| ✓ | sheets +sheet-list | shortcut | sheets_sheet_list_workflow_test.go::TestSheets_SheetListWorkflow | `--spreadsheet-token` | pinned to forward +workbook-info's sheets entries verbatim |
| ✕ | sheets +sheet-move | shortcut | | dry-run only | no live reordering workflow yet; request shape covered by sheets_sheet_shortcuts_dryrun_test.go |
| ✓ | sheets +sheet-rename | shortcut | sheets_sheet_shortcuts_workflow_test.go::TestSheets_SheetShortcutsWorkflow/rename sheet with +sheet-rename as bot | `--spreadsheet-token`; `--sheet-id`; `--title` | dry-run shape also covered by sheets_sheet_shortcuts_dryrun_test.go |
| ✓ | sheets +workbook-create | shortcut | sheets_crud_workflow_test.go::TestSheets_CRUDE2EWorkflow/create spreadsheet with +workbook-create as bot; sheets_filter_workflow_test.go::TestSheets_FilterWorkflow/create spreadsheet with initial data as bot; sheets_create_workflow_test.go::TestSheets_CreateWorkflowAsUser/create spreadsheet with +workbook-create as user | `--title`; `--folder-token` | token read from `data.spreadsheet.spreadsheet_token` |
| ✓ | sheets +workbook-export | shortcut | sheets_crud_workflow_test.go::TestSheets_CRUDE2EWorkflow/export spreadsheet with +workbook-export as bot | `--spreadsheet-token`; `--file-extension`; `--output-path` | |
| ✓ | sheets +workbook-info | shortcut | sheets_crud_workflow_test.go::TestSheets_CRUDE2EWorkflow/get spreadsheet info with +workbook-info as bot; sheets_filter_workflow_test.go::TestSheets_FilterWorkflow/get sheet info as bot; sheets_create_workflow_test.go::TestSheets_CreateWorkflowAsUser/get spreadsheet info with +workbook-info as user | `--spreadsheet-token` | returns the workbook structure only: sub-sheets under `data.sheets`, no spreadsheet token echo |
| ✓ | sheets spreadsheet.sheet.filters create | api | sheets_filter_workflow_test.go::TestSheets_FilterWorkflow/create filter with spreadsheet.sheet.filters create as bot | `spreadsheet_token`; `sheet_id` in `--params`; filter JSON in `--data` | |
| ✓ | sheets spreadsheet.sheet.filters delete | api | sheets_filter_workflow_test.go::TestSheets_FilterWorkflow/delete filter with spreadsheet.sheet.filters delete as bot | `spreadsheet_token`; `sheet_id` in `--params` | |
| ✓ | sheets spreadsheet.sheet.filters get | api | sheets_filter_workflow_test.go::TestSheets_FilterWorkflow/get filter with spreadsheet.sheet.filters get as bot | `spreadsheet_token`; `sheet_id` in `--params` | |
| ✓ | sheets spreadsheet.sheet.filters update | api | sheets_filter_workflow_test.go::TestSheets_FilterWorkflow/update filter with spreadsheet.sheet.filters update as bot | `spreadsheet_token`; `sheet_id` in `--params`; filter JSON in `--data` | |
| ✕ | sheets spreadsheet.sheets find | api | | none | no direct API workflow yet |
| ✓ | sheets spreadsheets create | api | sheets_crud_workflow_test.go::TestSheets_SpreadsheetsResource/create spreadsheet with spreadsheets create as bot | `title` in `--data` | |
| ✓ | sheets spreadsheets get | api | sheets_crud_workflow_test.go::TestSheets_SpreadsheetsResource/get spreadsheet with spreadsheets get as bot | `spreadsheet_token` in `--params` | |
| ✓ | sheets spreadsheets patch | api | sheets_crud_workflow_test.go::TestSheets_SpreadsheetsResource/patch spreadsheet with spreadsheets patch as bot | `spreadsheet_token` in `--params`; title patch in `--data` | |