Repository: googleapis/api-linter
Pull request #164 (2019-09-18 02:14:47)

AIP-126: all enum value must be UPPER_SNAKE_CASE.
I did a lot of copy and paste -- I think we can generate some codes, e.g., the initial file structure for each AIP (aip0126.go and aip0126_test.go).


Patch:
diff --git a/rules/aip0126/aip0126.go b/rules/aip0126/aip0126.go
new file mode 100644
index 0000000..9c8cec5
--- /dev/null
+++ b/rules/aip0126/aip0126.go
@@ -0,0 +1,10 @@
+package aip0126
+
+import "github.com/googleapis/api-linter/lint"
+
+// AddRules adds all of the AIP-126 rules to the provided registry.
+func AddRules(r lint.RuleRegistry) {
+	r.Register(
+		enumValueUpperSnakeCase,
+	)
+}
diff --git a/rules/aip0126/upper_snake_case.go b/rules/aip0126/upper_snake_case.go
new file mode 100644
index 0000000..d23fa17
--- /dev/null
+++ b/rules/aip0126/upper_snake_case.go
@@ -0,0 +1,33 @@
+package aip0126
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/googleapis/api-linter/lint"
+	"github.com/jhump/protoreflect/desc"
+	"github.com/stoewer/go-strcase"
+)
+
+// All enum values must use UPPER_SNAKE_CASE.
+var enumValueUpperSnakeCase = &lint.EnumRule{
+	Name: lint.NewRuleName("core", "0126", "upper-snake"),
+	URI:  "https://aip.dev/126#guidance",
+	LintEnum: func(e *desc.EnumDescriptor) []lint.Problem {
+		var problems []lint.Problem
+		for _, v := range e.GetValues() {
+			if got, want := v.GetName(), toUpperSnakeCase(v.GetName()); got != want {
+				problems = append(problems, lint.Problem{
+					Message:    fmt.Sprintf("Enum value %q must use UPPER_SNAKE_CASE.", got),
+					Suggestion: want,
+					Descriptor: v,
+				})
+			}
+		}
+		return problems
+	},
+}
+
+func toUpperSnakeCase(s string) string {
+	return strings.ToUpper(strcase.SnakeCase(s))
+}


Test patch:
diff --git a/rules/aip0126/api0126_test.go b/rules/aip0126/api0126_test.go
new file mode 100644
index 0000000..de242fd
--- /dev/null
+++ b/rules/aip0126/api0126_test.go
@@ -0,0 +1,18 @@
+package aip0126
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/googleapis/api-linter/lint"
+)
+
+func TestAddRules(t *testing.T) {
+	rules := make(lint.RuleRegistry)
+	AddRules(rules)
+	for ruleName := range rules {
+		if !strings.HasPrefix(string(ruleName), "core::0126") {
+			t.Errorf("Rule %s is not namespaced to core::0126.", ruleName)
+		}
+	}
+}
diff --git a/rules/aip0126/upper_snake_case_test.go b/rules/aip0126/upper_snake_case_test.go
new file mode 100644
index 0000000..0c65542
--- /dev/null
+++ b/rules/aip0126/upper_snake_case_test.go
@@ -0,0 +1,51 @@
+package aip0126
+
+import (
+	"testing"
+
+	"github.com/jhump/protoreflect/desc/builder"
+)
+
+func TestUpperSnake(t *testing.T) {
+	// Define permutations.
+	tests := []struct {
+		name             string
+		enumValues       []string
+		wantProblemCount int
+		wantSuggestions  []string
+	}{
+		{"ValidOneWord", []string{"ONE"}, 0, nil},
+		{"ValidTwoWords", []string{"ONE_TWO"}, 0, nil},
+		{"InvalidOneWord", []string{"one"}, 1, []string{"ONE"}},
+		{"InvalidTwoWordsCamel", []string{"oneTwo"}, 1, []string{"ONE_TWO"}},
+		{"InvalidTwoWordsLowerSnake", []string{"one_two"}, 1, []string{"ONE_TWO"}},
+		{"OneProblem", []string{"one_two", "THREE_FOUR"}, 1, []string{"ONE_TWO"}},
+		{"TwoProblems", []string{"one_two", "three_four"}, 2, []string{"ONE_TWO", "THREE_FOUR"}},
+	}
+
+	// Test each permutation.
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			enumBuilder := builder.NewEnum("Number")
+			for _, enumValue := range test.enumValues {
+				enumBuilder.AddValue(builder.NewEnumValue(enumValue))
+			}
+
+			enum, err := enumBuilder.Build()
+			if err != nil {
+				t.Fatalf("Could not build an enum with values %v", test.enumValues)
+			}
+
+			problems := enumValueUpperSnakeCase.LintEnum(enum)
+			if got, want := len(problems), test.wantProblemCount; got != want {
+				t.Errorf("rule enumValueUpperSnakeCase got %d problems, but wanted %d", got, want)
+			}
+
+			for i, problem := range problems {
+				if got, want := problem.Suggestion, test.wantSuggestions[i]; got != want {
+					t.Errorf("rule enumValueUpperSnakeCase got suggestion %q, but wanted %q", got, want)
+				}
+			}
+		})
+	}
+}