package api_test

import (
	"encoding/json"
	"reflect"
	"strings"
	"testing"
	"time"

	"github.com/hashicorp/vault/api"
)

func TestParseSecret(t *testing.T) {
	t.Parallel()

	raw := strings.TrimSpace(`
{
	"lease_id": "foo",
	"renewable": true,
	"lease_duration": 10,
	"data": {
		"key": "value"
	},
	"warnings": [
		"a warning!"
	],
	"wrap_info": {
		"token": "token",
		"accessor": "accessor",
		"ttl": 60,
		"creation_time": "2016-06-07T15:52:10-04:00",
		"wrapped_accessor": "abcd1234"
	}
}`)

	rawTime, _ := time.Parse(time.RFC3339, "2016-06-07T15:52:10-04:00")

	secret, err := api.ParseSecret(strings.NewReader(raw))
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	expected := &api.Secret{
		LeaseID:       "foo",
		Renewable:     true,
		LeaseDuration: 10,
		Data: map[string]interface{}{
			"key": "value",
		},
		Warnings: []string{
			"a warning!",
		},
		WrapInfo: &api.SecretWrapInfo{
			Token:           "token",
			Accessor:        "accessor",
			TTL:             60,
			CreationTime:    rawTime,
			WrappedAccessor: "abcd1234",
		},
	}
	if !reflect.DeepEqual(secret, expected) {
		t.Fatalf("bad:\ngot\n%#v\nexpected\n%#v\n", secret, expected)
	}
}

func TestSecret_TokenID(t *testing.T) {
	t.Parallel()

	cases := []struct {
		name   string
		secret *api.Secret
		exp    string
		err    bool
	}{
		{
			"nil",
			nil,
			"",
			false,
		},
		{
			"nil_auth",
			&api.Secret{
				Auth: nil,
			},
			"",
			false,
		},
		{
			"empty_auth_client_token",
			&api.Secret{
				Auth: &api.SecretAuth{
					ClientToken: "",
				},
			},
			"",
			false,
		},
		{
			"real_auth_client_token",
			&api.Secret{
				Auth: &api.SecretAuth{
					ClientToken: "my-token",
				},
			},
			"my-token",
			false,
		},
		{
			"nil_data",
			&api.Secret{
				Data: nil,
			},
			"",
			false,
		},
		{
			"empty_data",
			&api.Secret{
				Data: map[string]interface{}{},
			},
			"",
			false,
		},
		{
			"data_not_string",
			&api.Secret{
				Data: map[string]interface{}{
					"id": 123,
				},
			},
			"",
			true,
		},
		{
			"data_string",
			&api.Secret{
				Data: map[string]interface{}{
					"id": "my-token",
				},
			},
			"my-token",
			false,
		},
	}

	for _, tc := range cases {
		tc := tc

		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			act, err := tc.secret.TokenID()
			if err != nil && !tc.err {
				t.Fatal(err)
			}
			if act != tc.exp {
				t.Errorf("expected %q to be %q", act, tc.exp)
			}
		})
	}

	t.Run("auth", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		if err := client.Sys().EnableAuth("userpass", "userpass", ""); err != nil {
			t.Fatal(err)
		}
		if _, err := client.Logical().Write("auth/userpass/users/test", map[string]interface{}{
			"password": "test",
			"policies": "default",
		}); err != nil {
			t.Fatal(err)
		}

		secret, err := client.Logical().Write("auth/userpass/login/test", map[string]interface{}{
			"password": "test",
		})
		if err != nil || secret == nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		tokenID, err := secret.TokenID()
		if err != nil {
			t.Fatal(err)
		}
		if tokenID != token {
			t.Errorf("expected %q to be %q", tokenID, token)
		}
	})

	t.Run("token create", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies: []string{"default"},
		})
		if err != nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		tokenID, err := secret.TokenID()
		if err != nil {
			t.Fatal(err)
		}
		if tokenID != token {
			t.Errorf("expected %q to be %q", tokenID, token)
		}
	})

	t.Run("token lookup", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies: []string{"default"},
		})
		if err != nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		secret, err = client.Auth().Token().Lookup(token)
		if err != nil {
			t.Fatal(err)
		}

		tokenID, err := secret.TokenID()
		if err != nil {
			t.Fatal(err)
		}
		if tokenID != token {
			t.Errorf("expected %q to be %q", tokenID, token)
		}
	})

	t.Run("token lookup-self", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies: []string{"default"},
		})
		if err != nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		client.SetToken(token)
		secret, err = client.Auth().Token().LookupSelf()
		if err != nil {
			t.Fatal(err)
		}

		tokenID, err := secret.TokenID()
		if err != nil {
			t.Fatal(err)
		}
		if tokenID != token {
			t.Errorf("expected %q to be %q", tokenID, token)
		}
	})

	t.Run("token renew", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies: []string{"default"},
		})
		if err != nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		secret, err = client.Auth().Token().Renew(token, 0)
		if err != nil {
			t.Fatal(err)
		}

		tokenID, err := secret.TokenID()
		if err != nil {
			t.Fatal(err)
		}
		if tokenID != token {
			t.Errorf("expected %q to be %q", tokenID, token)
		}
	})

	t.Run("token renew-self", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies: []string{"default"},
		})
		if err != nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		client.SetToken(token)
		secret, err = client.Auth().Token().RenewSelf(0)
		if err != nil {
			t.Fatal(err)
		}

		tokenID, err := secret.TokenID()
		if err != nil {
			t.Fatal(err)
		}
		if tokenID != token {
			t.Errorf("expected %q to be %q", tokenID, token)
		}
	})
}

func TestSecret_TokenAccessor(t *testing.T) {
	t.Parallel()

	cases := []struct {
		name   string
		secret *api.Secret
		exp    string
		err    bool
	}{
		{
			"nil",
			nil,
			"",
			false,
		},
		{
			"nil_auth",
			&api.Secret{
				Auth: nil,
			},
			"",
			false,
		},
		{
			"empty_auth_accessor",
			&api.Secret{
				Auth: &api.SecretAuth{
					Accessor: "",
				},
			},
			"",
			false,
		},
		{
			"real_auth_accessor",
			&api.Secret{
				Auth: &api.SecretAuth{
					Accessor: "my-accessor",
				},
			},
			"my-accessor",
			false,
		},
		{
			"nil_data",
			&api.Secret{
				Data: nil,
			},
			"",
			false,
		},
		{
			"empty_data",
			&api.Secret{
				Data: map[string]interface{}{},
			},
			"",
			false,
		},
		{
			"data_not_string",
			&api.Secret{
				Data: map[string]interface{}{
					"accessor": 123,
				},
			},
			"",
			true,
		},
		{
			"data_string",
			&api.Secret{
				Data: map[string]interface{}{
					"accessor": "my-accessor",
				},
			},
			"my-accessor",
			false,
		},
	}

	for _, tc := range cases {
		tc := tc

		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			act, err := tc.secret.TokenAccessor()
			if err != nil && !tc.err {
				t.Fatal(err)
			}
			if act != tc.exp {
				t.Errorf("expected %q to be %q", act, tc.exp)
			}
		})
	}

	t.Run("auth", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		if err := client.Sys().EnableAuth("userpass", "userpass", ""); err != nil {
			t.Fatal(err)
		}
		if _, err := client.Logical().Write("auth/userpass/users/test", map[string]interface{}{
			"password": "test",
			"policies": "default",
		}); err != nil {
			t.Fatal(err)
		}

		secret, err := client.Logical().Write("auth/userpass/login/test", map[string]interface{}{
			"password": "test",
		})
		if err != nil || secret == nil {
			t.Fatal(err)
		}
		_, accessor := secret.Auth.ClientToken, secret.Auth.Accessor

		newAccessor, err := secret.TokenAccessor()
		if err != nil {
			t.Fatal(err)
		}
		if newAccessor != accessor {
			t.Errorf("expected %q to be %q", newAccessor, accessor)
		}
	})

	t.Run("token create", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies: []string{"default"},
		})
		if err != nil {
			t.Fatal(err)
		}
		_, accessor := secret.Auth.ClientToken, secret.Auth.Accessor

		newAccessor, err := secret.TokenAccessor()
		if err != nil {
			t.Fatal(err)
		}
		if newAccessor != accessor {
			t.Errorf("expected %q to be %q", newAccessor, accessor)
		}
	})

	t.Run("token lookup", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies: []string{"default"},
		})
		if err != nil {
			t.Fatal(err)
		}
		token, accessor := secret.Auth.ClientToken, secret.Auth.Accessor

		secret, err = client.Auth().Token().Lookup(token)
		if err != nil {
			t.Fatal(err)
		}

		newAccessor, err := secret.TokenAccessor()
		if err != nil {
			t.Fatal(err)
		}
		if newAccessor != accessor {
			t.Errorf("expected %q to be %q", newAccessor, accessor)
		}
	})

	t.Run("token lookup-self", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies: []string{"default"},
		})
		if err != nil {
			t.Fatal(err)
		}
		token, accessor := secret.Auth.ClientToken, secret.Auth.Accessor

		client.SetToken(token)
		secret, err = client.Auth().Token().LookupSelf()
		if err != nil {
			t.Fatal(err)
		}

		newAccessor, err := secret.TokenAccessor()
		if err != nil {
			t.Fatal(err)
		}
		if newAccessor != accessor {
			t.Errorf("expected %q to be %q", newAccessor, accessor)
		}
	})

	t.Run("token renew", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies: []string{"default"},
		})
		if err != nil {
			t.Fatal(err)
		}
		token, accessor := secret.Auth.ClientToken, secret.Auth.Accessor

		secret, err = client.Auth().Token().Renew(token, 0)
		if err != nil {
			t.Fatal(err)
		}

		newAccessor, err := secret.TokenAccessor()
		if err != nil {
			t.Fatal(err)
		}
		if newAccessor != accessor {
			t.Errorf("expected %q to be %q", newAccessor, accessor)
		}
	})

	t.Run("token renew-self", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies: []string{"default"},
		})
		if err != nil {
			t.Fatal(err)
		}
		token, accessor := secret.Auth.ClientToken, secret.Auth.Accessor

		client.SetToken(token)
		secret, err = client.Auth().Token().RenewSelf(0)
		if err != nil {
			t.Fatal(err)
		}

		newAccessor, err := secret.TokenAccessor()
		if err != nil {
			t.Fatal(err)
		}
		if newAccessor != accessor {
			t.Errorf("expected %q to be %q", newAccessor, accessor)
		}
	})
}

func TestSecret_TokenRemainingUses(t *testing.T) {
	t.Parallel()

	cases := []struct {
		name   string
		secret *api.Secret
		exp    int
	}{
		{
			"nil",
			nil,
			-1,
		},
		{
			"nil_data",
			&api.Secret{
				Data: nil,
			},
			-1,
		},
		{
			"empty_data",
			&api.Secret{
				Data: map[string]interface{}{},
			},
			-1,
		},
		{
			"data_not_json_number",
			&api.Secret{
				Data: map[string]interface{}{
					"num_uses": 123,
				},
			},
			123,
		},
		{
			"data_json_number",
			&api.Secret{
				Data: map[string]interface{}{
					"num_uses": json.Number("123"),
				},
			},
			123,
		},
	}

	for _, tc := range cases {
		tc := tc

		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			act, err := tc.secret.TokenRemainingUses()
			if tc.exp != -1 && err != nil {
				t.Fatal(err)
			}
			if act != tc.exp {
				t.Errorf("expected %d to be %d", act, tc.exp)
			}
		})
	}

	t.Run("auth", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		uses := 5

		if err := client.Sys().EnableAuth("userpass", "userpass", ""); err != nil {
			t.Fatal(err)
		}
		if _, err := client.Logical().Write("auth/userpass/users/test", map[string]interface{}{
			"password": "test",
			"policies": "default",
			"num_uses": uses,
		}); err != nil {
			t.Fatal(err)
		}

		secret, err := client.Logical().Write("auth/userpass/login/test", map[string]interface{}{
			"password": "test",
		})
		if err != nil || secret == nil {
			t.Fatal(err)
		}

		// Remaining uses is not returned from this API
		uses = -1
		remaining, err := secret.TokenRemainingUses()
		if err != nil {
			t.Fatal(err)
		}
		if remaining != uses {
			t.Errorf("expected %d to be %d", remaining, uses)
		}
	})

	t.Run("token create", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		uses := 5

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies: []string{"default"},
			NumUses:  uses,
		})
		if err != nil {
			t.Fatal(err)
		}

		// /auth/token/create does not return the number of uses
		uses = -1
		remaining, err := secret.TokenRemainingUses()
		if err != nil {
			t.Fatal(err)
		}
		if remaining != uses {
			t.Errorf("expected %d to be %d", remaining, uses)
		}
	})

	t.Run("token lookup", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		uses := 5

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies: []string{"default"},
			NumUses:  uses,
		})
		if err != nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		secret, err = client.Auth().Token().Lookup(token)
		if err != nil {
			t.Fatal(err)
		}

		remaining, err := secret.TokenRemainingUses()
		if err != nil {
			t.Fatal(err)
		}
		if remaining != uses {
			t.Errorf("expected %d to be %d", remaining, uses)
		}
	})

	t.Run("token lookup-self", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		uses := 5

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies: []string{"default"},
			NumUses:  uses,
		})
		if err != nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		client.SetToken(token)
		secret, err = client.Auth().Token().LookupSelf()
		if err != nil {
			t.Fatal(err)
		}

		uses = uses - 1 // we just used it
		remaining, err := secret.TokenRemainingUses()
		if err != nil {
			t.Fatal(err)
		}
		if remaining != uses {
			t.Errorf("expected %d to be %d", remaining, uses)
		}
	})

	t.Run("token renew", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		uses := 5

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies: []string{"default"},
			NumUses:  uses,
		})
		if err != nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		secret, err = client.Auth().Token().Renew(token, 0)
		if err != nil {
			t.Fatal(err)
		}

		// /auth/token/renew does not return the number of uses
		uses = -1
		remaining, err := secret.TokenRemainingUses()
		if err != nil {
			t.Fatal(err)
		}
		if remaining != uses {
			t.Errorf("expected %d to be %d", remaining, uses)
		}
	})

	t.Run("token renew-self", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		uses := 5

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies: []string{"default"},
			NumUses:  uses,
		})
		if err != nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		client.SetToken(token)
		secret, err = client.Auth().Token().RenewSelf(0)
		if err != nil {
			t.Fatal(err)
		}

		// /auth/token/renew-self does not return the number of uses
		uses = -1
		remaining, err := secret.TokenRemainingUses()
		if err != nil {
			t.Fatal(err)
		}
		if remaining != uses {
			t.Errorf("expected %d to be %d", remaining, uses)
		}
	})
}

func TestSecret_TokenPolicies(t *testing.T) {
	t.Parallel()

	cases := []struct {
		name   string
		secret *api.Secret
		exp    []string
		err    bool
	}{
		{
			"nil",
			nil,
			nil,
			false,
		},
		{
			"nil_auth",
			&api.Secret{
				Auth: nil,
			},
			nil,
			false,
		},
		{
			"nil_auth_policies",
			&api.Secret{
				Auth: &api.SecretAuth{
					Policies: nil,
				},
			},
			nil,
			false,
		},
		{
			"empty_auth_policies",
			&api.Secret{
				Auth: &api.SecretAuth{
					Policies: []string{},
				},
			},
			nil,
			false,
		},
		{
			"real_auth_policies",
			&api.Secret{
				Auth: &api.SecretAuth{
					Policies: []string{"foo"},
				},
			},
			[]string{"foo"},
			false,
		},
		{
			"nil_data",
			&api.Secret{
				Data: nil,
			},
			nil,
			false,
		},
		{
			"empty_data",
			&api.Secret{
				Data: map[string]interface{}{},
			},
			nil,
			false,
		},
		{
			"data_not_slice",
			&api.Secret{
				Data: map[string]interface{}{
					"policies": 123,
				},
			},
			nil,
			true,
		},
		{
			"data_slice",
			&api.Secret{
				Data: map[string]interface{}{
					"policies": []interface{}{"foo"},
				},
			},
			[]string{"foo"},
			false,
		},
	}

	for _, tc := range cases {
		tc := tc

		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			act, err := tc.secret.TokenPolicies()
			if err != nil && !tc.err {
				t.Fatal(err)
			}
			if !reflect.DeepEqual(act, tc.exp) {
				t.Errorf("expected %#v to be %#v", act, tc.exp)
			}
		})
	}

	t.Run("auth", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		policies := []string{"bar", "default", "foo"}

		if err := client.Sys().EnableAuth("userpass", "userpass", ""); err != nil {
			t.Fatal(err)
		}
		if _, err := client.Logical().Write("auth/userpass/users/test", map[string]interface{}{
			"password": "test",
			"policies": strings.Join(policies, ","),
		}); err != nil {
			t.Fatal(err)
		}

		secret, err := client.Logical().Write("auth/userpass/login/test", map[string]interface{}{
			"password": "test",
		})
		if err != nil || secret == nil {
			t.Fatal(err)
		}

		tPol, err := secret.TokenPolicies()
		if err != nil {
			t.Fatal(err)
		}
		if !reflect.DeepEqual(tPol, policies) {
			t.Errorf("expected %#v to be %#v", tPol, policies)
		}
	})

	t.Run("token create", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		policies := []string{"bar", "default", "foo"}

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies: policies,
		})
		if err != nil {
			t.Fatal(err)
		}

		tPol, err := secret.TokenPolicies()
		if err != nil {
			t.Fatal(err)
		}
		if !reflect.DeepEqual(tPol, policies) {
			t.Errorf("expected %#v to be %#v", tPol, policies)
		}
	})

	t.Run("token lookup", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		policies := []string{"bar", "default", "foo"}

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies: policies,
		})
		if err != nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		secret, err = client.Auth().Token().Lookup(token)
		if err != nil {
			t.Fatal(err)
		}

		tPol, err := secret.TokenPolicies()
		if err != nil {
			t.Fatal(err)
		}
		if !reflect.DeepEqual(tPol, policies) {
			t.Errorf("expected %#v to be %#v", tPol, policies)
		}
	})

	t.Run("token lookup-self", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		policies := []string{"bar", "default", "foo"}

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies: policies,
		})
		if err != nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		client.SetToken(token)
		secret, err = client.Auth().Token().LookupSelf()
		if err != nil {
			t.Fatal(err)
		}

		tPol, err := secret.TokenPolicies()
		if err != nil {
			t.Fatal(err)
		}
		if !reflect.DeepEqual(tPol, policies) {
			t.Errorf("expected %#v to be %#v", tPol, policies)
		}
	})

	t.Run("token renew", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		policies := []string{"bar", "default", "foo"}

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies: policies,
		})
		if err != nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		secret, err = client.Auth().Token().Renew(token, 0)
		if err != nil {
			t.Fatal(err)
		}

		tPol, err := secret.TokenPolicies()
		if err != nil {
			t.Fatal(err)
		}
		if !reflect.DeepEqual(tPol, policies) {
			t.Errorf("expected %#v to be %#v", tPol, policies)
		}
	})

	t.Run("token renew-self", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		policies := []string{"bar", "default", "foo"}

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies: policies,
		})
		if err != nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		client.SetToken(token)
		secret, err = client.Auth().Token().RenewSelf(0)
		if err != nil {
			t.Fatal(err)
		}

		tPol, err := secret.TokenPolicies()
		if err != nil {
			t.Fatal(err)
		}
		if !reflect.DeepEqual(tPol, policies) {
			t.Errorf("expected %#v to be %#v", tPol, policies)
		}
	})
}

func TestSecret_TokenMetadata(t *testing.T) {
	t.Parallel()

	cases := []struct {
		name   string
		secret *api.Secret
		exp    map[string]string
		err    bool
	}{
		{
			"nil",
			nil,
			nil,
			false,
		},
		{
			"nil_auth",
			&api.Secret{
				Auth: nil,
			},
			nil,
			false,
		},
		{
			"nil_auth_metadata",
			&api.Secret{
				Auth: &api.SecretAuth{
					Metadata: nil,
				},
			},
			nil,
			false,
		},
		{
			"empty_auth_metadata",
			&api.Secret{
				Auth: &api.SecretAuth{
					Metadata: map[string]string{},
				},
			},
			nil,
			false,
		},
		{
			"real_auth_metadata",
			&api.Secret{
				Auth: &api.SecretAuth{
					Metadata: map[string]string{"foo": "bar"},
				},
			},
			map[string]string{"foo": "bar"},
			false,
		},
		{
			"nil_data",
			&api.Secret{
				Data: nil,
			},
			nil,
			false,
		},
		{
			"empty_data",
			&api.Secret{
				Data: map[string]interface{}{},
			},
			nil,
			false,
		},
		{
			"data_not_map",
			&api.Secret{
				Data: map[string]interface{}{
					"metadata": 123,
				},
			},
			nil,
			true,
		},
		{
			"data_map",
			&api.Secret{
				Data: map[string]interface{}{
					"metadata": map[string]interface{}{"foo": "bar"},
				},
			},
			map[string]string{"foo": "bar"},
			false,
		},
		{
			"data_map_bad_type",
			&api.Secret{
				Data: map[string]interface{}{
					"metadata": map[string]interface{}{"foo": 123},
				},
			},
			nil,
			true,
		},
	}

	for _, tc := range cases {
		tc := tc

		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			act, err := tc.secret.TokenMetadata()
			if err != nil && !tc.err {
				t.Fatal(err)
			}
			if !reflect.DeepEqual(act, tc.exp) {
				t.Errorf("expected %#v to be %#v", act, tc.exp)
			}
		})
	}

	t.Run("auth", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		metadata := map[string]string{"username": "test"}

		if err := client.Sys().EnableAuth("userpass", "userpass", ""); err != nil {
			t.Fatal(err)
		}
		if _, err := client.Logical().Write("auth/userpass/users/test", map[string]interface{}{
			"password": "test",
			"policies": "default",
		}); err != nil {
			t.Fatal(err)
		}

		secret, err := client.Logical().Write("auth/userpass/login/test", map[string]interface{}{
			"password": "test",
		})
		if err != nil || secret == nil {
			t.Fatal(err)
		}

		tMeta, err := secret.TokenMetadata()
		if err != nil {
			t.Fatal(err)
		}
		if !reflect.DeepEqual(tMeta, metadata) {
			t.Errorf("expected %#v to be %#v", tMeta, metadata)
		}
	})

	t.Run("token create", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		metadata := map[string]string{"username": "test"}

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Metadata: metadata,
			Policies: []string{"default"},
		})
		if err != nil {
			t.Fatal(err)
		}

		tMeta, err := secret.TokenMetadata()
		if err != nil {
			t.Fatal(err)
		}
		if !reflect.DeepEqual(tMeta, metadata) {
			t.Errorf("expected %#v to be %#v", tMeta, metadata)
		}
	})

	t.Run("token lookup", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		metadata := map[string]string{"username": "test"}

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Metadata: metadata,
			Policies: []string{"default"},
		})
		if err != nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		secret, err = client.Auth().Token().Lookup(token)
		if err != nil {
			t.Fatal(err)
		}

		tMeta, err := secret.TokenMetadata()
		if err != nil {
			t.Fatal(err)
		}
		if !reflect.DeepEqual(tMeta, metadata) {
			t.Errorf("expected %#v to be %#v", tMeta, metadata)
		}
	})

	t.Run("token lookup-self", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		metadata := map[string]string{"username": "test"}

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Metadata: metadata,
			Policies: []string{"default"},
		})
		if err != nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		client.SetToken(token)
		secret, err = client.Auth().Token().LookupSelf()
		if err != nil {
			t.Fatal(err)
		}

		tMeta, err := secret.TokenMetadata()
		if err != nil {
			t.Fatal(err)
		}
		if !reflect.DeepEqual(tMeta, metadata) {
			t.Errorf("expected %#v to be %#v", tMeta, metadata)
		}
	})

	t.Run("token renew", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		metadata := map[string]string{"username": "test"}

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Metadata: metadata,
			Policies: []string{"default"},
		})
		if err != nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		secret, err = client.Auth().Token().Renew(token, 0)
		if err != nil {
			t.Fatal(err)
		}

		tMeta, err := secret.TokenMetadata()
		if err != nil {
			t.Fatal(err)
		}
		if !reflect.DeepEqual(tMeta, metadata) {
			t.Errorf("expected %#v to be %#v", tMeta, metadata)
		}
	})

	t.Run("token renew-self", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		metadata := map[string]string{"username": "test"}

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Metadata: metadata,
			Policies: []string{"default"},
		})
		if err != nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		client.SetToken(token)
		secret, err = client.Auth().Token().RenewSelf(0)
		if err != nil {
			t.Fatal(err)
		}

		tMeta, err := secret.TokenMetadata()
		if err != nil {
			t.Fatal(err)
		}
		if !reflect.DeepEqual(tMeta, metadata) {
			t.Errorf("expected %#v to be %#v", tMeta, metadata)
		}
	})
}

func TestSecret_TokenIsRenewable(t *testing.T) {
	t.Parallel()

	cases := []struct {
		name   string
		secret *api.Secret
		exp    bool
	}{
		{
			"nil",
			nil,
			false,
		},
		{
			"nil_auth",
			&api.Secret{
				Auth: nil,
			},
			false,
		},
		{
			"auth_renewable_false",
			&api.Secret{
				Auth: &api.SecretAuth{
					Renewable: false,
				},
			},
			false,
		},
		{
			"auth_renewable_true",
			&api.Secret{
				Auth: &api.SecretAuth{
					Renewable: true,
				},
			},
			true,
		},
		{
			"nil_data",
			&api.Secret{
				Data: nil,
			},
			false,
		},
		{
			"empty_data",
			&api.Secret{
				Data: map[string]interface{}{},
			},
			false,
		},
		{
			"data_not_bool",
			&api.Secret{
				Data: map[string]interface{}{
					"renewable": 123,
				},
			},
			true,
		},
		{
			"data_bool_string",
			&api.Secret{
				Data: map[string]interface{}{
					"renewable": "true",
				},
			},
			true,
		},
		{
			"data_bool_true",
			&api.Secret{
				Data: map[string]interface{}{
					"renewable": true,
				},
			},
			true,
		},
		{
			"data_bool_false",
			&api.Secret{
				Data: map[string]interface{}{
					"renewable": true,
				},
			},
			true,
		},
	}

	for _, tc := range cases {
		tc := tc

		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			act, err := tc.secret.TokenIsRenewable()
			if err != nil {
				t.Fatal(err)
			}
			if act != tc.exp {
				t.Errorf("expected %t to be %t", act, tc.exp)
			}
		})
	}

	t.Run("auth", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		renewable := true

		if err := client.Sys().EnableAuth("userpass", "userpass", ""); err != nil {
			t.Fatal(err)
		}
		if _, err := client.Logical().Write("auth/userpass/users/test", map[string]interface{}{
			"password": "test",
			"policies": "default",
		}); err != nil {
			t.Fatal(err)
		}

		secret, err := client.Logical().Write("auth/userpass/login/test", map[string]interface{}{
			"password": "test",
		})
		if err != nil || secret == nil {
			t.Fatal(err)
		}

		tRenew, err := secret.TokenIsRenewable()
		if err != nil {
			t.Fatal(err)
		}
		if tRenew != renewable {
			t.Errorf("expected %t to be %t", tRenew, renewable)
		}
	})

	t.Run("token create", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		renewable := true

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies:  []string{"default"},
			Renewable: &renewable,
		})
		if err != nil {
			t.Fatal(err)
		}

		tRenew, err := secret.TokenIsRenewable()
		if err != nil {
			t.Fatal(err)
		}
		if tRenew != renewable {
			t.Errorf("expected %t to be %t", tRenew, renewable)
		}
	})

	t.Run("token lookup", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		renewable := true

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies:  []string{"default"},
			Renewable: &renewable,
		})
		if err != nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		secret, err = client.Auth().Token().Lookup(token)
		if err != nil {
			t.Fatal(err)
		}

		tRenew, err := secret.TokenIsRenewable()
		if err != nil {
			t.Fatal(err)
		}
		if tRenew != renewable {
			t.Errorf("expected %t to be %t", tRenew, renewable)
		}
	})

	t.Run("token lookup-self", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		renewable := true

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies:  []string{"default"},
			Renewable: &renewable,
		})
		if err != nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		client.SetToken(token)
		secret, err = client.Auth().Token().LookupSelf()
		if err != nil {
			t.Fatal(err)
		}

		tRenew, err := secret.TokenIsRenewable()
		if err != nil {
			t.Fatal(err)
		}
		if tRenew != renewable {
			t.Errorf("expected %t to be %t", tRenew, renewable)
		}
	})

	t.Run("token renew", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		renewable := true

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies:  []string{"default"},
			Renewable: &renewable,
		})
		if err != nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		secret, err = client.Auth().Token().Renew(token, 0)
		if err != nil {
			t.Fatal(err)
		}

		tRenew, err := secret.TokenIsRenewable()
		if err != nil {
			t.Fatal(err)
		}
		if tRenew != renewable {
			t.Errorf("expected %t to be %t", tRenew, renewable)
		}
	})

	t.Run("token renew-self", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		renewable := true

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies:  []string{"default"},
			Renewable: &renewable,
		})
		if err != nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		client.SetToken(token)
		secret, err = client.Auth().Token().RenewSelf(0)
		if err != nil {
			t.Fatal(err)
		}

		tRenew, err := secret.TokenIsRenewable()
		if err != nil {
			t.Fatal(err)
		}
		if tRenew != renewable {
			t.Errorf("expected %t to be %t", tRenew, renewable)
		}
	})
}

func TestSecret_TokenTTL(t *testing.T) {
	t.Parallel()

	cases := []struct {
		name   string
		secret *api.Secret
		exp    time.Duration
	}{
		{
			"nil",
			nil,
			0,
		},
		{
			"nil_auth",
			&api.Secret{
				Auth: nil,
			},
			0,
		},
		{
			"nil_auth_lease_duration",
			&api.Secret{
				Auth: &api.SecretAuth{
					LeaseDuration: 0,
				},
			},
			0,
		},
		{
			"real_auth_lease_duration",
			&api.Secret{
				Auth: &api.SecretAuth{
					LeaseDuration: 3600,
				},
			},
			1 * time.Hour,
		},
		{
			"nil_data",
			&api.Secret{
				Data: nil,
			},
			0,
		},
		{
			"empty_data",
			&api.Secret{
				Data: map[string]interface{}{},
			},
			0,
		},
		{
			"data_not_json_number",
			&api.Secret{
				Data: map[string]interface{}{
					"ttl": 123,
				},
			},
			123 * time.Second,
		},
		{
			"data_json_number",
			&api.Secret{
				Data: map[string]interface{}{
					"ttl": json.Number("3600"),
				},
			},
			1 * time.Hour,
		},
	}

	for _, tc := range cases {
		tc := tc

		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			act, err := tc.secret.TokenTTL()
			if err != nil {
				t.Fatal(err)
			}
			if act != tc.exp {
				t.Errorf("expected %q to be %q", act, tc.exp)
			}
		})
	}

	t.Run("auth", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		ttl := 30 * time.Minute

		if err := client.Sys().EnableAuth("userpass", "userpass", ""); err != nil {
			t.Fatal(err)
		}
		if _, err := client.Logical().Write("auth/userpass/users/test", map[string]interface{}{
			"password":         "test",
			"policies":         "default",
			"ttl":              ttl.String(),
			"explicit_max_ttl": ttl.String(),
		}); err != nil {
			t.Fatal(err)
		}

		secret, err := client.Logical().Write("auth/userpass/login/test", map[string]interface{}{
			"password": "test",
		})
		if err != nil || secret == nil {
			t.Fatal(err)
		}

		tokenTTL, err := secret.TokenTTL()
		if err != nil {
			t.Fatal(err)
		}
		if tokenTTL == 0 || tokenTTL > ttl {
			t.Errorf("expected %q to non-zero and less than %q", tokenTTL, ttl)
		}
	})

	t.Run("token create", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		ttl := 30 * time.Minute

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies:       []string{"default"},
			TTL:            ttl.String(),
			ExplicitMaxTTL: ttl.String(),
		})
		if err != nil {
			t.Fatal(err)
		}

		tokenTTL, err := secret.TokenTTL()
		if err != nil {
			t.Fatal(err)
		}
		if tokenTTL == 0 || tokenTTL > ttl {
			t.Errorf("expected %q to non-zero and less than %q", tokenTTL, ttl)
		}
	})

	t.Run("token lookup", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		ttl := 30 * time.Minute

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies:       []string{"default"},
			TTL:            ttl.String(),
			ExplicitMaxTTL: ttl.String(),
		})
		if err != nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		secret, err = client.Auth().Token().Lookup(token)
		if err != nil {
			t.Fatal(err)
		}

		tokenTTL, err := secret.TokenTTL()
		if err != nil {
			t.Fatal(err)
		}
		if tokenTTL == 0 || tokenTTL > ttl {
			t.Errorf("expected %q to non-zero and less than %q", tokenTTL, ttl)
		}
	})

	t.Run("token lookup-self", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		ttl := 30 * time.Minute

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies:       []string{"default"},
			TTL:            ttl.String(),
			ExplicitMaxTTL: ttl.String(),
		})
		if err != nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		client.SetToken(token)
		secret, err = client.Auth().Token().LookupSelf()
		if err != nil {
			t.Fatal(err)
		}

		tokenTTL, err := secret.TokenTTL()
		if err != nil {
			t.Fatal(err)
		}
		if tokenTTL == 0 || tokenTTL > ttl {
			t.Errorf("expected %q to non-zero and less than %q", tokenTTL, ttl)
		}
	})

	t.Run("token renew", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		ttl := 30 * time.Minute

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies:       []string{"default"},
			TTL:            ttl.String(),
			ExplicitMaxTTL: ttl.String(),
		})
		if err != nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		secret, err = client.Auth().Token().Renew(token, 0)
		if err != nil {
			t.Fatal(err)
		}

		tokenTTL, err := secret.TokenTTL()
		if err != nil {
			t.Fatal(err)
		}
		if tokenTTL == 0 || tokenTTL > ttl {
			t.Errorf("expected %q to non-zero and less than %q", tokenTTL, ttl)
		}
	})

	t.Run("token renew-self", func(t *testing.T) {
		t.Parallel()

		client, closer := testVaultServer(t)
		defer closer()

		ttl := 30 * time.Minute

		secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
			Policies:       []string{"default"},
			TTL:            ttl.String(),
			ExplicitMaxTTL: ttl.String(),
		})
		if err != nil {
			t.Fatal(err)
		}
		token := secret.Auth.ClientToken

		client.SetToken(token)
		secret, err = client.Auth().Token().RenewSelf(0)
		if err != nil {
			t.Fatal(err)
		}

		tokenTTL, err := secret.TokenTTL()
		if err != nil {
			t.Fatal(err)
		}
		if tokenTTL == 0 || tokenTTL > ttl {
			t.Errorf("expected %q to non-zero and less than %q", tokenTTL, ttl)
		}
	})
}
