github.com/googleapis/api-linter@v1.65.2/rules/aip0127/http_template_syntax_test.go (about) 1 // Copyright 2022 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 aip0127 16 17 import ( 18 "testing" 19 20 "github.com/googleapis/api-linter/rules/internal/testutils" 21 ) 22 23 func TestHttpTemplateSyntax(t *testing.T) { 24 tests := []struct { 25 testName string 26 URI string 27 valid bool 28 }{ 29 // NOTE: These examples are contrived to test the enforcment of the 30 // template syntax. Many of these examples either fail or do not make 31 // sense in the context of other AIP rules. 32 33 // Valid cases 34 {"SingleLiteral", "/v1", true}, 35 {"VersionedTemplate", "/{$api_version}/books", true}, 36 {"TwoLiterals", "/v1/books", true}, 37 {"ThreeLiterals", "/v1/books/shelves", true}, 38 {"SingleLiteralWithVerb", "/v1:verb", true}, 39 {"MultipleLiteralsWithVerb", "/v1/books:verb", true}, 40 {"SingleWildcard", "/v1/*", true}, 41 {"DoubleWildcard", "/v1/**", true}, 42 {"SingleWildcardWithVerb", "/v1/*:verb", true}, 43 {"DoubleWildcardWithVerb", "/v1/**:verb", true}, 44 {"SingleWildcardFollowedByLiteral", "/v1/*/books", true}, 45 {"DoubleWildcardFollowedByLiteral", "/v1/**/books", true}, 46 {"LiteralFollowedBySingleWildcard", "/v1/books/*", true}, 47 {"LiteralFollowedByDoubleWildcard", "/v1/books/**", true}, 48 {"VariableWithFieldpath", "/v1/{field}", true}, 49 {"VariableWithNestedFieldpath", "/v1/{field.subfield}", true}, 50 {"VariableWithUltraNestedFieldpath", "/v1/{field.subfield.subsubfield}", true}, 51 {"VariableWithLiteralTemplate", "/v1/{field=books}", true}, 52 {"VariableWithSingleWildcardTemplate", "/v1/{field=*}", true}, 53 {"VariableWithDoubleWildcardTemplate", "/v1/{field=**}", true}, 54 {"VariableWithSingleWildcardFollowedByLiteral", "/v1/{field=*/books}", true}, 55 {"VariableWithDoubleWildcardFollowedByLiteral", "/v1/{field=**/books}", true}, 56 {"VariableWithLiteralFollowedBySingleWildcard", "/v1/{field=books/*}", true}, 57 {"VariableWithLiteralFollowedByDoubleWildcard", "/v1/{field=books/**}", true}, 58 {"VariableFollowedByLiteral", "/v1/{field}/books", true}, 59 {"VariableFollowedByVariable", "/v1/{field}/{otherField}", true}, 60 {"VariableWithTemplateFollowedByLiteral", "/v1/{field=books/*}/shelves", true}, 61 {"VariableFollowedByVariableWithTemplate", "/v1/{field}/{otherField=books/*}", true}, 62 {"VariableWithTemplateFollowedByVariableWithTemplate", "/v1/{field=books/*}/{otherField=shelves/*}", true}, 63 64 // Invalid cases 65 {"LiteralWithoutLeadingSlash", "v1", false}, 66 {"LiteralFollowedBySlash", "/v1/", false}, 67 {"WrongVerbDelimiter", "/v1-verb", false}, 68 {"MultipleVerbs", "/v1:verb:verb", false}, 69 {"VerbFollowedBySlash", "/v1:verb/", false}, 70 {"MultipleLiteralsWithWrongDelimiter", "/v1|books", false}, 71 {"SingleWildcardFollowedBySlash", "/v1/*/", false}, 72 {"DoubleWildcardFollowedBySlash", "/v1/**/", false}, 73 {"TripleWildcard", "/v1/***", false}, 74 {"WrongVariableMarker", "/v1/[field]", false}, 75 {"WrongVariableSubfieldOperator", "/v1/[field->subfield]", false}, 76 {"VariableTemplateContainsVariable", "/v1/{field={otherField=*}}", false}, 77 {"WrongVariableTemplateAssignmentOperator", "/v1/{fieldâbooks}", false}, 78 } 79 for _, test := range tests { 80 t.Run(test.testName, func(t *testing.T) { 81 file := testutils.ParseProto3Tmpl(t, ` 82 import "google/api/annotations.proto"; 83 service Library { 84 rpc FooMethod(FooMethodRequest) returns (FooMethodResponse) { 85 option (google.api.http) = { 86 get: "{{.URI}}" 87 }; 88 } 89 } 90 message FooMethodRequest {} 91 message FooMethodResponse {} 92 `, test) 93 94 problems := httpTemplateSyntax.Lint(file) 95 96 if test.valid && len(problems) > 0 { 97 t.Fatalf("Expected valid HTTP path template syntax but got invalid") 98 } 99 100 if !test.valid && len(problems) == 0 { 101 t.Fatalf("Expected invalid HTTP path template syntax but got valid") 102 } 103 }) 104 } 105 }