Skip to main content
Glama

protolint-mcp

by yoheimuta
hasExtendedVisitor_test.go13.4 kB
package visitor_test import ( "reflect" "testing" "github.com/yoheimuta/go-protoparser/v4/parser/meta" "github.com/yoheimuta/protolint/internal/linter/file" "github.com/yoheimuta/protolint/internal/setting_test" "github.com/yoheimuta/protolint/internal/util_test" "github.com/yoheimuta/protolint/linter/autodisable" "github.com/yoheimuta/protolint/linter/rule" "github.com/yoheimuta/protolint/linter/visitor" "github.com/yoheimuta/protolint/linter/report" "github.com/yoheimuta/go-protoparser/v4/parser" ) type testVisitor struct { *visitor.BaseAddVisitor next bool } func (v *testVisitor) VisitMessage(message *parser.Message) bool { v.AddFailuref(message.Meta.Pos, "Test Message") return v.next } type testVisitorInvalidEnumField struct { *visitor.BaseAddVisitor next bool } func (v *testVisitorInvalidEnumField) VisitEnumField(field *parser.EnumField) bool { v.AddFailuref(field.Meta.Pos, "Failed field") return v.next } func TestRunVisitor(t *testing.T) { tests := []struct { name string inputVisitor *testVisitor inputProto *parser.Proto inputRuleID string wantExistErr bool wantFailures []report.Failure }{ { name: "visit no messages", inputVisitor: &testVisitor{ BaseAddVisitor: visitor.NewBaseAddVisitor("MESSAGE_NAMES_UPPER_CAMEL_CASE", "error"), }, inputProto: &parser.Proto{ Meta: &parser.ProtoMeta{Filename: ""}, }, }, { name: "visit a message", inputVisitor: &testVisitor{ BaseAddVisitor: visitor.NewBaseAddVisitor("MESSAGE_NAMES_UPPER_CAMEL_CASE", "error"), }, inputProto: &parser.Proto{ Meta: &parser.ProtoMeta{Filename: ""}, ProtoBody: []parser.Visitee{ &parser.Message{ Meta: meta.Meta{ Pos: meta.Position{ Filename: "example.proto", Offset: 100, Line: 10, Column: 5, }, }, }, }, }, wantFailures: []report.Failure{ report.Failuref( meta.Position{ Filename: "example.proto", Offset: 100, Line: 10, Column: 5, }, "MESSAGE_NAMES_UPPER_CAMEL_CASE", string(rule.SeverityError), "Test Message", ), }, }, { name: "visit messages", inputVisitor: &testVisitor{ BaseAddVisitor: visitor.NewBaseAddVisitor("MESSAGE_NAMES_UPPER_CAMEL_CASE", "error"), }, inputProto: &parser.Proto{ Meta: &parser.ProtoMeta{Filename: ""}, ProtoBody: []parser.Visitee{ &parser.Message{ Meta: meta.Meta{ Pos: meta.Position{ Filename: "example.proto", Offset: 100, Line: 10, Column: 5, }, }, }, &parser.Message{ Meta: meta.Meta{ Pos: meta.Position{ Filename: "example.proto", Offset: 200, Line: 20, Column: 10, }, }, }, }, }, wantFailures: []report.Failure{ report.Failuref( meta.Position{ Filename: "example.proto", Offset: 100, Line: 10, Column: 5, }, "MESSAGE_NAMES_UPPER_CAMEL_CASE", string(rule.SeverityError), "Test Message", ), report.Failuref( meta.Position{ Filename: "example.proto", Offset: 200, Line: 20, Column: 10, }, "MESSAGE_NAMES_UPPER_CAMEL_CASE", string(rule.SeverityError), "Test Message", ), }, }, { name: "visit messages recursively", inputVisitor: &testVisitor{ BaseAddVisitor: visitor.NewBaseAddVisitor("MESSAGE_NAMES_UPPER_CAMEL_CASE", "error"), next: true, }, inputProto: &parser.Proto{ Meta: &parser.ProtoMeta{Filename: ""}, ProtoBody: []parser.Visitee{ &parser.Message{ MessageBody: []parser.Visitee{ &parser.Message{ Meta: meta.Meta{ Pos: meta.Position{ Filename: "example.proto", Offset: 200, Line: 20, Column: 10, }, }, }, }, Meta: meta.Meta{ Pos: meta.Position{ Filename: "example.proto", Offset: 100, Line: 10, Column: 5, }, }, }, }, }, wantFailures: []report.Failure{ report.Failuref( meta.Position{ Filename: "example.proto", Offset: 100, Line: 10, Column: 5, }, "MESSAGE_NAMES_UPPER_CAMEL_CASE", string(rule.SeverityError), "Test Message", ), report.Failuref( meta.Position{ Filename: "example.proto", Offset: 200, Line: 20, Column: 10, }, "MESSAGE_NAMES_UPPER_CAMEL_CASE", string(rule.SeverityError), "Test Message", ), }, }, { name: "visit a message. one is disabled.", inputVisitor: &testVisitor{ BaseAddVisitor: visitor.NewBaseAddVisitor("MESSAGE_NAMES_UPPER_CAMEL_CASE", "error"), }, inputProto: &parser.Proto{ Meta: &parser.ProtoMeta{Filename: ""}, ProtoBody: []parser.Visitee{ &parser.Message{ Meta: meta.Meta{ Pos: meta.Position{ Filename: "example.proto", Offset: 100, Line: 10, Column: 5, }, }, Comments: []*parser.Comment{ { Raw: `// protolint:disable:next MESSAGE_NAMES_UPPER_CAMEL_CASE`, }, }, }, &parser.Message{ Meta: meta.Meta{ Pos: meta.Position{ Filename: "example.proto", Offset: 200, Line: 20, Column: 10, }, }, }, }, }, inputRuleID: `MESSAGE_NAMES_UPPER_CAMEL_CASE`, wantFailures: []report.Failure{ report.Failuref( meta.Position{ Filename: "example.proto", Offset: 200, Line: 20, Column: 10, }, "MESSAGE_NAMES_UPPER_CAMEL_CASE", string(rule.SeverityError), "Test Message", ), }, }, { name: "visit a message. one is disabled by an inline comment.", inputVisitor: &testVisitor{ BaseAddVisitor: visitor.NewBaseAddVisitor("MESSAGE_NAMES_UPPER_CAMEL_CASE", "error"), }, inputProto: &parser.Proto{ Meta: &parser.ProtoMeta{Filename: ""}, ProtoBody: []parser.Visitee{ &parser.Message{ Meta: meta.Meta{ Pos: meta.Position{ Filename: "example.proto", Offset: 100, Line: 10, Column: 5, }, }, InlineComment: &parser.Comment{ Raw: `// protolint:disable:this MESSAGE_NAMES_UPPER_CAMEL_CASE`, }, }, &parser.Message{ Meta: meta.Meta{ Pos: meta.Position{ Filename: "example.proto", Offset: 200, Line: 20, Column: 10, }, }, }, }, }, inputRuleID: `MESSAGE_NAMES_UPPER_CAMEL_CASE`, wantFailures: []report.Failure{ report.Failuref( meta.Position{ Filename: "example.proto", Offset: 200, Line: 20, Column: 10, }, "MESSAGE_NAMES_UPPER_CAMEL_CASE", string(rule.SeverityError), "Test Message", ), }, }, { name: "visit messages. others are disabled.", inputVisitor: &testVisitor{ BaseAddVisitor: visitor.NewBaseAddVisitor("MESSAGE_NAMES_UPPER_CAMEL_CASE", "error"), }, inputProto: &parser.Proto{ Meta: &parser.ProtoMeta{Filename: ""}, ProtoBody: []parser.Visitee{ &parser.Message{ Meta: meta.Meta{ Pos: meta.Position{ Filename: "example.proto", Offset: 100, Line: 10, Column: 5, }, }, Comments: []*parser.Comment{ { Raw: `// protolint:disable MESSAGE_NAMES_UPPER_CAMEL_CASE`, }, }, }, &parser.Message{ Meta: meta.Meta{ Pos: meta.Position{ Filename: "example.proto", Offset: 200, Line: 20, Column: 10, }, }, }, &parser.Message{ Meta: meta.Meta{ Pos: meta.Position{ Filename: "example.proto", Offset: 300, Line: 30, Column: 15, }, }, Comments: []*parser.Comment{ { Raw: `// protolint:enable MESSAGE_NAMES_UPPER_CAMEL_CASE`, }, }, }, }, }, inputRuleID: `MESSAGE_NAMES_UPPER_CAMEL_CASE`, wantFailures: []report.Failure{ report.Failuref( meta.Position{ Filename: "example.proto", Offset: 300, Line: 30, Column: 15, }, "MESSAGE_NAMES_UPPER_CAMEL_CASE", string(rule.SeverityError), "Test Message", ), }, }, { name: "visit messages. others are disabled by a last line comment.", inputVisitor: &testVisitor{ BaseAddVisitor: visitor.NewBaseAddVisitor("MESSAGE_NAMES_UPPER_CAMEL_CASE", "error"), }, inputProto: &parser.Proto{ Meta: &parser.ProtoMeta{Filename: ""}, ProtoBody: []parser.Visitee{ &parser.Message{ Meta: meta.Meta{ Pos: meta.Position{ Filename: "example.proto", Offset: 100, Line: 10, Column: 5, }, }, Comments: []*parser.Comment{ { Raw: `// protolint:disable MESSAGE_NAMES_UPPER_CAMEL_CASE`, }, }, }, &parser.Message{ Meta: meta.Meta{ Pos: meta.Position{ Filename: "example.proto", Offset: 200, Line: 20, Column: 10, }, }, }, &parser.Comment{ Raw: `// protolint:enable MESSAGE_NAMES_UPPER_CAMEL_CASE`, }, &parser.Message{ Meta: meta.Meta{ Pos: meta.Position{ Filename: "example.proto", Offset: 300, Line: 30, Column: 15, }, }, }, }, }, inputRuleID: `MESSAGE_NAMES_UPPER_CAMEL_CASE`, wantFailures: []report.Failure{ report.Failuref( meta.Position{ Filename: "example.proto", Offset: 300, Line: 30, Column: 15, }, "MESSAGE_NAMES_UPPER_CAMEL_CASE", string(rule.SeverityError), "Test Message", ), }, }, } for _, test := range tests { test := test t.Run(test.name, func(t *testing.T) { got, err := visitor.RunVisitor( test.inputVisitor, test.inputProto, test.inputRuleID, ) if test.wantExistErr { if err == nil { t.Errorf("got err nil, but want err") } return } if err != nil { t.Errorf("got err %v, but want nil", err) return } if !reflect.DeepEqual(got, test.wantFailures) { t.Errorf("got %v, but want %v", got, test.wantFailures) } }) } } func TestRunVisitorAutoDisable(t *testing.T) { tests := []struct { name string inputVisitor visitor.HasExtendedVisitor inputFilename string inputRuleID string inputPlacementType autodisable.PlacementType wantExistErr bool wantFailureCount int wantFilename string }{ { name: "Do nothing in case of no failures", inputVisitor: &testVisitor{ BaseAddVisitor: visitor.NewBaseAddVisitor("ENUM_FIELD_NAMES_UPPER_SNAKE_CASE", "error"), }, inputFilename: "invalid.proto", inputRuleID: "ENUM_FIELD_NAMES_UPPER_SNAKE_CASE", inputPlacementType: autodisable.Next, wantFilename: "invalid.proto", }, { name: "Insert a disable:next comment", inputVisitor: &testVisitorInvalidEnumField{ BaseAddVisitor: visitor.NewBaseAddVisitor("ENUM_FIELD_NAMES_UPPER_SNAKE_CASE", "error"), }, inputFilename: "invalid.proto", inputRuleID: "ENUM_FIELD_NAMES_UPPER_SNAKE_CASE", inputPlacementType: autodisable.Next, wantFailureCount: 1, wantFilename: "disable_next.proto", }, { name: "Insert a disable:this comment", inputVisitor: &testVisitorInvalidEnumField{ BaseAddVisitor: visitor.NewBaseAddVisitor("ENUM_FIELD_NAMES_UPPER_SNAKE_CASE", "error"), }, inputFilename: "invalid.proto", inputRuleID: "ENUM_FIELD_NAMES_UPPER_SNAKE_CASE", inputPlacementType: autodisable.ThisThenNext, wantFailureCount: 1, wantFilename: "disable_this.proto", }, } for _, test := range tests { test := test t.Run(test.name, func(t *testing.T) { input, err := util_test.NewTestData(setting_test.TestDataPath("visitor", test.inputFilename)) if err != nil { t.Errorf("got err %v", err) return } want, err := util_test.NewTestData(setting_test.TestDataPath("visitor", test.wantFilename)) if err != nil { t.Errorf("got err %v", err) return } proto, err := file.NewProtoFile(input.FilePath, input.FilePath).Parse(false) if err != nil { t.Errorf("%v", err) return } got, err := visitor.RunVisitorAutoDisable( test.inputVisitor, proto, test.inputRuleID, test.inputPlacementType, ) if test.wantExistErr { if err == nil { t.Errorf("got err nil, but want err") } return } else if err != nil { t.Errorf("got err %v, but want nil", err) return } if len(got) != test.wantFailureCount { t.Errorf("len(got) %v, but want %v", len(got), test.wantFailureCount) } got2, _ := input.Data() if !reflect.DeepEqual(got2, want.OriginData) { t.Errorf( "got %s(%v), but want %s(%v)", string(got2), got, string(want.OriginData), want.OriginData, ) } err = input.Restore() if err != nil { t.Errorf("got err %v", err) } }) } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/yoheimuta/protolint'

If you have feedback or need assistance with the MCP directory API, please join our Discord server