github.com/yoheimuta/protolint@v0.49.8-0.20240515023657-4ecaebb7575d/internal/addon/rules/proto3FieldsAvoidRequiredRule_test.go (about)

     1  package rules_test
     2  
     3  import (
     4  	"reflect"
     5  	"testing"
     6  
     7  	"github.com/yoheimuta/go-protoparser/v4/parser"
     8  	"github.com/yoheimuta/go-protoparser/v4/parser/meta"
     9  	"github.com/yoheimuta/protolint/internal/addon/rules"
    10  	"github.com/yoheimuta/protolint/linter/report"
    11  	"github.com/yoheimuta/protolint/linter/rule"
    12  )
    13  
    14  func TestProto3FieldsAvoidRequiredRule_Apply(t *testing.T) {
    15  	tests := []struct {
    16  		name         string
    17  		inputProto   *parser.Proto
    18  		wantFailures []report.Failure
    19  	}{
    20  		{
    21  			name: "no failures for proto without fields",
    22  			inputProto: &parser.Proto{
    23  				ProtoBody: []parser.Visitee{
    24  					&parser.Enum{},
    25  				},
    26  			},
    27  		},
    28  		{
    29  			name: "no failures for proto with not required field names",
    30  			inputProto: &parser.Proto{
    31  				Syntax: &parser.Syntax{
    32  					ProtobufVersion: "proto3",
    33  				},
    34  				ProtoBody: []parser.Visitee{
    35  					&parser.Service{},
    36  					&parser.Message{
    37  						MessageBody: []parser.Visitee{
    38  							&parser.Field{
    39  								FieldName: "song_name",
    40  							},
    41  							&parser.Field{
    42  								IsRepeated: true,
    43  								FieldName:  "singer",
    44  							},
    45  							&parser.Field{
    46  								IsOptional: true,
    47  								FieldName:  "singer",
    48  							},
    49  							&parser.MapField{
    50  								MapName: "song_name2",
    51  							},
    52  							&parser.Oneof{
    53  								OneofFields: []*parser.OneofField{
    54  									{
    55  										FieldName: "song_name3",
    56  									},
    57  								},
    58  							},
    59  						},
    60  					},
    61  				},
    62  			},
    63  		},
    64  		{
    65  			name: "no failures for proto with required field names for proto2",
    66  			inputProto: &parser.Proto{
    67  				Syntax: &parser.Syntax{
    68  					ProtobufVersion: "proto2",
    69  				},
    70  				ProtoBody: []parser.Visitee{
    71  					&parser.Service{},
    72  					&parser.Message{
    73  						MessageBody: []parser.Visitee{
    74  							&parser.Field{
    75  								IsRequired: true,
    76  								FieldName:  "song_name",
    77  							},
    78  							&parser.Field{
    79  								IsRequired: true,
    80  								FieldName:  "singer",
    81  							},
    82  						},
    83  					},
    84  				},
    85  			},
    86  		},
    87  		{
    88  			name: "failures for proto with required field names for proto3",
    89  			inputProto: &parser.Proto{
    90  				Syntax: &parser.Syntax{
    91  					ProtobufVersion: "proto3",
    92  				},
    93  				ProtoBody: []parser.Visitee{
    94  					&parser.Message{
    95  						MessageBody: []parser.Visitee{
    96  							&parser.Field{
    97  								IsRequired: true,
    98  								FieldName:  "song_Name",
    99  								Meta: meta.Meta{
   100  									Pos: meta.Position{
   101  										Filename: "example.proto",
   102  										Offset:   100,
   103  										Line:     5,
   104  										Column:   10,
   105  									},
   106  								},
   107  							},
   108  							&parser.Field{
   109  								IsRequired: true,
   110  								FieldName:  "song.name",
   111  								Meta: meta.Meta{
   112  									Pos: meta.Position{
   113  										Filename: "example.proto",
   114  										Offset:   200,
   115  										Line:     10,
   116  										Column:   20,
   117  									},
   118  								},
   119  							},
   120  						},
   121  					},
   122  				},
   123  			},
   124  			wantFailures: []report.Failure{
   125  				report.Failuref(
   126  					meta.Position{
   127  						Filename: "example.proto",
   128  						Offset:   100,
   129  						Line:     5,
   130  						Column:   10,
   131  					},
   132  					"PROTO3_FIELDS_AVOID_REQUIRED",
   133  					`Field "song_Name" should avoid required for proto3`,
   134  				),
   135  				report.Failuref(
   136  					meta.Position{
   137  						Filename: "example.proto",
   138  						Offset:   200,
   139  						Line:     10,
   140  						Column:   20,
   141  					},
   142  					"PROTO3_FIELDS_AVOID_REQUIRED",
   143  					`Field "song.name" should avoid required for proto3`,
   144  				),
   145  			},
   146  		},
   147  	}
   148  
   149  	for _, test := range tests {
   150  		test := test
   151  		t.Run(test.name, func(t *testing.T) {
   152  			rule := rules.NewProto3FieldsAvoidRequiredRule(rule.SeverityError, false)
   153  
   154  			got, err := rule.Apply(test.inputProto)
   155  			if err != nil {
   156  				t.Errorf("got err %v, but want nil", err)
   157  				return
   158  			}
   159  			if !reflect.DeepEqual(got, test.wantFailures) {
   160  				t.Errorf("got %v, but want %v", got, test.wantFailures)
   161  			}
   162  		})
   163  	}
   164  }
   165  
   166  func TestProto3FieldsAvoidRequiredRule_Apply_fix(t *testing.T) {
   167  	tests := []struct {
   168  		name          string
   169  		inputFilename string
   170  		wantFilename  string
   171  	}{
   172  		{
   173  			name:          "no fix for a correct proto",
   174  			inputFilename: "avoid_required.proto",
   175  			wantFilename:  "avoid_required.proto",
   176  		},
   177  		{
   178  			name:          "fix for an incorrect proto",
   179  			inputFilename: "invalid.proto",
   180  			wantFilename:  "avoid_required.proto",
   181  		},
   182  	}
   183  
   184  	for _, test := range tests {
   185  		test := test
   186  		t.Run(test.name, func(t *testing.T) {
   187  			r := rules.NewProto3FieldsAvoidRequiredRule(rule.SeverityError, true)
   188  			testApplyFix(t, r, test.inputFilename, test.wantFilename)
   189  		})
   190  	}
   191  }