diff options
| author | Paul Buetow <paul@buetow.org> | 2025-08-17 18:52:51 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-08-17 18:52:51 +0300 |
| commit | 454451105ad3522d2ac3d22136eedee4a4d034af (patch) | |
| tree | aa4b5723809c4d45bfc9094a38c01c6415582f9c /internal/hexailsp/run_test.go | |
| parent | 498923e77c201ca90dc35c7934f4f7f1c9c3ccd2 (diff) | |
cli+lsp: refactor main packages into internal runners; add tests
- Move CLI logic to internal/hexaicli with Run/RunWithClient
- Move LSP logic to internal/hexailsp with Run/RunWithFactory
- Extract helpers; keep behavior identical for both binaries
- Add unit tests for hexaicli (input parsing, messages, streaming) and
hexailsp (factory wiring, client creation, logging settings)
- Add top-of-file summaries and 'Not yet reviewed by a human' comments to all Go files
- Update README with internal package docs
Diffstat (limited to 'internal/hexailsp/run_test.go')
| -rw-r--r-- | internal/hexailsp/run_test.go | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/internal/hexailsp/run_test.go b/internal/hexailsp/run_test.go new file mode 100644 index 0000000..2c0fcaf --- /dev/null +++ b/internal/hexailsp/run_test.go @@ -0,0 +1,143 @@ +// Summary: Tests for the Hexai LSP runner using a fake server factory and environment keys. +// Not yet reviewed by a human +package hexailsp + +import ( + "bytes" + "log" + "io" + "os" + "path/filepath" + "testing" + + "hexai/internal/appconfig" + "hexai/internal/llm" + "hexai/internal/lsp" + "hexai/internal/logging" +) + +// fake server capturing options and recording run calls +type fakeServer struct{ + ran bool + opts lsp.ServerOptions +} +func (f *fakeServer) Run() error { f.ran = true; return nil } + +func TestRunWithFactory_UsesDefaultsAndCallsServer(t *testing.T) { + var stderr bytes.Buffer + logger := log.New(&stderr, "hexai-lsp ", 0) + cfg := appconfig.Load(nil) // defaults + var gotOpts lsp.ServerOptions + factory := func(r io.Reader, w io.Writer, logger *log.Logger, opts lsp.ServerOptions) ServerRunner { + gotOpts = opts + return &fakeServer{opts: opts} + } + if err := RunWithFactory("", bytes.NewBuffer(nil), bytes.NewBuffer(nil), logger, cfg, nil, factory); err != nil { + t.Fatalf("RunWithFactory error: %v", err) + } + if gotOpts.MaxTokens != cfg.MaxTokens { + t.Fatalf("MaxTokens want %d got %d", cfg.MaxTokens, gotOpts.MaxTokens) + } + if gotOpts.ContextMode != cfg.ContextMode { + t.Fatalf("ContextMode want %q got %q", cfg.ContextMode, gotOpts.ContextMode) + } + if gotOpts.WindowLines != cfg.ContextWindowLines { + t.Fatalf("WindowLines want %d got %d", cfg.ContextWindowLines, gotOpts.WindowLines) + } + if gotOpts.MaxContextTokens != cfg.MaxContextTokens { + t.Fatalf("MaxContextTokens want %d got %d", cfg.MaxContextTokens, gotOpts.MaxContextTokens) + } + if gotOpts.NoDiskIO != cfg.NoDiskIO { + t.Fatalf("NoDiskIO want %v got %v", cfg.NoDiskIO, gotOpts.NoDiskIO) + } + if gotOpts.Client != nil { // with no env, openai client fails to build + t.Fatalf("expected nil client when API key missing") + } +} + +func TestRunWithFactory_BuildsClientWhenKeysPresent(t *testing.T) { + // Set a dummy OpenAI key to allow client creation + old := os.Getenv("OPENAI_API_KEY") + t.Cleanup(func(){ _ = os.Setenv("OPENAI_API_KEY", old) }) + _ = os.Setenv("OPENAI_API_KEY", "dummy") + + var stderr bytes.Buffer + logger := log.New(&stderr, "hexai-lsp ", 0) + cfg := appconfig.Load(nil) // defaults, provider=openai by default + var got llm.Client + factory := func(r io.Reader, w io.Writer, logger *log.Logger, opts lsp.ServerOptions) ServerRunner { + got = opts.Client + return &fakeServer{opts: opts} + } + if err := RunWithFactory("", bytes.NewBuffer(nil), bytes.NewBuffer(nil), logger, cfg, nil, factory); err != nil { + t.Fatalf("RunWithFactory error: %v", err) + } + if got == nil { + t.Fatalf("expected non-nil client when OPENAI_API_KEY is set") + } +} + +func TestRun_RespectsLogPathFlag(t *testing.T) { + tmp := t.TempDir() + logFile := filepath.Join(tmp, "hexai-lsp.log") + // Run with real Run but nil env key so client disabled; ensure no panic and file created + if err := Run(logFile, bytes.NewBuffer(nil), bytes.NewBuffer(nil), bytes.NewBuffer(nil)); err != nil { + t.Fatalf("Run error: %v", err) + } + if _, err := os.Stat(logFile); err != nil { + t.Fatalf("expected log file to be created: %v", err) + } +} + +func TestRunWithFactory_NormalizesContextMode_AndSetsPreviewLimit(t *testing.T) { + t.Cleanup(func(){ logging.SetLogPreviewLimit(0) }) + var stderr bytes.Buffer + logger := log.New(&stderr, "hexai-lsp ", 0) + cfg := appconfig.App{ + ContextMode: " File-On-New-Func ", + LogPreviewLimit: 3, + } + var gotOpts lsp.ServerOptions + factory := func(r io.Reader, w io.Writer, logger *log.Logger, opts lsp.ServerOptions) ServerRunner { + gotOpts = opts + return &fakeServer{opts: opts} + } + if err := RunWithFactory("", bytes.NewBuffer(nil), bytes.NewBuffer(nil), logger, cfg, nil, factory); err != nil { + t.Fatalf("RunWithFactory error: %v", err) + } + if gotOpts.ContextMode != "file-on-new-func" { + t.Fatalf("ContextMode not normalized: %q", gotOpts.ContextMode) + } + if logging.PreviewForLog("abcdef") != "abc…" { + t.Fatalf("PreviewForLog not respecting limit: %q", logging.PreviewForLog("abcdef")) + } +} + +func TestRunWithFactory_LogContextFlag(t *testing.T) { + var stderr bytes.Buffer + logger := log.New(&stderr, "hexai-lsp ", 0) + cfg := appconfig.App{} + var got1, got2 lsp.ServerOptions + first := true + factory := func(r io.Reader, w io.Writer, logger *log.Logger, opts lsp.ServerOptions) ServerRunner { + if first { + got1 = opts + first = false + } else { + got2 = opts + } + return &fakeServer{opts: opts} + } + if err := RunWithFactory("/tmp/some.log", bytes.NewBuffer(nil), bytes.NewBuffer(nil), logger, cfg, nil, factory); err != nil { + t.Fatalf("RunWithFactory error: %v", err) + } + if !got1.LogContext { + t.Fatalf("expected LogContext true when logPath is non-empty") + } + if err := RunWithFactory("", bytes.NewBuffer(nil), bytes.NewBuffer(nil), logger, cfg, nil, factory); err != nil { + t.Fatalf("RunWithFactory error: %v", err) + } + if got2.LogContext { + t.Fatalf("expected LogContext false when logPath is empty") + } +} |
