mirror of
https://github.com/larksuite/cli.git
synced 2026-07-07 09:11:44 +08:00
Adds an `--api-version v2` path to the docs shortcuts, backed by the
`docs_ai/v1/documents` OpenAPI. DocxXML is the default document format
and Markdown is available as an alternative. Content input is unified
across the three shortcuts via `--content` + `--doc-format`. The v1
(MCP) path is preserved for backward compatibility and now prints a
deprecation notice on use.
Shortcuts:
- `docs +create --api-version v2`: create a document from XML or
Markdown, with optional `--parent-token` or `--parent-position`.
Bot identity continues to auto-grant the current CLI user
full_access on the new document.
- `docs +fetch --api-version v2`: adds `--detail simple|with-ids|full`
for export granularity and `--scope full|outline|range|keyword|section`
for partial reads, along with `--context-before` / `--context-after`,
`--max-depth`, and `--revision-id`.
- `docs +update --api-version v2`: introduces structured operations
via `--command`: `str_replace`, `block_delete`, `block_insert_after`,
`block_copy_insert_after`, `block_replace`, `block_move_after`,
`overwrite`, `append`.
Framework support in `shortcuts/common`:
- `OutRaw` / `OutFormatRaw` emit the JSON envelope with HTML escaping
disabled so XML/HTML document bodies are preserved verbatim.
- New `Shortcut.PostMount` hook runs after a cobra.Command is fully
configured; used here to install a version-aware help function
that hides flags belonging to the inactive `--api-version`.
Also refreshes the lark-doc skill pack (SKILL.md, create/fetch/update
references, new lark-doc-xml and lark-doc-md references, style and
workflow guides), README examples, and downstream skill call sites
(lark-drive, lark-vc, lark-whiteboard, lark-workflow-meeting-summary,
lark-event).
Change-Id: Ide2d86b190a4e21095ae29096e7fb00031d80489
104 lines
2.9 KiB
Go
104 lines
2.9 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
package doc
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
// ── V2 tests ──
|
|
|
|
func TestValidCommandsV2(t *testing.T) {
|
|
expected := map[string]bool{
|
|
"str_replace": true,
|
|
"block_delete": true,
|
|
"block_insert_after": true,
|
|
"block_copy_insert_after": true,
|
|
"block_replace": true,
|
|
"block_move_after": true,
|
|
"overwrite": true,
|
|
"append": true,
|
|
}
|
|
if len(validCommandsV2) != len(expected) {
|
|
t.Fatalf("expected %d commands, got %d", len(expected), len(validCommandsV2))
|
|
}
|
|
for cmd := range validCommandsV2 {
|
|
if !expected[cmd] {
|
|
t.Fatalf("unexpected command %q in validCommandsV2", cmd)
|
|
}
|
|
}
|
|
}
|
|
|
|
// ── V1 tests ──
|
|
|
|
func TestIsWhiteboardCreateMarkdown(t *testing.T) {
|
|
t.Run("blank whiteboard tags", func(t *testing.T) {
|
|
markdown := "<whiteboard type=\"blank\"></whiteboard>\n<whiteboard type=\"blank\"></whiteboard>"
|
|
if !isWhiteboardCreateMarkdown(markdown) {
|
|
t.Fatalf("expected blank whiteboard markdown to be treated as whiteboard creation")
|
|
}
|
|
})
|
|
|
|
t.Run("mermaid code block", func(t *testing.T) {
|
|
markdown := "```mermaid\ngraph TD\nA-->B\n```"
|
|
if !isWhiteboardCreateMarkdown(markdown) {
|
|
t.Fatalf("expected mermaid markdown to be treated as whiteboard creation")
|
|
}
|
|
})
|
|
|
|
t.Run("plain markdown", func(t *testing.T) {
|
|
markdown := "## plain text"
|
|
if isWhiteboardCreateMarkdown(markdown) {
|
|
t.Fatalf("did not expect plain markdown to be treated as whiteboard creation")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestNormalizeWhiteboardResult(t *testing.T) {
|
|
t.Run("adds empty board_tokens when whiteboard creation response omits it", func(t *testing.T) {
|
|
result := map[string]interface{}{
|
|
"success": true,
|
|
}
|
|
|
|
normalizeWhiteboardResult(result, "<whiteboard type=\"blank\"></whiteboard>")
|
|
|
|
got, ok := result["board_tokens"].([]string)
|
|
if !ok {
|
|
t.Fatalf("expected board_tokens to be []string, got %T", result["board_tokens"])
|
|
}
|
|
if len(got) != 0 {
|
|
t.Fatalf("expected empty board_tokens, got %#v", got)
|
|
}
|
|
})
|
|
|
|
t.Run("normalizes board_tokens to string slice", func(t *testing.T) {
|
|
result := map[string]interface{}{
|
|
"board_tokens": []interface{}{"board_1", "board_2"},
|
|
}
|
|
|
|
normalizeWhiteboardResult(result, "<whiteboard type=\"blank\"></whiteboard>")
|
|
|
|
want := []string{"board_1", "board_2"}
|
|
got, ok := result["board_tokens"].([]string)
|
|
if !ok {
|
|
t.Fatalf("expected board_tokens to be []string, got %T", result["board_tokens"])
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("board_tokens mismatch: got %#v want %#v", got, want)
|
|
}
|
|
})
|
|
|
|
t.Run("leaves non whiteboard response unchanged", func(t *testing.T) {
|
|
result := map[string]interface{}{
|
|
"success": true,
|
|
}
|
|
|
|
normalizeWhiteboardResult(result, "## plain text")
|
|
|
|
if _, ok := result["board_tokens"]; ok {
|
|
t.Fatalf("did not expect board_tokens for non-whiteboard markdown")
|
|
}
|
|
})
|
|
}
|