sessions_test.go
1 package cmd 2 3 import ( 4 "bytes" 5 "os" 6 "path/filepath" 7 "strings" 8 "testing" 9 10 "github.com/spf13/viper" 11 12 "github.com/Kocoro-lab/ShanClaw/internal/config" 13 ) 14 15 // TestSessionsSync_ReadsConfigViaCobra is the regression guard for the class 16 // of bug where a cobra subcommand RunE forgets to call config.Load() and 17 // silently runs on an uninitialized viper. Before the fix, this test would 18 // see "sync is disabled" on stdout regardless of the yaml file because viper 19 // returned the SetDefault value of `sync.enabled=false`. After the fix, the 20 // yaml `sync.enabled: true` flows through and the dry-run codepath runs to 21 // completion, emitting a `sync: outcome=noop ...` summary instead. 22 func TestSessionsSync_ReadsConfigViaCobra(t *testing.T) { 23 home := t.TempDir() 24 shannonDir := filepath.Join(home, ".shannon") 25 if err := os.MkdirAll(shannonDir, 0700); err != nil { 26 t.Fatalf("mkdir shannon dir: %v", err) 27 } 28 cfgYAML := "sync:\n enabled: true\n dry_run: true\n" 29 if err := os.WriteFile(filepath.Join(shannonDir, "config.yaml"), []byte(cfgYAML), 0600); err != nil { 30 t.Fatalf("write config: %v", err) 31 } 32 33 withIsolatedEnv(t, home) 34 viper.Reset() 35 36 var stdout, stderr bytes.Buffer 37 rootCmd.SetOut(&stdout) 38 rootCmd.SetErr(&stderr) 39 rootCmd.SetArgs([]string{"sessions", "sync"}) 40 t.Cleanup(func() { 41 rootCmd.SetArgs(nil) 42 rootCmd.SetOut(nil) 43 rootCmd.SetErr(nil) 44 }) 45 46 if err := rootCmd.Execute(); err != nil { 47 t.Fatalf("rootCmd.Execute: %v (stderr=%q)", err, stderr.String()) 48 } 49 50 out := stdout.String() 51 if strings.Contains(out, "sync is disabled") { 52 t.Fatalf("config not loaded — Bug 1 regression; stdout=%q", out) 53 } 54 if !strings.Contains(out, "sync: outcome=") { 55 t.Fatalf("expected `sync: outcome=...` summary on stdout; got %q (stderr=%q)", out, stderr.String()) 56 } 57 } 58 59 // TestCloudAliasesResolveToTopLevel verifies RegisterAlias wiring: callers 60 // reading `cloud.api_key` / `cloud.endpoint` get the top-level `api_key` / 61 // `endpoint` values. This is Bug 2's regression guard. 62 func TestCloudAliasesResolveToTopLevel(t *testing.T) { 63 home := t.TempDir() 64 shannonDir := filepath.Join(home, ".shannon") 65 if err := os.MkdirAll(shannonDir, 0700); err != nil { 66 t.Fatalf("mkdir shannon dir: %v", err) 67 } 68 cfgYAML := "api_key: top-level-key-value\nendpoint: https://example.test\n" 69 if err := os.WriteFile(filepath.Join(shannonDir, "config.yaml"), []byte(cfgYAML), 0600); err != nil { 70 t.Fatalf("write config: %v", err) 71 } 72 73 withIsolatedEnv(t, home) 74 viper.Reset() 75 76 if _, err := config.Load(); err != nil { 77 t.Fatalf("config.Load: %v", err) 78 } 79 80 if got := viper.GetString("cloud.api_key"); got != "top-level-key-value" { 81 t.Fatalf("cloud.api_key alias: got %q, want top-level value", got) 82 } 83 if got := viper.GetString("cloud.endpoint"); got != "https://example.test" { 84 t.Fatalf("cloud.endpoint alias: got %q, want top-level value", got) 85 } 86 } 87 88 // withIsolatedEnv redirects HOME (and XDG_CONFIG_HOME for good measure) to 89 // the supplied tempdir for the duration of the test. Cleanup restores the 90 // prior values. 91 func withIsolatedEnv(t *testing.T, home string) { 92 t.Helper() 93 t.Setenv("HOME", home) 94 t.Setenv("XDG_CONFIG_HOME", filepath.Join(home, ".config")) 95 }