Skip to content

feat: add stable cli e2e tests#401

Open
yxzhaao wants to merge 7 commits intolarksuite:mainfrom
yxzhaao:feat/cli-e2e-tests-for-bot
Open

feat: add stable cli e2e tests#401
yxzhaao wants to merge 7 commits intolarksuite:mainfrom
yxzhaao:feat/cli-e2e-tests-for-bot

Conversation

@yxzhaao
Copy link
Copy Markdown

@yxzhaao yxzhaao commented Apr 10, 2026

Change-Id: I62edf59d179e407954f65f82e94cf5dcf4938080

Summary

This PR adds a stable subset of CLI E2E tests.

Covered commands:

  • base: +base-create, +base-get, +table-create, +table-get, +table-list, +advperm-enable, +role-list, +role-get, +role-update, +role-delete
  • calendar: +event-create, +calendar-create, +calendar-list, +calendar-get, +calendar-update, +agenda, +suggestion
  • contact: +get-user, plus raw api get /open-apis/contact/v3/users for bot-side user discovery
  • docs: +docs-create, +docs-get, +docs-update
  • drive: files create_folder, files move, permission.members auth, user subscription, user subscription_status, user remove_subscription
  • im: +chat-create, +chat-update, chats get, chats link, +messages-send, +messages-mget, +messages-reply
  • sheets: +sheet-create, +sheet-get, +sheet-update, +sheet-delete, spreadsheets get, +filter, +find
  • wiki: nodes create, nodes copy, nodes list, spaces get, spaces get_node, spaces list

Changes

  • Change 1
  • Change 2

Test Plan

  • Unit tests pass
  • Manual local verification confirms the lark xxx command works as expected

Related Issues

  • None

Summary by CodeRabbit

  • Tests
    • Added extensive end-to-end CLI tests covering base (tables/roles/workflow), calendar (events/agenda/suggestions/calendars/meeting time), docs (create/update/fetch), drive (files/folders/move/permissions/subscriptions/import), IM (chats/messages/reply/link), sheets (CRUD/filters/find/export), and wiki node workflows.
  • Tests / Helpers
    • Added reusable E2E helpers for JSON parsing, conditional skipping, resource creation/cleanup, file upload/import, temp attachments, and timestamped naming.

Change-Id: I62edf59d179e407954f65f82e94cf5dcf4938080
@github-actions github-actions bot added the size/S Low-risk docs, CI, test, or chore only changes label Apr 10, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 10, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds comprehensive CLI end-to-end tests and package-local test helpers across base, calendar, contact, docs, drive, im, sheets, and wiki domains, implementing resource lifecycle workflows, task polling, JSON extraction, CI-aware skipping, and standardized cleanup utilities.

Changes

Cohort / File(s) Summary
Base workflows & helpers
tests/cli_e2e/base/base_basic_workflow_test.go, tests/cli_e2e/base/base_role_workflow_test.go, tests/cli_e2e/base/helpers_test.go
New base E2E tests (basic workflow, role CRUD) and extensive helpers for creating/copying bases, tables/fields/records/views/dashboards/blocks/forms/roles/workflows; JSON parsing, CI-aware skip logic, and cleanup orchestration.
Calendar workflows & helpers
tests/cli_e2e/calendar/...
tests/cli_e2e/calendar/calendar_create_event_test.go, tests/cli_e2e/calendar/calendar_find_meeting_time_test.go, tests/cli_e2e/calendar/calendar_manage_calendar_test.go, tests/cli_e2e/calendar/calendar_view_agenda_test.go, tests/cli_e2e/calendar/helpers_test.go
Event create/get/delete, meeting-time suggestions, calendar lifecycle (list/primary/create/patch/search/delete), agenda views; helpers for creating events and fetching primary calendar ID.
Contact
tests/cli_e2e/contact/contact_shortcut_test.go
Discovers users via raw API and validates bot-based user lookup by open_id.
Docs
tests/cli_e2e/docs/docs_create_fetch_test.go, tests/cli_e2e/docs/docs_update_test.go
Document create/fetch and update workflows with title/content assertions and basic cleanup/no-op hooks.
Drive workflows & helpers
tests/cli_e2e/drive/...
tests/cli_e2e/drive/drive_files_workflow_test.go, tests/cli_e2e/drive/drive_move_workflow_test.go, tests/cli_e2e/drive/drive_permission_user_workflow_test.go, tests/cli_e2e/drive/helpers_test.go
Folder creation, file upload/import, move with task polling, permission auth checks, user subscription lifecycle; helpers for temp files, upload/import, polling, and cleanup.
IM workflows & helpers
tests/cli_e2e/im/...
tests/cli_e2e/im/chat_workflow_test.go, tests/cli_e2e/im/message_workflow_test.go, tests/cli_e2e/im/helpers_test.go
Chat create (private/public/options), send (text/markdown), message mget/reply, chat link generation; helpers for chat creation, sending messages, and timestamped suffixes.
Sheets workflows
tests/cli_e2e/sheets/...
tests/cli_e2e/sheets/sheets_crud_workflow_test.go, tests/cli_e2e/sheets/sheets_filter_workflow_test.go
Spreadsheet CRUD and resource subcommands (create/info/write/read/append/find/export), filter create/update/delete, and find operations.
Wiki workflows & helpers
tests/cli_e2e/wiki/...
tests/cli_e2e/wiki/wiki_workflow_test.go, tests/cli_e2e/wiki/helpers_test.go
Wiki node create/read/list/copy flows with JSON payload extraction, capability-aware skipping, and test suffix utilities.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

size/XL, domain/im

Suggested reviewers

  • fangshuyu-768
  • MaxHuang22
  • liangshuo-1

Poem

🐰 I hopped through tests from base to wiki bright,
Helpers in paw to keep each cleanup light,
Events, chats, sheets checked under moon and sun,
Drive tasks polled swiftly until the work was done,
Hooray — the CLI garden's tested and tight!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.88% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description provides a comprehensive summary and lists covered commands across all modules, but the Changes section contains incomplete placeholder text ('Change 1', 'Change 2') rather than actual changes. Replace placeholder text in Changes section with specific descriptions of the test files and helper functions added (e.g., listing new test functions and helpers for each module).
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: add stable cli e2e tests' clearly summarizes the main change—adding end-to-end tests for CLI commands across multiple modules.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Apr 10, 2026

CLA assistant check
All committers have signed the CLA.

@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Apr 10, 2026

Greptile Summary

This PR adds a large suite of CLI E2E tests spanning base, calendar, contact, docs, drive, IM, sheets, and wiki, plus enhances core.go with RunCmdWithRetry, GenerateSuffix, and IsAPIErrorResult. All previous blocking concerns (missing docs/helpers_test.go, unused dead helpers in im, no-op "verify delete acknowledged" sub-test) have been resolved.

Confidence Score: 5/5

Safe to merge; all remaining findings are P2 style/cleanup suggestions that do not affect correctness.

All previously flagged blocking issues are resolved: docs/helpers_test.go is present, unused IM helpers were removed, and the calendar no-assertion sub-test is gone. The remaining findings are cleanup hygiene issues that do not affect test correctness or CI reliability in normal conditions.

tests/cli_e2e/wiki/wiki_workflow_test.go (no cleanup + flaky page_token assertion) and tests/cli_e2e/drive/helpers_test.go (silent cleanup errors)

Important Files Changed

Filename Overview
tests/cli_e2e/core.go Adds RetryOptions, RunCmdWithRetry, GenerateSuffix, IsAPIErrorResult, and firstJSONPayload — all well-implemented with correct backoff and context-cancellation handling.
tests/cli_e2e/base/helpers_test.go Solid helper pattern: timeout-bounded cleanupContext, reportCleanupFailure with not-found suppression — a good template other packages should follow.
tests/cli_e2e/drive/helpers_test.go createDriveFolder cleanup uses context.Background() (no timeout) and discards the RunCmd result entirely, unlike the base package pattern.
tests/cli_e2e/im/helpers_test.go sendMessage accepts a parentT *testing.T parameter that is never used inside the function.
tests/cli_e2e/wiki/wiki_workflow_test.go Creates two wiki nodes with no cleanup (nodes accumulate per run); list spaces assertion on data.page_token is flaky when only one space exists.
tests/cli_e2e/sheets/sheets_crud_workflow_test.go Comprehensive CRUD + filter + export workflow; the +export sub-test skips AssertExitCode, inconsistent with all other sub-tests.
tests/cli_e2e/docs/helpers_test.go Provides createDocsFolderWithRetry and createDocWithRetry referenced by both docs test files, resolving the previously flagged compile error.
tests/cli_e2e/wiki/helpers_test.go findWikiNodeByToken has a good loop-detection guard (seenPageTokens) to prevent infinite pagination.

Reviews (8): Last reviewed commit: "test: stabilize cli e2e bot-only coverag..." | Re-trigger Greptile

Change-Id: I4436100c30adf2694cd06953961f8d77f576fc1e
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

🟠 Major comments (26)
tests/cli_e2e/drive/drive_files_workflow_test.go-41-46 (1)

41-46: ⚠️ Potential issue | 🟠 Major

Make cleanup bounded and assert deletion success.

Cleanup currently uses context.Background() and ignores errors/result (Lines 42-45), which can silently leak folders or hang teardown.

Proposed fix
 		parentT.Cleanup(func() {
-			clie2e.RunCmd(context.Background(), clie2e.Request{
+			cleanupCtx, cleanupCancel := context.WithTimeout(context.Background(), 30*time.Second)
+			defer cleanupCancel()
+			cleanupResult, cleanupErr := clie2e.RunCmd(cleanupCtx, clie2e.Request{
 				Args:   []string{"drive", "files", "delete"},
 				Params: map[string]any{"file_token": folderToken, "type": "folder"},
 			})
+			require.NoError(parentT, cleanupErr)
+			cleanupResult.AssertExitCode(parentT, 0)
 		})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/drive/drive_files_workflow_test.go` around lines 41 - 46, The
cleanup closure uses context.Background() and ignores the RunCmd result, which
can hang or silently fail; change the call inside parentT.Cleanup to use
parentT.Context() instead of context.Background(), capture the returned
result/error from clie2e.RunCmd(Request{Args:[]string{"drive","files","delete"},
Params: map[string]any{"file_token": folderToken, "type":"folder"}}), and assert
deletion succeeded (fail the test or log on non-nil error or non-success
response). Keep the same Request/Params and reference RunCmd, Request, and
folderToken when making the change.
tests/cli_e2e/drive/drive_files_workflow_test.go-17-21 (1)

17-21: ⚠️ Potential issue | 🟠 Major

Add config-dir isolation for this E2E test.

This test can read/write CLI config through the real binary path; please isolate it with t.Setenv at setup (Line 17) to avoid cross-test and local-environment interference.

Proposed fix
 func TestDrive_FilesCreateFolderWorkflow(t *testing.T) {
+	t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())
 	parentT := t
 	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
 	t.Cleanup(cancel)

As per coding guidelines: **/*_test.go: Isolate config state in Go tests by using t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/drive/drive_files_workflow_test.go` around lines 17 - 21, The
test TestDrive_FilesCreateFolderWorkflow should isolate CLI config by calling
t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) at setup (immediately after
obtaining parentT or ctx) so the test uses a private config directory; add that
t.Setenv call before any CLI binary invocations or config reads in the
TestDrive_FilesCreateFolderWorkflow function.
tests/cli_e2e/docs/docs_update_test.go-18-22 (1)

18-22: ⚠️ Potential issue | 🟠 Major

Isolate CLI config state here as well for deterministic E2E behavior.

This test also relies on global config location; that can leak state across tests/runs.

Suggested fix
 func TestDocs_UpdateWorkflow(t *testing.T) {
+	t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())
 	parentT := t
 	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
 	t.Cleanup(cancel)

As per coding guidelines, **/*_test.go: "Isolate config state in Go tests by using t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/docs/docs_update_test.go` around lines 18 - 22, The test
TestDocs_UpdateWorkflow should isolate CLI config state to avoid leaking across
runs: in the TestDocs_UpdateWorkflow function set the environment variable
LARKSUITE_CLI_CONFIG_DIR to a temporary directory using
t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) (or equivalent) before
creating the context/cancel so the CLI reads a per-test config dir; ensure this
is done near the start of TestDocs_UpdateWorkflow to make E2E behavior
deterministic.
tests/cli_e2e/docs/docs_create_fetch_test.go-18-22 (1)

18-22: ⚠️ Potential issue | 🟠 Major

Isolate CLI config state for this test to avoid cross-test interference.

This test currently uses shared default CLI config state. Please scope config storage to a temp dir within the test.

Suggested fix
 func TestDocs_CreateAndFetchWorkflow(t *testing.T) {
+	t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())
 	parentT := t
 	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
 	t.Cleanup(cancel)

As per coding guidelines, **/*_test.go: "Isolate config state in Go tests by using t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/docs/docs_create_fetch_test.go` around lines 18 - 22, The test
TestDocs_CreateAndFetchWorkflow currently uses shared CLI config; to isolate
state, set the LARKSUITE_CLI_CONFIG_DIR env var to a temp dir at the start of
the test (e.g., call t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) inside
TestDocs_CreateAndFetchWorkflow before creating ctx) so the CLI config is scoped
to this test and won't interfere with other tests.
tests/cli_e2e/sheets/sheets_filter_workflow_test.go-19-23 (1)

19-23: ⚠️ Potential issue | 🟠 Major

Isolate config dir in both E2E tests to avoid cross-test contamination.

These tests invoke real CLI flows and should set a per-test config directory; otherwise auth/profile state can leak across tests and produce flaky results.

✅ Suggested patch
 func TestSheets_FilterWorkflow(t *testing.T) {
+	t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())
 	parentT := t
 	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
 	t.Cleanup(cancel)
@@
 func TestSheets_FindWorkflow(t *testing.T) {
+	t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())
 	parentT := t
 	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
 	t.Cleanup(cancel)

As per coding guidelines: **/*_test.go: Isolate config state in Go tests by using t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()).

Also applies to: 176-180

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/sheets/sheets_filter_workflow_test.go` around lines 19 - 23,
The test TestSheets_FilterWorkflow (and the other E2E test block around the same
file) must isolate CLI config to avoid cross-test contamination; add a per-test
config dir by calling t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) early in
each test (e.g., immediately after parentT := t or in the setup for
TestSheets_FilterWorkflow and the other test at ~lines 176-180) so the CLI uses
a unique config directory for that run and prevents auth/profile state leakage
across tests.
tests/cli_e2e/sheets/sheets_crud_workflow_test.go-21-24 (1)

21-24: ⚠️ Potential issue | 🟠 Major

Add per-test CLI config isolation to prevent state leakage.

Both top-level tests should isolate CLI config state before invoking commands. Without this, test order and parallel suite activity can leak identity/config and cause flakes.

✅ Suggested patch
 func TestSheets_CRUDE2EWorkflow(t *testing.T) {
+	t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())
 	parentT := t
 	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
 	t.Cleanup(cancel)
@@
 func TestSheets_SpreadsheetsResource(t *testing.T) {
+	t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())
 	parentT := t
 	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
 	t.Cleanup(cancel)

As per coding guidelines: **/*_test.go: Isolate config state in Go tests by using t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()).

Also applies to: 170-174

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/sheets/sheets_crud_workflow_test.go` around lines 21 - 24, Add
per-test CLI config isolation by calling t.Setenv("LARKSUITE_CLI_CONFIG_DIR",
t.TempDir()) at the start of each top-level test in this file (e.g., inside
TestSheets_CRUDE2EWorkflow immediately after obtaining parentT/ctx and likewise
in the other top-level test in this file) so each test uses an isolated config
dir and prevents state leakage between tests.
tests/cli_e2e/sheets/sheets_crud_workflow_test.go-150-166 (1)

150-166: ⚠️ Potential issue | 🟠 Major

Make export assertion strict; current flow can pass on partial failure.

Line 163 explicitly tolerates an export race and skips AssertExitCode/AssertStdoutStatus. That can hide real command failures and undermines the “stable E2E” objective.

✅ Suggested patch
 	t.Run("export spreadsheet with +export", func(t *testing.T) {
 		require.NotEmpty(t, spreadsheetToken, "spreadsheet token is required")
-
-		// Export is an async operation; verify it initiates correctly
-		// The command may have filesystem race issues but the API call succeeds
+		outputPath := fmt.Sprintf("%s/export.xlsx", t.TempDir())
 		result, err := clie2e.RunCmd(ctx, clie2e.Request{
 			Args: []string{
 				"sheets", "+export",
 				"--spreadsheet-token", spreadsheetToken,
 				"--file-extension", "xlsx",
+				"--output-path", outputPath,
 			},
 		})
 		require.NoError(t, err)
-		// Export initiates successfully and returns file_token even if there's a temp file race
+		result.AssertExitCode(t, 0)
+		result.AssertStdoutStatus(t, true)
 		assert.NotEmpty(t, gjson.Get(result.Stdout, "data.file_token").String(),
 			"export should return file_token, stdout: %s", result.Stdout)
 	})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/sheets/sheets_crud_workflow_test.go` around lines 150 - 166,
The test currently ignores command failures by tolerating a temp-file race after
calling clie2e.RunCmd; update the "export spreadsheet with +export" t.Run to
assert the CLI command succeeded before checking data.file_token by invoking the
project's run-result assertions (use the existing clie2e result helpers)
immediately after clie2e.RunCmd (check result exit code is 0 and stdout status
is success) and only then validate gjson.Get(result.Stdout, "data.file_token")
is not empty; keep the require.NoError check for err and remove the comment that
skips AssertExitCode/AssertStdoutStatus so failures surface reliably.
tests/cli_e2e/wiki/wiki_workflow_test.go-123-146 (1)

123-146: ⚠️ Potential issue | 🟠 Major

Node lookup only checks one page and can miss the created/copied node.

Both list-node checks assume the target appears in the first page (page_size: 50). In non-trivial spaces this can flake. Iterate via data.page_token until found or exhausted.

🔍 Pagination-safe pattern
-		result, err := clie2e.RunCmd(ctx, clie2e.Request{
-			Args:      []string{"wiki", "nodes", "list"},
-			DefaultAs: "bot",
-			Params: map[string]any{
-				"space_id":  spaceID,
-				"page_size": 50,
-			},
-		})
-		require.NoError(t, err)
-		if result.ExitCode != 0 {
-			skipIfWikiUnavailable(t, result, "requires bot wiki node list capability")
-		}
-		result.AssertExitCode(t, 0)
-		result.AssertStdoutStatus(t, 0)
-
-		nodeItem := gjson.Get(result.Stdout, `data.items.#(node_token=="`+createdNodeToken+`")`)
+		pageToken := ""
+		var nodeItem gjson.Result
+		for {
+			params := map[string]any{"space_id": spaceID, "page_size": 50}
+			if pageToken != "" {
+				params["page_token"] = pageToken
+			}
+			result, err := clie2e.RunCmd(ctx, clie2e.Request{
+				Args:      []string{"wiki", "nodes", "list"},
+				DefaultAs: "bot",
+				Params:    params,
+			})
+			require.NoError(t, err)
+			if result.ExitCode != 0 {
+				skipIfWikiUnavailable(t, result, "requires bot wiki node list capability")
+			}
+			result.AssertExitCode(t, 0)
+			result.AssertStdoutStatus(t, 0)
+
+			nodeItem = gjson.Get(result.Stdout, `data.items.#(node_token=="`+createdNodeToken+`")`)
+			if nodeItem.Exists() {
+				break
+			}
+			pageToken = gjson.Get(result.Stdout, "data.page_token").String()
+			if pageToken == "" {
+				break
+			}
+		}
 		assert.True(t, nodeItem.Exists(), "stdout:\n%s", result.Stdout)

Also applies to: 172-194

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/wiki/wiki_workflow_test.go` around lines 123 - 146, The current
test only checks the first page of "wiki nodes list" and can miss the
created/copied node; modify the test that runs clie2e.RunCmd with Args
{"wiki","nodes","list"} to loop over pagination by inspecting
result.Stdout.data.page_token and re-calling clie2e.RunCmd (preserving page_size
and space_id) until either a matching node (use gjson query for node_token ==
createdNodeToken) is found or page_token is empty; update the assertion logic
around nodeItem (and mirror the same pagination-safe change for the other
similar block that checks nodes) so the test only fails if the node is not found
after exhausting pages.
tests/cli_e2e/wiki/wiki_workflow_test.go-17-20 (1)

17-20: ⚠️ Potential issue | 🟠 Major

Isolate CLI config per test to avoid cross-test state leakage.

This test should set its own config directory before invoking CLI commands; otherwise shared state can make E2E outcomes order-dependent.

✅ Minimal fix
 func TestWiki_NodeWorkflow(t *testing.T) {
+	t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())
 	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
 	t.Cleanup(cancel)

As per coding guidelines: Isolate config state in Go tests by using t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/wiki/wiki_workflow_test.go` around lines 17 - 20, The test
TestWiki_NodeWorkflow uses global CLI config and can leak state between tests;
fix it by setting an isolated config dir at the start of the test (before any
CLI invocations) using t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) inside
the TestWiki_NodeWorkflow function (ensure this runs immediately after
ctx/cancel setup and before calling any CLI helper or command).
tests/cli_e2e/base/helpers_test.go-123-143 (1)

123-143: ⚠️ Potential issue | 🟠 Major

createBase lacks cleanup because a +base-delete command is not available.

Unlike createTable, createField, and other helpers that register t.Cleanup to delete resources, createBase cannot implement cleanup—there is no +base-delete command in the CLI. This inconsistency means bases will accumulate in the test environment. Either add support for base deletion or document why bases should not be cleaned up.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/base/helpers_test.go` around lines 123 - 143, createBase
currently creates bases without registering t.Cleanup (unlike helpers such as
createTable/createField) because there is no +base-delete CLI command; either
add CLI support for base deletion and update createBase to call
clie2e.RunCmd(... "+base-delete" ...) inside t.Cleanup to remove the created
base (use the returned baseToken to identify the base), or if deletion is
intentionally unsupported add a clear comment and test-suite documentation
explaining why bases are not cleaned up and the expected lifecycle; reference
the createBase function, t.Cleanup, and the missing "+base-delete" command when
implementing the fix.
tests/cli_e2e/drive/drive_permission_user_workflow_test.go-84-97 (1)

84-97: ⚠️ Potential issue | 🟠 Major

Add retry/backoff for subscription_status verification.

Immediate status checks after subscribe can intermittently fail due to propagation delay. Poll with a short bounded retry window before asserting data presence.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/drive/drive_permission_user_workflow_test.go` around lines 84 -
97, The test's immediate check of subscription_status can race with propagation;
wrap the clie2e.RunCmd call for "drive user subscription_status" in a short
bounded retry loop with exponential/backoff sleeps (e.g., 3–5 attempts,
increasing delays) before asserting result.AssertExitCode and require.NotEmpty
on the gjson.Get(..., "data") result. Retry using the same ctx, Args and Params
(eventType) and break early on success; after exhausting attempts fail the test
with the final stdout. Ensure the retry surrounds the RunCmd invocation and the
subsequent gjson.Get/require.NotEmpty check so transient empty responses are
retried.
tests/cli_e2e/drive/helpers_test.go-96-105 (1)

96-105: ⚠️ Potential issue | 🟠 Major

Single-shot import polling is flaky for async completion.

For async import, one +task_result call is not enough for deterministic tests. Poll with bounded retry until token is present (or context timeout), then assert.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/drive/helpers_test.go` around lines 96 - 105, The single-shot
poll using clie2e.RunCmd for "+task_result" is flaky; change the block that runs
clie2e.RunCmd (where ticket != "" and currently assigns pollResult, pollErr,
pollResult.AssertExitCode and docToken) to loop with a bounded retry/backoff
until gjson.Get(pollResult.Stdout, "data.token").String() yields a non-empty
docToken or the ctx times out; on each iteration call clie2e.RunCmd with
clie2e.Request{Args: []string{"drive", "+task_result", "--ticket", ticket,
"--scenario", "import"}}, check pollErr and exit code, sleep briefly between
attempts (or use exponential backoff), then after the loop assert docToken is
non-empty (require.NotEmpty/require.True) so the test deterministically waits
for async completion.
tests/cli_e2e/drive/drive_move_workflow_test.go-41-53 (1)

41-53: ⚠️ Potential issue | 🟠 Major

task_check path should poll until terminal state.

This does only one +task_result check; move tasks can still be in progress. Add bounded polling and assert terminal success (ready=true, failed=false) before passing the test.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/drive/drive_move_workflow_test.go` around lines 41 - 53, The
test currently performs a single clie2e.RunCmd call for the "+task_result" path
using taskID (in the drive_move_workflow_test.go block) which can miss
in-progress move tasks; replace the single check with a bounded retry loop that
repeatedly calls clie2e.RunCmd for the same Args (including "--task-id" taskID
and "--scenario" "task_check") until a terminal state or timeout is reached,
asserting each RunCmd succeeds, and finally assert the terminal result indicates
success by verifying the task JSON fields (e.g., ready==true and failed==false)
instead of only using taskResult.AssertStdoutStatus; ensure the loop has a
reasonable max retries and sleep between attempts to avoid flakiness.
tests/cli_e2e/drive/helpers_test.go-30-32 (1)

30-32: ⚠️ Potential issue | 🟠 Major

Use collision-proof temp filenames for e2e stability.

Second-resolution timestamps can collide across parallel/subsequent runs and cause flaky overwrites. Use os.CreateTemp in the fixture directory instead.

Suggested fix
-	fileName := suffix + "-" + time.Now().UTC().Format("20060102-150405") + ".txt"
-	filePath := filepath.Join(testDir, fileName)
-	err = os.WriteFile(filePath, []byte(content), 0o644)
+	f, err := os.CreateTemp(testDir, suffix+"-*.txt")
+	require.NoError(t, err)
+	filePath := f.Name()
+	_, err = f.WriteString(content)
+	require.NoError(t, err)
+	require.NoError(t, f.Close())
-	fileName := "drive-e2e-" + suffix + "-" + time.Now().UTC().Format("20060102-150405") + ".md"
-	mdFile := filepath.Join(testDir, fileName)
-	err = os.WriteFile(mdFile, []byte(content), 0o644)
+	f, err := os.CreateTemp(testDir, "drive-e2e-"+suffix+"-*.md")
+	require.NoError(t, err)
+	mdFile := f.Name()
+	_, err = f.WriteString(content)
+	require.NoError(t, err)
+	require.NoError(t, f.Close())

Based on learnings: "In larksuite/cli Go tests (files matching *_test.go), when creating or populating test fixtures under t.TempDir(), use the standard library filesystem APIs: os.Mkdir, os.Create, os.CreateTemp, and os.WriteFile."

Also applies to: 78-80

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/drive/helpers_test.go` around lines 30 - 32, The current test
creates a file with a timestamp-based name (fileName/filePath) then writes it
via os.WriteFile, which can collide; replace that pattern by using os.CreateTemp
in the fixture directory (e.g., call os.CreateTemp(testDir, suffix+"-*.txt") to
get a unique temp file), write the content to the returned *os.File (or use
os.WriteFile on the created path) and close the file; update both occurrences
referenced (the block creating fileName/filePath around lines shown and the
similar block at 78-80) so tests use os.CreateTemp instead of timestamp names.
tests/cli_e2e/calendar/calendar_manage_calendar_test.go-17-20 (1)

17-20: ⚠️ Potential issue | 🟠 Major

Missing per-test config isolation.

Please isolate CLI config path for this test to avoid cross-test coupling.

🔧 Proposed fix
 func TestCalendar_ManageCalendar(t *testing.T) {
+	t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())
 	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
 	t.Cleanup(cancel)
As per coding guidelines, `Isolate config state in Go tests by using t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())`.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/calendar/calendar_manage_calendar_test.go` around lines 17 -
20, TestCalendar_ManageCalendar currently shares global CLI config state across
tests; update the test setup in TestCalendar_ManageCalendar to isolate config by
calling t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) before any CLI usage
(e.g., right after creating ctx/cancel or at start of the test) so the test uses
a unique temporary config directory and avoids cross-test coupling.
tests/cli_e2e/calendar/calendar_view_agenda_test.go-16-19 (1)

16-19: ⚠️ Potential issue | 🟠 Major

Set isolated config directory for test stability.

This test should isolate config state to prevent interference from other tests.

🔧 Proposed fix
 func TestCalendar_ViewAgenda(t *testing.T) {
+	t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())
 	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
 	t.Cleanup(cancel)
As per coding guidelines, `Isolate config state in Go tests by using t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())`.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/calendar/calendar_view_agenda_test.go` around lines 16 - 19, In
TestCalendar_ViewAgenda, isolate CLI config state by setting the
LARKSUITE_CLI_CONFIG_DIR env var to a temp dir at the start of the test (use
t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())); add this immediately after
creating ctx/cancel so TestCalendar_ViewAgenda uses a unique config directory
and avoids cross-test interference.
tests/cli_e2e/calendar/helpers_test.go-39-53 (1)

39-53: ⚠️ Potential issue | 🟠 Major

Bound cleanup delete with a timeout.

Using context.Background() in teardown can hang the suite if the delete command blocks. Use a short timeout context for cleanup.

🔧 Proposed fix
 	parentT.Cleanup(func() {
-		deleteResult, deleteErr := clie2e.RunCmd(context.Background(), clie2e.Request{
+		cleanupCtx, cleanupCancel := context.WithTimeout(context.Background(), 30*time.Second)
+		defer cleanupCancel()
+		deleteResult, deleteErr := clie2e.RunCmd(cleanupCtx, clie2e.Request{
 			Args: []string{"calendar", "events", "delete"},
 			Params: map[string]any{
 				"calendar_id": calendarID,
 				"event_id":    eventID,
 			},
 		})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/calendar/helpers_test.go` around lines 39 - 53, The cleanup
uses context.Background() when calling clie2e.RunCmd inside parentT.Cleanup
which can block; change to a short timeout context by creating a context with
context.WithTimeout (e.g., 5–10s) and pass that ctx to clie2e.RunCmd, ensure you
call the cancel function in a defer, and handle a timeout error path (log or
report that the delete timed out) before checking deleteResult.ExitCode so the
teardown cannot hang indefinitely.
tests/cli_e2e/calendar/calendar_find_meeting_time_test.go-16-19 (1)

16-19: ⚠️ Potential issue | 🟠 Major

Isolate CLI config state for this E2E test.

Add per-test config isolation before running commands to avoid state bleed across tests.

🔧 Proposed fix
 func TestCalendar_FindMeetingTime(t *testing.T) {
+	t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())
 	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
 	t.Cleanup(cancel)
As per coding guidelines, `Isolate config state in Go tests by using t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())`.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/calendar/calendar_find_meeting_time_test.go` around lines 16 -
19, In TestCalendar_FindMeetingTime, isolate CLI config state by calling
t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) early in the test (after
ctx/cancel setup and before any CLI invocations) so each run uses a unique
temporary config directory; modify the TestCalendar_FindMeetingTime function to
add this t.Setenv(...) line (use t.TempDir() to create the directory) to prevent
state bleed across tests.
tests/cli_e2e/calendar/calendar_create_event_test.go-18-21 (1)

18-21: ⚠️ Potential issue | 🟠 Major

Add config isolation at test start.

This test should set an isolated CLI config directory to avoid test state leakage.

🔧 Proposed fix
 func TestCalendar_CreateEvent(t *testing.T) {
+	t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())
 	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
 	t.Cleanup(cancel)
As per coding guidelines, `Isolate config state in Go tests by using t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())`.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/calendar/calendar_create_event_test.go` around lines 18 - 21,
In TestCalendar_CreateEvent, set an isolated CLI config directory at the start
to avoid state leakage by calling t.Setenv("LARKSUITE_CLI_CONFIG_DIR",
t.TempDir()) right after creating the test context (in the
TestCalendar_CreateEvent function) so the test uses a temp config directory and
does not share global CLI state.
tests/cli_e2e/calendar/calendar_manage_calendar_test.go-44-99 (1)

44-99: ⚠️ Potential issue | 🟠 Major

Register best-effort cleanup immediately after calendar creation.

Deletion only in the final subtest is brittle; failures/timeouts earlier can leave orphan calendars.

🔧 Proposed fix
 	var createdCalendarID string
+	var calendarDeleted bool
@@
 	t.Run("create calendar", func(t *testing.T) {
 		result, err := clie2e.RunCmd(ctx, clie2e.Request{
@@
 		createdCalendarID = gjson.Get(result.Stdout, "data.calendar.calendar_id").String()
 		require.NotEmpty(t, createdCalendarID)
+
+		t.Cleanup(func() {
+			if createdCalendarID == "" || calendarDeleted {
+				return
+			}
+			cleanupCtx, cleanupCancel := context.WithTimeout(context.Background(), 30*time.Second)
+			defer cleanupCancel()
+			_, _ = clie2e.RunCmd(cleanupCtx, clie2e.Request{
+				Args: []string{"calendar", "calendars", "delete"},
+				Params: map[string]any{
+					"calendar_id": createdCalendarID,
+				},
+			})
+		})
 	})
@@
 	t.Run("delete calendar", func(t *testing.T) {
@@
 		require.NoError(t, err)
 		result.AssertExitCode(t, 0)
 		result.AssertStdoutStatus(t, 0)
+		calendarDeleted = true
 	})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/calendar/calendar_manage_calendar_test.go` around lines 44 -
99, The create-calendar subtest registers the calendar ID in createdCalendarID
but only deletes it later, which can leave orphans if earlier steps fail;
immediately after successfully creating the calendar in the "create calendar"
test (where createdCalendarID is set from gjson.Get(result.Stdout,
"data.calendar.calendar_id")), register a cleanup using t.Cleanup (or defer
inside the subtest) that calls the same clie2e.RunCmd with Args
{"calendar","calendars","delete"} and Params {"calendar_id": createdCalendarID}
so the calendar is removed even if subsequent subtests fail; update references
in the "create calendar" block to ensure createdCalendarID is captured and
cleaned up deterministically.
tests/cli_e2e/im/chat_workflow_test.go-192-195 (1)

192-195: ⚠️ Potential issue | 🟠 Major

Do not print share links in test logs.

share_link is an access-bearing URL and should not be emitted to CI logs.

Suggested fix
 		shareLink := gjson.Get(result.Stdout, "data.share_link").String()
 		require.NotEmpty(t, shareLink, "share_link should not be empty")
-		t.Logf("Generated share link: %s", shareLink)
 	})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/im/chat_workflow_test.go` around lines 192 - 195, The test
currently logs the access-bearing URL via the variable shareLink and
t.Logf("Generated share link: %s", shareLink); remove the sensitive output by
deleting the t.Logf call (or replace it with a non-sensitive assertion/log such
as require.NotEmpty(t, shareLink) and a generic t.Logf("share link
generated")/no details). Locate the code that assigns shareLink and the t.Logf
invocation in the test (shareLink := gjson.Get(result.Stdout,
"data.share_link").String() and t.Logf(...)) and ensure no direct share_link
value is printed to CI logs.
tests/cli_e2e/im/message_workflow_test.go-39-40 (1)

39-40: ⚠️ Potential issue | 🟠 Major

Assert the fetched batch contains the requested message ID.

require.NotEmpty(data) is too weak; this can pass even when the target message is missing. Validate that one returned item matches messageID.

Suggested fix
 		messages := gjson.Get(result.Stdout, "data").Array()
 		require.NotEmpty(t, messages, "should get at least one message")
+		found := false
+		for _, m := range messages {
+			if m.Get("message_id").String() == messageID {
+				found = true
+				break
+			}
+		}
+		require.True(t, found, "expected message_id %s in mget response", messageID)
 	})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/im/message_workflow_test.go` around lines 39 - 40, The test
currently only asserts messages := gjson.Get(result.Stdout, "data").Array() and
require.NotEmpty(t, messages) which can pass even if the requested messageID is
absent; update the assertion to verify at least one returned object has id ==
messageID by iterating messages (or using a gjson path) and asserting a match
for messageID (referencing variables messages, result.Stdout and messageID in
message_workflow_test.go) so the fetched batch actually contains the requested
message.
tests/cli_e2e/im/chat_workflow_test.go-115-137 (1)

115-137: ⚠️ Potential issue | 🟠 Major

Validate update effects, not just command success.

The test currently passes even if +chat-update returns success but does not persist changes. Add a follow-up im chats get assertion for updated name and description.

Suggested enhancement
 	t.Run("update chat description", func(t *testing.T) {
 		result, err := clie2e.RunCmd(ctx, clie2e.Request{
 			Args: []string{"im", "+chat-update",
 				"--chat-id", chatID,
 				"--description", updatedDescription,
 			},
 		})
 		require.NoError(t, err)
 		result.AssertExitCode(t, 0)
 		result.AssertStdoutStatus(t, true)
+
+		getResult, getErr := clie2e.RunCmd(ctx, clie2e.Request{
+			Args:   []string{"im", "chats", "get"},
+			Params: map[string]any{"chat_id": chatID},
+		})
+		require.NoError(t, getErr)
+		getResult.AssertExitCode(t, 0)
+		getResult.AssertStdoutStatus(t, 0)
+		require.Equal(t, updatedName, gjson.Get(getResult.Stdout, "data.name").String())
+		require.Equal(t, updatedDescription, gjson.Get(getResult.Stdout, "data.description").String())
 	})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/im/chat_workflow_test.go` around lines 115 - 137, After calling
clie2e.RunCmd for "+chat-update" (the tests that update name and description),
add a follow-up verification by running clie2e.RunCmd with Args for "im",
"chats", "get", "--chat-id", chatID and assert the returned chat's fields match
the updated values; specifically, after the "+chat-update" that uses updatedName
call "im chats get" and assert the response contains updatedName, and after the
"+chat-update" that uses updatedDescription call "im chats get" and assert the
response contains updatedDescription, using the same
result.AssertExitCode/assertions pattern to ensure persistence.
tests/cli_e2e/im/helpers_test.go-169-171 (1)

169-171: ⚠️ Potential issue | 🟠 Major

Increase suffix entropy to avoid flaky name collisions.

generateSuffix() is second-granular, so parallel/fast E2E runs can generate identical resource names and intermittently fail with “already exists” conflicts.

Suggested fix
 import (
 	"context"
+	"fmt"
 	"testing"
 	"time"
@@
 func generateSuffix() string {
-	return time.Now().UTC().Format("20060102-150405")
+	now := time.Now().UTC()
+	return fmt.Sprintf("%s-%09d", now.Format("20060102-150405"), now.Nanosecond())
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/im/helpers_test.go` around lines 169 - 171, The suffix
generation in generateSuffix() is only second-granular causing collisions;
update generateSuffix() to add higher entropy (e.g. append
time.Now().UTC().Format("20060102-150405.000000000") or include
time.Now().UnixNano() and/or a short cryptographic or math/rand alphanumeric
segment) so names are unique across parallel fast E2E runs; ensure tests that
consume generateSuffix() continue to accept the new format.
tests/cli_e2e/im/message_workflow_test.go-58-81 (1)

58-81: ⚠️ Potential issue | 🟠 Major

Verify reply payload integrity (data.message_id).

Both reply subtests only check command success. They should also assert non-empty returned data.message_id (and ideally that it differs from the original messageID).

Suggested fix
 	t.Run("reply to message with text", func(t *testing.T) {
 		result, err := clie2e.RunCmd(ctx, clie2e.Request{
 			Args: []string{"im", "+messages-reply",
 				"--message-id", messageID,
 				"--text", replyText,
 			},
 		})
 		require.NoError(t, err)
 		result.AssertExitCode(t, 0)
 		result.AssertStdoutStatus(t, true)
+		replyID := gjson.Get(result.Stdout, "data.message_id").String()
+		require.NotEmpty(t, replyID, "reply message_id should not be empty")
+		require.NotEqual(t, messageID, replyID, "reply should produce a new message_id")
 	})
@@
 	t.Run("reply to message with markdown", func(t *testing.T) {
 		markdownReply := "**Bold** reply"
 		result, err := clie2e.RunCmd(ctx, clie2e.Request{
 			Args: []string{"im", "+messages-reply",
 				"--message-id", messageID,
 				"--markdown", markdownReply,
 			},
 		})
 		require.NoError(t, err)
 		result.AssertExitCode(t, 0)
 		result.AssertStdoutStatus(t, true)
+		replyID := gjson.Get(result.Stdout, "data.message_id").String()
+		require.NotEmpty(t, replyID, "reply message_id should not be empty")
+		require.NotEqual(t, messageID, replyID, "reply should produce a new message_id")
 	})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/im/message_workflow_test.go` around lines 58 - 81, The tests
"reply to message with text" and "reply to message with markdown" currently only
assert command success; add assertions that the CLI response contains a
non-empty reply message id and that it differs from the original messageID.
After calling clie2e.RunCmd (in both subtests where variables replyText and
markdownReply are used), parse the command output/result to extract
data.message_id (or use result helper methods if available), assert it is not
empty and assert it is not equal to messageID; keep these checks alongside the
existing Require/AssertExitCode and AssertStdoutStatus calls to ensure payload
integrity.
tests/cli_e2e/im/chat_workflow_test.go-17-21 (1)

17-21: ⚠️ Potential issue | 🟠 Major

Add config-dir isolation to prevent polluting real user CLI state.

These tests invoke the real CLI binary via exec.CommandContext(), which exercises the actual NewDefault() factory path that reads filesystem config. Without isolation, they will use and potentially mutate the real user's ~/.config/lark-cli state.

Add this near the top of each test function (or in a test helper called from each test):

t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())

This isolates config to a temporary directory per test, following the required pattern for Go tests in this codebase.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/im/chat_workflow_test.go` around lines 17 - 21, The test
TestIM_ChatCreateSendWorkflow invokes the real CLI via exec.CommandContext()
which triggers NewDefault() to read/write the real user's config; to isolate
state, set the environment variable LARKSUITE_CLI_CONFIG_DIR to a per-test temp
directory at the start of the test (or in a helper called by the test) using
t.Setenv with t.TempDir() so the CLI uses an isolated config dir and cannot
mutate real user config.
🟡 Minor comments (2)
tests/cli_e2e/drive/helpers_test.go-28-33 (1)

28-33: ⚠️ Potential issue | 🟡 Minor

Handle fixture directory creation errors explicitly.

Both setup paths ignore os.MkdirAll errors, which can hide the real setup failure and produce noisier downstream assertions.

Suggested fix
-	_ = os.MkdirAll(testDir, 0755)
+	err := os.MkdirAll(testDir, 0o755)
+	require.NoError(t, err)
...
-	err := os.WriteFile(filePath, []byte(content), 0644)
+	err = os.WriteFile(filePath, []byte(content), 0o644)
 	require.NoError(t, err)
-	_ = os.MkdirAll(testDir, 0755)
+	err := os.MkdirAll(testDir, 0o755)
+	require.NoError(t, err)
...
-	err := os.WriteFile(mdFile, []byte(content), 0644)
+	err = os.WriteFile(mdFile, []byte(content), 0o644)
 	require.NoError(t, err)

Also applies to: 75-81

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/drive/helpers_test.go` around lines 28 - 33, The setup is
ignoring errors from os.MkdirAll(testDir, 0755); update both occurrences (the
one creating testDir and the one at lines ~75-81) to capture the returned error
and assert it using require.NoError(t, err) (or fail the test via t.Fatalf) so
directory creation failures are reported immediately; look for the os.MkdirAll
calls and add error handling around them referencing testDir and the test's t
variable.
tests/cli_e2e/calendar/calendar_create_event_test.go-95-107 (1)

95-107: ⚠️ Potential issue | 🟡 Minor

verify delete acknowledged is effectively a no-op.

This block has no assertion on deletion behavior, so it can pass without validating anything meaningful.

🔧 Minimal fix (remove no-op check)
-	// Step 5: Verify delete was acknowledged (event may have eventual consistency)
-	t.Run("verify delete acknowledged", func(t *testing.T) {
-		require.NotEmpty(t, eventID)
-		result, err := clie2e.RunCmd(ctx, clie2e.Request{
-			Args: []string{"calendar", "events", "get"},
-			Params: map[string]any{
-				"calendar_id": calendarID,
-				"event_id":    eventID,
-			},
-		})
-		require.NoError(t, err)
-		// Note: API may have eventual consistency - delete acknowledged but get may still succeed briefly
-		_ = result
-	})
+	// Step 5 removed: previous check had no assertions.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/calendar/calendar_create_event_test.go` around lines 95 - 107,
The test block named "verify delete acknowledged" is a no-op and should either
be removed or replaced with a real assertion: call clie2e.RunCmd (as currently
done) with calendar_id and event_id and then assert the delete was acknowledged
by checking the response or error (e.g. require.Error(t, err) or
require.Contains(t, result.String(), "not found") / assert HTTP-not-found in the
result) using eventID and calendarID; update the t.Run body to assert the
expected "not found" or deletion-acknowledged signal instead of discarding
result.
🧹 Nitpick comments (5)
tests/cli_e2e/wiki/helpers_test.go (1)

21-36: Make JSON extraction resilient when stdout is non-empty but non-JSON.

Current logic only checks stderr when stdout is empty. If stdout has noise and stderr contains the JSON error envelope, this helper fails and skip handling won’t run.

♻️ Suggested hardening
 func wikiJSONPayload(t *testing.T, result *clie2e.Result) string {
 	t.Helper()

-	raw := strings.TrimSpace(result.Stdout)
-	if raw == "" {
-		raw = strings.TrimSpace(result.Stderr)
-	}
-
-	start := strings.LastIndex(raw, "\n{")
-	if start >= 0 {
-		start++
-	} else {
-		start = strings.Index(raw, "{")
-	}
-	require.NotEqualf(t, -1, start, "json payload not found:\nstdout:\n%s\nstderr:\n%s", result.Stdout, result.Stderr)
-
-	payload := raw[start:]
-	require.Truef(t, gjson.Valid(payload), "invalid json payload:\n%s", payload)
-	return payload
+	extract := func(raw string) (string, bool) {
+		if raw == "" {
+			return "", false
+		}
+		start := strings.LastIndex(raw, "\n{")
+		if start >= 0 {
+			start++
+		} else {
+			start = strings.Index(raw, "{")
+		}
+		if start < 0 {
+			return "", false
+		}
+		payload := raw[start:]
+		return payload, gjson.Valid(payload)
+	}
+
+	if payload, ok := extract(strings.TrimSpace(result.Stdout)); ok {
+		return payload
+	}
+	if payload, ok := extract(strings.TrimSpace(result.Stderr)); ok {
+		return payload
+	}
+	require.Failf(t, "json payload not found", "stdout:\n%s\nstderr:\n%s", result.Stdout, result.Stderr)
+	return ""
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/wiki/helpers_test.go` around lines 21 - 36, The JSON extraction
currently only falls back to stderr when stdout is empty, causing failures if
stdout contains non-JSON noise and stderr holds the JSON envelope; update the
logic around raw/result to try both outputs: first attempt to extract and
validate a JSON payload from result.Stdout (using the existing LastIndex/Index
logic and gjson.Valid), and if that yields no valid JSON, repeat the same
extraction on result.Stderr, then require a payload; refer to the variables and
helpers like result.Stdout, result.Stderr, raw, start, payload, and gjson.Valid
when implementing the fallback.
tests/cli_e2e/base/base_role_workflow_test.go (1)

118-129: Double-deletion: explicit delete subtest + createRole cleanup handler.

The createRole helper (line 35) registers a t.Cleanup handler that will delete the role. However, this "delete" subtest also explicitly deletes the same role. This results in:

  1. Subtest deletes the role
  2. Cleanup handler attempts to delete the already-deleted role

While isCleanupSuppressedResult handles "not found" errors gracefully, this pattern is unclear. Consider one of:

  • Remove the explicit "delete" subtest since cleanup handles it
  • Don't use createRole for this test and manage the lifecycle manually
  • Add a mechanism to disable the cleanup handler after explicit deletion
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/base/base_role_workflow_test.go` around lines 118 - 129, The
test double-deletes the role because createRole registers a t.Cleanup that
deletes the role, while the "delete" subtest also deletes it; remove the
explicit "delete" t.Run block (the subtest that calls clie2e.RunCmd with
"+role-delete") and rely on the createRole cleanup, keeping the createRole
helper and its t.Cleanup intact so the role lifecycle is managed in one place.
tests/cli_e2e/base/helpers_test.go (1)

510-522: Consider using t.TempDir() instead of the working directory for temporary files.

writeTempAttachment writes files to os.Getwd() (the current working directory). Using t.TempDir() would:

  1. Ensure automatic cleanup even if the test panics before the t.Cleanup runs
  2. Avoid potential filename collisions in parallel test runs
  3. Keep the working directory clean
♻️ Suggested refactor
 func writeTempAttachment(t *testing.T, content string) string {
 	t.Helper()

-	wd, err := os.Getwd()
-	require.NoError(t, err)
-
-	path := filepath.Join(wd, "attachment-"+testSuffix()+".txt")
+	path := filepath.Join(t.TempDir(), "attachment-"+testSuffix()+".txt")
 	err = os.WriteFile(path, []byte(content), 0o644)
 	require.NoError(t, err)
-	t.Cleanup(func() {
-		_ = os.Remove(path)
-	})
-	return "./" + filepath.Base(path)
+	return path
 }

Note: If the CLI requires a relative path specifically, you may need to adjust the return value accordingly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/base/helpers_test.go` around lines 510 - 522, The helper
writeTempAttachment currently writes files into os.Getwd(); change it to create
the file inside the test-specific directory returned by t.TempDir() to avoid
collisions and ensure automatic cleanup: use t.TempDir() to build the file path
(instead of os.Getwd()), write the content there, register no extra cleanup
(t.TempDir is auto-removed), and if the CLI requires a relative path, return the
path relative to the current working directory (e.g., "./"+filepath.Base(path))
or adjust tests to accept absolute paths; update references in tests that call
writeTempAttachment accordingly.
tests/cli_e2e/drive/drive_permission_user_workflow_test.go (1)

99-110: Consider asserting post-remove state for full lifecycle coverage.

After remove_subscription, add a follow-up subscription_status check (with bounded retry) to confirm unsubscription actually took effect.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/drive/drive_permission_user_workflow_test.go` around lines 99 -
110, After the "remove subscription" t.Run block that calls clie2e.RunCmd with
Args ["drive","user","remove_subscription"], add a follow-up check that calls
clie2e.RunCmd for the "subscription_status" command (or equivalent endpoint) to
verify the subscription is removed; implement a bounded retry loop (e.g., fixed
attempts with short sleeps) around the subscription_status call to tolerate
eventual consistency, assert exit code 0 and that the returned status indicates
unsubscribed, and fail the test if the retry window expires—use the same ctx,
clie2e.Request pattern and Result assertions (result.AssertExitCode /
result.AssertStdoutStatus or JSON status assertions) so the lifecycle is fully
validated.
tests/cli_e2e/im/chat_workflow_test.go (1)

28-57: Reuse existing message helpers to reduce duplicated E2E plumbing.

This block repeats the exact RunCmd + assert + extract message_id logic already centralized in sendMessage/sendMarkdown from tests/cli_e2e/im/helpers_test.go.

Suggested refactor
 	t.Run("send text message to chat", func(t *testing.T) {
-		result, err := clie2e.RunCmd(ctx, clie2e.Request{
-			Args: []string{"im", "+messages-send",
-				"--chat-id", chatID,
-				"--text", messageText,
-			},
-		})
-		require.NoError(t, err)
-		result.AssertExitCode(t, 0)
-		result.AssertStdoutStatus(t, true)
-
-		messageID := gjson.Get(result.Stdout, "data.message_id").String()
+		messageID := sendMessage(t, parentT, ctx, chatID, messageText)
 		require.NotEmpty(t, messageID, "message_id should not be empty")
 	})

 	t.Run("send markdown message to chat", func(t *testing.T) {
 		markdownContent := "**Bold** and *italic* text"
-		result, err := clie2e.RunCmd(ctx, clie2e.Request{
-			Args: []string{"im", "+messages-send",
-				"--chat-id", chatID,
-				"--markdown", markdownContent,
-			},
-		})
-		require.NoError(t, err)
-		result.AssertExitCode(t, 0)
-		result.AssertStdoutStatus(t, true)
-
-		messageID := gjson.Get(result.Stdout, "data.message_id").String()
+		messageID := sendMarkdown(t, parentT, ctx, chatID, markdownContent)
 		require.NotEmpty(t, messageID, "message_id should not be empty")
 	})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/im/chat_workflow_test.go` around lines 28 - 57, The two test
subcases duplicate RunCmd/assert/extract logic; replace them with calls to the
centralized helpers sendMessage and sendMarkdown from
tests/cli_e2e/im/helpers_test.go: for the "send text message to chat" case call
sendMessage(ctx, t, clie2e, chatID, messageText) (or the helper's exact
signature) and assert the returned message ID is non-empty; for the "send
markdown message to chat" case call sendMarkdown(ctx, t, clie2e, chatID,
markdownContent) and similarly assert the returned message ID is non-empty,
removing the duplicated RunCmd / require / result handling blocks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 52e5ca1e-ad67-44dc-a364-c504d40548b4

📥 Commits

Reviewing files that changed from the base of the PR and between 344ff88 and 91c28d7.

📒 Files selected for processing (22)
  • tests/cli_e2e/base/base_basic_workflow_test.go
  • tests/cli_e2e/base/base_role_workflow_test.go
  • tests/cli_e2e/base/helpers_test.go
  • tests/cli_e2e/calendar/calendar_create_event_test.go
  • tests/cli_e2e/calendar/calendar_find_meeting_time_test.go
  • tests/cli_e2e/calendar/calendar_manage_calendar_test.go
  • tests/cli_e2e/calendar/calendar_view_agenda_test.go
  • tests/cli_e2e/calendar/helpers_test.go
  • tests/cli_e2e/contact/contact_shortcut_test.go
  • tests/cli_e2e/docs/docs_create_fetch_test.go
  • tests/cli_e2e/docs/docs_update_test.go
  • tests/cli_e2e/drive/drive_files_workflow_test.go
  • tests/cli_e2e/drive/drive_move_workflow_test.go
  • tests/cli_e2e/drive/drive_permission_user_workflow_test.go
  • tests/cli_e2e/drive/helpers_test.go
  • tests/cli_e2e/im/chat_workflow_test.go
  • tests/cli_e2e/im/helpers_test.go
  • tests/cli_e2e/im/message_workflow_test.go
  • tests/cli_e2e/sheets/sheets_crud_workflow_test.go
  • tests/cli_e2e/sheets/sheets_filter_workflow_test.go
  • tests/cli_e2e/wiki/helpers_test.go
  • tests/cli_e2e/wiki/wiki_workflow_test.go

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
tests/cli_e2e/im/helpers_test.go (1)

19-19: No-op cleanup + extra parameter make the helper API misleading.

createChat registers an empty cleanup callback, and sendMessage accepts parentT but doesn’t use it. Consider removing parentT from both helpers (or wiring a real cleanup path).

♻️ Proposed refactor
-func createChat(t *testing.T, parentT *testing.T, ctx context.Context, name string) string {
+func createChat(t *testing.T, ctx context.Context, name string) string {
@@
-	parentT.Cleanup(func() {
-		// Best-effort cleanup - chat will be automatically orphaned
-		// since im chats delete command is not available
-	})
-
 	return chatID
 }
@@
-func sendMessage(t *testing.T, parentT *testing.T, ctx context.Context, chatID string, text string) string {
+func sendMessage(t *testing.T, ctx context.Context, chatID string, text string) string {

Also applies to: 35-38, 44-44

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/im/helpers_test.go` at line 19, The helper API is misleading
because createChat registers a no-op cleanup and sendMessage accepts an unused
parentT parameter; update the helpers by removing the unused parentT parameter
from both createChat and sendMessage and delete the RegisterCleanup no-op call
in createChat (or alternatively implement a real cleanup flow and wire parentT
into that cleanup) so the function signatures and behavior accurately reflect
their responsibilities; locate createChat and sendMessage in the
tests/cli_e2e/im/helpers_test.go file to make these changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/cli_e2e/drive/helpers_test.go`:
- Around line 60-64: The cleanup currently calls parentT.Cleanup with an
unbounded context and ignores errors from clie2e.RunCmd; update the cleanup
closure used with parentT.Cleanup to create a context with timeout (e.g.,
context.WithTimeout) and call clie2e.RunCmd with that context, capture its
returned error and assert/fail if non-nil so teardown failures are visible;
apply the same change to the other cleanup block around lines 109-113 and
reference the clie2e.Request construction and the clie2e.RunCmd invocation to
ensure the deletion command is run with a bounded context and checked result.
- Around line 30-32: Replace the manual timestamp-based filename and
os.WriteFile call with os.CreateTemp to avoid collisions: create a temp file in
testDir using os.CreateTemp(testDir, suffix+"-*.txt") (or use os.CreateTemp("",
suffix+"-*.txt") if testDir is empty), write the test content into the returned
*os.File (or call os.WriteFile on the temp file path), and ensure you close the
file; update the same change for the similar occurrences around lines 78-80
where fileName/filePath and os.WriteFile are used.
- Around line 28-29: The call to os.MkdirAll(testDir, 0755) (and the other
occurrence at lines ~75-77) ignores returned errors; update both sites to
capture the error and fail the test on error (e.g., err := os.MkdirAll(testDir,
0755); if err != nil { t.Fatalf("failed to create %s: %v", testDir, err) } or
use require.NoError(t, err)) so directory setup failures are reported instead of
being swallowed.
- Around line 96-105: The single-shot poll using clie2e.RunCmd with Args
containing "+task_result" and ticket can cause flakes; replace it with a retry
loop that repeatedly calls clie2e.RunCmd(Request{Args: []string{"drive",
"+task_result", "--ticket", ticket, "--scenario", "import"}}) at a short
interval until gjson.Get(pollResult.Stdout, "data.token").String() returns a
non-empty token (docToken) or a timeout is reached; on each attempt assert exit
code and stdout status and break when token is present, and if the timeout
elapses return a clear test failure—implement this around the existing ticket
check and assign docToken only when the token is ready.

In `@tests/cli_e2e/im/helpers_test.go`:
- Around line 63-66: generateSuffix currently uses second precision which leads
to collisions; update the function (generateSuffix) to include higher-resolution
time (e.g., milliseconds or nanoseconds) in the formatted suffix so each call is
unique across rapid/parallel test runs—replace the Format pattern to include
fractional seconds (or use time.Now().UTC().UnixNano() converted to a string)
and ensure callers still expect a string suffix.

---

Nitpick comments:
In `@tests/cli_e2e/im/helpers_test.go`:
- Line 19: The helper API is misleading because createChat registers a no-op
cleanup and sendMessage accepts an unused parentT parameter; update the helpers
by removing the unused parentT parameter from both createChat and sendMessage
and delete the RegisterCleanup no-op call in createChat (or alternatively
implement a real cleanup flow and wire parentT into that cleanup) so the
function signatures and behavior accurately reflect their responsibilities;
locate createChat and sendMessage in the tests/cli_e2e/im/helpers_test.go file
to make these changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 471df52c-6c7f-4469-a5aa-8a23061e353a

📥 Commits

Reviewing files that changed from the base of the PR and between 91c28d7 and 0da7a69.

📒 Files selected for processing (5)
  • tests/cli_e2e/calendar/calendar_create_event_test.go
  • tests/cli_e2e/docs/docs_create_fetch_test.go
  • tests/cli_e2e/docs/docs_update_test.go
  • tests/cli_e2e/drive/helpers_test.go
  • tests/cli_e2e/im/helpers_test.go
✅ Files skipped from review due to trivial changes (1)
  • tests/cli_e2e/docs/docs_create_fetch_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/cli_e2e/docs/docs_update_test.go
  • tests/cli_e2e/calendar/calendar_create_event_test.go

Comment on lines +60 to +64
parentT.Cleanup(func() {
clie2e.RunCmd(context.Background(), clie2e.Request{
Args: []string{"drive", "files", "delete"},
Params: map[string]any{"file_token": fileToken, "type": "file"},
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Make cleanup commands bounded and asserted.

Cleanup currently ignores command errors and uses context.Background() with no timeout, which can hide leaked artifacts or hang teardown.

Suggested fix
 	parentT.Cleanup(func() {
-		clie2e.RunCmd(context.Background(), clie2e.Request{
+		cleanupCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+		defer cancel()
+		cleanupRes, cleanupErr := clie2e.RunCmd(cleanupCtx, clie2e.Request{
 			Args:   []string{"drive", "files", "delete"},
 			Params: map[string]any{"file_token": fileToken, "type": "file"},
 		})
+		require.NoError(parentT, cleanupErr)
+		cleanupRes.AssertExitCode(parentT, 0)
 	})
...
 	parentT.Cleanup(func() {
-		clie2e.RunCmd(context.Background(), clie2e.Request{
+		cleanupCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+		defer cancel()
+		cleanupRes, cleanupErr := clie2e.RunCmd(cleanupCtx, clie2e.Request{
 			Args:   []string{"drive", "files", "delete"},
 			Params: map[string]any{"file_token": docToken, "type": "docx"},
 		})
+		require.NoError(parentT, cleanupErr)
+		cleanupRes.AssertExitCode(parentT, 0)
 	})

Also applies to: 109-113

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/drive/helpers_test.go` around lines 60 - 64, The cleanup
currently calls parentT.Cleanup with an unbounded context and ignores errors
from clie2e.RunCmd; update the cleanup closure used with parentT.Cleanup to
create a context with timeout (e.g., context.WithTimeout) and call clie2e.RunCmd
with that context, capture its returned error and assert/fail if non-nil so
teardown failures are visible; apply the same change to the other cleanup block
around lines 109-113 and reference the clie2e.Request construction and the
clie2e.RunCmd invocation to ensure the deletion command is run with a bounded
context and checked result.

Change-Id: I51e77d857f1fd9aec5ee34adf5045cc695239f21
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
tests/cli_e2e/drive/helpers_test.go (1)

27-28: Use testFileDir constant consistently.

Both helpers rebuild the same directory path instead of using the shared constant, which increases drift risk.

Also applies to: 84-85

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/drive/helpers_test.go` around lines 27 - 28, Replace the
duplicated construction of the test directory path with the shared testFileDir
constant: where the code builds testDir using filepath.Join("tests", "cli_e2e",
"drive", "testfiles") and calls os.MkdirAll(testDir, 0o755) (e.g., variable
testDir and the filepath.Join/os.MkdirAll usage), use the existing testFileDir
constant instead and pass that into os.MkdirAll; do the same fix for the other
occurrence around the second helper (the analogous filepath.Join/os.MkdirAll at
lines 84-85).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/cli_e2e/drive/helpers_test.go`:
- Around line 104-107: The test currently only calls result.AssertExitCode(t, 0)
before extracting ticket and docToken from result.Stdout; add a guard call to
result.AssertStdoutStatus(t, 0) (the same envelope-level assertion used at line
59 and 117) immediately after AssertExitCode and before extracting gjson.Get(...
"data.ticket") and gjson.Get(... "data.token") so the test fails early on
envelope errors and safely proceeds to read ticket and docToken.
- Around line 109-126: The polling loop for getting docToken uses RunCmd(ctx,
...) directly so a single RunCmd call can hang and bypass the outer deadline;
change the loop to create a per-attempt context with timeout (e.g.,
context.WithTimeout(parentCtx, smallPerAttemptTimeout or remaining time until
deadline) and pass that ctx to clie2e.RunCmd(Request{...}) for each iteration,
ensuring you call cancel() after each attempt; keep checking the overall
deadline (time.Now().After(deadline)) and break/fail as before if exceeded.
Reference: clie2e.RunCmd, clie2e.Request, ctx, deadline, ticket, docToken in the
loop.

---

Nitpick comments:
In `@tests/cli_e2e/drive/helpers_test.go`:
- Around line 27-28: Replace the duplicated construction of the test directory
path with the shared testFileDir constant: where the code builds testDir using
filepath.Join("tests", "cli_e2e", "drive", "testfiles") and calls
os.MkdirAll(testDir, 0o755) (e.g., variable testDir and the
filepath.Join/os.MkdirAll usage), use the existing testFileDir constant instead
and pass that into os.MkdirAll; do the same fix for the other occurrence around
the second helper (the analogous filepath.Join/os.MkdirAll at lines 84-85).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0d460d7a-e0be-4206-bb0f-ab07cd155c7d

📥 Commits

Reviewing files that changed from the base of the PR and between 0da7a69 and e7f3f14.

📒 Files selected for processing (2)
  • tests/cli_e2e/drive/helpers_test.go
  • tests/cli_e2e/im/helpers_test.go
✅ Files skipped from review due to trivial changes (1)
  • tests/cli_e2e/im/helpers_test.go

yxzhaao added 2 commits April 10, 2026 18:53
Change-Id: I3d4f034145bd69fb7640e707fcda05146b8754c7
Change-Id: I40d906c9168754ad71ef9fb770ff4c340fc19beb
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
tests/cli_e2e/drive/helpers_test.go (2)

103-118: ⚠️ Potential issue | 🟠 Major

Bound each poll attempt with its own timeout.

Line 105 uses ctx directly for RunCmd. If one call blocks, the Line 115 deadline check won’t execute, and polling can hang.

Suggested patch
 	if ticket != "" {
 		deadline := time.Now().Add(45 * time.Second)
 		for {
-			pollResult, pollErr := clie2e.RunCmd(ctx, clie2e.Request{
+			remaining := time.Until(deadline)
+			if remaining <= 0 {
+				t.Fatalf("import task did not return token before timeout, ticket=%s", ticket)
+			}
+
+			attemptTimeout := 10 * time.Second
+			if remaining < attemptTimeout {
+				attemptTimeout = remaining
+			}
+
+			pollCtx, cancel := context.WithTimeout(ctx, attemptTimeout)
+			pollResult, pollErr := clie2e.RunCmd(pollCtx, clie2e.Request{
 				Args: []string{"drive", "+task_result", "--ticket", ticket, "--scenario", "import"},
 			})
+			cancel()
 			require.NoError(t, pollErr)
 			pollResult.AssertExitCode(t, 0)
 			pollResult.AssertStdoutStatus(t, true)
 			docToken = gjson.Get(pollResult.Stdout, "data.token").String()
 			if docToken != "" {
 				break
 			}
-			if time.Now().After(deadline) {
-				t.Fatalf("import task did not return token before timeout, ticket=%s", ticket)
-			}
 			time.Sleep(2 * time.Second)
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/drive/helpers_test.go` around lines 103 - 118, The polling loop
uses the parent ctx directly for clie2e.RunCmd, so a single blocking call can
hang the whole loop; wrap each RunCmd invocation with a short per-attempt
context timeout (e.g., create ctxAttempt, cancel := context.WithTimeout(ctx,
5*time.Second) before calling clie2e.RunCmd and pass ctxAttempt, then call
cancel() after) so each poll attempt times out independently and the outer
deadline (deadline variable) can still be checked and enforced; update the
RunCmd calls in this loop to use the per-attempt context and handle
context.DeadlineExceeded/errors from RunCmd appropriately.

97-100: ⚠️ Potential issue | 🟡 Minor

Add envelope status assertion before extracting import fields.

Line 97 checks only exit code. Add AssertStdoutStatus before reading data.ticket/data.token so parse logic is guarded consistently.

Suggested patch
 	require.NoError(t, err)
 	result.AssertExitCode(t, 0)
+	result.AssertStdoutStatus(t, true)

 	ticket := gjson.Get(result.Stdout, "data.ticket").String()
 	docToken := gjson.Get(result.Stdout, "data.token").String()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/cli_e2e/drive/helpers_test.go` around lines 97 - 100, Before extracting
import fields, call the test helper to assert the envelope status: add a call to
result.AssertStdoutStatus(t, "success") (or the canonical expected status string
used in other tests) immediately after result.AssertExitCode(t, 0) and before
reading gjson for "data.ticket" and "data.token" so the parse is guarded
consistently; locate the block around result.AssertExitCode,
result.AssertStdoutStatus, and the gjson.Get(...) lines and insert the
AssertStdoutStatus call there.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@tests/cli_e2e/drive/helpers_test.go`:
- Around line 103-118: The polling loop uses the parent ctx directly for
clie2e.RunCmd, so a single blocking call can hang the whole loop; wrap each
RunCmd invocation with a short per-attempt context timeout (e.g., create
ctxAttempt, cancel := context.WithTimeout(ctx, 5*time.Second) before calling
clie2e.RunCmd and pass ctxAttempt, then call cancel() after) so each poll
attempt times out independently and the outer deadline (deadline variable) can
still be checked and enforced; update the RunCmd calls in this loop to use the
per-attempt context and handle context.DeadlineExceeded/errors from RunCmd
appropriately.
- Around line 97-100: Before extracting import fields, call the test helper to
assert the envelope status: add a call to result.AssertStdoutStatus(t,
"success") (or the canonical expected status string used in other tests)
immediately after result.AssertExitCode(t, 0) and before reading gjson for
"data.ticket" and "data.token" so the parse is guarded consistently; locate the
block around result.AssertExitCode, result.AssertStdoutStatus, and the
gjson.Get(...) lines and insert the AssertStdoutStatus call there.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ffeab0f1-f1a9-4fc4-b43c-630024f4838b

📥 Commits

Reviewing files that changed from the base of the PR and between 6e1647b and 2752f41.

📒 Files selected for processing (2)
  • tests/cli_e2e/drive/helpers_test.go
  • tests/cli_e2e/im/helpers_test.go
✅ Files skipped from review due to trivial changes (1)
  • tests/cli_e2e/im/helpers_test.go

Change-Id: I73c21b4b38d4ced7ea27cb327075957ec2b9a2a2
liangshuo-1
liangshuo-1 previously approved these changes Apr 10, 2026
@github-actions
Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@6923808329ef35b4be5ccd2ff33d2979359dba60

🧩 Skill update

npx skills add yxzhaao/cli#feat/cli-e2e-tests-for-bot -y -g

@yxzhaao yxzhaao force-pushed the feat/cli-e2e-tests-for-bot branch from a8330f9 to 517b59f Compare April 11, 2026 11:11
Change-Id: Ied897c37c4f42e446d55d110461aa34ae198195d
@yxzhaao yxzhaao force-pushed the feat/cli-e2e-tests-for-bot branch from 517b59f to 141d802 Compare April 11, 2026 11:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/S Low-risk docs, CI, test, or chore only changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants