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

     1  package rules_test
     2  
     3  import (
     4  	"os"
     5  	"reflect"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/yoheimuta/protolint/internal/linter/file"
    10  	"github.com/yoheimuta/protolint/internal/setting_test"
    11  	"github.com/yoheimuta/protolint/internal/util_test"
    12  	"github.com/yoheimuta/protolint/linter/rule"
    13  	"github.com/yoheimuta/protolint/linter/strs"
    14  
    15  	"github.com/yoheimuta/go-protoparser/v4/parser/meta"
    16  
    17  	"github.com/yoheimuta/go-protoparser/v4/parser"
    18  
    19  	"github.com/yoheimuta/protolint/internal/addon/rules"
    20  	"github.com/yoheimuta/protolint/linter/report"
    21  )
    22  
    23  func TestFileNamesLowerSnakeCaseRule_Apply(t *testing.T) {
    24  	tests := []struct {
    25  		name          string
    26  		inputProto    *parser.Proto
    27  		inputExcluded []string
    28  		wantFailures  []report.Failure
    29  	}{
    30  		{
    31  			name: "no failures for proto with a valid file name",
    32  			inputProto: &parser.Proto{
    33  				Meta: &parser.ProtoMeta{
    34  					Filename: "../proto/simple.proto",
    35  				},
    36  			},
    37  		},
    38  		{
    39  			name: "no failures for proto with a valid lower snake case file name",
    40  			inputProto: &parser.Proto{
    41  				Meta: &parser.ProtoMeta{
    42  					Filename: "../proto/lower_snake_case.proto",
    43  				},
    44  			},
    45  		},
    46  		{
    47  			name: "no failures for excluded proto",
    48  			inputProto: &parser.Proto{
    49  				Meta: &parser.ProtoMeta{
    50  					Filename: "proto/lowerSnakeCase.proto",
    51  				},
    52  			},
    53  			inputExcluded: []string{
    54  				"proto/lowerSnakeCase.proto",
    55  			},
    56  		},
    57  		{
    58  			name: "a failure for proto with a camel case file name",
    59  			inputProto: &parser.Proto{
    60  				Meta: &parser.ProtoMeta{
    61  					Filename: "proto/lowerSnakeCase.proto",
    62  				},
    63  			},
    64  			wantFailures: []report.Failure{
    65  				report.Failuref(
    66  					meta.Position{
    67  						Filename: "proto/lowerSnakeCase.proto",
    68  						Offset:   0,
    69  						Line:     1,
    70  						Column:   1,
    71  					},
    72  					"FILE_NAMES_LOWER_SNAKE_CASE",
    73  					`File name "lowerSnakeCase.proto" should be lower_snake_case.proto like "lower_snake_case.proto".`,
    74  				),
    75  			},
    76  		},
    77  		{
    78  			name: "a failure for proto with an invalid file extension",
    79  			inputProto: &parser.Proto{
    80  				Meta: &parser.ProtoMeta{
    81  					Filename: "proto/lowerSnakeCase.txt",
    82  				},
    83  			},
    84  			wantFailures: []report.Failure{
    85  				report.Failuref(
    86  					meta.Position{
    87  						Filename: "proto/lowerSnakeCase.txt",
    88  						Offset:   0,
    89  						Line:     1,
    90  						Column:   1,
    91  					},
    92  					"FILE_NAMES_LOWER_SNAKE_CASE",
    93  					`File name "lowerSnakeCase.txt" should be lower_snake_case.proto like "lower_snake_case.proto".`,
    94  				),
    95  			},
    96  		},
    97  		{
    98  			name: "a failure for proto with an invalid separater",
    99  			inputProto: &parser.Proto{
   100  				Meta: &parser.ProtoMeta{
   101  					Filename: "proto/dot.separated.proto",
   102  				},
   103  			},
   104  			wantFailures: []report.Failure{
   105  				report.Failuref(
   106  					meta.Position{
   107  						Filename: "proto/dot.separated.proto",
   108  						Offset:   0,
   109  						Line:     1,
   110  						Column:   1,
   111  					},
   112  					"FILE_NAMES_LOWER_SNAKE_CASE",
   113  					`File name "dot.separated.proto" should be lower_snake_case.proto like "dot_separated.proto".`,
   114  				),
   115  			},
   116  		},
   117  		{
   118  			name: "a failure for proto with a kebab case file name",
   119  			inputProto: &parser.Proto{
   120  				Meta: &parser.ProtoMeta{
   121  					Filename: "proto/user-role.proto",
   122  				},
   123  			},
   124  			wantFailures: []report.Failure{
   125  				report.Failuref(
   126  					meta.Position{
   127  						Filename: "proto/user-role.proto",
   128  						Offset:   0,
   129  						Line:     1,
   130  						Column:   1,
   131  					},
   132  					"FILE_NAMES_LOWER_SNAKE_CASE",
   133  					`File name "user-role.proto" should be lower_snake_case.proto like "user_role.proto".`,
   134  				),
   135  			},
   136  		},
   137  	}
   138  
   139  	for _, test := range tests {
   140  		test := test
   141  		t.Run(test.name, func(t *testing.T) {
   142  			rule := rules.NewFileNamesLowerSnakeCaseRule(rule.SeverityError, test.inputExcluded, false)
   143  
   144  			got, err := rule.Apply(test.inputProto)
   145  			if err != nil {
   146  				t.Errorf("got err %v, but want nil", err)
   147  				return
   148  			}
   149  			if !reflect.DeepEqual(got, test.wantFailures) {
   150  				t.Errorf("got %v, but want %v", got, test.wantFailures)
   151  			}
   152  		})
   153  	}
   154  }
   155  
   156  func TestFileNamesLowerSnakeCaseRule_Apply_fix(t *testing.T) {
   157  	tests := []struct {
   158  		name          string
   159  		inputExcluded []string
   160  		inputFilename string
   161  		wantFilename  string
   162  		wantAbort     bool
   163  	}{
   164  		{
   165  			name:          "no fix for a correct proto",
   166  			inputFilename: "lower_snake_case.proto",
   167  			wantFilename:  "lower_snake_case.proto",
   168  		},
   169  		{
   170  			name:          "abort to fix the proto because of alreadyExists",
   171  			inputFilename: "lowerSnakeCase.proto",
   172  			wantAbort:     true,
   173  		},
   174  		{
   175  			name:          "fix for an incorrect proto",
   176  			inputFilename: "UpperCamelCase.proto",
   177  			wantFilename:  "upper_camel_case.proto",
   178  		},
   179  		{
   180  			name:          "fix for a kebab case proto",
   181  			inputFilename: "kebab-case.proto",
   182  			wantFilename:  "kebab_case.proto",
   183  		},
   184  	}
   185  
   186  	for _, test := range tests {
   187  		test := test
   188  		t.Run(test.name, func(t *testing.T) {
   189  			r := rules.NewFileNamesLowerSnakeCaseRule(rule.SeverityError, test.inputExcluded, true)
   190  
   191  			dataDir := strs.ToLowerCamelCase(r.ID())
   192  			input, err := util_test.NewTestData(setting_test.TestDataPath("rules", dataDir, test.inputFilename))
   193  			if err != nil {
   194  				t.Errorf("got err %v", err)
   195  				return
   196  			}
   197  			proto, err := file.NewProtoFile(input.FilePath, input.FilePath).Parse(false)
   198  			if err != nil {
   199  				t.Errorf(err.Error())
   200  				return
   201  			}
   202  			fs, err := r.Apply(proto)
   203  			if err != nil {
   204  				t.Errorf("got err %v, but want nil", err)
   205  				return
   206  			}
   207  			if test.wantAbort {
   208  				if _, err := os.Stat(input.FilePath); os.IsNotExist(err) {
   209  					t.Errorf("not found %q, but want to locate it", input.FilePath)
   210  					return
   211  				}
   212  				for _, f := range fs {
   213  					if strings.Contains(f.Message(), "Failed to rename") {
   214  						return
   215  					}
   216  				}
   217  				t.Error("not found failure message, but want to include it")
   218  				return
   219  			}
   220  
   221  			wantPath := setting_test.TestDataPath("rules", dataDir, test.wantFilename)
   222  			if _, err := os.Stat(wantPath); os.IsNotExist(err) {
   223  				t.Errorf("not found %q, but want to locate it", wantPath)
   224  				return
   225  			}
   226  
   227  			err = os.Rename(wantPath, input.FilePath)
   228  			if err != nil {
   229  				t.Errorf("got err %v", err)
   230  			}
   231  		})
   232  	}
   233  }