summaryrefslogtreecommitdiff
path: root/internal/lsp/codeaction_custom_errors_test.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-09-15 08:07:51 +0300
committerPaul Buetow <paul@buetow.org>2025-09-15 08:07:51 +0300
commitdac90ba0e3036a15779da70e771c5cf2818c817f (patch)
treebd6ddb850a640b3fce79f5e65b27ce41b95e11a3 /internal/lsp/codeaction_custom_errors_test.go
parent828d1cc59ac22d10cd1298aac0488f868e2280db (diff)
release: v0.10.1v0.10.1
- Fix TUI 'p' hotkey: open editor for Custom prompt - Introduce ActionCustomPrompt to disambiguate from Custom actions submenu - Bump version to 0.10.1
Diffstat (limited to 'internal/lsp/codeaction_custom_errors_test.go')
-rw-r--r--internal/lsp/codeaction_custom_errors_test.go159
1 files changed, 86 insertions, 73 deletions
diff --git a/internal/lsp/codeaction_custom_errors_test.go b/internal/lsp/codeaction_custom_errors_test.go
index 2f42f65..ca6111f 100644
--- a/internal/lsp/codeaction_custom_errors_test.go
+++ b/internal/lsp/codeaction_custom_errors_test.go
@@ -1,92 +1,105 @@
package lsp
import (
- "bytes"
- "context"
- "encoding/json"
- "errors"
- "testing"
+ "bytes"
+ "context"
+ "encoding/json"
+ "errors"
+ "testing"
- "codeberg.org/snonux/hexai/internal/llm"
+ "codeberg.org/snonux/hexai/internal/llm"
)
func TestResolveCodeAction_Custom_UnknownID(t *testing.T) {
- s := newTestServer()
- // No matching custom action configured
- s.customActions = []CustomAction{{ID: "known", Title: "Known", Instruction: "x"}}
- uri := "file:///t.go"
- payload := struct {
- Type string `json:"type"`
- ID string `json:"id"`
- URI string `json:"uri"`
- Range Range `json:"range"`
- Selection string `json:"selection"`
- }{Type: "custom", ID: "missing", URI: uri, Range: Range{}, Selection: "abc"}
- raw, _ := json.Marshal(payload)
- ca := CodeAction{Title: "Hexai: X", Data: raw}
- if _, ok := s.resolveCodeAction(ca); ok {
- t.Fatalf("expected resolve to fail for unknown custom id")
- }
+ s := newTestServer()
+ // No matching custom action configured
+ s.customActions = []CustomAction{{ID: "known", Title: "Known", Instruction: "x"}}
+ uri := "file:///t.go"
+ payload := struct {
+ Type string `json:"type"`
+ ID string `json:"id"`
+ URI string `json:"uri"`
+ Range Range `json:"range"`
+ Selection string `json:"selection"`
+ }{Type: "custom", ID: "missing", URI: uri, Range: Range{}, Selection: "abc"}
+ raw, _ := json.Marshal(payload)
+ ca := CodeAction{Title: "Hexai: X", Data: raw}
+ if _, ok := s.resolveCodeAction(ca); ok {
+ t.Fatalf("expected resolve to fail for unknown custom id")
+ }
}
type errLLM struct{}
-func (errLLM) Chat(_ context.Context, _ []llm.Message, _ ...llm.RequestOption) (string, error) { return "", errors.New("boom") }
+
+func (errLLM) Chat(_ context.Context, _ []llm.Message, _ ...llm.RequestOption) (string, error) {
+ return "", errors.New("boom")
+}
func (errLLM) Name() string { return "prov" }
func (errLLM) DefaultModel() string { return "m" }
func TestResolveCodeAction_Custom_EmptyAndError(t *testing.T) {
- // empty output case
- s1 := newTestServer()
- s1.llmClient = fakeLLM{resp: " \n\n"}
- s1.customActions = []CustomAction{{ID: "empty", Title: "Empty", Instruction: "x"}}
- raw1, _ := json.Marshal(struct{ Type, ID, URI, Selection string; Range Range }{"custom", "empty", "file:///t.go", "sel", Range{}})
- if resolved, ok := s1.resolveCodeAction(CodeAction{Data: raw1}); ok || resolved.Edit != nil {
- t.Fatalf("expected no edit for empty llm output")
- }
+ // empty output case
+ s1 := newTestServer()
+ s1.llmClient = fakeLLM{resp: " \n\n"}
+ s1.customActions = []CustomAction{{ID: "empty", Title: "Empty", Instruction: "x"}}
+ raw1, _ := json.Marshal(struct {
+ Type, ID, URI, Selection string
+ Range Range
+ }{"custom", "empty", "file:///t.go", "sel", Range{}})
+ if resolved, ok := s1.resolveCodeAction(CodeAction{Data: raw1}); ok || resolved.Edit != nil {
+ t.Fatalf("expected no edit for empty llm output")
+ }
- // error case
- s2 := newTestServer()
- s2.llmClient = errLLM{}
- s2.customActions = []CustomAction{{ID: "err", Title: "Err", Instruction: "x"}}
- raw2, _ := json.Marshal(struct{ Type, ID, URI, Selection string; Range Range }{"custom", "err", "file:///t.go", "sel", Range{}})
- if resolved, ok := s2.resolveCodeAction(CodeAction{Data: raw2}); ok || resolved.Edit != nil {
- t.Fatalf("expected no edit for llm error")
- }
+ // error case
+ s2 := newTestServer()
+ s2.llmClient = errLLM{}
+ s2.customActions = []CustomAction{{ID: "err", Title: "Err", Instruction: "x"}}
+ raw2, _ := json.Marshal(struct {
+ Type, ID, URI, Selection string
+ Range Range
+ }{"custom", "err", "file:///t.go", "sel", Range{}})
+ if resolved, ok := s2.resolveCodeAction(CodeAction{Data: raw2}); ok || resolved.Edit != nil {
+ t.Fatalf("expected no edit for llm error")
+ }
}
func TestHandleCodeAction_Custom_SelectionSuppressedWhenEmpty(t *testing.T) {
- s := newTestServer()
- s.llmClient = fakeLLM{resp: "IGN"}
- // One selection-scoped and one diagnostics-scoped custom
- s.customActions = []CustomAction{
- {ID: "sel", Title: "Sel", Scope: "selection", Instruction: "x"},
- {ID: "diag", Title: "Diag", Scope: "diagnostics", User: "{{diagnostics}}"},
- }
- uri := "file:///t.go"
- s.setDocument(uri, "package p\nfunc f(){}\n")
- // Empty selection range (start==end)
- p := CodeActionParams{TextDocument: TextDocumentIdentifier{URI: uri}, Range: Range{Start: Position{Line: 1}, End: Position{Line: 1}}}
- // include a diagnostic so diagnostics action is allowed
- ctx := CodeActionContext{Diagnostics: []Diagnostic{{Range: Range{Start: Position{Line: 1}}, Message: "x"}}}
- rawCtx, _ := json.Marshal(ctx)
- p.Context = json.RawMessage(rawCtx)
- // Build request
- req := Request{JSONRPC: "2.0", ID: json.RawMessage("1"), Method: "textDocument/codeAction"}
- req.Params, _ = json.Marshal(p)
- // capture
- var out bytes.Buffer
- s.out = &out
- s.handleCodeAction(req)
- resp := captureResponse(t, &out)
- rb, _ := json.Marshal(resp.Result)
- var actions []CodeAction
- _ = json.Unmarshal(rb, &actions)
- seenSel, seenDiag := false, false
- for _, a := range actions {
- if a.Title == "Hexai: Sel" { seenSel = true }
- if a.Title == "Hexai: Diag" { seenDiag = true }
- }
- if seenSel || !seenDiag {
- t.Fatalf("expected only diagnostics custom when selection is empty; got %+v", actions)
- }
+ s := newTestServer()
+ s.llmClient = fakeLLM{resp: "IGN"}
+ // One selection-scoped and one diagnostics-scoped custom
+ s.customActions = []CustomAction{
+ {ID: "sel", Title: "Sel", Scope: "selection", Instruction: "x"},
+ {ID: "diag", Title: "Diag", Scope: "diagnostics", User: "{{diagnostics}}"},
+ }
+ uri := "file:///t.go"
+ s.setDocument(uri, "package p\nfunc f(){}\n")
+ // Empty selection range (start==end)
+ p := CodeActionParams{TextDocument: TextDocumentIdentifier{URI: uri}, Range: Range{Start: Position{Line: 1}, End: Position{Line: 1}}}
+ // include a diagnostic so diagnostics action is allowed
+ ctx := CodeActionContext{Diagnostics: []Diagnostic{{Range: Range{Start: Position{Line: 1}}, Message: "x"}}}
+ rawCtx, _ := json.Marshal(ctx)
+ p.Context = json.RawMessage(rawCtx)
+ // Build request
+ req := Request{JSONRPC: "2.0", ID: json.RawMessage("1"), Method: "textDocument/codeAction"}
+ req.Params, _ = json.Marshal(p)
+ // capture
+ var out bytes.Buffer
+ s.out = &out
+ s.handleCodeAction(req)
+ resp := captureResponse(t, &out)
+ rb, _ := json.Marshal(resp.Result)
+ var actions []CodeAction
+ _ = json.Unmarshal(rb, &actions)
+ seenSel, seenDiag := false, false
+ for _, a := range actions {
+ if a.Title == "Hexai: Sel" {
+ seenSel = true
+ }
+ if a.Title == "Hexai: Diag" {
+ seenDiag = true
+ }
+ }
+ if seenSel || !seenDiag {
+ t.Fatalf("expected only diagnostics custom when selection is empty; got %+v", actions)
+ }
}