test: cover in-memory Content upload paths for clipboard feature

Adds unit tests for the new Content io.Reader branches introduced by
the clipboard feature:

- UploadDriveMediaAll with in-memory Content (drive_media_upload.go 87.5%)
- UploadDriveMediaMultipart with in-memory Content (84.6%)
- uploadDocMediaFile single-part and multipart with clipboard bytes
  (doc_media_upload.go 0% -> 88.9%)

Adds TestNewRuntimeContextForAPI helper that wires Factory, context,
and bot identity so package tests can invoke DoAPI without mounting
the full cobra command tree.
This commit is contained in:
baiqing
2026-04-17 19:10:02 +08:00
parent 71084fb7b5
commit 58f8bae6e4
3 changed files with 210 additions and 0 deletions

View File

@@ -106,6 +106,98 @@ func TestUploadDriveMediaAllBuildsMultipartBody(t *testing.T) {
}
}
func TestUploadDriveMediaAllWithInMemoryContent(t *testing.T) {
// When Content is provided, FilePath is ignored — the in-memory reader
// is streamed directly into the multipart form. Used by the clipboard
// upload path.
runtime, reg := newDriveMediaUploadTestRuntime(t)
withDriveMediaUploadWorkingDir(t, t.TempDir())
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_mem_123"},
},
}
reg.Register(uploadStub)
payload := []byte{0x89, 0x50, 0x4e, 0x47, 0xde, 0xad}
fileToken, err := UploadDriveMediaAll(runtime, DriveMediaUploadAllConfig{
Content: bytes.NewReader(payload),
FileName: "clipboard.png",
FileSize: int64(len(payload)),
ParentType: "docx_image",
ParentNode: strPtr("blk_parent"),
})
if err != nil {
t.Fatalf("UploadDriveMediaAll() error: %v", err)
}
if fileToken != "file_mem_123" {
t.Fatalf("fileToken = %q, want %q", fileToken, "file_mem_123")
}
body := decodeCapturedDriveMediaMultipartBody(t, uploadStub)
if got := body.Fields["file_name"]; got != "clipboard.png" {
t.Fatalf("file_name = %q, want %q", got, "clipboard.png")
}
if got := body.Files["file"]; !bytes.Equal(got, payload) {
t.Fatalf("uploaded file bytes mismatch; got %v, want %v", got, payload)
}
}
func TestUploadDriveMediaMultipartWithInMemoryContent(t *testing.T) {
// Clipboard multipart upload: Content reader replaces FilePath, and the
// server-declared block plan is honored exactly.
runtime, reg := newDriveMediaUploadTestRuntime(t)
withDriveMediaUploadWorkingDir(t, t.TempDir())
size := MaxDriveMediaUploadSinglePartSize + 1
reg.Register(&httpmock.Stub{
Method: "POST",
URL: "/open-apis/drive/v1/medias/upload_prepare",
Body: map[string]interface{}{
"code": 0,
"data": map[string]interface{}{
"upload_id": "upload_mem_1",
"block_size": float64(4 * 1024 * 1024),
"block_num": float64(6),
},
},
})
for i := 0; i < 6; i++ {
reg.Register(&httpmock.Stub{
Method: "POST",
URL: "/open-apis/drive/v1/medias/upload_part",
Body: map[string]interface{}{"code": 0, "msg": "ok"},
})
}
reg.Register(&httpmock.Stub{
Method: "POST",
URL: "/open-apis/drive/v1/medias/upload_finish",
Body: map[string]interface{}{
"code": 0,
"data": map[string]interface{}{"file_token": "file_mem_multi"},
},
})
payload := bytes.Repeat([]byte{0xAB}, int(size))
fileToken, err := UploadDriveMediaMultipart(runtime, DriveMediaMultipartUploadConfig{
Content: bytes.NewReader(payload),
FileName: "clipboard.png",
FileSize: size,
ParentType: "docx_image",
ParentNode: "",
})
if err != nil {
t.Fatalf("UploadDriveMediaMultipart() error: %v", err)
}
if fileToken != "file_mem_multi" {
t.Fatalf("fileToken = %q, want %q", fileToken, "file_mem_multi")
}
}
func TestUploadDriveMediaMultipartBuildsPreparePartsAndFinish(t *testing.T) {
runtime, reg := newDriveMediaUploadTestRuntime(t)
withDriveMediaUploadWorkingDir(t, t.TempDir())

View File

@@ -9,6 +9,7 @@ import (
"github.com/spf13/cobra"
"github.com/larksuite/cli/internal/cmdutil"
"github.com/larksuite/cli/internal/core"
)
@@ -37,3 +38,16 @@ func TestNewRuntimeContextWithBotInfo(cmd *cobra.Command, cfg *core.CliConfig, i
})
return rctx
}
// TestNewRuntimeContextForAPI creates a RuntimeContext ready for HTTP tests:
// sets Cmd, Config, Factory, context, and bot identity so callers can invoke
// DoAPI / CallAPI directly without wiring through a cobra parent command.
func TestNewRuntimeContextForAPI(ctx context.Context, cmd *cobra.Command, cfg *core.CliConfig, f *cmdutil.Factory) *RuntimeContext {
return &RuntimeContext{
ctx: ctx,
Cmd: cmd,
Config: cfg,
Factory: f,
resolvedAs: core.AsBot,
}
}

View File

@@ -190,6 +190,110 @@ func TestDocMediaInsertDryRunUsesMultipartForLargeFile(t *testing.T) {
}
}
func TestUploadDocMediaFileWithContentUsesSinglePartUpload(t *testing.T) {
// Clipboard path: in-memory bytes (no FilePath) route through
// UploadDriveMediaAll when small enough. This also exercises the
// drive_route_token extra built from docID.
f, _, _, reg := cmdutil.TestFactory(t, docsTestConfigWithAppID("docs-upload-content-app"))
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_content_123"},
},
}
reg.Register(uploadStub)
runtime := common.TestNewRuntimeContextForAPI(
context.Background(),
&cobra.Command{Use: "docs +media-upload"},
docsTestConfigWithAppID("docs-upload-content-app"),
f,
)
payload := []byte{0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a} // PNG magic bytes
fileToken, err := uploadDocMediaFile(
runtime,
"", // no FilePath
bytes.NewReader(payload),
"clipboard.png",
int64(len(payload)),
"docx_image",
"blk_parent",
"doxcnDocID123",
)
if err != nil {
t.Fatalf("uploadDocMediaFile() error: %v", err)
}
if fileToken != "file_content_123" {
t.Fatalf("fileToken = %q, want %q", fileToken, "file_content_123")
}
if !strings.Contains(string(uploadStub.CapturedBody), `drive_route_token`) {
t.Fatalf("expected drive_route_token in extra, captured body did not include it")
}
}
func TestUploadDocMediaFileWithContentUsesMultipart(t *testing.T) {
// Clipboard path: in-memory bytes route through UploadDriveMediaMultipart
// when size exceeds the single-part threshold.
f, _, _, reg := cmdutil.TestFactory(t, docsTestConfigWithAppID("docs-upload-content-multi"))
reg.Register(&httpmock.Stub{
Method: "POST",
URL: "/open-apis/drive/v1/medias/upload_prepare",
Body: map[string]interface{}{
"code": 0,
"data": map[string]interface{}{
"upload_id": "upload_content_multi",
"block_size": float64(4 * 1024 * 1024),
"block_num": float64(6),
},
},
})
for i := 0; i < 6; i++ {
reg.Register(&httpmock.Stub{
Method: "POST",
URL: "/open-apis/drive/v1/medias/upload_part",
Body: map[string]interface{}{"code": 0, "msg": "ok"},
})
}
reg.Register(&httpmock.Stub{
Method: "POST",
URL: "/open-apis/drive/v1/medias/upload_finish",
Body: map[string]interface{}{
"code": 0,
"data": map[string]interface{}{"file_token": "file_content_multi_done"},
},
})
runtime := common.TestNewRuntimeContextForAPI(
context.Background(),
&cobra.Command{Use: "docs +media-upload"},
docsTestConfigWithAppID("docs-upload-content-multi"),
f,
)
size := common.MaxDriveMediaUploadSinglePartSize + 1
payload := bytes.Repeat([]byte{0xAB}, int(size))
fileToken, err := uploadDocMediaFile(
runtime,
"",
bytes.NewReader(payload),
"clipboard.png",
size,
"docx_image",
"blk_parent",
"", // no docID → no extra
)
if err != nil {
t.Fatalf("uploadDocMediaFile() error: %v", err)
}
if fileToken != "file_content_multi_done" {
t.Fatalf("fileToken = %q, want %q", fileToken, "file_content_multi_done")
}
}
func TestDocMediaInsertExecuteResolvesWikiBeforeFileCheck(t *testing.T) {
f, _, stderr, reg := cmdutil.TestFactory(t, docsTestConfigWithAppID("docs-insert-exec-app"))
reg.Register(&httpmock.Stub{