From b4cafc9d402ea8b62b49bf690ff4dacb9f808264 Mon Sep 17 00:00:00 2001 From: Sachin Holla <51310506+sachinholla@users.noreply.github.com> Date: Thu, 16 Jul 2020 05:12:56 +0530 Subject: [PATCH] Use translib's new api-tests app in REST gotest (#52) Why I did it REST go tests were failing due to failures in ACL request. - How I did it 1) Modified ProcessXXX testcases to use new api-tests app instead of ACL app. This allows running end-to-end positive and negative tests without depending on db state and ACL logic. Added few more test cases. Depends on Azure/sonic-mgmt-common#13. 2) Added few more negative test cases to test translib integration. 3) Added a new tool rest-gotest.sh to run REST server tests from source or already built test binary. Can be used with tparse or gotestsum too. rest-gotest.sh /* run all tests from source */ rest-gotest.sh -bin /* run already built test binary */ rest-gotest.sh -run Process /* run only ProcessXXX tests */ rest-gotest.sh -bin -json | tparse gotestsum -f testname --raw-command -- rest-gotest.sh -json - How to verify it REST server gotest - Description for the changelog Use translib's new api-tests app in REST gotest --- rest/Makefile | 4 +- rest/server/handler_test.go | 68 ++++++++++++++++++++++++------ tools/test/rest-gotest.sh | 82 +++++++++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+), 14 deletions(-) create mode 100755 tools/test/rest-gotest.sh diff --git a/rest/Makefile b/rest/Makefile index 227c85582c..308f2cc714 100644 --- a/rest/Makefile +++ b/rest/Makefile @@ -61,8 +61,8 @@ else endif # Gotest binary for REST Server -$(REST_TEST_BIN): $(REST_TEST_SRCS) $(REST_GO_SRCS) | $(REST_BUILD_DIR)/ - $(GO) test -mod=vendor -cover -c ../rest/server -o $@ +$(REST_TEST_BIN): $(REST_TEST_SRCS) $(REST_SRCS) | $(REST_BUILD_DIR)/ + $(GO) test -mod=vendor -tags test -cover -c ../rest/server -o $@ # Compile certificate generator from standard crypto/tls/generate_cert.go. # Source file will be available in GOROOT/src. diff --git a/rest/server/handler_test.go b/rest/server/handler_test.go index 16e1ca21f5..67a9064618 100644 --- a/rest/server/handler_test.go +++ b/rest/server/handler_test.go @@ -20,6 +20,7 @@ package server import ( + "encoding/json" "encoding/xml" "errors" "fmt" @@ -440,38 +441,62 @@ func testRespData(r *http.Request, rc *RequestContext, data []byte, expType stri func TestProcessGET(t *testing.T) { w := httptest.NewRecorder() - Process(w, prepareRequest(t, "GET", "/test", "")) - verifyResponse(t, w, 500) + Process(w, prepareRequest(t, "GET", "/api-tests:sample", "")) + verifyResponseData(t, w, 200, jsonObj{"path": "/api-tests:sample", "depth": 0}) } -func TestProcessGET_ACL(t *testing.T) { +func TestProcessGET_error(t *testing.T) { w := httptest.NewRecorder() - Process(w, prepareRequest(t, "GET", "/openconfig-acl:acl", "")) - verifyResponse(t, w, 200) + Process(w, prepareRequest(t, "GET", "/api-tests:sample/error/not-found", "")) + verifyResponse(t, w, 404) } func TestProcessPUT(t *testing.T) { w := httptest.NewRecorder() - Process(w, prepareRequest(t, "PUT", "/test", "{}")) - verifyResponse(t, w, 500) + Process(w, prepareRequest(t, "PUT", "/api-tests:sample", "{}")) + verifyResponse(t, w, 204) +} + +func TestProcessPUT_error(t *testing.T) { + w := httptest.NewRecorder() + Process(w, prepareRequest(t, "PUT", "/api-tests:sample/error/not-supported", "{}")) + verifyResponse(t, w, 405) } func TestProcessPOST(t *testing.T) { w := httptest.NewRecorder() - Process(w, prepareRequest(t, "POST", "/test", "{}")) - verifyResponse(t, w, 500) + Process(w, prepareRequest(t, "POST", "/api-tests:sample", "{}")) + verifyResponse(t, w, 201) +} + +func TestProcessPOST_error(t *testing.T) { + w := httptest.NewRecorder() + Process(w, prepareRequest(t, "POST", "/api-tests:sample/error/invalid-args", "{}")) + verifyResponse(t, w, 400) } func TestProcessPATCH(t *testing.T) { w := httptest.NewRecorder() - Process(w, prepareRequest(t, "PATCH", "/test", "{}")) + Process(w, prepareRequest(t, "PATCH", "/api-tests:sample", "{}")) + verifyResponse(t, w, 204) +} + +func TestProcessPATCH_error(t *testing.T) { + w := httptest.NewRecorder() + Process(w, prepareRequest(t, "PATCH", "/api-tests:sample/error/unknown", "{}")) verifyResponse(t, w, 500) } func TestProcessDELETE(t *testing.T) { w := httptest.NewRecorder() - Process(w, prepareRequest(t, "DELETE", "/test", "{}")) - verifyResponse(t, w, 500) + Process(w, prepareRequest(t, "DELETE", "/api-tests:sample", "{}")) + verifyResponse(t, w, 204) +} + +func TestProcessDELETE_error(t *testing.T) { + w := httptest.NewRecorder() + Process(w, prepareRequest(t, "DELETE", "/api-tests:sample/error/not-found", "")) + verifyResponse(t, w, 404) } func TestProcessBadMethod(t *testing.T) { @@ -522,3 +547,22 @@ func verifyResponse(t *testing.T, w *httptest.ResponseRecorder, expCode int) { t.Fatalf("Expecting response status %d; got %d", expCode, w.Code) } } + +type jsonObj map[string]interface{} + +func verifyResponseData(t *testing.T, w *httptest.ResponseRecorder, + expCode int, expData jsonObj) { + verifyResponse(t, w, expCode) + + data := make(jsonObj) + err := json.Unmarshal(w.Body.Bytes(), &data) + if err != nil { + t.Fatalf("Unmarshal error: %v", err) + } + + for k, v := range expData { + if fmt.Sprintf("%v", v) != fmt.Sprintf("%v", data[k]) { + t.Fatalf("Data mismatch for key '%s'; exp='%v', found='%v'", k, v, data[k]) + } + } +} diff --git a/tools/test/rest-gotest.sh b/tools/test/rest-gotest.sh new file mode 100755 index 0000000000..9c1c85dfe6 --- /dev/null +++ b/tools/test/rest-gotest.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +################################################################################ +# # +# Copyright 2020 Broadcom. The term Broadcom refers to Broadcom Inc. and/or # +# its subsidiaries. # +# # +# 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. # +# # +################################################################################ + +set -e + +[[ -z ${TOPDIR} ]] && TOPDIR=$(git rev-parse --show-toplevel) || true +[[ -z ${GO} ]] && GO=go || true + +TESTARGS=() + +while [[ $# -gt 0 ]]; do +case "$1" in + -h|-help|--help) + echo "usage: $(basename $0) [-bin] [-json] [-run NAME] [-auth] [ARGS...]" + echo "" + echo " -bin Run test binary {TOPDIR}/build/tests/rest/server.test" + echo " -json Prints output in json format" + echo " -run NAME Run specific test cases." + echo " -auth Run auth tests; shorthand for '-run Auth -authtest local'" + echo " ARGS... Arguments to test program (log level, auth test arguments etc)" + exit 0 ;; + -bin) + TESTBIN=${TOPDIR}/build/tests/rest/server.test + shift ;; + -json) + JSON=1 + shift ;; + -run) + TESTCASE="$2" + shift 2 ;; + -auth) + TESTCASE="Auth" + TESTARGS+=("-authtest" "local") + shift ;; + *) + TESTARGS+=("$1") + shift;; +esac +done + +MGMT_COMMON_DIR=$(realpath ${TOPDIR}/../sonic-mgmt-common) + +export CVL_SCHEMA_PATH=${MGMT_COMMON_DIR}/build/cvl/schema + +PKG="github.com/Azure/sonic-mgmt-framework/rest/server" +DIR="${PWD}" + +if [[ -z ${TESTBIN} ]]; then + # run "go test" from source + CMD=( ${GO} test "${PKG}" -mod=vendor -v -cover -tags test ) + [[ -z ${JSON} ]] || CMD+=( -json ) + [[ -z ${TESTCASE} ]] || CMD+=( -run "${TESTCASE}" ) + CMD+=( -args ) #keep it last +else + # run compiled test binary + DIR="$(dirname ${TESTBIN})" + CMD=( ./$(basename ${TESTBIN}) -test.v ) + [[ -z ${JSON} ]] || CMD=( ${GO} tool test2json -p "${PKG}" -t "${CMD[@]}" ) + [[ -z ${TESTCASE} ]] || CMD+=( -test.run "${TESTCASE}" ) +fi + +[[ "${TESTARGS[@]}" =~ -(also)?logtostderr ]] || TESTARGS+=( -logtostderr ) + +(cd "${DIR}" && "${CMD[@]}" "${TESTARGS[@]}") +