Files
marte_dev_tools/test/lsp_server_test.go

200 lines
4.7 KiB
Go

package integration
import (
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
"github.com/marte-community/marte-dev-tools/internal/index"
"github.com/marte-community/marte-dev-tools/internal/lsp"
"github.com/marte-community/marte-dev-tools/internal/parser"
)
func TestInitProjectScan(t *testing.T) {
// 1. Setup temp dir with files
tmpDir, err := os.MkdirTemp("", "lsp_test")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpDir)
// File 1: Definition
if err := os.WriteFile(filepath.Join(tmpDir, "def.marte"), []byte("#package Test.Common\n+Target = { Class = C }"), 0644); err != nil {
t.Fatal(err)
}
// File 2: Reference
if err := os.WriteFile(filepath.Join(tmpDir, "ref.marte"), []byte("#package Test.Common\n+Source = { Class = C Link = Target }"), 0644); err != nil {
t.Fatal(err)
}
// 2. Initialize
lsp.Tree = index.NewProjectTree() // Reset global tree
initParams := lsp.InitializeParams{RootPath: tmpDir}
paramsBytes, _ := json.Marshal(initParams)
msg := &lsp.JsonRpcMessage{
Method: "initialize",
Params: paramsBytes,
ID: 1,
}
lsp.HandleMessage(msg)
// Query the reference in ref.marte at "Target"
defParams := lsp.DefinitionParams{
TextDocument: lsp.TextDocumentIdentifier{URI: "file://" + filepath.Join(tmpDir, "ref.marte")},
Position: lsp.Position{Line: 1, Character: 29},
}
res := lsp.HandleDefinition(defParams)
if res == nil {
t.Fatal("Definition not found via LSP after initialization")
}
locs, ok := res.([]lsp.Location)
if !ok {
t.Fatalf("Expected []lsp.Location, got %T", res)
}
if len(locs) == 0 {
t.Fatal("No locations found")
}
// Verify uri points to def.marte
expectedURI := "file://" + filepath.Join(tmpDir, "def.marte")
if locs[0].URI != expectedURI {
t.Errorf("Expected URI %s, got %s", expectedURI, locs[0].URI)
}
}
func TestHandleDefinition(t *testing.T) {
// Reset tree for test
lsp.Tree = index.NewProjectTree()
content := `
+MyObject = {
Class = Type
}
+RefObject = {
Class = Type
RefField = MyObject
}
`
path := "/test.marte"
p := parser.NewParser(content)
config, err := p.Parse()
if err != nil {
t.Fatalf("Parse failed: %v", err)
}
lsp.Tree.AddFile(path, config)
lsp.Tree.ResolveReferences()
t.Logf("Refs: %d", len(lsp.Tree.References))
for _, r := range lsp.Tree.References {
t.Logf(" %s at %d:%d", r.Name, r.Position.Line, r.Position.Column)
}
// Test Go to Definition on MyObject reference
params := lsp.DefinitionParams{
TextDocument: lsp.TextDocumentIdentifier{URI: "file://" + path},
Position: lsp.Position{Line: 6, Character: 15}, // "MyObject" in RefField = MyObject
}
result := lsp.HandleDefinition(params)
if result == nil {
t.Fatal("HandleDefinition returned nil")
}
locations, ok := result.([]lsp.Location)
if !ok {
t.Fatalf("Expected []lsp.Location, got %T", result)
}
if len(locations) != 1 {
t.Fatalf("Expected 1 location, got %d", len(locations))
}
if locations[0].Range.Start.Line != 1 { // +MyObject is on line 2 (0-indexed 1)
t.Errorf("Expected definition on line 1, got %d", locations[0].Range.Start.Line)
}
}
func TestHandleReferences(t *testing.T) {
// Reset tree for test
lsp.Tree = index.NewProjectTree()
content := `
+MyObject = {
Class = Type
}
+RefObject = {
Class = Type
RefField = MyObject
}
+AnotherRef = {
Ref = MyObject
}
`
path := "/test.marte"
p := parser.NewParser(content)
config, err := p.Parse()
if err != nil {
t.Fatalf("Parse failed: %v", err)
}
lsp.Tree.AddFile(path, config)
lsp.Tree.ResolveReferences()
// Test Find References for MyObject (triggered from its definition)
params := lsp.ReferenceParams{
TextDocument: lsp.TextDocumentIdentifier{URI: "file://" + path},
Position: lsp.Position{Line: 1, Character: 1}, // "+MyObject"
Context: lsp.ReferenceContext{IncludeDeclaration: true},
}
locations := lsp.HandleReferences(params)
if len(locations) != 3 { // 1 declaration + 2 references
t.Fatalf("Expected 3 locations, got %d", len(locations))
}
}
func TestLSPFormatting(t *testing.T) {
// Setup
content := `
#package Proj.Main
+Object={
Field=1
}
`
uri := "file:///test.marte"
// Open (populate Documents map)
lsp.Documents[uri] = content
// Format
params := lsp.DocumentFormattingParams{
TextDocument: lsp.TextDocumentIdentifier{URI: uri},
}
edits := lsp.HandleFormatting(params)
if len(edits) != 1 {
t.Fatalf("Expected 1 edit, got %d", len(edits))
}
newText := edits[0].NewText
expected := `#package Proj.Main
+Object = {
Field = 1
}
`
// Normalize newlines for comparison just in case
if strings.TrimSpace(strings.ReplaceAll(newText, "\r\n", "\n")) != strings.TrimSpace(strings.ReplaceAll(expected, "\r\n", "\n")) {
t.Errorf("Formatting mismatch.\nExpected:\n%s\nGot:\n%s", expected, newText)
}
}