github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/internal/lsp/lsp_test.go (about) 1 package lsp 2 3 import ( 4 "testing" 5 6 "github.com/sourcegraph/go-lsp" 7 "github.com/stretchr/testify/require" 8 ) 9 10 func TestInitialize(t *testing.T) { 11 tester := newLSPTester(t) 12 tester.initialize() 13 } 14 15 func TestAnotherCommandBeforeInitialize(t *testing.T) { 16 tester := newLSPTester(t) 17 18 lerr, serverState := sendAndExpectError(tester, "textDocument/formatting", lsp.FormattingOptions{}) 19 require.Equal(t, serverStateNotInitialized, serverState) 20 require.Equal(t, codeUninitialized, lerr.Code) 21 } 22 23 func TestDocumentChange(t *testing.T) { 24 tester := newLSPTester(t) 25 tester.initialize() 26 27 tester.setFileContents("file:///test", "test") 28 29 contents, ok := tester.server.files.Get("file:///test") 30 require.True(t, ok) 31 require.Equal(t, "test", contents.contents) 32 33 tester.setFileContents("file:///test", "test2") 34 35 contents, ok = tester.server.files.Get("file:///test") 36 require.True(t, ok) 37 require.Equal(t, "test2", contents.contents) 38 } 39 40 func TestDocumentNoDiagnostics(t *testing.T) { 41 tester := newLSPTester(t) 42 tester.initialize() 43 44 tester.setFileContents("file:///test", "definition user{}") 45 46 resp, _ := sendAndReceive[FullDocumentDiagnosticReport](tester, "textDocument/diagnostic", 47 TextDocumentDiagnosticParams{ 48 TextDocument: TextDocument{URI: "file:///test"}, 49 }) 50 require.Equal(t, "full", resp.Kind) 51 require.Len(t, resp.Items, 0) 52 } 53 54 func TestDocumentErrorDiagnostics(t *testing.T) { 55 tester := newLSPTester(t) 56 tester.initialize() 57 58 tester.setFileContents("file:///test", "test") 59 60 resp, _ := sendAndReceive[FullDocumentDiagnosticReport](tester, "textDocument/diagnostic", 61 TextDocumentDiagnosticParams{ 62 TextDocument: TextDocument{URI: "file:///test"}, 63 }) 64 require.Equal(t, "full", resp.Kind) 65 require.Len(t, resp.Items, 1) 66 require.Equal(t, lsp.Error, resp.Items[0].Severity) 67 require.Equal(t, "Unexpected token at root level: TokenTypeIdentifier", resp.Items[0].Message) 68 require.Equal(t, lsp.Range{ 69 Start: lsp.Position{Line: 0, Character: 0}, 70 End: lsp.Position{Line: 0, Character: 0}, 71 }, resp.Items[0].Range) 72 73 tester.setFileContents("file:///test", "definition user{}") 74 75 resp, _ = sendAndReceive[FullDocumentDiagnosticReport](tester, "textDocument/diagnostic", 76 TextDocumentDiagnosticParams{ 77 TextDocument: TextDocument{URI: "file:///test"}, 78 }) 79 require.Equal(t, "full", resp.Kind) 80 require.Len(t, resp.Items, 0) 81 } 82 83 func TestDocumentWarningDiagnostics(t *testing.T) { 84 tester := newLSPTester(t) 85 tester.initialize() 86 87 tester.setFileContents("file:///test", ` 88 definition user {} 89 90 definition resource { 91 relation viewer: user 92 permission view_resource = viewer 93 } 94 `) 95 96 resp, _ := sendAndReceive[FullDocumentDiagnosticReport](tester, "textDocument/diagnostic", 97 TextDocumentDiagnosticParams{ 98 TextDocument: TextDocument{URI: "file:///test"}, 99 }) 100 require.Equal(t, "full", resp.Kind) 101 require.Len(t, resp.Items, 1) 102 require.Equal(t, lsp.DiagnosticSeverity(lsp.Warning), resp.Items[0].Severity) 103 require.Equal(t, `Permission "view_resource" references parent type "resource" in its name; it is recommended to drop the suffix`, resp.Items[0].Message) 104 require.Equal(t, lsp.Range{ 105 Start: lsp.Position{Line: 5, Character: 3}, 106 End: lsp.Position{Line: 5, Character: 3}, 107 }, resp.Items[0].Range) 108 } 109 110 func TestDocumentDiagnosticsForTypeError(t *testing.T) { 111 tester := newLSPTester(t) 112 tester.initialize() 113 114 tester.setFileContents("file:///test", `definition user {} 115 116 definition resource { 117 relation foo: something 118 } 119 `) 120 121 resp, _ := sendAndReceive[FullDocumentDiagnosticReport](tester, "textDocument/diagnostic", 122 TextDocumentDiagnosticParams{ 123 TextDocument: TextDocument{URI: "file:///test"}, 124 }) 125 require.Equal(t, "full", resp.Kind) 126 require.Len(t, resp.Items, 1) 127 require.Equal(t, lsp.Error, resp.Items[0].Severity) 128 require.Contains(t, resp.Items[0].Message, "object definition `something` not found") 129 require.Equal(t, lsp.Range{ 130 Start: lsp.Position{Line: 3, Character: 16}, 131 End: lsp.Position{Line: 3, Character: 16}, 132 }, resp.Items[0].Range) 133 } 134 135 func TestDocumentFormat(t *testing.T) { 136 tester := newLSPTester(t) 137 tester.initialize() 138 139 tester.setFileContents("file:///test", "definition user{}") 140 141 resp, _ := sendAndReceive[[]lsp.TextEdit](tester, "textDocument/formatting", 142 lsp.DocumentFormattingParams{ 143 TextDocument: lsp.TextDocumentIdentifier{URI: "file:///test"}, 144 }) 145 require.Len(t, resp, 1) 146 require.Equal(t, lsp.Range{ 147 Start: lsp.Position{Line: 0, Character: 0}, 148 End: lsp.Position{Line: 10000000, Character: 100000000}, 149 }, resp[0].Range) 150 require.Equal(t, "definition user {}", resp[0].NewText) 151 } 152 153 func TestDocumentOpenedClosed(t *testing.T) { 154 tester := newLSPTester(t) 155 tester.initialize() 156 157 sendAndReceive[any](tester, "textDocument/didOpen", lsp.DidOpenTextDocumentParams{ 158 TextDocument: lsp.TextDocumentItem{ 159 URI: lsp.DocumentURI("file:///test"), 160 LanguageID: "test", 161 Version: 1, 162 Text: "definition user{}", 163 }, 164 }) 165 166 contents, ok := tester.server.files.Get(lsp.DocumentURI("file:///test")) 167 require.True(t, ok) 168 require.Equal(t, "definition user{}", contents.contents) 169 170 sendAndReceive[any](tester, "textDocument/didClose", lsp.DidCloseTextDocumentParams{ 171 TextDocument: lsp.TextDocumentIdentifier{ 172 URI: lsp.DocumentURI("file:///test"), 173 }, 174 }) 175 176 _, ok = tester.server.files.Get(lsp.DocumentURI("file:///test")) 177 require.False(t, ok) 178 } 179 180 func TestDocumentHover(t *testing.T) { 181 tester := newLSPTester(t) 182 tester.initialize() 183 184 sendAndReceive[any](tester, "textDocument/didOpen", lsp.DidOpenTextDocumentParams{ 185 TextDocument: lsp.TextDocumentItem{ 186 URI: lsp.DocumentURI("file:///test"), 187 LanguageID: "test", 188 Version: 1, 189 Text: `definition user {} 190 191 definition resource { 192 relation viewer: user 193 } 194 `, 195 }, 196 }) 197 198 resp, _ := sendAndReceive[Hover](tester, "textDocument/hover", lsp.TextDocumentPositionParams{ 199 TextDocument: lsp.TextDocumentIdentifier{ 200 URI: lsp.DocumentURI("file:///test"), 201 }, 202 Position: lsp.Position{Line: 3, Character: 18}, 203 }) 204 205 require.Equal(t, "definition user {}", resp.Contents.Value) 206 require.Equal(t, "spicedb", resp.Contents.Language) 207 }