From 67649490142bac8a51e32961714e8e77076a2a4e Mon Sep 17 00:00:00 2001 From: qingniaotonghua <1021281778@qq.com> Date: Fri, 26 Jun 2026 11:45:37 +0800 Subject: [PATCH] fix: remove unsed files --- ...26_06_23_apps_observability_envvar_test.go | 632 ------------------ 1 file changed, 632 deletions(-) delete mode 100644 tests_e2e/apps/2026_06_23_apps_observability_envvar_test.go diff --git a/tests_e2e/apps/2026_06_23_apps_observability_envvar_test.go b/tests_e2e/apps/2026_06_23_apps_observability_envvar_test.go deleted file mode 100644 index d171b3284..000000000 --- a/tests_e2e/apps/2026_06_23_apps_observability_envvar_test.go +++ /dev/null @@ -1,632 +0,0 @@ -// Copyright (c) 2026 Lark Technologies Pte. Ltd. -// SPDX-License-Identifier: MIT - -package apps - -import ( - "context" - "os" - "path/filepath" - "strings" - "testing" - "time" - - clie2e "github.com/larksuite/cli/tests/cli_e2e" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/tidwall/gjson" -) - -const ( - appsE2EAppID = "app_x" - appsSecretValue = "super-secret-value-for-e2e" -) - -func TestAppsObservabilityDryRunContract(t *testing.T) { - cases := []struct { - name string - args []string - method string - url string - assertBody func(*testing.T, string) - }{ - { - name: "log_list_request_shape", - args: []string{ - "apps", "+log-list", - "--app-id", appsE2EAppID, - "--env", "online", - "--level", "error", - "--since", "2026-06-23T10:00:00Z", - "--until", "2026-06-23T11:00:00Z", - "--log-id", "LOG1", - "--log-id", "LOG2", - "--trace-id", "trace-1", - "--keyword", "timeout", - "--min-duration", "200", - "--page-size", "50", - "--page-token", "next-token", - }, - method: "POST", - url: "/open-apis/spark/v1/apps/app_x/search_logs", - assertBody: func(t *testing.T, stdout string) { - assert.Equal(t, "runtime", gjson.Get(stdout, "api.0.body.app_env").String(), "stdout:\n%s", stdout) - assert.Equal(t, "1782208800000000000", gjson.Get(stdout, "api.0.body.start_timestamp_ns").String(), "stdout:\n%s", stdout) - assert.Equal(t, "1782212400000000000", gjson.Get(stdout, "api.0.body.end_timestamp_ns").String(), "stdout:\n%s", stdout) - assert.Equal(t, int64(50), gjson.Get(stdout, "api.0.body.limit").Int(), "stdout:\n%s", stdout) - assert.Equal(t, "next-token", gjson.Get(stdout, "api.0.body.page_token").String(), "stdout:\n%s", stdout) - requireStringArray(t, stdout, "api.0.body.filter.levels", []string{"ERROR"}) - requireStringArray(t, stdout, "api.0.body.filter.log_ids", []string{"LOG1", "LOG2"}) - requireStringArray(t, stdout, "api.0.body.filter.trace_ids", []string{"trace-1"}) - assert.Equal(t, "timeout", gjson.Get(stdout, "api.0.body.filter.keyword").String(), "stdout:\n%s", stdout) - assert.Equal(t, int64(200), gjson.Get(stdout, "api.0.body.filter.min_duration_ms").Int(), "stdout:\n%s", stdout) - }, - }, - { - name: "log_get_uses_search_logs_with_limit_one", - args: []string{ - "apps", "+log-get", - "--app-id", appsE2EAppID, - "--env", "online", - "--log-id", "LOG763372528845174288", - }, - method: "POST", - url: "/open-apis/spark/v1/apps/app_x/search_logs", - assertBody: func(t *testing.T, stdout string) { - assert.Equal(t, "runtime", gjson.Get(stdout, "api.0.body.app_env").String(), "stdout:\n%s", stdout) - assert.Equal(t, int64(1), gjson.Get(stdout, "api.0.body.limit").Int(), "stdout:\n%s", stdout) - requireStringArray(t, stdout, "api.0.body.filter.log_ids", []string{"LOG763372528845174288"}) - }, - }, - { - name: "trace_list_request_shape", - args: []string{ - "apps", "+trace-list", - "--app-id", appsE2EAppID, - "--env", "online", - "--trace-id", "trace-1", - "--root-span", "api-gateway", - "--user-id", "ou_user", - "--since", "2026-06-23T10:00:00Z", - "--until", "2026-06-23T11:00:00Z", - "--page-size", "25", - "--page-token", "next-token", - }, - method: "POST", - url: "/open-apis/spark/v1/apps/app_x/search_traces", - assertBody: func(t *testing.T, stdout string) { - assert.Equal(t, "runtime", gjson.Get(stdout, "api.0.body.app_env").String(), "stdout:\n%s", stdout) - assert.Equal(t, "1782208800000000000", gjson.Get(stdout, "api.0.body.start_timestamp_ns").String(), "stdout:\n%s", stdout) - assert.Equal(t, "1782212400000000000", gjson.Get(stdout, "api.0.body.end_timestamp_ns").String(), "stdout:\n%s", stdout) - assert.Equal(t, int64(25), gjson.Get(stdout, "api.0.body.limit").Int(), "stdout:\n%s", stdout) - assert.Equal(t, "next-token", gjson.Get(stdout, "api.0.body.page_token").String(), "stdout:\n%s", stdout) - requireStringArray(t, stdout, "api.0.body.filter.trace_ids", []string{"trace-1"}) - assert.Equal(t, "api-gateway", gjson.Get(stdout, "api.0.body.filter.keyword").String(), "stdout:\n%s", stdout) - requireStringArray(t, stdout, "api.0.body.filter.user_ids", []string{"ou_user"}) - }, - }, - { - name: "trace_get_request_shape", - args: []string{ - "apps", "+trace-get", - "--app-id", appsE2EAppID, - "--env", "online", - "--trace-id", "359d7ab1d9e222b43ee56619a55f937a", - }, - method: "POST", - url: "/open-apis/spark/v1/apps/app_x/trace", - assertBody: func(t *testing.T, stdout string) { - assert.Equal(t, "runtime", gjson.Get(stdout, "api.0.body.app_env").String(), "stdout:\n%s", stdout) - assert.Equal(t, "359d7ab1d9e222b43ee56619a55f937a", gjson.Get(stdout, "api.0.body.trace_id").String(), "stdout:\n%s", stdout) - }, - }, - { - name: "metric_query_request_shape", - args: []string{ - "apps", "+metric-query", - "--app-id", appsE2EAppID, - "--env", "online", - "--metric", "requests", - "--series", "total", - "--since", "2026-06-23T10:00:00Z", - "--until", "2026-06-23T11:00:00Z", - "--page", "/home", - "--api", "/api/orders", - "--down-sample", "1m", - }, - method: "POST", - url: "/open-apis/spark/v1/apps/app_x/query_metrics_data", - assertBody: func(t *testing.T, stdout string) { - assert.False(t, gjson.Get(stdout, "api.0.body.app_env").Exists(), "metric OpenAPI body should not include app_env, stdout:\n%s", stdout) - assert.Equal(t, "1782208800", gjson.Get(stdout, "api.0.body.start_timestamp").String(), "stdout:\n%s", stdout) - assert.Equal(t, "1782212400", gjson.Get(stdout, "api.0.body.end_timestamp").String(), "stdout:\n%s", stdout) - requireStringArray(t, stdout, "api.0.body.metric_names", []string{"client_api_request_count"}) - assert.Equal(t, "/home", gjson.Get(stdout, "api.0.body.filter.pages.0").String(), "stdout:\n%s", stdout) - assert.Equal(t, "/api/orders", gjson.Get(stdout, "api.0.body.filter.apis.0").String(), "stdout:\n%s", stdout) - assert.Equal(t, "1m", gjson.Get(stdout, "api.0.body.down_sample").String(), "stdout:\n%s", stdout) - assert.True(t, gjson.Get(stdout, "api.0.body.need_pack_lack_point").Exists(), "stdout:\n%s", stdout) - assert.False(t, gjson.Get(stdout, "api.0.body.need_pack_lack_point").Bool(), "stdout:\n%s", stdout) - }, - }, - { - name: "analytics_query_request_shape", - args: []string{ - "apps", "+analytics-query", - "--app-id", appsE2EAppID, - "--env", "online", - "--analytics", "users", - "--series", "active-users", - "--since", "2026-06-23T10:00:00Z", - "--until", "2026-06-23T11:00:00Z", - "--page", "/home", - "--device-type", "desktop", - "--granularity", "week", - }, - method: "POST", - url: "/open-apis/spark/v1/apps/app_x/query_analytics_data", - assertBody: func(t *testing.T, stdout string) { - assert.False(t, gjson.Get(stdout, "api.0.body.app_env").Exists(), "analytics OpenAPI body should not include app_env, stdout:\n%s", stdout) - assert.Equal(t, "1782208800000000000", gjson.Get(stdout, "api.0.body.start_timestamp_ns").String(), "stdout:\n%s", stdout) - assert.Equal(t, "1782212400000000000", gjson.Get(stdout, "api.0.body.end_timestamp_ns").String(), "stdout:\n%s", stdout) - requireStringArray(t, stdout, "api.0.body.metric_types", []string{"ACTIVE_USER"}) - assert.Equal(t, "WEEK", gjson.Get(stdout, "api.0.body.time_aggregation_unit").String(), "stdout:\n%s", stdout) - assert.Equal(t, "/home", gjson.Get(stdout, "api.0.body.filter.page").String(), "stdout:\n%s", stdout) - assert.Equal(t, "desktop", gjson.Get(stdout, "api.0.body.filter.device_types.0").String(), "stdout:\n%s", stdout) - assert.True(t, gjson.Get(stdout, "api.0.body.need_pack_lack_point").Exists(), "stdout:\n%s", stdout) - assert.False(t, gjson.Get(stdout, "api.0.body.need_pack_lack_point").Bool(), "stdout:\n%s", stdout) - assert.False(t, gjson.Get(stdout, "api.0.body.group_by").Exists(), "group_by is intentionally unsupported for now, stdout:\n%s", stdout) - }, - }, - } - - for _, tc := range cases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - t.Cleanup(cancel) - - result := runAppsDryRunCommand(t, ctx, tc.args, false) - result.AssertExitCode(t, 0) - assert.Equal(t, tc.method, gjson.Get(result.Stdout, "api.0.method").String(), "stdout:\n%s", result.Stdout) - assert.Equal(t, tc.url, gjson.Get(result.Stdout, "api.0.url").String(), "stdout:\n%s", result.Stdout) - tc.assertBody(t, result.Stdout) - }) - } -} - -func TestAppsObservabilityRejectsNonOnlineEnv(t *testing.T) { - cases := []struct { - name string - args []string - }{ - { - name: "log_list", - args: []string{"apps", "+log-list", "--app-id", appsE2EAppID, "--env", "dev"}, - }, - { - name: "log_get", - args: []string{"apps", "+log-get", "--app-id", appsE2EAppID, "--env", "dev", "--log-id", "LOG763372528845174288"}, - }, - { - name: "trace_list", - args: []string{"apps", "+trace-list", "--app-id", appsE2EAppID, "--env", "dev"}, - }, - { - name: "trace_get", - args: []string{"apps", "+trace-get", "--app-id", appsE2EAppID, "--env", "dev", "--trace-id", "359d7ab1d9e222b43ee56619a55f937a"}, - }, - { - name: "metric_query", - args: []string{"apps", "+metric-query", "--app-id", appsE2EAppID, "--env", "dev", "--metric", "requests"}, - }, - { - name: "analytics_query", - args: []string{"apps", "+analytics-query", "--app-id", appsE2EAppID, "--env", "dev", "--analytics", "users"}, - }, - } - - for _, tc := range cases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - t.Cleanup(cancel) - - result := runAppsDryRunCommand(t, ctx, tc.args, false) - result.AssertExitCode(t, 2) - raw := errorEnvelope(t, result) - assert.Equal(t, "validation", gjson.Get(raw, "error.type").String(), "error envelope:\n%s", raw) - assert.Equal(t, "invalid_argument", gjson.Get(raw, "error.subtype").String(), "error envelope:\n%s", raw) - assert.Equal(t, "--env", gjson.Get(raw, "error.param").String(), "error envelope:\n%s", raw) - assert.Contains(t, gjson.Get(raw, "error.message").String(), "observability commands only support online", "error envelope:\n%s", raw) - }) - } -} - -func TestAppsEnvVarDryRunAndSafety(t *testing.T) { - t.Run("env_pull_uses_dev_post_body_contract", func(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - t.Cleanup(cancel) - projectDir := filepath.Join(t.TempDir(), "demo") - - result := runAppsDryRunCommand(t, ctx, []string{ - "apps", "+env-pull", - "--app-id", appsE2EAppID, - "--project-path", projectDir, - }, false) - result.AssertExitCode(t, 0) - assert.Equal(t, "POST", gjson.Get(result.Stdout, "api.0.method").String(), "stdout:\n%s", result.Stdout) - assert.Equal(t, "/open-apis/spark/v1/apps/app_x/env_vars", gjson.Get(result.Stdout, "api.0.url").String(), "stdout:\n%s", result.Stdout) - assert.Equal(t, "dev", gjson.Get(result.Stdout, "api.0.body.env").String(), "stdout:\n%s", result.Stdout) - assert.False(t, gjson.Get(result.Stdout, "api.0.params.include_values").Exists(), "stdout:\n%s", result.Stdout) - assert.Equal(t, filepath.Join(projectDir, ".env.local"), gjson.Get(result.Stdout, "env_file").String(), "stdout:\n%s", result.Stdout) - assert.False(t, gjson.Get(result.Stdout, "env_keys").Exists(), "env-pull dry-run must not expose key list, stdout:\n%s", result.Stdout) - assert.NotContains(t, result.Stdout, appsSecretValue, "env-pull dry-run must not leak env values, stdout:\n%s", result.Stdout) - }) - - t.Run("envvar_list_defaults_to_dev_without_values", func(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - t.Cleanup(cancel) - - result := runAppsDryRunCommand(t, ctx, []string{ - "apps", "+envvar-list", - "--app-id", appsE2EAppID, - }, false) - result.AssertExitCode(t, 0) - assert.Equal(t, "POST", gjson.Get(result.Stdout, "api.0.method").String(), "stdout:\n%s", result.Stdout) - assert.Equal(t, "/open-apis/spark/v1/apps/app_x/env_vars", gjson.Get(result.Stdout, "api.0.url").String(), "stdout:\n%s", result.Stdout) - assert.Equal(t, "dev", gjson.Get(result.Stdout, "api.0.body.env").String(), "stdout:\n%s", result.Stdout) - assert.False(t, gjson.Get(result.Stdout, "api.0.params.include_values").Exists(), "stdout:\n%s", result.Stdout) - assert.False(t, gjson.Get(result.Stdout, "api.0.body.value").Exists(), "list dry-run must not send values, stdout:\n%s", result.Stdout) - }) - - t.Run("envvar_set_dev_post_redacts_value", func(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - t.Cleanup(cancel) - - result := runAppsDryRunCommand(t, ctx, []string{ - "apps", "+envvar-set", - "--app-id", appsE2EAppID, - "--env", "dev", - "--key", "API_HOST", - "--value", appsSecretValue, - }, false) - result.AssertExitCode(t, 0) - assert.Equal(t, "POST", gjson.Get(result.Stdout, "api.0.method").String(), "stdout:\n%s", result.Stdout) - assert.Equal(t, "/open-apis/spark/v1/apps/app_x/create_or_update_env_var", gjson.Get(result.Stdout, "api.0.url").String(), "stdout:\n%s", result.Stdout) - assert.Equal(t, "dev", gjson.Get(result.Stdout, "api.0.body.env").String(), "stdout:\n%s", result.Stdout) - assert.Equal(t, "API_HOST", gjson.Get(result.Stdout, "api.0.body.key").String(), "stdout:\n%s", result.Stdout) - assert.NotContains(t, result.Stdout, appsSecretValue, "envvar-set dry-run must not leak raw value in stdout:\n%s", result.Stdout) - assert.NotContains(t, result.Stderr, appsSecretValue, "envvar-set dry-run must not leak raw value in stderr:\n%s", result.Stderr) - }) - - t.Run("envvar_set_online_dry_run_does_not_require_yes", func(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - t.Cleanup(cancel) - - result := runAppsDryRunCommand(t, ctx, []string{ - "apps", "+envvar-set", - "--app-id", appsE2EAppID, - "--env", "online", - "--key", "API_HOST", - "--value", appsSecretValue, - }, false) - result.AssertExitCode(t, 0) - assert.Equal(t, "POST", gjson.Get(result.Stdout, "api.0.method").String(), "stdout:\n%s", result.Stdout) - assert.Equal(t, "/open-apis/spark/v1/apps/app_x/create_or_update_env_var", gjson.Get(result.Stdout, "api.0.url").String(), "stdout:\n%s", result.Stdout) - assert.Equal(t, "online", gjson.Get(result.Stdout, "api.0.body.env").String(), "stdout:\n%s", result.Stdout) - assert.Equal(t, "API_HOST", gjson.Get(result.Stdout, "api.0.body.key").String(), "stdout:\n%s", result.Stdout) - assert.NotContains(t, result.Stdout, appsSecretValue, "online dry-run must not leak raw value in stdout:\n%s", result.Stdout) - }) - - t.Run("envvar_set_online_requires_yes_without_dry_run", func(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - t.Cleanup(cancel) - - result := runAppsCommand(t, ctx, []string{ - "apps", "+envvar-set", - "--app-id", appsE2EAppID, - "--env", "online", - "--key", "API_HOST", - "--value", appsSecretValue, - }, false) - result.AssertExitCode(t, 10) - raw := errorEnvelope(t, result) - assert.Equal(t, "confirmation", gjson.Get(raw, "error.type").String(), "error envelope:\n%s", raw) - assert.Equal(t, "confirmation_required", gjson.Get(raw, "error.subtype").String(), "error envelope:\n%s", raw) - assert.Contains(t, gjson.Get(raw, "error.hint").String(), "add --yes to confirm", "error envelope:\n%s", raw) - assert.NotContains(t, raw, appsSecretValue, "confirmation error must not leak raw value:\n%s", raw) - }) - - t.Run("envvar_delete_dry_run_body", func(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - t.Cleanup(cancel) - - result := runAppsDryRunCommand(t, ctx, []string{ - "apps", "+envvar-delete", - "--app-id", appsE2EAppID, - "--env", "dev", - "--key", "API_HOST", - "--key", "API_TOKEN", - }, true) - result.AssertExitCode(t, 0) - assert.Equal(t, "POST", gjson.Get(result.Stdout, "api.0.method").String(), "stdout:\n%s", result.Stdout) - assert.Equal(t, "/open-apis/spark/v1/apps/app_x/delete_env_vars", gjson.Get(result.Stdout, "api.0.url").String(), "stdout:\n%s", result.Stdout) - assert.Equal(t, "dev", gjson.Get(result.Stdout, "api.0.body.env").String(), "stdout:\n%s", result.Stdout) - requireStringArray(t, result.Stdout, "api.0.body.keys", []string{"API_HOST", "API_TOKEN"}) - assert.False(t, gjson.Get(result.Stdout, "api.0.body.value").Exists(), "delete body must not contain values, stdout:\n%s", result.Stdout) - }) - - t.Run("envvar_delete_requires_yes_without_dry_run", func(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - t.Cleanup(cancel) - - result := runAppsCommand(t, ctx, []string{ - "apps", "+envvar-delete", - "--app-id", appsE2EAppID, - "--env", "dev", - "--key", "API_HOST", - }, false) - result.AssertExitCode(t, 10) - raw := errorEnvelope(t, result) - assert.Equal(t, "confirmation", gjson.Get(raw, "error.type").String(), "error envelope:\n%s", raw) - assert.Equal(t, "confirmation_required", gjson.Get(raw, "error.subtype").String(), "error envelope:\n%s", raw) - }) -} - -func TestAppsObservabilityLiveFixtureOutputs(t *testing.T) { - appID := os.Getenv("LARK_CLI_E2E_APPS_OBSERVABILITY_APP_ID") - logID := os.Getenv("LARK_CLI_E2E_APPS_LOG_ID") - traceID := os.Getenv("LARK_CLI_E2E_APPS_TRACE_ID") - if appID == "" || logID == "" || traceID == "" { - t.Skip("FIXTURE: Set LARK_CLI_E2E_APPS_OBSERVABILITY_APP_ID, LARK_CLI_E2E_APPS_LOG_ID, and LARK_CLI_E2E_APPS_TRACE_ID to an online app with visible log, trace, metric, and analytics data") - } - - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - t.Cleanup(cancel) - - t.Run("log_get_returns_fixture_log", func(t *testing.T) { - result := runAppsLiveCommand(t, ctx, []string{ - "apps", "+log-get", - "--app-id", appID, - "--env", "online", - "--log-id", logID, - }, false) - result.AssertExitCode(t, 0) - result.AssertStdoutStatus(t, true) - assert.Equal(t, logID, gjson.Get(result.Stdout, "data.log_id").String(), "stdout:\n%s", result.Stdout) - }) - - t.Run("trace_get_returns_fixture_trace", func(t *testing.T) { - result := runAppsLiveCommand(t, ctx, []string{ - "apps", "+trace-get", - "--app-id", appID, - "--env", "online", - "--trace-id", traceID, - }, false) - result.AssertExitCode(t, 0) - result.AssertStdoutStatus(t, true) - assert.Equal(t, traceID, gjson.Get(result.Stdout, "data.trace_id").String(), "stdout:\n%s", result.Stdout) - require.NotEmpty(t, gjson.Get(result.Stdout, "data.spans").Array(), "trace should include spans, stdout:\n%s", result.Stdout) - }) - - t.Run("metric_query_returns_request_series", func(t *testing.T) { - result := runAppsLiveCommand(t, ctx, []string{ - "apps", "+metric-query", - "--app-id", appID, - "--env", "online", - "--metric", "requests", - "--series", "total", - }, false) - result.AssertExitCode(t, 0) - result.AssertStdoutStatus(t, true) - items := gjson.Get(result.Stdout, "data.items").Array() - require.NotEmpty(t, items, "fixture app should have request metric points, stdout:\n%s", result.Stdout) - assert.True(t, items[0].Get("values.total").Exists(), "request metric should expose total values, stdout:\n%s", result.Stdout) - }) - - t.Run("analytics_query_returns_active_users", func(t *testing.T) { - result := runAppsLiveCommand(t, ctx, []string{ - "apps", "+analytics-query", - "--app-id", appID, - "--env", "online", - "--analytics", "users", - "--series", "active-users", - }, false) - result.AssertExitCode(t, 0) - result.AssertStdoutStatus(t, true) - items := gjson.Get(result.Stdout, "data.items").Array() - require.NotEmpty(t, items, "fixture app should have analytics points, stdout:\n%s", result.Stdout) - assert.True(t, items[0].Get("values.active-users").Exists(), "analytics should expose active-users values, stdout:\n%s", result.Stdout) - }) -} - -func TestAppsEnvVarLiveWorkflow(t *testing.T) { - appID := os.Getenv("LARK_CLI_E2E_APPS_ENVVAR_APP_ID") - if appID == "" { - t.Skip("FIXTURE: Set LARK_CLI_E2E_APPS_ENVVAR_APP_ID to an app where the user identity may create, list, and delete online env vars") - } - - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - t.Cleanup(cancel) - - suffix := strings.NewReplacer("-", "_").Replace(clie2e.GenerateSuffix()) - key := "LARK_CLI_E2E_" + suffix - value := "secret-value-" + suffix - created := false - - t.Cleanup(func() { - if !created { - return - } - cleanupCtx, cleanupCancel := clie2e.CleanupContext() - defer cleanupCancel() - deleteResult, deleteErr := clie2e.RunCmd(cleanupCtx, clie2e.Request{ - Args: []string{ - "apps", "+envvar-delete", - "--app-id", appID, - "--env", "online", - "--key", key, - }, - DefaultAs: "user", - Env: appsNoNoticeEnv(), - Yes: true, - }) - clie2e.ReportCleanupFailure(t, "delete apps envvar "+key, deleteResult, deleteErr) - }) - - t.Run("set_online_redacts_value", func(t *testing.T) { - result := runAppsLiveCommand(t, ctx, []string{ - "apps", "+envvar-set", - "--app-id", appID, - "--env", "online", - "--key", key, - "--value", value, - }, true) - if result.ExitCode == 0 { - created = true - } - result.AssertExitCode(t, 0) - result.AssertStdoutStatus(t, true) - assert.Equal(t, key, gjson.Get(result.Stdout, "data.key").String(), "stdout:\n%s", result.Stdout) - assert.Equal(t, "online", gjson.Get(result.Stdout, "data.env").String(), "stdout:\n%s", result.Stdout) - assert.Contains(t, []string{"set", "created", "updated"}, gjson.Get(result.Stdout, "data.action").String(), "stdout:\n%s", result.Stdout) - assert.NotContains(t, result.Stdout, value, "set output must not leak raw value, stdout:\n%s", result.Stdout) - assert.NotContains(t, result.Stderr, value, "set output must not leak raw value, stderr:\n%s", result.Stderr) - }) - - t.Run("list_include_values_observes_created_key", func(t *testing.T) { - result, err := clie2e.RunCmdWithRetry(ctx, clie2e.Request{ - Args: []string{ - "apps", "+envvar-list", - "--app-id", appID, - "--env", "online", - "--include-values", - }, - DefaultAs: "user", - Env: appsNoNoticeEnv(), - }, clie2e.RetryOptions{ - ShouldRetry: func(result *clie2e.Result) bool { - return result == nil || result.ExitCode != 0 || !envVarKeyExists(result.Stdout, key) - }, - }) - require.NoError(t, err) - result.AssertExitCode(t, 0) - result.AssertStdoutStatus(t, true) - item, found := envVarItem(result.Stdout, key) - require.True(t, found, "list should include created key %q, stdout:\n%s", key, result.Stdout) - assert.Equal(t, "online", item.Get("env").String(), "stdout:\n%s", result.Stdout) - assert.Equal(t, value, item.Get("value").String(), "include-values should expose the explicitly requested test value, stdout:\n%s", result.Stdout) - }) - - t.Run("delete_removes_key", func(t *testing.T) { - deleteResult := runAppsLiveCommand(t, ctx, []string{ - "apps", "+envvar-delete", - "--app-id", appID, - "--env", "online", - "--key", key, - }, true) - if deleteResult.ExitCode == 0 { - created = false - } - deleteResult.AssertExitCode(t, 0) - deleteResult.AssertStdoutStatus(t, true) - requireStringArray(t, deleteResult.Stdout, "data.deleted_keys", []string{key}) - assert.Equal(t, "online", gjson.Get(deleteResult.Stdout, "data.env").String(), "stdout:\n%s", deleteResult.Stdout) - - listResult, err := clie2e.RunCmdWithRetry(ctx, clie2e.Request{ - Args: []string{ - "apps", "+envvar-list", - "--app-id", appID, - "--env", "online", - "--include-values", - }, - DefaultAs: "user", - Env: appsNoNoticeEnv(), - }, clie2e.RetryOptions{ - ShouldRetry: func(result *clie2e.Result) bool { - return result == nil || result.ExitCode != 0 || envVarKeyExists(result.Stdout, key) - }, - }) - require.NoError(t, err) - listResult.AssertExitCode(t, 0) - listResult.AssertStdoutStatus(t, true) - assert.False(t, envVarKeyExists(listResult.Stdout, key), "deleted key should be absent, stdout:\n%s", listResult.Stdout) - }) -} - -func runAppsDryRunCommand(t *testing.T, ctx context.Context, args []string, yes bool) *clie2e.Result { - t.Helper() - dryRunArgs := append([]string{}, args...) - dryRunArgs = append(dryRunArgs, "--dry-run") - return runAppsCommandWithEnv(t, ctx, dryRunArgs, yes, appsDryRunEnv()) -} - -func runAppsCommand(t *testing.T, ctx context.Context, args []string, yes bool) *clie2e.Result { - t.Helper() - return runAppsCommandWithEnv(t, ctx, args, yes, appsDryRunEnv()) -} - -func runAppsLiveCommand(t *testing.T, ctx context.Context, args []string, yes bool) *clie2e.Result { - t.Helper() - return runAppsCommandWithEnv(t, ctx, args, yes, appsNoNoticeEnv()) -} - -func runAppsCommandWithEnv(t *testing.T, ctx context.Context, args []string, yes bool, env map[string]string) *clie2e.Result { - t.Helper() - result, err := clie2e.RunCmd(ctx, clie2e.Request{ - Args: args, - DefaultAs: "user", - Env: env, - Yes: yes, - }) - require.NoError(t, err) - return result -} - -func appsDryRunEnv() map[string]string { - env := appsNoNoticeEnv() - env["LARKSUITE_CLI_APP_ID"] = "cli-e2e-app-id" - env["LARKSUITE_CLI_APP_SECRET"] = "cli-e2e-app-secret" - env["LARKSUITE_CLI_BRAND"] = "feishu" - return env -} - -func appsNoNoticeEnv() map[string]string { - return map[string]string{ - "LARKSUITE_CLI_NO_UPDATE_NOTIFIER": "1", - "LARKSUITE_CLI_NO_SKILLS_NOTIFIER": "1", - } -} - -func requireStringArray(t *testing.T, stdout string, path string, want []string) { - t.Helper() - got := gjson.Get(stdout, path).Array() - require.Len(t, got, len(want), "path %s should contain %d items, stdout:\n%s", path, len(want), stdout) - for i, value := range want { - assert.Equal(t, value, got[i].String(), "path %s[%d], stdout:\n%s", path, i, stdout) - } -} - -func errorEnvelope(t *testing.T, result *clie2e.Result) string { - t.Helper() - raw := strings.TrimSpace(result.Stdout) - if raw == "" { - raw = strings.TrimSpace(result.Stderr) - } - require.NotEmpty(t, raw, "expected structured error output, stdout:\n%s\nstderr:\n%s", result.Stdout, result.Stderr) - return raw -} - -func envVarKeyExists(stdout string, key string) bool { - _, found := envVarItem(stdout, key) - return found -} - -func envVarItem(stdout string, key string) (gjson.Result, bool) { - for _, item := range gjson.Get(stdout, "data.items").Array() { - if item.Get("key").String() == key { - return item, true - } - } - return gjson.Result{}, false -}