mirror of
https://github.com/larksuite/cli.git
synced 2026-08-03 08:32:46 +08:00
fix(config): pin saved-config commands to the saved default
- config show now resolves the saved default profile unconditionally: its own help (and the skill routing) promise "saved config, not current usage", but the output still followed --profile / LARKSUITE_CLI_PROFILE. A behavior test locks the boundary the help text claims. - profile list dual-publishes the deprecated `active` field as an alias of `default` for one deprecation cycle, so external consumers reading `.active` keep working; it still marks the saved default, never the currently effective identity.
This commit is contained in:
@@ -116,6 +116,37 @@ func TestConfigShowRun_NotConfiguredReturnsStructuredError(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// config show promises "saved config, not current usage" (help + skill
|
||||
// routing): the session profile (--profile / LARKSUITE_CLI_PROFILE) must not
|
||||
// change what it shows.
|
||||
func TestConfigShowRun_IgnoresSessionProfile(t *testing.T) {
|
||||
t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())
|
||||
multi := &core.MultiAppConfig{
|
||||
CurrentApp: "tenant_a",
|
||||
Apps: []core.AppConfig{
|
||||
{Name: "tenant_a", AppId: "cli_a", AppSecret: core.PlainSecret("s-a"), Brand: core.BrandFeishu},
|
||||
{Name: "tenant_b", AppId: "cli_b", AppSecret: core.PlainSecret("s-b"), Brand: core.BrandFeishu},
|
||||
},
|
||||
}
|
||||
if err := core.SaveMultiAppConfig(multi); err != nil {
|
||||
t.Fatalf("SaveMultiAppConfig: %v", err)
|
||||
}
|
||||
|
||||
f, stdout, _, _ := cmdutil.TestFactory(t, nil)
|
||||
f.Invocation.Profile = "tenant_b" // session selection must not leak in
|
||||
|
||||
if err := configShowRun(&ConfigShowOptions{Factory: f}); err != nil {
|
||||
t.Fatalf("configShowRun: %v", err)
|
||||
}
|
||||
out := stdout.String()
|
||||
if !strings.Contains(out, `"cli_a"`) || !strings.Contains(out, `"tenant_a"`) {
|
||||
t.Fatalf("output = %s, want the saved default tenant_a/cli_a", out)
|
||||
}
|
||||
if strings.Contains(out, `"cli_b"`) {
|
||||
t.Fatalf("output = %s, session profile tenant_b must not change saved-config view", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigShowRun_NoActiveProfileReturnsStructuredError(t *testing.T) {
|
||||
t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())
|
||||
multi := &core.MultiAppConfig{
|
||||
|
||||
@@ -54,7 +54,10 @@ func configShowRun(opts *ConfigShowOptions) error {
|
||||
if config == nil || len(config.Apps) == 0 {
|
||||
return core.NotConfiguredError()
|
||||
}
|
||||
app := config.CurrentAppConfig(f.Invocation.Profile)
|
||||
// Saved config only: the session profile (--profile / LARKSUITE_CLI_PROFILE)
|
||||
// must not change what this command shows — the help and skill routing
|
||||
// promise "saved config, not current usage" (use whoami for that).
|
||||
app := config.CurrentAppConfig("")
|
||||
if app == nil {
|
||||
return errs.NewConfigError(errs.SubtypeNotConfigured, "no active profile").WithHint("run: lark-cli profile list")
|
||||
}
|
||||
|
||||
@@ -18,12 +18,17 @@ import (
|
||||
|
||||
// profileListItem is the JSON output for a single profile entry.
|
||||
type profileListItem struct {
|
||||
Name string `json:"name"`
|
||||
AppID string `json:"appId"`
|
||||
Brand core.LarkBrand `json:"brand"`
|
||||
Default bool `json:"default"`
|
||||
User string `json:"user,omitempty"`
|
||||
TokenStatus string `json:"tokenStatus,omitempty"`
|
||||
Name string `json:"name"`
|
||||
AppID string `json:"appId"`
|
||||
Brand core.LarkBrand `json:"brand"`
|
||||
Default bool `json:"default"`
|
||||
// Active is a deprecated alias of Default, dual-published for one
|
||||
// deprecation cycle so external consumers reading `.active` keep
|
||||
// working. It marks the saved default profile — never the currently
|
||||
// effective identity (use whoami for that). Remove in the next major.
|
||||
Active bool `json:"active"`
|
||||
User string `json:"user,omitempty"`
|
||||
TokenStatus string `json:"tokenStatus,omitempty"`
|
||||
}
|
||||
|
||||
// NewCmdProfileList creates the profile list subcommand.
|
||||
@@ -71,6 +76,7 @@ func profileListRun(f *cmdutil.Factory) error {
|
||||
AppID: app.AppId,
|
||||
Brand: app.Brand,
|
||||
Default: name == currentName,
|
||||
Active: name == currentName,
|
||||
}
|
||||
|
||||
if len(app.Users) > 0 {
|
||||
|
||||
@@ -307,8 +307,10 @@ func TestProfileListRun_OutputsProfiles(t *testing.T) {
|
||||
t.Fatalf("Unmarshal() error = %v; output=%s", err, stdout.String())
|
||||
}
|
||||
raw := stdout.String()
|
||||
if strings.Contains(raw, `"active"`) {
|
||||
t.Fatalf("profile list output contains legacy active field: %s", raw)
|
||||
// `active` is dual-published as a deprecated alias of `default` for one
|
||||
// deprecation cycle so external consumers reading `.active` keep working.
|
||||
if !strings.Contains(raw, `"active"`) {
|
||||
t.Fatalf("profile list output missing deprecated active alias: %s", raw)
|
||||
}
|
||||
if !strings.Contains(raw, `"default"`) {
|
||||
t.Fatalf("profile list output missing default field: %s", raw)
|
||||
@@ -322,6 +324,11 @@ func TestProfileListRun_OutputsProfiles(t *testing.T) {
|
||||
if got[1].Name != "target" || got[1].Default {
|
||||
t.Fatalf("got[1] = %#v, want non-default target profile", got[1])
|
||||
}
|
||||
for i, item := range got {
|
||||
if item.Active != item.Default {
|
||||
t.Fatalf("got[%d]: active = %v, default = %v; the alias must mirror default", i, item.Active, item.Default)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestProfileListRun_NotConfiguredReturnsEmptyList(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user