/******************************************************************************
 * 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: auth_test.go
 * @Description:
 * @Author: jiangxunyu@pingcap.com
 * @Version: 1.0.0
 * @Date: 2022/6/29 16:59
*******************************************************************************/

package interceptor

import (
	"context"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/gin-gonic/gin"

	"github.com/auth0/go-jwt-middleware/v2/validator"
	"github.com/patrickmn/go-cache"

	"github.com/tidbcloud/observability/pkg/common/constants"

	"github.com/stretchr/testify/assert"
	"github.com/tidbcloud/observability/pkg/globalservice"
	metaservice "github.com/tidbcloud/observability/pkg/metamanager/etcd"
	testClient "k8s.io/client-go/kubernetes/fake"
)

func Test_InitAuthValidator(t *testing.T) {
	cli := testClient.NewSimpleClientset()
	err := metaservice.NewConfigMapMetaService(cli).CreateMeta(context.TODO(), constants.ObservabilityBaseServiceName, `
		{
			"auth0": {
				"issuerUrl": "https://test.us.auth0.com/"
			}
		}
		`)
	assert.NoError(t, err)
	err = globalservice.NewManager(context.TODO(), testClient.NewSimpleClientset())
	assert.NoError(t, err)

	t.Run("success", func(t *testing.T) {
		err = InitAuthValidator()
		assert.NoError(t, err)
		assert.NotNil(t, jwtValidator)
		assert.NotNil(t, tokenCache)
	})
	t.Run("issuer url is empty", func(t *testing.T) {
		cli := testClient.NewSimpleClientset()
		err := metaservice.NewConfigMapMetaService(cli).UpdateMeta(context.TODO(), constants.ObservabilityBaseServiceName, `
		{
			"auth0": {
				"issuerUrl": ""
			}
		}
		`)
		assert.NoError(t, err)

		err = globalservice.GetGlobalManager().LoadGlobalMetaCache(context.TODO())
		assert.NoError(t, err)

		err = InitAuthValidator()
		assert.Error(t, err)
	})
}

func Test_Auth(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{
			Header: map[string][]string{
				"Authorization": {"Bearer testToken"},
				"Content-Type":  {"application/json"},
			},
		}
		tokenCache = cache.New(constants.DefaultCacheTimeout, constants.DefaultCacheTimeout*2)

		Auth(c)
		assert.Equal(t, http.StatusUnauthorized, w.Code)
	})
}

func Test_verifyIdentity(t *testing.T) {
	t.Run("token is cached", func(t *testing.T) {
		tokenCache = cache.New(constants.DefaultCacheTimeout, constants.DefaultCacheTimeout*2)
		tokenCache.Set("testToken", time.Now().Unix()+10000, 100*time.Second)
		err := verifyIdentity("Bearer testToken")
		assert.NoError(t, err)
		tokenCache.Flush()
	})

	t.Run("token is empty", func(t *testing.T) {
		err := verifyIdentity("")
		assert.Error(t, err)
	})

	t.Run("token prefix not match", func(t *testing.T) {
		err := verifyIdentity("test testToken")
		assert.Error(t, err)
	})
}

func Test_validateJwtToken(t *testing.T) {
	t.Run("validate token error", func(t *testing.T) {
		keyFunc := func(context.Context) (interface{}, error) {
			return []byte("secret"), nil
		}
		var err error
		jwtValidator, err = validator.New(keyFunc, validator.RS256, "testIssuer", []string{"testAudience"})
		assert.NoError(t, err)

		err = validateJwtToken("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjZ6Zng2YUh6dlVhUTVaV3hZbVJxRiJ9.eyJpc3MiOiJodHRwczovL3RpZGItc29jMi51cy5hdXRoMC5jb20vIiwic3ViIjoiU2Q0YWVrUENhNW5NVVNRQkl2ZjhlR1NMODk1V3RWNG9AY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vdGlkYi1zb2MyLnVzLmF1dGgwLmNvbS9hcGkvdjIvIiwiaWF0IjoxNjU2NDg2NjEwLCJleHAiOjE2NTY1NzMwMTAsImF6cCI6IlNkNGFla1BDYTVuTVVTUUJJdmY4ZUdTTDg5NVd0VjRvIiwic2NvcGUiOiJyZWFkOmNsaWVudF9ncmFudHMiLCJndHkiOiJjbGllbnQtY3JlZGVudGlhbHMifQ.MOOe3lw6QdPtSg3di_cTdGUdNyysPJFzOmPmQt8HE42sQrczphHI-VcBWijxZYnS6ITgPMnDdUBUWmQgY3m8rO5kFPkaGCatO-xGabDKRaIbMM4GyTMJ3aEKj9gzQNt5FpYhif7O8KWyyPzRQ3Bs2RhzaH73qoUvXUo-Mkk3WxUFgiAI-AgrBkGgULMBcNW4WDlBBcYno71KnEm8-WwbloHLyH7HUy0qIiZ1jzRAG3vvfTZi87Q_Ne5ioSQKkq_UoBpJrLoXoMckJxO9Y9PtDGX1miS5MMzHlymra2lZqnv2CjUYXkqmcyR7g6g78WLswg2PPtuN3j6cYt88n90LBA")
		assert.Error(t, err)
	})
}

func Test_expireToken(t *testing.T) {
	t.Run("validate token error", func(t *testing.T) {
		tokenClaims := &validator.ValidatedClaims{
			RegisteredClaims: validator.RegisteredClaims{
				Issuer:   "testIssuer",
				Audience: []string{"testAudience"},
			},
		}

		tokenCache = cache.New(constants.DefaultCacheTimeout, constants.DefaultCacheTimeout*2)
		expireToken("test", tokenClaims)
		assert.Equal(t, 1, len(tokenCache.Items()))
		tokenCache.Flush()
	})
}
