From db751fe7c27903196ed39af00c8ce84b3a27768c Mon Sep 17 00:00:00 2001 From: Stefan VanBuren Date: Mon, 5 Jan 2026 11:52:12 -0500 Subject: [PATCH 1/5] Add definition tests --- private/buf/buflsp/definition_test.go | 171 ++++++++++++++++++ .../buf/buflsp/testdata/definition/buf.yaml | 9 + .../testdata/definition/definition.proto | 99 ++++++++++ 3 files changed, 279 insertions(+) create mode 100644 private/buf/buflsp/definition_test.go create mode 100644 private/buf/buflsp/testdata/definition/buf.yaml create mode 100644 private/buf/buflsp/testdata/definition/definition.proto diff --git a/private/buf/buflsp/definition_test.go b/private/buf/buflsp/definition_test.go new file mode 100644 index 0000000000..598c9fadd3 --- /dev/null +++ b/private/buf/buflsp/definition_test.go @@ -0,0 +1,171 @@ +// Copyright 2020-2025 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package buflsp + +import ( + "path/filepath" + "runtime" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.lsp.dev/protocol" +) + +func TestDefinition(t *testing.T) { + t.Parallel() + + if runtime.GOOS == "windows" { + t.Skip("Skipping on Windows") + } + + ctx := t.Context() + + testProtoPath, err := filepath.Abs("testdata/definition/definition.proto") + require.NoError(t, err) + + clientJSONConn, testURI := setupLSPServer(t, testProtoPath) + + tests := []struct { + name string + line uint32 + character uint32 + expectedDefLine uint32 + expectedDefCharMin uint32 + expectedDefCharMax uint32 + expectNoDefinition bool + }{ + { + name: "definition_of_account_type_reference", + line: 10, // Line with "AccountType type = 2;" + character: 2, // On "AccountType" type + expectedDefLine: 41, // enum AccountType definition + expectedDefCharMin: 5, + expectedDefCharMax: 16, + }, + { + name: "definition_of_person_type_reference", + line: 13, // Line with "Person owner = 3;" + character: 2, // On "Person" type + expectedDefLine: 17, // message Person definition + expectedDefCharMin: 8, + expectedDefCharMax: 14, + }, + { + name: "definition_of_address_type_reference", + line: 25, // Line with "Address address = 3;" + character: 2, // On "Address" type + expectedDefLine: 29, // message Address definition + expectedDefCharMin: 8, + expectedDefCharMax: 15, + }, + { + name: "definition_of_country_code_reference", + line: 37, // Line with "CountryCode country = 3;" + character: 2, // On "CountryCode" type + expectedDefLine: 53, // enum CountryCode definition + expectedDefCharMin: 5, + expectedDefCharMax: 16, + }, + { + name: "definition_of_rpc_request_type", + line: 67, // Line with "rpc GetAccount(GetAccountRequest)" + character: 18, // On "GetAccountRequest" + expectedDefLine: 74, // message GetAccountRequest definition + expectedDefCharMin: 8, + expectedDefCharMax: 25, + }, + { + name: "definition_of_rpc_response_type", + line: 67, // Line with "returns (GetAccountResponse)" + character: 45, // On "GetAccountResponse" + expectedDefLine: 80, // message GetAccountResponse definition + expectedDefCharMin: 8, + expectedDefCharMax: 26, + }, + { + name: "definition_of_account_field_in_request", + line: 88, // Line with "Account account = 1;" in CreateAccountRequest + character: 2, // On "Account" type + expectedDefLine: 5, // message Account definition + expectedDefCharMin: 8, + expectedDefCharMax: 15, + }, + { + name: "definition_of_field_name", + line: 10, // Line with "AccountType type = 2;" + character: 14, // On "type" field name + expectedDefLine: 10, + expectedDefCharMin: 14, + expectedDefCharMax: 18, + }, + { + name: "definition_of_service", + line: 65, // Line with "service AccountService {" + character: 8, // On "AccountService" + expectedDefLine: 65, + expectedDefCharMin: 8, + expectedDefCharMax: 22, + }, + { + name: "definition_of_syntax_keyword", + line: 0, // Line with "syntax = "proto3";" + character: 0, // On "syntax" + expectNoDefinition: true, + }, + { + name: "definition_of_package_keyword", + line: 2, // Line with "package definition.v1;" + character: 0, // On "package" + expectNoDefinition: true, + }, + { + name: "definition_of_package_name", + line: 2, // Line with "package definition.v1;" + character: 8, // On "definition" + expectNoDefinition: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + var locations []protocol.Location + _, defErr := clientJSONConn.Call(ctx, protocol.MethodTextDocumentDefinition, protocol.DefinitionParams{ + TextDocumentPositionParams: protocol.TextDocumentPositionParams{ + TextDocument: protocol.TextDocumentIdentifier{ + URI: testURI, + }, + Position: protocol.Position{ + Line: tt.line, + Character: tt.character, + }, + }, + }, &locations) + require.NoError(t, defErr) + + if tt.expectNoDefinition { + assert.Empty(t, locations, "expected no definition locations") + } else { + require.Len(t, locations, 1, "expected exactly one definition location") + location := locations[0] + assert.Equal(t, testURI, location.URI) + assert.Equal(t, tt.expectedDefLine, location.Range.Start.Line) + assert.GreaterOrEqual(t, location.Range.Start.Character, tt.expectedDefCharMin) + assert.LessOrEqual(t, location.Range.Start.Character, tt.expectedDefCharMax) + } + }) + } +} diff --git a/private/buf/buflsp/testdata/definition/buf.yaml b/private/buf/buflsp/testdata/definition/buf.yaml new file mode 100644 index 0000000000..f74da98a3c --- /dev/null +++ b/private/buf/buflsp/testdata/definition/buf.yaml @@ -0,0 +1,9 @@ +version: v2 +modules: + - path: . +lint: + use: + - STANDARD +breaking: + use: + - FILE diff --git a/private/buf/buflsp/testdata/definition/definition.proto b/private/buf/buflsp/testdata/definition/definition.proto new file mode 100644 index 0000000000..7dad2c74e4 --- /dev/null +++ b/private/buf/buflsp/testdata/definition/definition.proto @@ -0,0 +1,99 @@ +syntax = "proto3"; + +package definition.v1; + +// Account represents a user account. +message Account { + // The account ID. + string id = 1; + + // The account type. + AccountType type = 2; + + // The account owner. + Person owner = 3; +} + +// Person represents an individual. +message Person { + // The person's name. + string name = 1; + + // The person's email. + string email = 2; + + // The person's address. + Address address = 3; +} + +// Address represents a physical address. +message Address { + // The street address. + string street = 1; + + // The city. + string city = 2; + + // The country code. + CountryCode country = 3; +} + +// AccountType represents the type of account. +enum AccountType { + // The account type is not specified. + ACCOUNT_TYPE_UNSPECIFIED = 0; + + // A personal account. + ACCOUNT_TYPE_PERSONAL = 1; + + // A business account. + ACCOUNT_TYPE_BUSINESS = 2; +} + +// CountryCode represents an ISO country code. +enum CountryCode { + // The country code is not specified. + COUNTRY_CODE_UNSPECIFIED = 0; + + // United States. + COUNTRY_CODE_US = 1; + + // Canada. + COUNTRY_CODE_CA = 2; +} + +// AccountService provides operations for managing accounts. +service AccountService { + // GetAccount retrieves an account by ID. + rpc GetAccount(GetAccountRequest) returns (GetAccountResponse); + + // CreateAccount creates a new account. + rpc CreateAccount(CreateAccountRequest) returns (CreateAccountResponse); +} + +// GetAccountRequest is the request for GetAccount. +message GetAccountRequest { + // The account ID to retrieve. + string account_id = 1; +} + +// GetAccountResponse is the response for GetAccount. +message GetAccountResponse { + // The retrieved account. + Account account = 1; +} + +// CreateAccountRequest is the request for CreateAccount. +message CreateAccountRequest { + // The account to create. + Account account = 1; + + // The account type. + AccountType type = 2; +} + +// CreateAccountResponse is the response for CreateAccount. +message CreateAccountResponse { + // The created account. + Account account = 1; +} From 9dfcb5ba91615d2c43a107dd7781797c7d5c0f4d Mon Sep 17 00:00:00 2001 From: Stefan VanBuren Date: Mon, 5 Jan 2026 12:20:39 -0500 Subject: [PATCH 2/5] Add type_definition tests --- .../buflsp/testdata/type_definition/buf.yaml | 9 + .../type_definition/type_definition.proto | 147 ++++++++++++++++ private/buf/buflsp/type_definition_test.go | 158 ++++++++++++++++++ 3 files changed, 314 insertions(+) create mode 100644 private/buf/buflsp/testdata/type_definition/buf.yaml create mode 100644 private/buf/buflsp/testdata/type_definition/type_definition.proto create mode 100644 private/buf/buflsp/type_definition_test.go diff --git a/private/buf/buflsp/testdata/type_definition/buf.yaml b/private/buf/buflsp/testdata/type_definition/buf.yaml new file mode 100644 index 0000000000..f74da98a3c --- /dev/null +++ b/private/buf/buflsp/testdata/type_definition/buf.yaml @@ -0,0 +1,9 @@ +version: v2 +modules: + - path: . +lint: + use: + - STANDARD +breaking: + use: + - FILE diff --git a/private/buf/buflsp/testdata/type_definition/type_definition.proto b/private/buf/buflsp/testdata/type_definition/type_definition.proto new file mode 100644 index 0000000000..5e98a55b9a --- /dev/null +++ b/private/buf/buflsp/testdata/type_definition/type_definition.proto @@ -0,0 +1,147 @@ +syntax = "proto3"; + +package type_definition.v1; + +// Product represents a product in the catalog. +message Product { + // The product ID. + string id = 1; + + // The product category. + Category category = 2; + + // The product vendor. + Vendor vendor = 3; + + // The product tags. + repeated Tag tags = 4; + + // The product metadata. + map metadata = 5; +} + +// Category represents a product category. +message Category { + // The category name. + string name = 1; + + // The parent category. + Category parent = 2; +} + +// Vendor represents a product vendor. +message Vendor { + // The vendor name. + string name = 1; + + // The vendor type. + VendorType type = 2; +} + +// Tag represents a product tag. +message Tag { + // The tag key. + string key = 1; + + // The tag value. + string value = 2; +} + +// Metadata represents arbitrary metadata. +message Metadata { + // The metadata value. + string value = 1; +} + +// VendorType represents the type of vendor. +enum VendorType { + // The vendor type is not specified. + VENDOR_TYPE_UNSPECIFIED = 0; + + // A direct vendor. + VENDOR_TYPE_DIRECT = 1; + + // A reseller vendor. + VENDOR_TYPE_RESELLER = 2; +} + +// Order represents a customer order. +message Order { + // The order ID. + string id = 1; + + // The ordered product. + Product product = 2; + + // The order status. + OrderStatus status = 3; + + // The customer. + Customer customer = 4; +} + +// Customer represents a customer. +message Customer { + // The customer name. + string name = 1; + + // The customer contact. + Contact contact = 2; +} + +// Contact represents contact information. +message Contact { + // The email address. + string email = 1; + + // The phone number. + string phone = 2; +} + +// OrderStatus represents the status of an order. +enum OrderStatus { + // The order status is not specified. + ORDER_STATUS_UNSPECIFIED = 0; + + // The order is pending. + ORDER_STATUS_PENDING = 1; + + // The order is confirmed. + ORDER_STATUS_CONFIRMED = 2; +} + +// ProductService provides operations for products. +service ProductService { + // GetProduct retrieves a product. + rpc GetProduct(GetProductRequest) returns (GetProductResponse); + + // CreateProduct creates a new product. + rpc CreateProduct(CreateProductRequest) returns (CreateProductResponse); +} + +// GetProductRequest is the request for GetProduct. +message GetProductRequest { + // The product ID. + string product_id = 1; +} + +// GetProductResponse is the response for GetProduct. +message GetProductResponse { + // The retrieved product. + Product product = 1; +} + +// CreateProductRequest is the request for CreateProduct. +message CreateProductRequest { + // The product to create. + Product product = 1; + + // The initial category. + Category category = 2; +} + +// CreateProductResponse is the response for CreateProduct. +message CreateProductResponse { + // The created product. + Product product = 1; +} diff --git a/private/buf/buflsp/type_definition_test.go b/private/buf/buflsp/type_definition_test.go new file mode 100644 index 0000000000..023feed855 --- /dev/null +++ b/private/buf/buflsp/type_definition_test.go @@ -0,0 +1,158 @@ +// Copyright 2020-2025 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package buflsp + +import ( + "path/filepath" + "runtime" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.lsp.dev/protocol" +) + +func TestTypeDefinition(t *testing.T) { + t.Parallel() + + if runtime.GOOS == "windows" { + t.Skip("Skipping on Windows") + } + + ctx := t.Context() + + testProtoPath, err := filepath.Abs("testdata/type_definition/type_definition.proto") + require.NoError(t, err) + + clientJSONConn, testURI := setupLSPServer(t, testProtoPath) + + tests := []struct { + name string + line uint32 + character uint32 + expectedTypeDefLine uint32 + expectedTypeDefChar uint32 + }{ + { + name: "type_definition_of_category_field", + line: 10, // Line with "Category category = 2;" + character: 2, // On "Category" type name + expectedTypeDefLine: 23, // message Category definition + expectedTypeDefChar: 8, + }, + { + name: "type_definition_of_vendor_field", + line: 13, // Line with "Vendor vendor = 3;" + character: 2, // On "Vendor" type name + expectedTypeDefLine: 32, // message Vendor definition + expectedTypeDefChar: 8, + }, + { + name: "type_definition_of_tags_field", + line: 16, // Line with "repeated Tag tags = 4;" + character: 13, // On "Tag" type name + expectedTypeDefLine: 41, // message Tag definition + expectedTypeDefChar: 8, + }, + { + name: "type_definition_of_metadata_field", + line: 19, // Line with "map metadata = 5;" + character: 16, // On "Metadata" type name + expectedTypeDefLine: 50, // message Metadata definition + expectedTypeDefChar: 8, + }, + { + name: "type_definition_of_parent_field", + line: 28, // Line with "Category parent = 2;" + character: 2, // On "Category" type name + expectedTypeDefLine: 23, // message Category definition (recursive reference) + expectedTypeDefChar: 8, + }, + { + name: "type_definition_of_type_field", + line: 37, // Line with "VendorType type = 2;" + character: 2, // On "VendorType" type name + expectedTypeDefLine: 56, // enum VendorType definition + expectedTypeDefChar: 5, + }, + { + name: "type_definition_of_product_field", + line: 73, // Line with "Product product = 2;" in Order message + character: 2, // On "Product" type name + expectedTypeDefLine: 5, // message Product definition + expectedTypeDefChar: 8, + }, + { + name: "type_definition_of_status_field", + line: 76, // Line with "OrderStatus status = 3;" + character: 2, // On "OrderStatus" type name + expectedTypeDefLine: 101, // enum OrderStatus definition + expectedTypeDefChar: 5, + }, + { + name: "type_definition_of_customer_field", + line: 79, // Line with "Customer customer = 4;" + character: 2, // On "Customer" type name + expectedTypeDefLine: 83, // message Customer definition + expectedTypeDefChar: 8, + }, + { + name: "type_definition_of_contact_field", + line: 88, // Line with "Contact contact = 2;" + character: 2, // On "Contact" type name + expectedTypeDefLine: 92, // message Contact definition + expectedTypeDefChar: 8, + }, + { + name: "type_definition_of_product_in_request", + line: 136, // Line with "Product product = 1;" in CreateProductRequest + character: 2, // On "Product" type name + expectedTypeDefLine: 5, // message Product definition + expectedTypeDefChar: 8, + }, + { + name: "type_definition_of_category_in_request", + line: 139, // Line with "Category category = 2;" in CreateProductRequest + character: 2, // On "Category" type name + expectedTypeDefLine: 23, // message Category definition + expectedTypeDefChar: 8, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + var locations []protocol.Location + _, typeDefErr := clientJSONConn.Call(ctx, protocol.MethodTextDocumentTypeDefinition, protocol.TypeDefinitionParams{ + TextDocumentPositionParams: protocol.TextDocumentPositionParams{ + TextDocument: protocol.TextDocumentIdentifier{ + URI: testURI, + }, + Position: protocol.Position{ + Line: tt.line, + Character: tt.character, + }, + }, + }, &locations) + require.NoError(t, typeDefErr) + + require.Len(t, locations, 1, "expected exactly one type definition location") + location := locations[0] + assert.Equal(t, testURI, location.URI) + assert.Equal(t, tt.expectedTypeDefLine, location.Range.Start.Line) + assert.Equal(t, tt.expectedTypeDefChar, location.Range.Start.Character) + }) + } +} From 8c5fe327cd20d7527ca5cd7184000d44bd53003f Mon Sep 17 00:00:00 2001 From: Stefan VanBuren Date: Mon, 5 Jan 2026 13:35:33 -0500 Subject: [PATCH 3/5] Run tests on windows; fix packages --- private/buf/buflsp/definition_test.go | 7 +------ private/buf/buflsp/hover_test.go | 4 ---- private/buf/buflsp/type_definition_test.go | 11 +++-------- 3 files changed, 4 insertions(+), 18 deletions(-) diff --git a/private/buf/buflsp/definition_test.go b/private/buf/buflsp/definition_test.go index 598c9fadd3..9f666cffbf 100644 --- a/private/buf/buflsp/definition_test.go +++ b/private/buf/buflsp/definition_test.go @@ -12,11 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -package buflsp +package buflsp_test import ( "path/filepath" - "runtime" "testing" "github.com/stretchr/testify/assert" @@ -27,10 +26,6 @@ import ( func TestDefinition(t *testing.T) { t.Parallel() - if runtime.GOOS == "windows" { - t.Skip("Skipping on Windows") - } - ctx := t.Context() testProtoPath, err := filepath.Abs("testdata/definition/definition.proto") diff --git a/private/buf/buflsp/hover_test.go b/private/buf/buflsp/hover_test.go index 41ce906289..4b17ac8baa 100644 --- a/private/buf/buflsp/hover_test.go +++ b/private/buf/buflsp/hover_test.go @@ -26,10 +26,6 @@ import ( func TestHover(t *testing.T) { t.Parallel() - // if runtime.GOOS == "windows" { - // t.Skip("Skipping on Windows") - // } - ctx := t.Context() testProtoPath, err := filepath.Abs("testdata/hover/test.proto") diff --git a/private/buf/buflsp/type_definition_test.go b/private/buf/buflsp/type_definition_test.go index 023feed855..f4930f0231 100644 --- a/private/buf/buflsp/type_definition_test.go +++ b/private/buf/buflsp/type_definition_test.go @@ -12,11 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -package buflsp +package buflsp_test import ( "path/filepath" - "runtime" "testing" "github.com/stretchr/testify/assert" @@ -27,10 +26,6 @@ import ( func TestTypeDefinition(t *testing.T) { t.Parallel() - if runtime.GOOS == "windows" { - t.Skip("Skipping on Windows") - } - ctx := t.Context() testProtoPath, err := filepath.Abs("testdata/type_definition/type_definition.proto") @@ -96,8 +91,8 @@ func TestTypeDefinition(t *testing.T) { }, { name: "type_definition_of_status_field", - line: 76, // Line with "OrderStatus status = 3;" - character: 2, // On "OrderStatus" type name + line: 76, // Line with "OrderStatus status = 3;" + character: 2, // On "OrderStatus" type name expectedTypeDefLine: 101, // enum OrderStatus definition expectedTypeDefChar: 5, }, From 73919f9e3046a7e0840171930f62c204ba6a0af8 Mon Sep 17 00:00:00 2001 From: Stefan VanBuren Date: Mon, 5 Jan 2026 14:29:03 -0500 Subject: [PATCH 4/5] Add different file with types; update tests We could eventually expand this to multiple modules, but keeping it simple for now. --- private/buf/buflsp/definition_test.go | 71 +++++++++++----- .../testdata/definition/definition.proto | 8 ++ .../buflsp/testdata/definition/types.proto | 24 ++++++ .../type_definition/type_definition.proto | 8 ++ .../testdata/type_definition/types.proto | 24 ++++++ private/buf/buflsp/type_definition_test.go | 84 +++++++++++++------ 6 files changed, 175 insertions(+), 44 deletions(-) create mode 100644 private/buf/buflsp/testdata/definition/types.proto create mode 100644 private/buf/buflsp/testdata/type_definition/types.proto diff --git a/private/buf/buflsp/definition_test.go b/private/buf/buflsp/definition_test.go index 9f666cffbf..3bf02b76f4 100644 --- a/private/buf/buflsp/definition_test.go +++ b/private/buf/buflsp/definition_test.go @@ -21,6 +21,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.lsp.dev/protocol" + "go.lsp.dev/uri" ) func TestDefinition(t *testing.T) { @@ -31,12 +32,17 @@ func TestDefinition(t *testing.T) { testProtoPath, err := filepath.Abs("testdata/definition/definition.proto") require.NoError(t, err) + typesProtoPath, err := filepath.Abs("testdata/definition/types.proto") + require.NoError(t, err) + clientJSONConn, testURI := setupLSPServer(t, testProtoPath) + typesURI := uri.New(typesProtoPath) tests := []struct { name string line uint32 character uint32 + expectedDefURI protocol.URI expectedDefLine uint32 expectedDefCharMin uint32 expectedDefCharMax uint32 @@ -44,76 +50,103 @@ func TestDefinition(t *testing.T) { }{ { name: "definition_of_account_type_reference", - line: 10, // Line with "AccountType type = 2;" + line: 12, // Line with "AccountType type = 2;" character: 2, // On "AccountType" type - expectedDefLine: 41, // enum AccountType definition + expectedDefURI: testURI, + expectedDefLine: 49, // enum AccountType definition expectedDefCharMin: 5, expectedDefCharMax: 16, }, { name: "definition_of_person_type_reference", - line: 13, // Line with "Person owner = 3;" + line: 15, // Line with "Person owner = 3;" character: 2, // On "Person" type - expectedDefLine: 17, // message Person definition + expectedDefURI: testURI, + expectedDefLine: 25, // message Person definition expectedDefCharMin: 8, expectedDefCharMax: 14, }, { name: "definition_of_address_type_reference", - line: 25, // Line with "Address address = 3;" + line: 33, // Line with "Address address = 3;" character: 2, // On "Address" type - expectedDefLine: 29, // message Address definition + expectedDefURI: testURI, + expectedDefLine: 37, // message Address definition expectedDefCharMin: 8, expectedDefCharMax: 15, }, { name: "definition_of_country_code_reference", - line: 37, // Line with "CountryCode country = 3;" + line: 45, // Line with "CountryCode country = 3;" character: 2, // On "CountryCode" type - expectedDefLine: 53, // enum CountryCode definition + expectedDefURI: testURI, + expectedDefLine: 61, // enum CountryCode definition expectedDefCharMin: 5, expectedDefCharMax: 16, }, { name: "definition_of_rpc_request_type", - line: 67, // Line with "rpc GetAccount(GetAccountRequest)" + line: 75, // Line with "rpc GetAccount(GetAccountRequest)" character: 18, // On "GetAccountRequest" - expectedDefLine: 74, // message GetAccountRequest definition + expectedDefURI: testURI, + expectedDefLine: 82, // message GetAccountRequest definition expectedDefCharMin: 8, expectedDefCharMax: 25, }, { name: "definition_of_rpc_response_type", - line: 67, // Line with "returns (GetAccountResponse)" + line: 75, // Line with "returns (GetAccountResponse)" character: 45, // On "GetAccountResponse" - expectedDefLine: 80, // message GetAccountResponse definition + expectedDefURI: testURI, + expectedDefLine: 88, // message GetAccountResponse definition expectedDefCharMin: 8, expectedDefCharMax: 26, }, { name: "definition_of_account_field_in_request", - line: 88, // Line with "Account account = 1;" in CreateAccountRequest + line: 96, // Line with "Account account = 1;" in CreateAccountRequest character: 2, // On "Account" type - expectedDefLine: 5, // message Account definition + expectedDefURI: testURI, + expectedDefLine: 7, // message Account definition expectedDefCharMin: 8, expectedDefCharMax: 15, }, { name: "definition_of_field_name", - line: 10, // Line with "AccountType type = 2;" + line: 12, // Line with "AccountType type = 2;" character: 14, // On "type" field name - expectedDefLine: 10, + expectedDefURI: testURI, + expectedDefLine: 12, expectedDefCharMin: 14, expectedDefCharMax: 18, }, { name: "definition_of_service", - line: 65, // Line with "service AccountService {" + line: 73, // Line with "service AccountService {" character: 8, // On "AccountService" - expectedDefLine: 65, + expectedDefURI: testURI, + expectedDefLine: 73, expectedDefCharMin: 8, expectedDefCharMax: 22, }, + { + name: "definition_of_status_imported_type", + line: 18, // Line with "Status status = 4;" + character: 2, // On "Status" type + expectedDefURI: typesURI, + expectedDefLine: 5, // enum Status definition in types.proto + expectedDefCharMin: 5, + expectedDefCharMax: 11, + }, + { + name: "definition_of_timestamp_imported_type", + line: 21, // Line with "Timestamp created_at = 5;" + character: 2, // On "Timestamp" type + expectedDefURI: typesURI, + expectedDefLine: 17, // message Timestamp definition in types.proto + expectedDefCharMin: 8, + expectedDefCharMax: 17, + }, { name: "definition_of_syntax_keyword", line: 0, // Line with "syntax = "proto3";" @@ -156,7 +189,7 @@ func TestDefinition(t *testing.T) { } else { require.Len(t, locations, 1, "expected exactly one definition location") location := locations[0] - assert.Equal(t, testURI, location.URI) + assert.Equal(t, tt.expectedDefURI, location.URI) assert.Equal(t, tt.expectedDefLine, location.Range.Start.Line) assert.GreaterOrEqual(t, location.Range.Start.Character, tt.expectedDefCharMin) assert.LessOrEqual(t, location.Range.Start.Character, tt.expectedDefCharMax) diff --git a/private/buf/buflsp/testdata/definition/definition.proto b/private/buf/buflsp/testdata/definition/definition.proto index 7dad2c74e4..98a50edf4c 100644 --- a/private/buf/buflsp/testdata/definition/definition.proto +++ b/private/buf/buflsp/testdata/definition/definition.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package definition.v1; +import "types.proto"; + // Account represents a user account. message Account { // The account ID. @@ -12,6 +14,12 @@ message Account { // The account owner. Person owner = 3; + + // The account status. + Status status = 4; + + // The creation timestamp. + Timestamp created_at = 5; } // Person represents an individual. diff --git a/private/buf/buflsp/testdata/definition/types.proto b/private/buf/buflsp/testdata/definition/types.proto new file mode 100644 index 0000000000..2534cd784b --- /dev/null +++ b/private/buf/buflsp/testdata/definition/types.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; + +package definition.v1; + +// Status represents the status of an entity. +enum Status { + // The status is not specified. + STATUS_UNSPECIFIED = 0; + + // The entity is active. + STATUS_ACTIVE = 1; + + // The entity is inactive. + STATUS_INACTIVE = 2; +} + +// Timestamp represents a point in time. +message Timestamp { + // Seconds since epoch. + int64 seconds = 1; + + // Nanoseconds. + int32 nanos = 2; +} diff --git a/private/buf/buflsp/testdata/type_definition/type_definition.proto b/private/buf/buflsp/testdata/type_definition/type_definition.proto index 5e98a55b9a..ad48bcdadc 100644 --- a/private/buf/buflsp/testdata/type_definition/type_definition.proto +++ b/private/buf/buflsp/testdata/type_definition/type_definition.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package type_definition.v1; +import "types.proto"; + // Product represents a product in the catalog. message Product { // The product ID. @@ -18,6 +20,12 @@ message Product { // The product metadata. map metadata = 5; + + // The product priority. + Priority priority = 6; + + // The audit information. + Audit audit = 7; } // Category represents a product category. diff --git a/private/buf/buflsp/testdata/type_definition/types.proto b/private/buf/buflsp/testdata/type_definition/types.proto new file mode 100644 index 0000000000..129ed0443a --- /dev/null +++ b/private/buf/buflsp/testdata/type_definition/types.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; + +package type_definition.v1; + +// Priority represents the priority level. +enum Priority { + // The priority is not specified. + PRIORITY_UNSPECIFIED = 0; + + // Low priority. + PRIORITY_LOW = 1; + + // High priority. + PRIORITY_HIGH = 2; +} + +// Audit represents audit information. +message Audit { + // The user who created this. + string created_by = 1; + + // The timestamp of creation. + int64 created_at = 2; +} diff --git a/private/buf/buflsp/type_definition_test.go b/private/buf/buflsp/type_definition_test.go index f4930f0231..b9fcf9fd3a 100644 --- a/private/buf/buflsp/type_definition_test.go +++ b/private/buf/buflsp/type_definition_test.go @@ -21,6 +21,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.lsp.dev/protocol" + "go.lsp.dev/uri" ) func TestTypeDefinition(t *testing.T) { @@ -31,97 +32,130 @@ func TestTypeDefinition(t *testing.T) { testProtoPath, err := filepath.Abs("testdata/type_definition/type_definition.proto") require.NoError(t, err) + typesProtoPath, err := filepath.Abs("testdata/type_definition/types.proto") + require.NoError(t, err) + clientJSONConn, testURI := setupLSPServer(t, testProtoPath) + typesURI := uri.New(typesProtoPath) tests := []struct { name string line uint32 character uint32 + expectedTypeDefURI protocol.URI expectedTypeDefLine uint32 expectedTypeDefChar uint32 }{ { name: "type_definition_of_category_field", - line: 10, // Line with "Category category = 2;" + line: 12, // Line with "Category category = 2;" character: 2, // On "Category" type name - expectedTypeDefLine: 23, // message Category definition + expectedTypeDefURI: testURI, + expectedTypeDefLine: 31, // message Category definition expectedTypeDefChar: 8, }, { name: "type_definition_of_vendor_field", - line: 13, // Line with "Vendor vendor = 3;" + line: 15, // Line with "Vendor vendor = 3;" character: 2, // On "Vendor" type name - expectedTypeDefLine: 32, // message Vendor definition + expectedTypeDefURI: testURI, + expectedTypeDefLine: 40, // message Vendor definition expectedTypeDefChar: 8, }, { name: "type_definition_of_tags_field", - line: 16, // Line with "repeated Tag tags = 4;" + line: 18, // Line with "repeated Tag tags = 4;" character: 13, // On "Tag" type name - expectedTypeDefLine: 41, // message Tag definition + expectedTypeDefURI: testURI, + expectedTypeDefLine: 49, // message Tag definition expectedTypeDefChar: 8, }, { name: "type_definition_of_metadata_field", - line: 19, // Line with "map metadata = 5;" + line: 21, // Line with "map metadata = 5;" character: 16, // On "Metadata" type name - expectedTypeDefLine: 50, // message Metadata definition + expectedTypeDefURI: testURI, + expectedTypeDefLine: 58, // message Metadata definition expectedTypeDefChar: 8, }, { name: "type_definition_of_parent_field", - line: 28, // Line with "Category parent = 2;" + line: 36, // Line with "Category parent = 2;" character: 2, // On "Category" type name - expectedTypeDefLine: 23, // message Category definition (recursive reference) + expectedTypeDefURI: testURI, + expectedTypeDefLine: 31, // message Category definition (recursive reference) expectedTypeDefChar: 8, }, { name: "type_definition_of_type_field", - line: 37, // Line with "VendorType type = 2;" + line: 45, // Line with "VendorType type = 2;" character: 2, // On "VendorType" type name - expectedTypeDefLine: 56, // enum VendorType definition + expectedTypeDefURI: testURI, + expectedTypeDefLine: 64, // enum VendorType definition expectedTypeDefChar: 5, }, { name: "type_definition_of_product_field", - line: 73, // Line with "Product product = 2;" in Order message + line: 81, // Line with "Product product = 2;" in Order message character: 2, // On "Product" type name - expectedTypeDefLine: 5, // message Product definition + expectedTypeDefURI: testURI, + expectedTypeDefLine: 7, // message Product definition expectedTypeDefChar: 8, }, { name: "type_definition_of_status_field", - line: 76, // Line with "OrderStatus status = 3;" + line: 84, // Line with "OrderStatus status = 3;" character: 2, // On "OrderStatus" type name - expectedTypeDefLine: 101, // enum OrderStatus definition + expectedTypeDefURI: testURI, + expectedTypeDefLine: 109, // enum OrderStatus definition expectedTypeDefChar: 5, }, { name: "type_definition_of_customer_field", - line: 79, // Line with "Customer customer = 4;" + line: 87, // Line with "Customer customer = 4;" character: 2, // On "Customer" type name - expectedTypeDefLine: 83, // message Customer definition + expectedTypeDefURI: testURI, + expectedTypeDefLine: 91, // message Customer definition expectedTypeDefChar: 8, }, { name: "type_definition_of_contact_field", - line: 88, // Line with "Contact contact = 2;" + line: 96, // Line with "Contact contact = 2;" character: 2, // On "Contact" type name - expectedTypeDefLine: 92, // message Contact definition + expectedTypeDefURI: testURI, + expectedTypeDefLine: 100, // message Contact definition expectedTypeDefChar: 8, }, { name: "type_definition_of_product_in_request", - line: 136, // Line with "Product product = 1;" in CreateProductRequest + line: 144, // Line with "Product product = 1;" in CreateProductRequest character: 2, // On "Product" type name - expectedTypeDefLine: 5, // message Product definition + expectedTypeDefURI: testURI, + expectedTypeDefLine: 7, // message Product definition expectedTypeDefChar: 8, }, { name: "type_definition_of_category_in_request", - line: 139, // Line with "Category category = 2;" in CreateProductRequest + line: 147, // Line with "Category category = 2;" in CreateProductRequest character: 2, // On "Category" type name - expectedTypeDefLine: 23, // message Category definition + expectedTypeDefURI: testURI, + expectedTypeDefLine: 31, // message Category definition + expectedTypeDefChar: 8, + }, + { + name: "type_definition_of_priority_imported_type", + line: 24, // Line with "Priority priority = 6;" + character: 2, // On "Priority" type name + expectedTypeDefURI: typesURI, + expectedTypeDefLine: 5, // enum Priority definition in types.proto + expectedTypeDefChar: 5, + }, + { + name: "type_definition_of_audit_imported_type", + line: 27, // Line with "Audit audit = 7;" + character: 2, // On "Audit" type name + expectedTypeDefURI: typesURI, + expectedTypeDefLine: 17, // message Audit definition in types.proto expectedTypeDefChar: 8, }, } @@ -145,7 +179,7 @@ func TestTypeDefinition(t *testing.T) { require.Len(t, locations, 1, "expected exactly one type definition location") location := locations[0] - assert.Equal(t, testURI, location.URI) + assert.Equal(t, tt.expectedTypeDefURI, location.URI) assert.Equal(t, tt.expectedTypeDefLine, location.Range.Start.Line) assert.Equal(t, tt.expectedTypeDefChar, location.Range.Start.Character) }) From 511befd0ef3ade87d50f47d92f84fe083997423e Mon Sep 17 00:00:00 2001 From: Stefan VanBuren Date: Mon, 5 Jan 2026 14:48:00 -0500 Subject: [PATCH 5/5] Re-run `make format` --- private/buf/buflsp/definition_test.go | 2 +- private/buf/buflsp/type_definition_test.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/private/buf/buflsp/definition_test.go b/private/buf/buflsp/definition_test.go index 3bf02b76f4..232620b05d 100644 --- a/private/buf/buflsp/definition_test.go +++ b/private/buf/buflsp/definition_test.go @@ -107,7 +107,7 @@ func TestDefinition(t *testing.T) { line: 96, // Line with "Account account = 1;" in CreateAccountRequest character: 2, // On "Account" type expectedDefURI: testURI, - expectedDefLine: 7, // message Account definition + expectedDefLine: 7, // message Account definition expectedDefCharMin: 8, expectedDefCharMax: 15, }, diff --git a/private/buf/buflsp/type_definition_test.go b/private/buf/buflsp/type_definition_test.go index b9fcf9fd3a..8c44e31020 100644 --- a/private/buf/buflsp/type_definition_test.go +++ b/private/buf/buflsp/type_definition_test.go @@ -99,13 +99,13 @@ func TestTypeDefinition(t *testing.T) { line: 81, // Line with "Product product = 2;" in Order message character: 2, // On "Product" type name expectedTypeDefURI: testURI, - expectedTypeDefLine: 7, // message Product definition + expectedTypeDefLine: 7, // message Product definition expectedTypeDefChar: 8, }, { name: "type_definition_of_status_field", - line: 84, // Line with "OrderStatus status = 3;" - character: 2, // On "OrderStatus" type name + line: 84, // Line with "OrderStatus status = 3;" + character: 2, // On "OrderStatus" type name expectedTypeDefURI: testURI, expectedTypeDefLine: 109, // enum OrderStatus definition expectedTypeDefChar: 5, @@ -131,7 +131,7 @@ func TestTypeDefinition(t *testing.T) { line: 144, // Line with "Product product = 1;" in CreateProductRequest character: 2, // On "Product" type name expectedTypeDefURI: testURI, - expectedTypeDefLine: 7, // message Product definition + expectedTypeDefLine: 7, // message Product definition expectedTypeDefChar: 8, }, { @@ -139,7 +139,7 @@ func TestTypeDefinition(t *testing.T) { line: 147, // Line with "Category category = 2;" in CreateProductRequest character: 2, // On "Category" type name expectedTypeDefURI: testURI, - expectedTypeDefLine: 31, // message Category definition + expectedTypeDefLine: 31, // message Category definition expectedTypeDefChar: 8, }, {