mirror of
https://github.com/larksuite/cli.git
synced 2026-07-09 10:29:48 +08:00
Make --environment the only accepted db environment flag across the db commands (execute, table-list/get, env-create, data export/import, changelog, audit status/enable/disable/list, quota). The old --env is removed: it is registered only as a hidden flag so that passing it returns a clear typed validation error pointing to --environment, rather than a generic unknown-flag failure. Update the lark-apps db references accordingly.
130 lines
4.9 KiB
Go
130 lines
4.9 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package apps
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/larksuite/cli/errs"
|
|
"github.com/larksuite/cli/internal/httpmock"
|
|
"github.com/larksuite/cli/shortcuts/common"
|
|
)
|
|
|
|
func assertHintContains(t *testing.T, sc common.Shortcut, args []string, stub *httpmock.Stub, want string) {
|
|
t.Helper()
|
|
factory, stdout, reg := newAppsExecuteFactory(t)
|
|
reg.Register(stub)
|
|
err := runAppsShortcut(t, sc, args, factory, stdout)
|
|
if err == nil {
|
|
t.Fatalf("expected failure, got nil; stdout=%s", stdout.String())
|
|
}
|
|
p, ok := errs.ProblemOf(err)
|
|
if !ok {
|
|
t.Fatalf("expected typed errs.Problem, got %T: %v", err, err)
|
|
}
|
|
if !strings.Contains(p.Hint, want) {
|
|
t.Fatalf("hint %q does not contain %q", p.Hint, want)
|
|
}
|
|
}
|
|
|
|
func TestAppsSessionCreate_4xxFailureCarriesListHint(t *testing.T) {
|
|
assertHintContains(t, AppsSessionCreate,
|
|
[]string{"+session-create", "--app-id", "app_x", "--as", "user"},
|
|
&httpmock.Stub{Method: "POST", URL: "/open-apis/spark/v1/apps/app_x/sessions",
|
|
Status: http.StatusNotFound, Body: map[string]interface{}{"msg": "app not found"}},
|
|
"apps +list")
|
|
}
|
|
|
|
func TestAppsSessionList_4xxFailureCarriesListHint(t *testing.T) {
|
|
assertHintContains(t, AppsSessionList,
|
|
[]string{"+session-list", "--app-id", "app_x", "--as", "user"},
|
|
&httpmock.Stub{Method: "GET", URL: "/open-apis/spark/v1/apps/app_x/sessions",
|
|
Status: http.StatusForbidden, Body: map[string]interface{}{"msg": "permission denied"}},
|
|
"apps +list")
|
|
}
|
|
|
|
func TestAppsUpdate_4xxFailureCarriesListHint(t *testing.T) {
|
|
assertHintContains(t, AppsUpdate,
|
|
[]string{"+update", "--app-id", "app_x", "--name", "n", "--as", "user"},
|
|
&httpmock.Stub{Method: "PATCH", URL: "/open-apis/spark/v1/apps/app_x",
|
|
Status: http.StatusNotFound, Body: map[string]interface{}{"msg": "app not found"}},
|
|
"apps +list")
|
|
}
|
|
|
|
func TestAppsReleaseList_4xxFailureCarriesListHint(t *testing.T) {
|
|
assertHintContains(t, AppsReleaseList,
|
|
[]string{"+release-list", "--app-id", "app_x", "--as", "user"},
|
|
&httpmock.Stub{Method: "GET", URL: "/open-apis/spark/v1/apps/app_x/releases",
|
|
Status: http.StatusForbidden, Body: map[string]interface{}{"msg": "permission denied"}},
|
|
"apps +list")
|
|
}
|
|
|
|
func TestAppsSessionStop_4xxFailureCarriesSessionHint(t *testing.T) {
|
|
assertHintContains(t, AppsSessionStop,
|
|
[]string{"+session-stop", "--app-id", "app_x", "--session-id", "s1", "--turn-id", "t1", "--as", "user"},
|
|
&httpmock.Stub{Method: "POST", URL: "/open-apis/spark/v1/apps/app_x/sessions/s1/stop",
|
|
Status: http.StatusNotFound, Body: map[string]interface{}{"msg": "session not found"}},
|
|
"+session-list")
|
|
}
|
|
|
|
func TestAppsCreate_4xxFailureCarriesTypeHint(t *testing.T) {
|
|
assertHintContains(t, AppsCreate,
|
|
[]string{"+create", "--name", "n", "--app-type", "html", "--as", "user"},
|
|
&httpmock.Stub{Method: "POST", URL: "/open-apis/spark/v1/apps",
|
|
Status: http.StatusForbidden, Body: map[string]interface{}{"msg": "permission denied"}},
|
|
"full_stack")
|
|
}
|
|
|
|
func TestAppsDBEnvCreate_4xxFailureCarriesHint(t *testing.T) {
|
|
assertHintContains(t, AppsDBEnvCreate,
|
|
[]string{"+db-env-create", "--app-id", "app_x", "--environment", "dev", "--yes", "--as", "user"},
|
|
&httpmock.Stub{Method: "POST", URL: "/open-apis/spark/v1/apps/app_x/db_dev_init",
|
|
Status: http.StatusConflict, Body: map[string]interface{}{"msg": "already multi-env"}},
|
|
"+db-table-list")
|
|
}
|
|
|
|
func TestAppsDBTableGet_4xxFailureCarriesHint(t *testing.T) {
|
|
assertHintContains(t, AppsDBTableGet,
|
|
[]string{"+db-table-get", "--app-id", "app_x", "--table", "users", "--as", "user"},
|
|
&httpmock.Stub{Method: "GET", URL: "/open-apis/spark/v1/apps/app_x/tables/users",
|
|
Status: http.StatusNotFound, Body: map[string]interface{}{"msg": "table not found"}},
|
|
"+db-table-list")
|
|
}
|
|
|
|
func TestAppsDBTableList_4xxFailureCarriesHint(t *testing.T) {
|
|
assertHintContains(t, AppsDBTableList,
|
|
[]string{"+db-table-list", "--app-id", "app_x", "--environment", "dev", "--as", "user"},
|
|
&httpmock.Stub{Method: "GET", URL: "/open-apis/spark/v1/apps/app_x/tables",
|
|
Status: http.StatusNotFound, Body: map[string]interface{}{"msg": "dev env not found"}},
|
|
"+db-env-create")
|
|
}
|
|
|
|
// withAppsHint must only fill an EMPTY hint; an upstream-provided hint wins.
|
|
func TestWithAppsHint_DoesNotOverrideUpstreamHint(t *testing.T) {
|
|
upstream := &errs.Problem{Message: "boom", Hint: "upstream specific hint"}
|
|
got := withAppsHint(upstream, appIDListHint)
|
|
p, ok := errs.ProblemOf(got)
|
|
if !ok {
|
|
t.Fatalf("expected typed problem, got %T", got)
|
|
}
|
|
if p.Hint != "upstream specific hint" {
|
|
t.Fatalf("upstream hint was overridden: %q", p.Hint)
|
|
}
|
|
}
|
|
|
|
// withAppsHint fills the hint when empty and leaves Message untouched.
|
|
func TestWithAppsHint_FillsEmptyHintKeepsMessage(t *testing.T) {
|
|
p0 := &errs.Problem{Message: "boom"}
|
|
got := withAppsHint(p0, appIDListHint)
|
|
p, _ := errs.ProblemOf(got)
|
|
if p.Hint != appIDListHint {
|
|
t.Fatalf("hint not filled: %q", p.Hint)
|
|
}
|
|
if p.Message != "boom" {
|
|
t.Fatalf("message mutated: %q", p.Message)
|
|
}
|
|
}
|