diff options
| -rw-r--r-- | .gitignore | 6 | ||||
| -rw-r--r-- | internal/hexaicli/run_test.go | 5 | ||||
| -rw-r--r-- | internal/lsp/codeaction_more_test.go | 69 |
3 files changed, 80 insertions, 0 deletions
@@ -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) } +} + |
