Repository: caicloud/nirvana
Pull request #415 (2020-12-11 06:27:31)

[release-0.3] fix(utils): 确保 params 跟 results 的个数与函数的参数及返回值的个数相同
This is an automated cherry-pick of #414

/assign iawia002

caicloud-bot: @caicloud-bot: Adding the "do-not-merge/release-note-label-needed" label because no release-note block was detected, please follow our [release note process](https://git.k8s.io/community/contributors/guide/release-notes.md) to remove it.

<details>

Instructions for interacting with me using PR comments are available [here](https://github.com/caicloud/engineering/blob/master/guidelines/caicloud_bot.md).  If you have questions or suggestions related to my behavior, please file an issue against the [caicloud/tools](https://github.com/caicloud/tools/issues/new?title=Prow%20issue:) repository.
</details>
caicloud-bot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: 
To fully approve this pull request, please assign additional approvers.
We suggest the following additional approver: **iawia002**

Assign the PR to them by writing `/assign @iawia002` in a comment when ready.

The full list of commands accepted by this bot can be found [here](https://github.com/caicloud/engineering/blob/master/guidelines/caicloud_bot.md#bot-commands).

The pull request process is described [here](https://github.com/caicloud/engineering/blob/master/guidelines/caicloud_bot.md#typical-workflow)

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/caicloud/nirvana/blob/master/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["iawia002"]} -->
iawia002: /release-note-none
/lgtm
caicloud-bot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/caicloud/nirvana/pull/415#issuecomment-743003631" title="LGTM">iawia002</a>*

The full list of commands accepted by this bot can be found [here](https://github.com/caicloud/engineering/blob/master/guidelines/caicloud_bot.md#bot-commands).

The pull request process is described [here](https://github.com/caicloud/engineering/blob/master/guidelines/caicloud_bot.md#typical-workflow)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/caicloud/nirvana/blob/master/OWNERS)~~ [iawia002]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->

Patch:
diff --git a/utils/api/definitions.go b/utils/api/definitions.go
index 92021a4..2c60e7b 100644
--- a/utils/api/definitions.go
+++ b/utils/api/definitions.go
@@ -18,6 +18,7 @@ package api
 
 import (
 	"encoding/json"
+	"fmt"
 	"net/http"
 
 	"github.com/caicloud/nirvana/definition"
@@ -120,6 +121,12 @@ func NewDefinition(tc *TypeContainer, d *definition.Definition, apiStyle service
 		cd.HTTPCode = http.StatusOK
 	}
 	functionType := tc.Type(cd.Function)
+	if len(functionType.In) != len(d.Parameters) {
+		return nil, fmt.Errorf("the number of parameters and function args are not equal: len(params)=%d, len(funcArgs)=%d", len(d.Parameters), len(functionType.In))
+	}
+	if len(functionType.Out) != len(d.Results) {
+		return nil, fmt.Errorf("the number of results and function return values are not equal: len(results)=%d, len(funcVals)=%d", len(d.Results), len(functionType.Out))
+	}
 	for i, p := range d.Parameters {
 		param := Parameter{
 			Source:      p.Source,
@@ -174,7 +181,7 @@ func NewDefinitions(tc *TypeContainer, definitions []definition.Definition, apiS
 	for i, d := range definitions {
 		cd, err := NewDefinition(tc, &d, apiStyle)
 		if err != nil {
-			return nil, err
+			return nil, fmt.Errorf("func=%s: %w", tc.NameOfInstance(d.Function), err)
 		}
 		result[i] = *cd
 	}
@@ -187,7 +194,7 @@ func NewPathDefinitions(tc *TypeContainer, definitions map[string][]definition.D
 	for path, defs := range definitions {
 		cds, err := NewDefinitions(tc, defs, apiStyle)
 		if err != nil {
-			return nil, err
+			return nil, fmt.Errorf("definitions of path %s: %w", path, err)
 		}
 		result[path] = cds
 


Test patch:
diff --git a/utils/api/definitions_test.go b/utils/api/definitions_test.go
new file mode 100644
index 0000000..99edfff
--- /dev/null
+++ b/utils/api/definitions_test.go
@@ -0,0 +1,114 @@
+/*
+Copyright 2020 Caicloud Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package api
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/caicloud/nirvana/definition"
+	"github.com/caicloud/nirvana/service"
+)
+
+func TestNewDefinitionShouldNotPanic(t *testing.T) {
+	testCases := []struct {
+		name      string
+		d         *definition.Definition
+		expectErr string
+	}{
+		{
+			name: "len(params) < len(args)",
+			d: &definition.Definition{
+				Method:     definition.Create,
+				Function:   func(s string) {},
+				Parameters: []definition.Parameter{},
+				Results:    []definition.Result{},
+			},
+			expectErr: "the number of parameters and function args are not equal",
+		},
+		{
+			name: "len(params) > len(args)",
+			d: &definition.Definition{
+				Method:   definition.Create,
+				Function: func() {},
+				Parameters: []definition.Parameter{
+					definition.QueryParameterFor("Action", "action"),
+				},
+				Results: []definition.Result{},
+			},
+			expectErr: "the number of parameters and function args are not equal",
+		},
+		{
+			name: "len(params) = len(args)",
+			d: &definition.Definition{
+				Method:   definition.Create,
+				Function: func(action string) {},
+				Parameters: []definition.Parameter{
+					definition.QueryParameterFor("Action", "action"),
+				},
+				Results: []definition.Result{},
+			},
+		},
+		{
+			name: "len(results) < len(returns)",
+			d: &definition.Definition{
+				Method:     definition.Create,
+				Function:   func() error { return nil },
+				Parameters: []definition.Parameter{},
+				Results:    []definition.Result{},
+			},
+			expectErr: "the number of results and function return values are not equal",
+		},
+		{
+			name: "len(results) > len(returns)",
+			d: &definition.Definition{
+				Method:     definition.Create,
+				Function:   func() {},
+				Parameters: []definition.Parameter{},
+				Results:    []definition.Result{definition.ErrorResult()},
+			},
+			expectErr: "the number of results and function return values are not equal",
+		},
+		{
+			name: "len(results) = len(returns)",
+			d: &definition.Definition{
+				Method:     definition.Create,
+				Function:   func() error { return nil },
+				Parameters: []definition.Parameter{},
+				Results:    []definition.Result{definition.ErrorResult()},
+			},
+		},
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			_, err := NewDefinition(NewTypeContainer(), testCase.d, service.APIStyleRPC)
+			if len(testCase.expectErr) > 0 {
+				if err == nil {
+					t.Fatalf("Unexpected success")
+				}
+				if !strings.Contains(err.Error(), testCase.expectErr) {
+					t.Fatalf("Unexpcted error: expected=%s\ngot=%s", testCase.expectErr, err.Error())
+				}
+				return
+			}
+			if err != nil {
+				t.Fatalf("Unexpected error: %v", err)
+			}
+		})
+	}
+}