- Add Nacos registry for service registration and deregistration. - Implement Redis registry for session management with heartbeat and session claiming. - Improve completion service with session handling and request validation. - Enhance WebSocket handling for completion requests with JSON-RPC support. - Add tests for new registry implementations and completion manager functionalities. - Refactor existing code for better readability and maintainability.
149 lines
4.0 KiB
Go
149 lines
4.0 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
|
||
}
|
||
|
||
// 首次请求应发送 didOpen,并继续请求 completion。
|
||
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)
|
||
}
|
||
}
|
||
|
||
// 同一文档第二次请求应发送 didChange,且版本号递增。
|
||
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)
|
||
}
|
||
}
|
||
|
||
// 参数不合法时应直接返回 ErrInvalidRequest。
|
||
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)
|
||
}
|
||
}
|
||
|
||
// 底层 client 出错应向上透传错误。
|
||
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")
|
||
}
|
||
}
|