From 9dedb7ab2c7985e1e542e10cf70b795530d8cdd5 Mon Sep 17 00:00:00 2001 From: baiqing Date: Sat, 18 Apr 2026 20:55:09 +0800 Subject: [PATCH] test: cover Execute clipboard branch via injectable readClipboardImage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Makes readClipboardImageBytes swappable in tests by routing the call through a package-level variable readClipboardImage. Tests inject a synthetic PNG payload so the full Execute clipboard flow (resolve → create block → upload in-memory bytes → bind) runs under unit test without a real pasteboard. Covers: - TestDocMediaInsertExecuteFromClipboard: end-to-end happy path - TestDocMediaInsertExecuteClipboardReadError: early-return on readClipboardImage() failure --- shortcuts/doc/doc_media_insert.go | 6 +- shortcuts/doc/doc_media_test.go | 107 ++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/shortcuts/doc/doc_media_insert.go b/shortcuts/doc/doc_media_insert.go index 85b6a5de..a52275de 100644 --- a/shortcuts/doc/doc_media_insert.go +++ b/shortcuts/doc/doc_media_insert.go @@ -21,6 +21,10 @@ var alignMap = map[string]int{ "right": 3, } +// readClipboardImage is the clipboard read function, swappable in tests to +// inject synthetic image bytes without depending on the host pasteboard. +var readClipboardImage = readClipboardImageBytes + var DocMediaInsert = common.Shortcut{ Service: "docs", Command: "+media-insert", @@ -112,7 +116,7 @@ var DocMediaInsert = common.Shortcut{ if runtime.Bool("from-clipboard") { fmt.Fprintf(runtime.IO().ErrOut, "Reading image from clipboard...\n") var err error - clipboardContent, err = readClipboardImageBytes() + clipboardContent, err = readClipboardImage() if err != nil { return err } diff --git a/shortcuts/doc/doc_media_test.go b/shortcuts/doc/doc_media_test.go index 14b7c206..1af462b2 100644 --- a/shortcuts/doc/doc_media_test.go +++ b/shortcuts/doc/doc_media_test.go @@ -7,6 +7,7 @@ import ( "bytes" "context" "encoding/json" + "fmt" "net/http" "os" "path/filepath" @@ -350,6 +351,112 @@ func TestUploadDocMediaFileWithContentUsesMultipart(t *testing.T) { } } +func TestDocMediaInsertExecuteFromClipboard(t *testing.T) { + // Covers the Execute clipboard branch end-to-end: read synthetic bytes, + // resolve docx root, create block, upload in-memory content, bind to block. + prev := readClipboardImage + t.Cleanup(func() { readClipboardImage = prev }) + payload := []byte{0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0xAA, 0xBB} + readClipboardImage = func() ([]byte, error) { return payload, nil } + + f, stdout, stderr, reg := cmdutil.TestFactory(t, docsTestConfigWithAppID("docs-clipboard-exec-app")) + documentID := "doxcnClipboardExec1" + + // Step 1: GET root block + reg.Register(&httpmock.Stub{ + Method: "GET", + URL: "/open-apis/docx/v1/documents/" + documentID + "/blocks/" + documentID, + Body: map[string]interface{}{ + "code": 0, "msg": "ok", + "data": map[string]interface{}{ + "block": map[string]interface{}{ + "block_id": documentID, + "children": []interface{}{"existing_block"}, + }, + }, + }, + }) + // Step 2: POST create child block + reg.Register(&httpmock.Stub{ + Method: "POST", + URL: "/open-apis/docx/v1/documents/" + documentID + "/blocks/" + documentID + "/children", + Body: map[string]interface{}{ + "code": 0, "msg": "ok", + "data": map[string]interface{}{ + "children": []interface{}{ + map[string]interface{}{"block_id": "new_image_block"}, + }, + }, + }, + }) + // Step 3: POST upload_all for in-memory bytes + uploadStub := &httpmock.Stub{ + Method: "POST", + URL: "/open-apis/drive/v1/medias/upload_all", + Body: map[string]interface{}{ + "code": 0, + "data": map[string]interface{}{"file_token": "file_clip_abc"}, + }, + } + reg.Register(uploadStub) + // Step 4: PATCH batch_update + reg.Register(&httpmock.Stub{ + Method: "PATCH", + URL: "/open-apis/docx/v1/documents/" + documentID + "/blocks/batch_update", + Body: map[string]interface{}{"code": 0, "msg": "ok"}, + }) + + err := mountAndRunDocs(t, DocMediaInsert, []string{ + "+media-insert", + "--doc", documentID, + "--from-clipboard", + "--as", "bot", + }, f, stdout) + if err != nil { + t.Fatalf("unexpected error: %v — stderr: %s", err, stderr.String()) + } + + // stderr should show clipboard read + file name "clipboard.png" + if !strings.Contains(stderr.String(), "Reading image from clipboard") { + t.Errorf("stderr missing clipboard-read log: %s", stderr.String()) + } + if !strings.Contains(stderr.String(), "clipboard.png") { + t.Errorf("stderr missing clipboard.png file name: %s", stderr.String()) + } + // stdout should include the file_token + if !strings.Contains(stdout.String(), "file_clip_abc") { + t.Errorf("stdout missing file_token: %s", stdout.String()) + } + + // Upload multipart body should contain the synthetic payload bytes. + if !bytes.Contains(uploadStub.CapturedBody, payload) { + t.Errorf("upload body missing clipboard payload bytes") + } +} + +func TestDocMediaInsertExecuteClipboardReadError(t *testing.T) { + // Covers the early-return when clipboard read fails (no osascript etc). + prev := readClipboardImage + t.Cleanup(func() { readClipboardImage = prev }) + readClipboardImage = func() ([]byte, error) { + return nil, fmt.Errorf("clipboard image upload is not supported on test") + } + + f, _, _, _ := cmdutil.TestFactory(t, docsTestConfigWithAppID("docs-clipboard-err-app")) + err := mountAndRunDocs(t, DocMediaInsert, []string{ + "+media-insert", + "--doc", "doxcnXXXXXXXXXXXXXXXXXX", + "--from-clipboard", + "--as", "bot", + }, f, nil) + if err == nil { + t.Fatal("expected clipboard read error, got nil") + } + if !strings.Contains(err.Error(), "clipboard image upload is not supported") { + t.Fatalf("unexpected error: %v", err) + } +} + func TestDocMediaInsertExecuteResolvesWikiBeforeFileCheck(t *testing.T) { f, _, stderr, reg := cmdutil.TestFactory(t, docsTestConfigWithAppID("docs-insert-exec-app")) reg.Register(&httpmock.Stub{