summaryrefslogtreecommitdiff
path: root/internal/lsp/handlers_end_to_end_test.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-09-04 14:24:36 +0300
committerPaul Buetow <paul@buetow.org>2025-09-04 14:24:36 +0300
commitd68e5b3b188585fe234d0ce295ec7f054c8bad5f (patch)
tree974e067d9894f0da38513acdc27b56729b0f06e4 /internal/lsp/handlers_end_to_end_test.go
parent3c322b7046669a77c276ce05469bfc2db0b446b2 (diff)
tests(lsp): push coverage over 80%\n- Add init/trigger, chat history, document handler, transport readMessage, and rewrite resolve tests\n- Cover deferShowDocument and shutdown reply\n- Now ~81.2% coverage for internal/lsp
Diffstat (limited to 'internal/lsp/handlers_end_to_end_test.go')
-rw-r--r--internal/lsp/handlers_end_to_end_test.go61
1 files changed, 54 insertions, 7 deletions
diff --git a/internal/lsp/handlers_end_to_end_test.go b/internal/lsp/handlers_end_to_end_test.go
index 9767fa6..ba4a0bc 100644
--- a/internal/lsp/handlers_end_to_end_test.go
+++ b/internal/lsp/handlers_end_to_end_test.go
@@ -3,6 +3,7 @@ package lsp
import (
"bytes"
"encoding/json"
+ "fmt"
"io"
"log"
"strings"
@@ -29,14 +30,35 @@ func captureResponse(t *testing.T, buf *bytes.Buffer) Response {
func captureRequest(t *testing.T, buf *bytes.Buffer) Request {
t.Helper()
raw := buf.String()
- idx := strings.Index(raw, "\r\n\r\n")
- if idx < 0 { t.Fatalf("no header/body separator in %q", raw) }
- body := raw[idx+4:]
- var req Request
- if err := json.Unmarshal([]byte(body), &req); err != nil {
- t.Fatalf("unmarshal request: %v", err)
+ // There may be multiple framed messages concatenated; scan for each
+ off := 0
+ for off < len(raw) {
+ rest := raw[off:]
+ idx := strings.Index(rest, "\r\n\r\n")
+ if idx < 0 { break }
+ body := rest[idx+4:]
+ // Content-Length header indicates body length; parse length from header
+ hdr := rest[:idx]
+ clen := 0
+ for _, line := range strings.Split(hdr, "\r\n") {
+ if strings.HasPrefix(strings.ToLower(line), "content-length:") {
+ var n int
+ _, _ = fmt.Sscanf(line, "Content-Length: %d", &n)
+ clen = n
+ break
+ }
+ }
+ if clen <= 0 || clen > len(body) { clen = len(body) }
+ piece := body[:clen]
+ var req Request
+ _ = json.Unmarshal([]byte(piece), &req)
+ if req.Method != "" {
+ return req
+ }
+ off += idx + 4 + clen
}
- return req
+ t.Fatalf("no request found in output")
+ return Request{}
}
func TestHandleCodeAction_ListsHexaiActions(t *testing.T) {
@@ -140,6 +162,31 @@ func TestHandleCodeAction_NoLLMOrEmptySelection_ReturnsEmpty(t *testing.T) {
func mustJSON(v any) json.RawMessage { b, _ := json.Marshal(v); return b }
+func TestHandle_UnknownMethod_ReturnsError(t *testing.T) {
+ var out bytes.Buffer
+ s := &Server{logger: log.New(io.Discard, "", 0), docs: make(map[string]*document), out: &out, handlers: map[string]func(Request){}}
+ req := Request{JSONRPC: "2.0", ID: json.RawMessage("9"), Method: "no/such"}
+ out.Reset()
+ s.handle(req)
+ resp := captureResponse(t, &out)
+ if resp.Error == nil || resp.Error.Code != -32601 { t.Fatalf("expected method not found error, got %+v", resp.Error) }
+}
+
+func TestHandle_Dispatch_Initialize(t *testing.T) {
+ var out bytes.Buffer
+ // Build a server via constructor to ensure handlers map is populated
+ s := NewServer(bytes.NewReader(nil), &out, log.New(io.Discard, "", 0), ServerOptions{})
+ req := Request{JSONRPC: "2.0", ID: json.RawMessage("13"), Method: "initialize"}
+ out.Reset()
+ s.handle(req)
+ resp := captureResponse(t, &out)
+ var init InitializeResult
+ b, _ := json.Marshal(resp.Result)
+ _ = json.Unmarshal(b, &init)
+ if init.Capabilities.CodeActionProvider == nil || init.Capabilities.CompletionProvider == nil { t.Fatalf("missing capabilities") }
+}
+
+
func TestDetectAndHandleChat_InsertsReply(t *testing.T) {
var out bytes.Buffer
s := &Server{logger: log.New(io.Discard, "", 0), docs: make(map[string]*document), out: &out}