Skip to content

Commit 74f2768

Browse files
Add LSP tests for definition / type definition (#4255)
1 parent db27d98 commit 74f2768

File tree

9 files changed

+714
-4
lines changed

9 files changed

+714
-4
lines changed
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
// Copyright 2020-2025 Buf Technologies, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package buflsp_test
16+
17+
import (
18+
"path/filepath"
19+
"testing"
20+
21+
"github.com/stretchr/testify/assert"
22+
"github.com/stretchr/testify/require"
23+
"go.lsp.dev/protocol"
24+
"go.lsp.dev/uri"
25+
)
26+
27+
func TestDefinition(t *testing.T) {
28+
t.Parallel()
29+
30+
ctx := t.Context()
31+
32+
testProtoPath, err := filepath.Abs("testdata/definition/definition.proto")
33+
require.NoError(t, err)
34+
35+
typesProtoPath, err := filepath.Abs("testdata/definition/types.proto")
36+
require.NoError(t, err)
37+
38+
clientJSONConn, testURI := setupLSPServer(t, testProtoPath)
39+
typesURI := uri.New(typesProtoPath)
40+
41+
tests := []struct {
42+
name string
43+
line uint32
44+
character uint32
45+
expectedDefURI protocol.URI
46+
expectedDefLine uint32
47+
expectedDefCharMin uint32
48+
expectedDefCharMax uint32
49+
expectNoDefinition bool
50+
}{
51+
{
52+
name: "definition_of_account_type_reference",
53+
line: 12, // Line with "AccountType type = 2;"
54+
character: 2, // On "AccountType" type
55+
expectedDefURI: testURI,
56+
expectedDefLine: 49, // enum AccountType definition
57+
expectedDefCharMin: 5,
58+
expectedDefCharMax: 16,
59+
},
60+
{
61+
name: "definition_of_person_type_reference",
62+
line: 15, // Line with "Person owner = 3;"
63+
character: 2, // On "Person" type
64+
expectedDefURI: testURI,
65+
expectedDefLine: 25, // message Person definition
66+
expectedDefCharMin: 8,
67+
expectedDefCharMax: 14,
68+
},
69+
{
70+
name: "definition_of_address_type_reference",
71+
line: 33, // Line with "Address address = 3;"
72+
character: 2, // On "Address" type
73+
expectedDefURI: testURI,
74+
expectedDefLine: 37, // message Address definition
75+
expectedDefCharMin: 8,
76+
expectedDefCharMax: 15,
77+
},
78+
{
79+
name: "definition_of_country_code_reference",
80+
line: 45, // Line with "CountryCode country = 3;"
81+
character: 2, // On "CountryCode" type
82+
expectedDefURI: testURI,
83+
expectedDefLine: 61, // enum CountryCode definition
84+
expectedDefCharMin: 5,
85+
expectedDefCharMax: 16,
86+
},
87+
{
88+
name: "definition_of_rpc_request_type",
89+
line: 75, // Line with "rpc GetAccount(GetAccountRequest)"
90+
character: 18, // On "GetAccountRequest"
91+
expectedDefURI: testURI,
92+
expectedDefLine: 82, // message GetAccountRequest definition
93+
expectedDefCharMin: 8,
94+
expectedDefCharMax: 25,
95+
},
96+
{
97+
name: "definition_of_rpc_response_type",
98+
line: 75, // Line with "returns (GetAccountResponse)"
99+
character: 45, // On "GetAccountResponse"
100+
expectedDefURI: testURI,
101+
expectedDefLine: 88, // message GetAccountResponse definition
102+
expectedDefCharMin: 8,
103+
expectedDefCharMax: 26,
104+
},
105+
{
106+
name: "definition_of_account_field_in_request",
107+
line: 96, // Line with "Account account = 1;" in CreateAccountRequest
108+
character: 2, // On "Account" type
109+
expectedDefURI: testURI,
110+
expectedDefLine: 7, // message Account definition
111+
expectedDefCharMin: 8,
112+
expectedDefCharMax: 15,
113+
},
114+
{
115+
name: "definition_of_field_name",
116+
line: 12, // Line with "AccountType type = 2;"
117+
character: 14, // On "type" field name
118+
expectedDefURI: testURI,
119+
expectedDefLine: 12,
120+
expectedDefCharMin: 14,
121+
expectedDefCharMax: 18,
122+
},
123+
{
124+
name: "definition_of_service",
125+
line: 73, // Line with "service AccountService {"
126+
character: 8, // On "AccountService"
127+
expectedDefURI: testURI,
128+
expectedDefLine: 73,
129+
expectedDefCharMin: 8,
130+
expectedDefCharMax: 22,
131+
},
132+
{
133+
name: "definition_of_status_imported_type",
134+
line: 18, // Line with "Status status = 4;"
135+
character: 2, // On "Status" type
136+
expectedDefURI: typesURI,
137+
expectedDefLine: 5, // enum Status definition in types.proto
138+
expectedDefCharMin: 5,
139+
expectedDefCharMax: 11,
140+
},
141+
{
142+
name: "definition_of_timestamp_imported_type",
143+
line: 21, // Line with "Timestamp created_at = 5;"
144+
character: 2, // On "Timestamp" type
145+
expectedDefURI: typesURI,
146+
expectedDefLine: 17, // message Timestamp definition in types.proto
147+
expectedDefCharMin: 8,
148+
expectedDefCharMax: 17,
149+
},
150+
{
151+
name: "definition_of_syntax_keyword",
152+
line: 0, // Line with "syntax = "proto3";"
153+
character: 0, // On "syntax"
154+
expectNoDefinition: true,
155+
},
156+
{
157+
name: "definition_of_package_keyword",
158+
line: 2, // Line with "package definition.v1;"
159+
character: 0, // On "package"
160+
expectNoDefinition: true,
161+
},
162+
{
163+
name: "definition_of_package_name",
164+
line: 2, // Line with "package definition.v1;"
165+
character: 8, // On "definition"
166+
expectNoDefinition: true,
167+
},
168+
}
169+
170+
for _, tt := range tests {
171+
t.Run(tt.name, func(t *testing.T) {
172+
t.Parallel()
173+
var locations []protocol.Location
174+
_, defErr := clientJSONConn.Call(ctx, protocol.MethodTextDocumentDefinition, protocol.DefinitionParams{
175+
TextDocumentPositionParams: protocol.TextDocumentPositionParams{
176+
TextDocument: protocol.TextDocumentIdentifier{
177+
URI: testURI,
178+
},
179+
Position: protocol.Position{
180+
Line: tt.line,
181+
Character: tt.character,
182+
},
183+
},
184+
}, &locations)
185+
require.NoError(t, defErr)
186+
187+
if tt.expectNoDefinition {
188+
assert.Empty(t, locations, "expected no definition locations")
189+
} else {
190+
require.Len(t, locations, 1, "expected exactly one definition location")
191+
location := locations[0]
192+
assert.Equal(t, tt.expectedDefURI, location.URI)
193+
assert.Equal(t, tt.expectedDefLine, location.Range.Start.Line)
194+
assert.GreaterOrEqual(t, location.Range.Start.Character, tt.expectedDefCharMin)
195+
assert.LessOrEqual(t, location.Range.Start.Character, tt.expectedDefCharMax)
196+
}
197+
})
198+
}
199+
}

private/buf/buflsp/hover_test.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,6 @@ import (
2626
func TestHover(t *testing.T) {
2727
t.Parallel()
2828

29-
// if runtime.GOOS == "windows" {
30-
// t.Skip("Skipping on Windows")
31-
// }
32-
3329
ctx := t.Context()
3430

3531
testProtoPath, err := filepath.Abs("testdata/hover/test.proto")
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
version: v2
2+
modules:
3+
- path: .
4+
lint:
5+
use:
6+
- STANDARD
7+
breaking:
8+
use:
9+
- FILE
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
syntax = "proto3";
2+
3+
package definition.v1;
4+
5+
import "types.proto";
6+
7+
// Account represents a user account.
8+
message Account {
9+
// The account ID.
10+
string id = 1;
11+
12+
// The account type.
13+
AccountType type = 2;
14+
15+
// The account owner.
16+
Person owner = 3;
17+
18+
// The account status.
19+
Status status = 4;
20+
21+
// The creation timestamp.
22+
Timestamp created_at = 5;
23+
}
24+
25+
// Person represents an individual.
26+
message Person {
27+
// The person's name.
28+
string name = 1;
29+
30+
// The person's email.
31+
string email = 2;
32+
33+
// The person's address.
34+
Address address = 3;
35+
}
36+
37+
// Address represents a physical address.
38+
message Address {
39+
// The street address.
40+
string street = 1;
41+
42+
// The city.
43+
string city = 2;
44+
45+
// The country code.
46+
CountryCode country = 3;
47+
}
48+
49+
// AccountType represents the type of account.
50+
enum AccountType {
51+
// The account type is not specified.
52+
ACCOUNT_TYPE_UNSPECIFIED = 0;
53+
54+
// A personal account.
55+
ACCOUNT_TYPE_PERSONAL = 1;
56+
57+
// A business account.
58+
ACCOUNT_TYPE_BUSINESS = 2;
59+
}
60+
61+
// CountryCode represents an ISO country code.
62+
enum CountryCode {
63+
// The country code is not specified.
64+
COUNTRY_CODE_UNSPECIFIED = 0;
65+
66+
// United States.
67+
COUNTRY_CODE_US = 1;
68+
69+
// Canada.
70+
COUNTRY_CODE_CA = 2;
71+
}
72+
73+
// AccountService provides operations for managing accounts.
74+
service AccountService {
75+
// GetAccount retrieves an account by ID.
76+
rpc GetAccount(GetAccountRequest) returns (GetAccountResponse);
77+
78+
// CreateAccount creates a new account.
79+
rpc CreateAccount(CreateAccountRequest) returns (CreateAccountResponse);
80+
}
81+
82+
// GetAccountRequest is the request for GetAccount.
83+
message GetAccountRequest {
84+
// The account ID to retrieve.
85+
string account_id = 1;
86+
}
87+
88+
// GetAccountResponse is the response for GetAccount.
89+
message GetAccountResponse {
90+
// The retrieved account.
91+
Account account = 1;
92+
}
93+
94+
// CreateAccountRequest is the request for CreateAccount.
95+
message CreateAccountRequest {
96+
// The account to create.
97+
Account account = 1;
98+
99+
// The account type.
100+
AccountType type = 2;
101+
}
102+
103+
// CreateAccountResponse is the response for CreateAccount.
104+
message CreateAccountResponse {
105+
// The created account.
106+
Account account = 1;
107+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
syntax = "proto3";
2+
3+
package definition.v1;
4+
5+
// Status represents the status of an entity.
6+
enum Status {
7+
// The status is not specified.
8+
STATUS_UNSPECIFIED = 0;
9+
10+
// The entity is active.
11+
STATUS_ACTIVE = 1;
12+
13+
// The entity is inactive.
14+
STATUS_INACTIVE = 2;
15+
}
16+
17+
// Timestamp represents a point in time.
18+
message Timestamp {
19+
// Seconds since epoch.
20+
int64 seconds = 1;
21+
22+
// Nanoseconds.
23+
int32 nanos = 2;
24+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
version: v2
2+
modules:
3+
- path: .
4+
lint:
5+
use:
6+
- STANDARD
7+
breaking:
8+
use:
9+
- FILE

0 commit comments

Comments
 (0)