go.temporal.io/server@v1.23.0/common/api/metadata_test.go (about)

     1  // The MIT License
     2  //
     3  // Copyright (c) 2020 Temporal Technologies Inc.  All rights reserved.
     4  //
     5  // Copyright (c) 2020 Uber Technologies, Inc.
     6  //
     7  // Permission is hereby granted, free of charge, to any person obtaining a copy
     8  // of this software and associated documentation files (the "Software"), to deal
     9  // in the Software without restriction, including without limitation the rights
    10  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    11  // copies of the Software, and to permit persons to whom the Software is
    12  // furnished to do so, subject to the following conditions:
    13  //
    14  // The above copyright notice and this permission notice shall be included in
    15  // all copies or substantial portions of the Software.
    16  //
    17  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    18  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    19  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    20  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    21  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    22  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    23  // THE SOFTWARE.
    24  
    25  package api
    26  
    27  import (
    28  	"reflect"
    29  	"regexp"
    30  	"testing"
    31  
    32  	"github.com/stretchr/testify/assert"
    33  	"github.com/stretchr/testify/require"
    34  	"go.temporal.io/api/operatorservice/v1"
    35  	"go.temporal.io/api/workflowservice/v1"
    36  	"golang.org/x/exp/maps"
    37  )
    38  
    39  var publicMethodRgx = regexp.MustCompile("^[A-Z]")
    40  
    41  func TestWorkflowServiceMetadata(t *testing.T) {
    42  	tp := reflect.TypeOf((*workflowservice.WorkflowServiceServer)(nil)).Elem()
    43  	checkService(t, tp, workflowServiceMetadata)
    44  }
    45  
    46  func TestOperatorServiceMetadata(t *testing.T) {
    47  	tp := reflect.TypeOf((*operatorservice.OperatorServiceServer)(nil)).Elem()
    48  	checkService(t, tp, operatorServiceMetadata)
    49  }
    50  
    51  func checkService(t *testing.T, tp reflect.Type, m map[string]MethodMetadata) {
    52  	methods := getMethodNames(tp)
    53  	require.ElementsMatch(t, methods, maps.Keys(m),
    54  		"If you're adding a new method to Workflow/OperatorService, please add metadata for it in metadata.go")
    55  
    56  	for _, method := range methods {
    57  		refMethod, ok := tp.MethodByName(method)
    58  		require.True(t, ok)
    59  
    60  		checkNamespace := false
    61  		hasNamespace := false
    62  		namespaceIsString := false
    63  
    64  		if refMethod.Type.NumIn() >= 2 {
    65  			// not streaming
    66  			checkNamespace = true
    67  			requestType := refMethod.Type.In(1).Elem()
    68  			var nsField reflect.StructField
    69  			nsField, hasNamespace = requestType.FieldByName("Namespace")
    70  			if hasNamespace {
    71  				namespaceIsString = nsField.Type == reflect.TypeOf("string")
    72  			}
    73  		}
    74  
    75  		md := m[method]
    76  		switch md.Scope {
    77  		case ScopeNamespace:
    78  			if checkNamespace {
    79  				assert.Truef(t, namespaceIsString, "%s with ScopeNamespace should have a Namespace field that is a string", method)
    80  			}
    81  		case ScopeCluster:
    82  			if checkNamespace {
    83  				assert.Falsef(t, hasNamespace, "%s with ScopeCluster should not have a Namespace field", method)
    84  			}
    85  		default:
    86  			t.Error("unknown Scope for", method)
    87  		}
    88  
    89  		switch md.Access {
    90  		case AccessReadOnly, AccessWrite, AccessAdmin:
    91  		default:
    92  			t.Error("unknown Access for", method)
    93  		}
    94  	}
    95  }
    96  
    97  func TestGetMethodMetadata(t *testing.T) {
    98  	md := GetMethodMetadata("/temporal.api.workflowservice.v1.WorkflowService/RespondActivityTaskCompleted")
    99  	assert.Equal(t, ScopeNamespace, md.Scope)
   100  	assert.Equal(t, AccessWrite, md.Access)
   101  
   102  	// all AdminService is cluster/admin
   103  	md = GetMethodMetadata("/temporal.server.api.adminservice.v1.AdminService/CloseShard")
   104  	assert.Equal(t, ScopeCluster, md.Scope)
   105  	assert.Equal(t, AccessAdmin, md.Access)
   106  
   107  	md = GetMethodMetadata("/OtherService/Method1")
   108  	assert.Equal(t, ScopeUnknown, md.Scope)
   109  	assert.Equal(t, AccessUnknown, md.Access)
   110  }
   111  
   112  func getMethodNames(tp reflect.Type) []string {
   113  	var out []string
   114  	for i := 0; i < tp.NumMethod(); i++ {
   115  		name := tp.Method(i).Name
   116  		// Don't collect unimplemented methods. This weeds out the
   117  		// `mustEmbedUnimplementedFooBarBaz` required by the GRPC v2 gateway
   118  		if publicMethodRgx.MatchString(name) {
   119  			out = append(out, name)
   120  		}
   121  	}
   122  	return out
   123  }