/******************************************************************************
 * Copyright (c)  2022 PingCAP                                                *
 * 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.                                             *
 ******************************************************************************/

/*******************************************************************************
 * @File: resolve_parameter_test.go
 * @Description:
 * @Author: jiangxunyu@pingcap.com
 * @Version: 1.0.0
 * @Date: 2022/7/8 18:16
*******************************************************************************/

package interceptor

import (
	"errors"
	"mime/multipart"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"

	"github.com/tidbcloud/infra-provider/regional-server/pkg/common/models"

	"github.com/Azure/go-autorest/autorest/mocks"
	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
)

func Test_ResolveRequestParameters(t *testing.T) {
	t.Run("success", func(t *testing.T) {
		gin.SetMode(gin.TestMode)
		w := httptest.NewRecorder()
		c, _ := gin.CreateTestContext(w)
		c.Request = &http.Request{
			Method: http.MethodPost,
			URL: &url.URL{
				Path:     "/api/v1/clusters",
				RawQuery: "vendor=aws&region=us-west-2",
			},
			Header: map[string][]string{
				"Auth": {"Bearer test_token"},
			},
			RequestURI: "/api/v1/cluster?vendor=aws&region=us-west-2",
			Body:       mocks.NewBody("{\"bodyKey\": \"testBodyValue\"}"),
		}

		t.Setenv("AWS_ROLE_ARN", "arn:aws:iam::1234567890:role/test")
		t.Setenv("AWS_WEB_IDENTITY_TOKEN_FILE", "./handler_test.go")

		ResolveRequestParameters(c)

		assert.Equal(t, http.StatusOK, w.Code)
		obj, ok := c.Get(models.GlobalServerRequestParametersKey)
		assert.True(t, ok)
		assert.NotNil(t, obj)
	})

	t.Run("error", func(t *testing.T) {
		gin.SetMode(gin.TestMode)
		w := httptest.NewRecorder()
		c, _ := gin.CreateTestContext(w)
		c.Request = &http.Request{
			Method: http.MethodPost,
			URL: &url.URL{
				Path:     "/api/v1/clusters",
				RawQuery: "vendor=aws&region=us-west-2",
			},
		}
		ResolveRequestParameters(c)

		assert.NotEqual(t, http.StatusOK, w.Code)
		assert.NotEmpty(t, w.Body.String())
	})
}

type mockReader struct {
}

func (r *mockReader) Read(p []byte) (n int, err error) {
	return 0, errors.New("read error")
}

func (r *mockReader) Close() error {
	return nil
}

func Test_getAllReqParams(t *testing.T) {
	t.Run("success", func(t *testing.T) {
		// Switch to test mode so you don't get such noisy output
		gin.SetMode(gin.TestMode)
		w := httptest.NewRecorder()
		c, _ := gin.CreateTestContext(w)
		c.Request = &http.Request{
			Method: http.MethodPost,
			URL: &url.URL{
				Scheme:   "http",
				Host:     "127.0.0.1:80",
				Path:     "/api/v1?a=b&c=d",
				RawQuery: "a=b&c=d",
			},
			Header: map[string][]string{
				"Content-Type": {"multipart/form-data"},
				"Auth":         {"Bearer test_token"},
			},
			Body:          mocks.NewBody("{\"bodyKey\": \"testBodyValue\"}"),
			ContentLength: 38,
			Host:          "127.0.0.1:80",
			Form: map[string][]string{
				"testFormKey": {"testFormValue"},
			},
			PostForm: map[string][]string{
				"testPostFormKey": {"testPostFormValue"},
			},
			MultipartForm: &multipart.Form{
				Value: map[string][]string{
					"testMultiFormKey": {"testMultiFormValue"},
				},
			},
			RequestURI: "http://127.0.0.1:80/api/v1?a=b&c=d",
		}
		c.Request.Method = http.MethodPost
		c.Request.RequestURI = "http://127.0.0.1:80"
		c.Params = []gin.Param{
			{
				Key:   "vendor",
				Value: "aws",
			},
			{
				Key:   "region",
				Value: "us-west-2",
			},
		}

		params, err := getAllRequestParameters(c)
		assert.NoError(t, err)
		assert.Equal(t, "aws", string(params.Vendor))
		assert.Equal(t, "us-west-2", params.Region)
	})

	t.Run("body param error", func(t *testing.T) {
		gin.SetMode(gin.TestMode)
		w := httptest.NewRecorder()
		c, _ := gin.CreateTestContext(w)
		c.Request = &http.Request{
			Method: http.MethodPost,
			URL: &url.URL{
				Scheme:   "http",
				Host:     "127.0.0.1:80",
				Path:     "/api/v1?a=b&c=d",
				RawQuery: "a=b&c=d",
			},
			Body: nil,
		}
		_, err := getAllRequestParameters(c)
		assert.Error(t, err)
	})

	t.Run("read body error", func(t *testing.T) {
		gin.SetMode(gin.TestMode)
		w := httptest.NewRecorder()
		c, _ := gin.CreateTestContext(w)
		c.Request = &http.Request{
			Method: http.MethodPost,
			URL: &url.URL{
				Scheme:   "http",
				Host:     "127.0.0.1:80",
				Path:     "/api/v1?a=b&c=d",
				RawQuery: "a=b&c=d",
			},
			Body: &mockReader{},
		}
		_, err := getAllRequestParameters(c)
		assert.Error(t, err)
	})

	t.Run("body param unmarshal error", func(t *testing.T) {
		gin.SetMode(gin.TestMode)
		w := httptest.NewRecorder()
		c, _ := gin.CreateTestContext(w)
		c.Request = &http.Request{
			Method: http.MethodPost,
			URL: &url.URL{
				Scheme:   "http",
				Host:     "127.0.0.1:80",
				Path:     "/api/v1?a=b&c=d",
				RawQuery: "a=b&c=d",
			},
			Body: mocks.NewBody("test"),
		}
		_, err := getAllRequestParameters(c)
		assert.Error(t, err)
	})
}
