Files
larksuite__cli/cmd/profile_env_integration_test.go

155 lines
5.6 KiB
Go

// Copyright (c) 2026 Lark Technologies Pte. Ltd.
// SPDX-License-Identifier: MIT
package cmd
import (
"encoding/json"
"strings"
"testing"
"github.com/larksuite/cli/errs"
"github.com/larksuite/cli/extension/platform"
"github.com/larksuite/cli/internal/core"
"github.com/larksuite/cli/internal/envvars"
"github.com/larksuite/cli/internal/output"
)
func TestExecuteProfileSelectorPrecedence(t *testing.T) {
for _, tc := range []struct {
name string
envProfile string
args []string
wantProfile string
wantSource string
}{
{"environment selects profile", "session", []string{"config", "show"}, "session", "environment"},
{"flag overrides environment", "session", []string{"config", "show", "--profile", "default"}, "default", "flag"},
{"empty flag uses persisted default", "session", []string{"config", "show", "--profile="}, "default", "config"},
{"empty environment uses persisted default", "", []string{"config", "show"}, "default", "config"},
} {
t.Run(tc.name, func(t *testing.T) {
tmpHome(t)
platform.ResetForTesting()
t.Cleanup(platform.ResetForTesting)
t.Setenv(envvars.CliProfile, tc.envProfile)
t.Setenv(envvars.CliAppID, "")
t.Setenv(envvars.CliAppSecret, "")
t.Setenv(envvars.CliUserAccessToken, "")
t.Setenv(envvars.CliTenantAccessToken, "")
t.Setenv("LARKSUITE_CLI_NO_UPDATE_NOTIFIER", "1")
t.Setenv("LARKSUITE_CLI_NO_SKILLS_NOTIFIER", "1")
multi := &core.MultiAppConfig{
CurrentApp: "default",
Apps: []core.AppConfig{
{Name: "default", AppId: "app-default", AppSecret: core.PlainSecret("secret-default"), Brand: core.BrandFeishu},
{Name: "session", AppId: "app-session", AppSecret: core.PlainSecret("secret-session"), Brand: core.BrandFeishu},
},
}
if err := core.SaveMultiAppConfig(multi); err != nil {
t.Fatalf("SaveMultiAppConfig() error = %v", err)
}
code, stdout, stderr := executeWithCapturedOS(t, nil, tc.args...)
if code != 0 {
t.Fatalf("ExecuteWithOptions() exit=%d stderr=%s", code, stderr)
}
var result struct {
Profile string `json:"profile"`
ProfileSource string `json:"profileSource"`
AppID string `json:"appId"`
}
if err := json.Unmarshal([]byte(stdout), &result); err != nil {
t.Fatalf("decode stdout: %v\nstdout: %s", err, stdout)
}
if result.Profile != tc.wantProfile || result.AppID != "app-"+tc.wantProfile {
t.Fatalf("config show = %+v, want profile %q", result, tc.wantProfile)
}
if result.ProfileSource != tc.wantSource {
t.Fatalf("profileSource = %q, want %q", result.ProfileSource, tc.wantSource)
}
saved, err := core.LoadMultiAppConfig()
if err != nil {
t.Fatalf("LoadMultiAppConfig() error = %v", err)
}
if saved.CurrentApp != "default" {
t.Fatalf("ephemeral selector persisted currentApp = %q", saved.CurrentApp)
}
})
}
}
func TestExecuteEnvironmentProfileNotFoundNamesTheVariable(t *testing.T) {
tmpHome(t)
platform.ResetForTesting()
t.Cleanup(platform.ResetForTesting)
t.Setenv(envvars.CliProfile, "ghost")
t.Setenv(envvars.CliAppID, "")
t.Setenv(envvars.CliAppSecret, "")
t.Setenv(envvars.CliUserAccessToken, "")
t.Setenv(envvars.CliTenantAccessToken, "")
t.Setenv("LARKSUITE_CLI_NO_UPDATE_NOTIFIER", "1")
t.Setenv("LARKSUITE_CLI_NO_SKILLS_NOTIFIER", "1")
multi := &core.MultiAppConfig{
CurrentApp: "default",
Apps: []core.AppConfig{
{Name: "default", AppId: "app-default", AppSecret: core.PlainSecret("secret-default"), Brand: core.BrandFeishu},
},
}
if err := core.SaveMultiAppConfig(multi); err != nil {
t.Fatalf("SaveMultiAppConfig() error = %v", err)
}
// config default-as previously answered this input error with the generic
// "no active profile" + a `config init --new` hint — active misdirection
// for an agent: init is a destructive OAuth setup flow while an intact
// profile sits in config.json. The envelope must name the environment
// variable and steer toward unsetting or fixing it.
for _, target := range [][]string{
{"config", "default-as"},
{"config", "show"},
{"auth", "list", "--json"},
} {
t.Run(target[len(target)-1], func(t *testing.T) {
code, stdout, stderr := executeWithCapturedOS(t, nil, target...)
if code != output.ExitAuth {
t.Fatalf("exit = %d, want %d\nstdout: %s\nstderr: %s", code, output.ExitAuth, stdout, stderr)
}
var envelope struct {
OK bool `json:"ok"`
Error struct {
Type errs.Category `json:"type"`
Subtype errs.Subtype `json:"subtype"`
Message string `json:"message"`
Hint string `json:"hint"`
Field string `json:"field"`
} `json:"error"`
}
if err := json.Unmarshal([]byte(stderr), &envelope); err != nil {
t.Fatalf("stderr is not a JSON envelope: %v\n%s", err, stderr)
}
if envelope.OK ||
envelope.Error.Type != errs.CategoryConfig ||
envelope.Error.Subtype != errs.SubtypeNotConfigured ||
envelope.Error.Field != envvars.CliProfile {
t.Errorf("envelope = %+v, want ok=false config/not_configured field=%s", envelope, envvars.CliProfile)
}
if want := `profile "ghost" not found`; envelope.Error.Message != want {
t.Errorf("message = %q, want %q", envelope.Error.Message, want)
}
if !strings.Contains(envelope.Error.Hint, "unset "+envvars.CliProfile) {
t.Errorf("hint = %q, want unset guidance", envelope.Error.Hint)
}
if strings.Contains(envelope.Error.Hint, "config init") {
t.Errorf("hint must not steer to config init: %q", envelope.Error.Hint)
}
// auth list must not disguise the input error as an empty account.
if strings.Contains(stdout, "not_logged_in") {
t.Errorf("stdout still reports not_logged_in:\n%s", stdout)
}
})
}
}