summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-09-04 08:54:18 +0300
committerPaul Buetow <paul@buetow.org>2025-09-04 08:54:18 +0300
commit9b5ce6a6ffc8d9ff10ae8d2c384efd91de8d2b76 (patch)
tree4711f887c03e20fab9a334962238c53ec6046941
parent28945db47e13b3de8345ee1cc0c8dece1d9a4e0e (diff)
tests: fix hexaicli OpenAI-key dependent tests; add lsp code action and helper tests; ignore coverage artifacts
-rw-r--r--.gitignore6
-rw-r--r--internal/hexaicli/run_test.go5
-rw-r--r--internal/lsp/codeaction_more_test.go69
3 files changed, 80 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index eb9dcfa..96c06fe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,9 @@
/hexai-lsp
/.gocache/
/.gomodcache/
+/*.out
+/*.html
+/docs/coverage/
+/docs/*.out
+/docs/*.html
+llm.out
diff --git a/internal/hexaicli/run_test.go b/internal/hexaicli/run_test.go
index 3fe59fb..69fe089 100644
--- a/internal/hexaicli/run_test.go
+++ b/internal/hexaicli/run_test.go
@@ -87,6 +87,9 @@ func TestRun_OpenAI_NoKey_ShowsError(t *testing.T) {
// write config with provider=openai
writeJSON(t, filepath.Join(dir, "hexai", "config.json"), map[string]any{"provider":"openai", "openai_model":"gpt-x"})
t.Setenv("XDG_CONFIG_HOME", dir)
+ // Ensure no OpenAI API key is present in environment
+ t.Setenv("HEXAI_OPENAI_API_KEY", "")
+ t.Setenv("OPENAI_API_KEY", "")
var out, errb bytes.Buffer
// Run expects parsed flags; here args irrelevant
err := Run(context.Background(), []string{"hello"}, strings.NewReader(""), &out, &errb)
@@ -110,6 +113,8 @@ func TestNewClientFromConfig_Ollama(t *testing.T) {
func TestNewClientFromConfig_OpenAI_MissingKey(t *testing.T) {
cfg := appconfig.App{ Provider: "openai", OpenAIBaseURL: "https://api", OpenAIModel: "gpt" }
+ t.Setenv("HEXAI_OPENAI_API_KEY", "")
+ t.Setenv("OPENAI_API_KEY", "")
if _, err := newClientFromConfig(cfg); err == nil {
t.Fatalf("expected error for missing openai key")
}
diff --git a/internal/lsp/codeaction_more_test.go b/internal/lsp/codeaction_more_test.go
new file mode 100644
index 0000000..e19c699
--- /dev/null
+++ b/internal/lsp/codeaction_more_test.go
@@ -0,0 +1,69 @@
+package lsp
+
+import (
+ "encoding/json"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+func TestBuildDocumentCodeAction_AndResolve(t *testing.T) {
+ s := newTestServer()
+ s.llmClient = fakeLLM{resp: "// doc\nfunc add(a,b int) int { return a+b }"}
+ uri := "file:///doc.go"
+ s.setDocument(uri, "package x\nfunc add(a,b int) int {return a+b}")
+ p := CodeActionParams{TextDocument: TextDocumentIdentifier{URI: uri}, Range: Range{Start: Position{Line:1, Character:0}, End: Position{Line:1, Character:10}}}
+ sel := "func add(a,b int) int {return a+b}"
+ ca := s.buildDocumentCodeAction(p, sel)
+ if ca == nil { t.Fatalf("expected document code action") }
+ resolved, ok := s.resolveCodeAction(*ca)
+ if !ok || resolved.Edit == nil { t.Fatalf("expected resolved edit") }
+ edits := resolved.Edit.Changes[uri]
+ if len(edits) != 1 || strings.TrimSpace(edits[0].NewText) == "" { t.Fatalf("expected replacement text") }
+}
+
+func TestBuildGoUnitTestCodeAction_AndResolveCreate(t *testing.T) {
+ s := newTestServer()
+ // place files under a temp dir to avoid collisions
+ dir := t.TempDir()
+ srcPath := filepath.Join(dir, "calc.go")
+ uri := "file://" + srcPath
+ src := "package calc\n\nfunc Sum(a, b int) int { return a+b }\n"
+ s.setDocument(uri, src)
+ // Offer action (not a _test.go)
+ p := CodeActionParams{TextDocument: TextDocumentIdentifier{URI: uri}, Range: Range{Start: Position{Line:2}}}
+ if a := s.buildGoUnitTestCodeAction(p); a == nil { t.Fatalf("expected go unit test action") }
+ // Resolve should create new test file with package+import and a test function
+ we, testURI, _, ok := s.resolveGoTest(uri, Position{Line:2})
+ if !ok { t.Fatalf("resolveGoTest failed") }
+ if len(we.DocumentChanges) != 2 { t.Fatalf("expected create + edits, got %d", len(we.DocumentChanges)) }
+ if !strings.HasSuffix(testURI, "_test.go") { t.Fatalf("unexpected test URI: %s", testURI) }
+}
+
+func TestBuildGoUnitTestCodeAction_SkipOnTestFile(t *testing.T) {
+ s := newTestServer()
+ uri := "file:///tmp/x_test.go"
+ s.setDocument(uri, "package p\nfunc T(){}")
+ p := CodeActionParams{TextDocument: TextDocumentIdentifier{URI: uri}}
+ if a := s.buildGoUnitTestCodeAction(p); a != nil { t.Fatalf("expected no action on _test.go") }
+}
+
+func TestDiagnosticsInRange(t *testing.T) {
+ s := newTestServer()
+ ctx := CodeActionContext{Diagnostics: []Diagnostic{
+ {Range: Range{Start: Position{Line: 3}, End: Position{Line: 3, Character: 5}}, Message: "in"},
+ {Range: Range{Start: Position{Line: 10}, End: Position{Line: 11}}, Message: "out"},
+ }}
+ raw, _ := json.Marshal(ctx)
+ got := s.diagnosticsInRange(json.RawMessage(raw), Range{Start: Position{Line:2}, End: Position{Line:4}})
+ if len(got) != 1 || got[0].Message != "in" { t.Fatalf("unexpected diags: %+v", got) }
+}
+
+func TestDocBeforeAfter(t *testing.T) {
+ s := newTestServer()
+ uri := "file:///d.go"
+ s.setDocument(uri, "ab\ncd\nef")
+ before, after := s.docBeforeAfter(uri, Position{Line:1, Character:1})
+ if before != "ab\nc" || after != "d\nef" { t.Fatalf("before=%q after=%q", before, after) }
+}
+