github.com/googleapis/api-linter@v1.65.2/rules/aip0191/file_option_consistency_test.go (about)

     1  // Copyright 2019 Google LLC
     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  // 		https://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 aip0191
    16  
    17  import (
    18  	"reflect"
    19  	"sort"
    20  	"testing"
    21  
    22  	"github.com/googleapis/api-linter/lint"
    23  	"github.com/googleapis/api-linter/rules/internal/testutils"
    24  	"github.com/jhump/protoreflect/desc"
    25  	"github.com/jhump/protoreflect/desc/builder"
    26  	"github.com/stoewer/go-strcase"
    27  	"google.golang.org/protobuf/proto"
    28  	dpb "google.golang.org/protobuf/types/descriptorpb"
    29  )
    30  
    31  func TestFileOptionConsistency(t *testing.T) {
    32  	// Make a "control file".
    33  	// This is a situation where using a builder is much easier
    34  	// than parsing a proto template.
    35  	controlFile := builder.NewFile("control.proto").SetPackageName(
    36  		"google.example.v1",
    37  	).SetOptions(getOptions(nil))
    38  
    39  	// Run our tests. Each test will make a "test file" that imports the
    40  	// control file.
    41  	for _, test := range []consistencyTest{
    42  		{"ValidSame", map[string]string{}},
    43  		{"InvalidCsharpNamespaceMissing", map[string]string{"csharp_namespace": ""}},
    44  		{"InvalidCsharpNamespaceMismatch", map[string]string{"csharp_namespace": "Example.V1"}},
    45  		{"InvalidJavaPackageMissing", map[string]string{"java_package": ""}},
    46  		{"InvalidJavaPackageMismatch", map[string]string{"java_package": "com.example.v1"}},
    47  		{"InvalidPhpNamespaceMissing", map[string]string{"php_namespace": ""}},
    48  		{"InvalidPhpNamespaceMismatch", map[string]string{"php_namespace": "Example\\V1"}},
    49  		{"InvalidPhpMetadataNamespace", map[string]string{"php_metadata_namespace": "Example\\V1"}},
    50  		{"InvalidPhpClassPrefix", map[string]string{"php_class_prefix": "ExampleProto"}},
    51  		{"InvalidObjcClassPrefixMissing", map[string]string{"objc_class_prefix": ""}},
    52  		{"InvalidObjcClassPrefixMismatch", map[string]string{"objc_class_prefix": "GEXV1"}},
    53  		{"InvalidRubyPackageMissing", map[string]string{"ruby_package": ""}},
    54  		{"InvalidRubyPackageMismatch", map[string]string{"ruby_package": "Example::V1"}},
    55  		{"InvalidSwiftPrefixMissing", map[string]string{"swift_prefix": ""}},
    56  		{"InvalidSwiftPrefixMismatch", map[string]string{"swift_prefix": "ExampleProto"}},
    57  		{"InvalidLotsOfReasons", map[string]string{
    58  			"csharp_namespace": "Example.V1",
    59  			"go_package":       "google.golang.org/googleapis/genproto/googleapis/example/v1;example",
    60  			"java_package":     "com.example.v1",
    61  			"ruby_package":     "Example::V1",
    62  			"swift_prefix":     "",
    63  		}},
    64  	} {
    65  		t.Run(test.name, func(t *testing.T) {
    66  			// Build our test file, which imports the control file.
    67  			testFile, err := builder.NewFile("test.proto").AddDependency(
    68  				controlFile,
    69  			).SetPackageName("google.example.v1").SetOptions(getOptions(test.options)).Build()
    70  			if err != nil {
    71  				t.Fatalf("Could not build test file.")
    72  			}
    73  			if diff := test.getProblems(testFile).Diff(fileOptionConsistency.Lint(testFile)); diff != "" {
    74  				t.Errorf(diff)
    75  			}
    76  		})
    77  	}
    78  
    79  	// Also ensure separate packages are ignored.
    80  	t.Run("ValidDifferentPackages", func(t *testing.T) {
    81  		testFile, err := builder.NewFile("test.proto").AddDependency(controlFile).SetOptions(
    82  			getOptions(map[string]string{"java_package": "com.wrong"}),
    83  		).SetPackageName("google.different.v1").Build()
    84  		if err != nil {
    85  			t.Fatalf("Could not build test file.")
    86  		}
    87  		if diff := (testutils.Problems{}).Diff(fileOptionConsistency.Lint(testFile)); diff != "" {
    88  			t.Errorf(diff)
    89  		}
    90  	})
    91  }
    92  
    93  func getOptions(fileopts map[string]string) *dpb.FileOptions {
    94  	opts := &dpb.FileOptions{
    95  		CsharpNamespace: proto.String("Google.Example.V1"),
    96  		GoPackage:       proto.String("github.com/googleapis/genproto/googleapis/example/v1;example"),
    97  		JavaPackage:     proto.String("com.google.example.v1"),
    98  		PhpNamespace:    proto.String("Google\\Example\\V1"),
    99  		ObjcClassPrefix: proto.String("GEX"),
   100  		RubyPackage:     proto.String("Google::Example::V1"),
   101  		SwiftPrefix:     proto.String("Google_Example_V1"),
   102  	}
   103  	for k, v := range fileopts {
   104  		field := strcase.UpperCamelCase(k)
   105  		reflect.ValueOf(opts).Elem().FieldByName(field).Set(reflect.ValueOf(proto.String(v)))
   106  	}
   107  	return opts
   108  }
   109  
   110  type consistencyTest struct {
   111  	name    string
   112  	options map[string]string
   113  }
   114  
   115  func (ct *consistencyTest) getProblems(f *desc.FileDescriptor) testutils.Problems {
   116  	p := testutils.Problems{}
   117  	for k := range ct.options {
   118  		p = append(p, lint.Problem{Message: k, Descriptor: f})
   119  	}
   120  	sort.Slice(p, func(i, j int) bool {
   121  		return p[i].Message < p[j].Message
   122  	})
   123  	return p
   124  }