1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
package hexaicli
import (
"bytes"
"context"
"log"
"path/filepath"
"strings"
"testing"
"codeberg.org/snonux/hexai/internal/appconfig"
"codeberg.org/snonux/hexai/internal/llm"
"codeberg.org/snonux/hexai/internal/stats"
)
type recordingCLIStatusSink struct {
startProvider string
startModel string
globalCalls int
}
func (s *recordingCLIStatusSink) SetLLMStart(provider, model string) error {
s.startProvider = provider
s.startModel = model
return nil
}
func (s *recordingCLIStatusSink) SetGlobal(stats.Snapshot, string, string, float64, int64) error {
s.globalCalls++
return nil
}
func TestRunner_UsesInjectedDependencies(t *testing.T) {
sink := &recordingCLIStatusSink{}
runner := &Runner{
loadConfig: func(context.Context, *log.Logger) appconfig.App {
return appconfig.App{
CoreConfig: appconfig.CoreConfig{Provider: "openai"},
PromptConfig: appconfig.PromptConfig{PromptCLIDefaultSystem: "SYS"},
}
},
openEditor: func(context.Context, []byte) (string, error) { return "PROMPT", nil },
newClient: func(appconfig.App) (client llm.Client, err error) {
return &fakeClient{name: "fake", model: "m", resp: "OUT"}, nil
},
statusSink: sink,
}
var stdout, stderr bytes.Buffer
if err := runner.Run(context.Background(), nil, strings.NewReader(""), &stdout, &stderr); err != nil {
t.Fatalf("Run: %v", err)
}
if stdout.String() != "OUT" {
t.Fatalf("stdout = %q, want OUT", stdout.String())
}
if sink.startProvider != "fake" || sink.startModel == "" {
t.Fatalf("unexpected start status: provider=%q model=%q", sink.startProvider, sink.startModel)
}
if sink.globalCalls != 1 {
t.Fatalf("expected one global status update, got %d", sink.globalCalls)
}
}
func TestRunner_ConfigSubcommand_OpensConfigFromContext(t *testing.T) {
var gotPath string
runner := NewRunner()
runner.openConfigEditor = func(_ context.Context, path string) error {
gotPath = path
return nil
}
cfgFile := filepath.Join(t.TempDir(), "hexai", "config.toml")
ctx := WithCLIConfigPath(context.Background(), cfgFile)
if err := runner.Run(ctx, []string{"config"}, strings.NewReader(""), &bytes.Buffer{}, &bytes.Buffer{}); err != nil {
t.Fatalf("Run: %v", err)
}
if gotPath != cfgFile {
t.Fatalf("opened %q, want %q", gotPath, cfgFile)
}
}
func TestRunner_ConfigSubcommand_UsesXDGWhenNoOverride(t *testing.T) {
xdg := t.TempDir()
t.Setenv("XDG_CONFIG_HOME", xdg)
var gotPath string
runner := NewRunner()
runner.openConfigEditor = func(_ context.Context, path string) error {
gotPath = path
return nil
}
want := filepath.Join(xdg, "hexai", "config.toml")
if err := runner.Run(context.Background(), []string{"config"}, strings.NewReader(""), &bytes.Buffer{}, &bytes.Buffer{}); err != nil {
t.Fatalf("Run: %v", err)
}
if gotPath != want {
t.Fatalf("opened %q, want %q", gotPath, want)
}
}
func TestRunner_ConfigSubcommand_RejectsExtraArgs(t *testing.T) {
runner := NewRunner()
var stderr bytes.Buffer
err := runner.Run(context.Background(), []string{"config", "nope"}, strings.NewReader(""), &bytes.Buffer{}, &stderr)
if err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "unexpected arguments") {
t.Fatalf("err = %v", err)
}
}
|