Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions sdk/go/openshell/v1/gateway/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (

// NewClient creates a fully wired SDK client from an on-disk gateway
// configuration. If name is empty, the active gateway (set via
// `openshell gateway use`) is used.
// `openshell gateway use`, or the system default) is used.
//
// The function resolves the gateway directory, parses metadata.json,
// loads tokens lazily, maps the auth mode to an SDK auth provider, and
Expand Down Expand Up @@ -66,8 +66,8 @@ func NewClient(name string, opts ...ClientOption) (*v1.Client, error) {
}

// LoadConfig reads and parses a gateway's on-disk configuration without
// creating a client connection. If name is empty, the active gateway is
// used.
// creating a client connection. If name is empty, the active gateway (user
// selection, then system default) is used.
//
// The returned [Config] is an immutable snapshot; changes to the on-disk
// files after this call are not reflected.
Expand Down
16 changes: 16 additions & 0 deletions sdk/go/openshell/v1/gateway/gateway_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,13 +387,29 @@ func TestNewClient_ActiveGateway(t *testing.T) {
func TestNewClient_NoActiveGateway(t *testing.T) {
tmp := t.TempDir()
t.Setenv("XDG_CONFIG_HOME", tmp)
t.Setenv(systemGatewayDirEnv, t.TempDir())
require.NoError(t, os.MkdirAll(filepath.Join(tmp, "openshell"), 0o755))

_, err := NewClient("")
require.Error(t, err)
assert.ErrorIs(t, err, ErrNoActiveGateway)
}

func TestLoadConfig_SystemActiveGateway(t *testing.T) {
user, system := t.TempDir(), t.TempDir()
t.Setenv("XDG_CONFIG_HOME", user)
t.Setenv(systemGatewayDirEnv, system)
writeFile(t, system, "active_gateway", "corp")
gwDir := filepath.Join(system, "gateways", "corp")
require.NoError(t, os.MkdirAll(gwDir, 0o755))
writeFile(t, gwDir, "metadata.json", `{"gateway_endpoint":"host:443"}`)

cfg, err := LoadConfig("")
require.NoError(t, err)
assert.Equal(t, "corp", cfg.Name)
assert.Equal(t, SourceSystem, cfg.Source)
}

func TestLoadConfig_ActiveGateway(t *testing.T) {
tmp := setupGateway(t, "my-active", `{"gateway_endpoint":"host:443","auth_mode":"oidc"}`)
writeFile(t, filepath.Join(tmp, "openshell"), "active_gateway", "my-active")
Expand Down
23 changes: 18 additions & 5 deletions sdk/go/openshell/v1/gateway/paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,16 +137,29 @@ func validateGatewayName(name string) error {
return nil
}

// resolveActiveGateway reads the active_gateway file from the user
// config directory and returns the validated gateway name. Returns
// ErrNoActiveGateway if the file is missing or empty.
// resolveActiveGateway returns the validated active gateway name. It reads
// the user active_gateway file and falls back to the system one when the user
// file is missing, empty, or invalid, matching the CLI. Returns
// ErrNoActiveGateway describing the user file when neither yields a name.
func resolveActiveGateway() (string, error) {
userBase, err := userConfigDir()
if err != nil {
return "", fmt.Errorf("%w: cannot determine config directory: %v", ErrNoActiveGateway, err)
err = fmt.Errorf("%w: cannot determine config directory: %v", ErrNoActiveGateway, err)
} else {
var name string
if name, err = readActiveGatewayFile(filepath.Join(userBase, activeGatewayFile)); err == nil {
return name, nil
}
}

if name, sysErr := readActiveGatewayFile(filepath.Join(systemConfigDir(), activeGatewayFile)); sysErr == nil {
return name, nil
}
return "", err
}

path := filepath.Join(userBase, activeGatewayFile)
// readActiveGatewayFile reads and validates the gateway name stored at path.
func readActiveGatewayFile(path string) (string, error) {
data, err := os.ReadFile(path)
if err != nil {
return "", fmt.Errorf("%w", ErrNoActiveGateway)
Expand Down
49 changes: 49 additions & 0 deletions sdk/go/openshell/v1/gateway/paths_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ func TestResolveActiveGateway_WhitespaceHandling(t *testing.T) {
func TestResolveActiveGateway_FileMissing(t *testing.T) {
tmp := t.TempDir()
t.Setenv("XDG_CONFIG_HOME", tmp)
t.Setenv(systemGatewayDirEnv, t.TempDir())
require.NoError(t, os.MkdirAll(filepath.Join(tmp, "openshell"), 0o755))

_, err := resolveActiveGateway()
Expand All @@ -208,6 +209,7 @@ func TestResolveActiveGateway_FileMissing(t *testing.T) {
func TestResolveActiveGateway_EmptyFile(t *testing.T) {
tmp := t.TempDir()
t.Setenv("XDG_CONFIG_HOME", tmp)
t.Setenv(systemGatewayDirEnv, t.TempDir())
require.NoError(t, os.MkdirAll(filepath.Join(tmp, "openshell"), 0o755))
require.NoError(t, os.WriteFile(filepath.Join(tmp, "openshell", "active_gateway"), []byte(" \n"), 0o644))

Expand All @@ -216,6 +218,53 @@ func TestResolveActiveGateway_EmptyFile(t *testing.T) {
assert.ErrorIs(t, err, ErrNoActiveGateway)
}

func TestResolveActiveGateway_FallsBackToSystem(t *testing.T) {
user, system := t.TempDir(), t.TempDir()
t.Setenv("XDG_CONFIG_HOME", user)
t.Setenv(systemGatewayDirEnv, system)
require.NoError(t, os.WriteFile(filepath.Join(system, "active_gateway"), []byte("corp\n"), 0o644))

name, err := resolveActiveGateway()
require.NoError(t, err)
assert.Equal(t, "corp", name)
}

func TestResolveActiveGateway_PrefersUser(t *testing.T) {
user, system := t.TempDir(), t.TempDir()
t.Setenv("XDG_CONFIG_HOME", user)
t.Setenv(systemGatewayDirEnv, system)
require.NoError(t, os.MkdirAll(filepath.Join(user, "openshell"), 0o755))
require.NoError(t, os.WriteFile(filepath.Join(user, "openshell", "active_gateway"), []byte("mine"), 0o644))
require.NoError(t, os.WriteFile(filepath.Join(system, "active_gateway"), []byte("corp"), 0o644))

name, err := resolveActiveGateway()
require.NoError(t, err)
assert.Equal(t, "mine", name)
}

func TestResolveActiveGateway_InvalidUserFallsBackToSystem(t *testing.T) {
user, system := t.TempDir(), t.TempDir()
t.Setenv("XDG_CONFIG_HOME", user)
t.Setenv(systemGatewayDirEnv, system)
require.NoError(t, os.MkdirAll(filepath.Join(user, "openshell"), 0o755))
require.NoError(t, os.WriteFile(filepath.Join(user, "openshell", "active_gateway"), []byte("../escape"), 0o644))
require.NoError(t, os.WriteFile(filepath.Join(system, "active_gateway"), []byte("corp"), 0o644))

name, err := resolveActiveGateway()
require.NoError(t, err)
assert.Equal(t, "corp", name)
}

func TestResolveActiveGateway_InvalidSystemIsIgnored(t *testing.T) {
user, system := t.TempDir(), t.TempDir()
t.Setenv("XDG_CONFIG_HOME", user)
t.Setenv(systemGatewayDirEnv, system)
require.NoError(t, os.WriteFile(filepath.Join(system, "active_gateway"), []byte("../escape"), 0o644))

_, err := resolveActiveGateway()
assert.ErrorIs(t, err, ErrNoActiveGateway)
}

func TestValidateGatewayName_SpecialCharacters(t *testing.T) {
cases := []struct {
name string
Expand Down
Loading