// Copyright (c) 2026 Lark Technologies Pte. Ltd. // SPDX-License-Identifier: MIT package apps import ( "context" "os" "path/filepath" "testing" "time" clie2e "github.com/larksuite/cli/tests/cli_e2e" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tidwall/gjson" ) // TestAppsHTMLPublishDryRun exercises the walker / manifest layer without // packing or uploading. --path goes through LocalFileIO which bounds reads to // the runtime cwd, so each sub-test seeds fixtures in a t.TempDir and runs // the binary with WorkDir set to that dir + relative --path. // // Hidden files are intentionally included — the walker is deliberately not // filtering, so the manifest must reflect everything the user pointed --path // at. Users are documented to pass clean build output directories (e.g. // ./dist), not source trees. func TestAppsHTMLPublishDryRun(t *testing.T) { setAppsDryRunEnv(t) t.Run("Directory_ReportsManifest", func(t *testing.T) { dir := t.TempDir() require.NoError(t, os.MkdirAll(filepath.Join(dir, "dist"), 0o755)) require.NoError(t, os.WriteFile(filepath.Join(dir, "dist", "index.html"), []byte("
hi"), 0o644)) require.NoError(t, os.WriteFile(filepath.Join(dir, "dist", "logo.svg"), []byte(""), 0o644)) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) t.Cleanup(cancel) result, err := clie2e.RunCmd(ctx, clie2e.Request{ Args: []string{ "apps", "+html-publish", "--app-id", "app_x", "--path", "./dist", "--dry-run", }, DefaultAs: "user", WorkDir: dir, }) require.NoError(t, err) result.AssertExitCode(t, 0) assert.Equal(t, "POST", gjson.Get(result.Stdout, "api.0.method").String()) assert.Equal(t, "/open-apis/spark/v1/apps/app_x/upload_and_release_html_code", gjson.Get(result.Stdout, "api.0.url").String()) // file_count / files / total_size_bytes sit at envelope top level // (not under api.0.body — manifest is dry-run metadata, not the HTTP body). assert.Equal(t, int64(2), gjson.Get(result.Stdout, "file_count").Int()) assert.Greater(t, gjson.Get(result.Stdout, "total_size_bytes").Int(), int64(0)) files := gjson.Get(result.Stdout, "files").Array() require.Len(t, files, 2) names := []string{files[0].String(), files[1].String()} assert.Contains(t, names, "index.html") assert.Contains(t, names, "logo.svg") }) t.Run("SingleFile_OneEntry", func(t *testing.T) { dir := t.TempDir() require.NoError(t, os.WriteFile(filepath.Join(dir, "page.html"), []byte(""), 0o644)) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) t.Cleanup(cancel) result, err := clie2e.RunCmd(ctx, clie2e.Request{ Args: []string{ "apps", "+html-publish", "--app-id", "app_x", "--path", "page.html", "--dry-run", }, DefaultAs: "user", WorkDir: dir, }) require.NoError(t, err) result.AssertExitCode(t, 0) assert.Equal(t, int64(1), gjson.Get(result.Stdout, "file_count").Int()) assert.Equal(t, "page.html", gjson.Get(result.Stdout, "files.0").String()) }) t.Run("HiddenFilesIncludedExceptGit", func(t *testing.T) { // The walker filters the .git directory (and a .git gitdir pointer file) // so a stray repo under --path doesn't ship its history / remote URL to a // public share URL. Generic hidden files like .DS_Store are NOT filtered — // only .git is — so users still see everything else they pointed --path at. dir := t.TempDir() require.NoError(t, os.MkdirAll(filepath.Join(dir, "dist"), 0o755)) require.NoError(t, os.WriteFile(filepath.Join(dir, "dist", "index.html"), []byte("