Files
MonocoEditor-With-Lsp-Backend/backend/internal/completion/service_test.go
2026-02-15 15:55:49 +08:00

145 lines
3.7 KiB
Go

package completion
import (
"context"
"errors"
"testing"
)
type fakeLSPClient struct {
openCalls []openCall
changeCalls []changeCall
completionCalls []completionCall
completionResp Response
openErr error
changeErr error
completionErr error
}
type openCall struct {
uri string
text string
version int
}
type changeCall struct {
uri string
text string
version int
}
type completionCall struct {
uri string
line int
character int
}
func (f *fakeLSPClient) DidOpen(_ context.Context, uri, text string, version int) error {
f.openCalls = append(f.openCalls, openCall{uri: uri, text: text, version: version})
return f.openErr
}
func (f *fakeLSPClient) DidChange(_ context.Context, uri, text string, version int) error {
f.changeCalls = append(f.changeCalls, changeCall{uri: uri, text: text, version: version})
return f.changeErr
}
func (f *fakeLSPClient) Completion(_ context.Context, uri string, line, character int) (Response, error) {
f.completionCalls = append(f.completionCalls, completionCall{uri: uri, line: line, character: character})
if f.completionErr != nil {
return Response{}, f.completionErr
}
return f.completionResp, nil
}
func TestServiceCompleteFirstRequestSendsDidOpen(t *testing.T) {
fake := &fakeLSPClient{
completionResp: Response{Items: []Item{{Label: "Println"}}, IsIncomplete: true},
}
svc := NewService(fake)
resp, err := svc.Complete(context.Background(), Request{
URI: "file:///main.go",
Text: "package main\nfunc main() { fmt.Pr }",
Line: 1,
Character: 23,
})
if err != nil {
t.Fatalf("Complete() error = %v", err)
}
if len(fake.openCalls) != 1 {
t.Fatalf("expected 1 didOpen call, got %d", len(fake.openCalls))
}
if fake.openCalls[0].version != 1 {
t.Fatalf("expected didOpen version=1, got %d", fake.openCalls[0].version)
}
if len(fake.changeCalls) != 0 {
t.Fatalf("expected 0 didChange calls, got %d", len(fake.changeCalls))
}
if len(fake.completionCalls) != 1 {
t.Fatalf("expected 1 completion call, got %d", len(fake.completionCalls))
}
if !resp.IsIncomplete || len(resp.Items) != 1 || resp.Items[0].Label != "Println" {
t.Fatalf("unexpected response: %+v", resp)
}
}
func TestServiceCompleteSecondRequestUsesDidChange(t *testing.T) {
fake := &fakeLSPClient{}
svc := NewService(fake)
_, err := svc.Complete(context.Background(), Request{
URI: "file:///main.go",
Text: "package main\nfunc main() {}",
Line: 1,
Character: 12,
})
if err != nil {
t.Fatalf("first Complete() error = %v", err)
}
_, err = svc.Complete(context.Background(), Request{
URI: "file:///main.go",
Text: "package main\nfunc main() { fmt.Pr }",
Line: 1,
Character: 23,
})
if err != nil {
t.Fatalf("second Complete() error = %v", err)
}
if len(fake.openCalls) != 1 {
t.Fatalf("expected 1 didOpen call, got %d", len(fake.openCalls))
}
if len(fake.changeCalls) != 1 {
t.Fatalf("expected 1 didChange call, got %d", len(fake.changeCalls))
}
if fake.changeCalls[0].version != 2 {
t.Fatalf("expected didChange version=2, got %d", fake.changeCalls[0].version)
}
}
func TestServiceCompleteValidatesRequest(t *testing.T) {
svc := NewService(&fakeLSPClient{})
_, err := svc.Complete(context.Background(), Request{URI: "", Text: "", Line: -1, Character: 0})
if !errors.Is(err, ErrInvalidRequest) {
t.Fatalf("expected ErrInvalidRequest, got %v", err)
}
}
func TestServiceCompleteReturnsClientError(t *testing.T) {
fake := &fakeLSPClient{openErr: errors.New("open failed")}
svc := NewService(fake)
_, err := svc.Complete(context.Background(), Request{
URI: "file:///main.go",
Text: "package main",
Line: 0,
Character: 0,
})
if err == nil {
t.Fatal("expected error, got nil")
}
}