// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package nomad

import (
	"fmt"
	"testing"
	"time"

	msgpackrpc "github.com/hashicorp/net-rpc-msgpackrpc"
	"github.com/hashicorp/nomad/acl"
	"github.com/hashicorp/nomad/ci"
	"github.com/hashicorp/nomad/helper/uuid"
	"github.com/hashicorp/nomad/nomad/mock"
	"github.com/hashicorp/nomad/nomad/structs"
	"github.com/hashicorp/nomad/testutil"
	"github.com/stretchr/testify/assert"
)

func TestNamespaceEndpoint_GetNamespace(t *testing.T) {
	ci.Parallel(t)
	assert := assert.New(t)
	s1, cleanupS1 := TestServer(t, nil)
	defer cleanupS1()
	codec := rpcClient(t, s1)
	testutil.WaitForLeader(t, s1.RPC)

	// Create the register request
	ns := mock.Namespace()
	s1.fsm.State().UpsertNamespaces(1000, []*structs.Namespace{ns})

	// Lookup the namespace
	get := &structs.NamespaceSpecificRequest{
		Name:         ns.Name,
		QueryOptions: structs.QueryOptions{Region: "global"},
	}
	var resp structs.SingleNamespaceResponse
	assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespace", get, &resp))
	assert.EqualValues(1000, resp.Index)
	assert.Equal(ns, resp.Namespace)

	// Lookup non-existing namespace
	get.Name = uuid.Generate()
	assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespace", get, &resp))
	assert.EqualValues(1000, resp.Index)
	assert.Nil(resp.Namespace)
}

func TestNamespaceEndpoint_GetNamespace_ACL(t *testing.T) {
	ci.Parallel(t)
	assert := assert.New(t)
	s1, root, cleanupS1 := TestACLServer(t, nil)
	defer cleanupS1()
	codec := rpcClient(t, s1)
	testutil.WaitForLeader(t, s1.RPC)

	// Create the register request
	ns1 := mock.Namespace()
	ns2 := mock.Namespace()
	state := s1.fsm.State()
	s1.fsm.State().UpsertNamespaces(1000, []*structs.Namespace{ns1, ns2})

	// Create the policy and tokens
	validToken := mock.CreatePolicyAndToken(t, state, 1002, "test-valid",
		mock.NamespacePolicy(ns1.Name, "", []string{acl.NamespaceCapabilityReadJob}))
	invalidToken := mock.CreatePolicyAndToken(t, state, 1003, "test-invalid",
		mock.NamespacePolicy(ns2.Name, "", []string{acl.NamespaceCapabilityReadJob}))

	get := &structs.NamespaceSpecificRequest{
		Name:         ns1.Name,
		QueryOptions: structs.QueryOptions{Region: "global"},
	}

	// Lookup the namespace without a token and expect failure
	{
		var resp structs.SingleNamespaceResponse
		err := msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespace", get, &resp)
		assert.NotNil(err)
		assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
	}

	// Try with an invalid token
	get.AuthToken = invalidToken.SecretID
	{
		var resp structs.SingleNamespaceResponse
		err := msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespace", get, &resp)
		assert.NotNil(err)
		assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
	}

	// Try with a valid token
	get.AuthToken = validToken.SecretID
	{
		var resp structs.SingleNamespaceResponse
		assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespace", get, &resp))
		assert.EqualValues(1000, resp.Index)
		assert.Equal(ns1, resp.Namespace)
	}

	// Try with a root token
	get.AuthToken = root.SecretID
	{
		var resp structs.SingleNamespaceResponse
		assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespace", get, &resp))
		assert.EqualValues(1000, resp.Index)
		assert.Equal(ns1, resp.Namespace)
	}
}

func TestNamespaceEndpoint_GetNamespace_Blocking(t *testing.T) {
	ci.Parallel(t)
	assert := assert.New(t)
	s1, cleanupS1 := TestServer(t, nil)
	defer cleanupS1()
	state := s1.fsm.State()
	codec := rpcClient(t, s1)
	testutil.WaitForLeader(t, s1.RPC)

	// Create the namespaces
	ns1 := mock.Namespace()
	ns2 := mock.Namespace()

	// First create an namespace
	time.AfterFunc(100*time.Millisecond, func() {
		assert.Nil(state.UpsertNamespaces(100, []*structs.Namespace{ns1}))
	})

	// Upsert the namespace we are watching later
	time.AfterFunc(200*time.Millisecond, func() {
		assert.Nil(state.UpsertNamespaces(200, []*structs.Namespace{ns2}))
	})

	// Lookup the namespace
	req := &structs.NamespaceSpecificRequest{
		Name: ns2.Name,
		QueryOptions: structs.QueryOptions{
			Region:        "global",
			MinQueryIndex: 150,
		},
	}
	var resp structs.SingleNamespaceResponse
	start := time.Now()
	assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespace", req, &resp))
	assert.EqualValues(200, resp.Index)
	assert.NotNil(resp.Namespace)
	assert.Equal(ns2.Name, resp.Namespace.Name)

	if elapsed := time.Since(start); elapsed < 200*time.Millisecond {
		t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
	}

	// Namespace delete triggers watches
	time.AfterFunc(100*time.Millisecond, func() {
		assert.Nil(state.DeleteNamespaces(300, []string{ns2.Name}))
	})

	req.QueryOptions.MinQueryIndex = 250
	var resp2 structs.SingleNamespaceResponse
	start = time.Now()
	assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespace", req, &resp2))
	assert.EqualValues(300, resp2.Index)
	assert.Nil(resp2.Namespace)

	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
		t.Fatalf("should block (returned in %s) %#v", elapsed, resp2)
	}
}

func TestNamespaceEndpoint_GetNamespaces(t *testing.T) {
	ci.Parallel(t)
	assert := assert.New(t)
	s1, cleanupS1 := TestServer(t, nil)
	defer cleanupS1()
	codec := rpcClient(t, s1)
	testutil.WaitForLeader(t, s1.RPC)

	// Create the register request
	ns1 := mock.Namespace()
	ns2 := mock.Namespace()
	s1.fsm.State().UpsertNamespaces(1000, []*structs.Namespace{ns1, ns2})

	// Lookup the namespace
	get := &structs.NamespaceSetRequest{
		Namespaces:   []string{ns1.Name, ns2.Name},
		QueryOptions: structs.QueryOptions{Region: "global"},
	}
	var resp structs.NamespaceSetResponse
	assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespaces", get, &resp))
	assert.EqualValues(1000, resp.Index)
	assert.Len(resp.Namespaces, 2)
	assert.Contains(resp.Namespaces, ns1.Name)
	assert.Contains(resp.Namespaces, ns2.Name)
}

func TestNamespaceEndpoint_GetNamespaces_ACL(t *testing.T) {
	ci.Parallel(t)
	assert := assert.New(t)
	s1, root, cleanupS1 := TestACLServer(t, nil)
	defer cleanupS1()
	codec := rpcClient(t, s1)
	testutil.WaitForLeader(t, s1.RPC)

	// Create the register request
	ns1 := mock.Namespace()
	ns2 := mock.Namespace()
	state := s1.fsm.State()
	state.UpsertNamespaces(1000, []*structs.Namespace{ns1, ns2})

	// Create the policy and tokens
	validToken := mock.CreatePolicyAndToken(t, state, 1002, "test-valid",
		mock.NamespacePolicy(ns1.Name, "", []string{acl.NamespaceCapabilityReadJob}))

	// Lookup the namespace
	get := &structs.NamespaceSetRequest{
		Namespaces:   []string{ns1.Name, ns2.Name},
		QueryOptions: structs.QueryOptions{Region: "global"},
	}

	// Lookup the namespaces without a token and expect a failure
	{
		var resp structs.NamespaceSetResponse
		assert.NotNil(msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespaces", get, &resp))
	}

	// Try with an non-management token
	get.AuthToken = validToken.SecretID
	{
		var resp structs.NamespaceSetResponse
		assert.NotNil(msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespaces", get, &resp))
	}

	// Try with a root token
	get.AuthToken = root.SecretID
	{
		var resp structs.NamespaceSetResponse
		assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespaces", get, &resp))
		assert.EqualValues(1000, resp.Index)
		assert.Len(resp.Namespaces, 2)
		assert.Contains(resp.Namespaces, ns1.Name)
		assert.Contains(resp.Namespaces, ns2.Name)
	}
}

func TestNamespaceEndpoint_GetNamespaces_Blocking(t *testing.T) {
	ci.Parallel(t)
	assert := assert.New(t)
	s1, cleanupS1 := TestServer(t, nil)
	defer cleanupS1()
	state := s1.fsm.State()
	codec := rpcClient(t, s1)
	testutil.WaitForLeader(t, s1.RPC)

	// Create the namespaces
	ns1 := mock.Namespace()
	ns2 := mock.Namespace()

	// First create an namespace
	time.AfterFunc(100*time.Millisecond, func() {
		assert.Nil(state.UpsertNamespaces(100, []*structs.Namespace{ns1}))
	})

	// Upsert the namespace we are watching later
	time.AfterFunc(200*time.Millisecond, func() {
		assert.Nil(state.UpsertNamespaces(200, []*structs.Namespace{ns2}))
	})

	// Lookup the namespace
	req := &structs.NamespaceSetRequest{
		Namespaces: []string{ns2.Name},
		QueryOptions: structs.QueryOptions{
			Region:        "global",
			MinQueryIndex: 150,
		},
	}
	var resp structs.NamespaceSetResponse
	start := time.Now()
	assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespaces", req, &resp))
	assert.EqualValues(200, resp.Index)
	assert.Len(resp.Namespaces, 1)
	assert.Contains(resp.Namespaces, ns2.Name)

	if elapsed := time.Since(start); elapsed < 200*time.Millisecond {
		t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
	}

	// Namespace delete triggers watches
	time.AfterFunc(100*time.Millisecond, func() {
		assert.Nil(state.DeleteNamespaces(300, []string{ns2.Name}))
	})

	req.QueryOptions.MinQueryIndex = 250
	var resp2 structs.NamespaceSetResponse
	start = time.Now()
	assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespaces", req, &resp2))
	assert.EqualValues(300, resp2.Index)
	assert.Empty(resp2.Namespaces)

	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
		t.Fatalf("should block (returned in %s) %#v", elapsed, resp2)
	}
}

func TestNamespaceEndpoint_List(t *testing.T) {
	ci.Parallel(t)
	assert := assert.New(t)
	s1, cleanupS1 := TestServer(t, nil)
	defer cleanupS1()
	codec := rpcClient(t, s1)
	testutil.WaitForLeader(t, s1.RPC)

	// Create the register request
	ns1 := mock.Namespace()
	ns2 := mock.Namespace()

	ns1.Name = "aaaaaaaa-3350-4b4b-d185-0e1992ed43e9"
	ns2.Name = "aaaabbbb-3350-4b4b-d185-0e1992ed43e9"
	assert.Nil(s1.fsm.State().UpsertNamespaces(1000, []*structs.Namespace{ns1, ns2}))

	// Lookup the namespaces
	get := &structs.NamespaceListRequest{
		QueryOptions: structs.QueryOptions{Region: "global"},
	}
	var resp structs.NamespaceListResponse
	assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.ListNamespaces", get, &resp))
	assert.EqualValues(1000, resp.Index)
	assert.Len(resp.Namespaces, 3)

	// Lookup the namespaces by prefix
	get = &structs.NamespaceListRequest{
		QueryOptions: structs.QueryOptions{
			Region: "global",
			Prefix: "aaaabb",
		},
	}
	var resp2 structs.NamespaceListResponse
	assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.ListNamespaces", get, &resp2))
	assert.EqualValues(1000, resp2.Index)
	assert.Len(resp2.Namespaces, 1)
}

func TestNamespaceEndpoint_List_ACL(t *testing.T) {
	ci.Parallel(t)
	assert := assert.New(t)
	s1, root, cleanupS1 := TestACLServer(t, nil)
	defer cleanupS1()
	codec := rpcClient(t, s1)
	testutil.WaitForLeader(t, s1.RPC)

	// Create the register request
	ns1 := mock.Namespace()
	ns2 := mock.Namespace()
	state := s1.fsm.State()

	ns1.Name = "aaaaaaaa-3350-4b4b-d185-0e1992ed43e9"
	ns2.Name = "bbbbbbbb-3350-4b4b-d185-0e1992ed43e9"
	assert.Nil(s1.fsm.State().UpsertNamespaces(1000, []*structs.Namespace{ns1, ns2}))

	validDefToken := mock.CreatePolicyAndToken(t, state, 1001, "test-def-valid",
		mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityReadFS}))
	validMultiToken := mock.CreatePolicyAndToken(t, state, 1002, "test-multi-valid", fmt.Sprintf("%s\n%s",
		mock.NamespacePolicy(ns1.Name, "", []string{acl.NamespaceCapabilityReadJob}),
		mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityReadJob})))
	invalidToken := mock.CreatePolicyAndToken(t, state, 1003, "test-invalid",
		mock.NamespacePolicy("invalid-namespace", "", []string{acl.NamespaceCapabilityReadJob}))

	get := &structs.NamespaceListRequest{
		QueryOptions: structs.QueryOptions{Region: "global"},
	}

	// Lookup the namespaces without a token and expect a failure
	{
		var resp structs.NamespaceListResponse
		err := msgpackrpc.CallWithCodec(codec, "Namespace.ListNamespaces", get, &resp)
		assert.Nil(err)
		assert.Len(resp.Namespaces, 0)
	}

	// Try with an invalid token
	get.AuthToken = invalidToken.SecretID
	{
		var resp structs.NamespaceListResponse
		err := msgpackrpc.CallWithCodec(codec, "Namespace.ListNamespaces", get, &resp)
		assert.Nil(err)
		assert.Len(resp.Namespaces, 0)
	}

	// Try with a valid token for one
	get.AuthToken = validDefToken.SecretID
	{
		var resp structs.NamespaceListResponse
		assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.ListNamespaces", get, &resp))
		assert.EqualValues(1000, resp.Index)
		assert.Len(resp.Namespaces, 1)
	}

	// Try with a valid token for two
	get.AuthToken = validMultiToken.SecretID
	{
		var resp structs.NamespaceListResponse
		assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.ListNamespaces", get, &resp))
		assert.EqualValues(1000, resp.Index)
		assert.Len(resp.Namespaces, 2)
	}

	// Try with a root token
	get.AuthToken = root.SecretID
	{
		var resp structs.NamespaceListResponse
		assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.ListNamespaces", get, &resp))
		assert.EqualValues(1000, resp.Index)
		assert.Len(resp.Namespaces, 3)
	}
}

func TestNamespaceEndpoint_List_Blocking(t *testing.T) {
	ci.Parallel(t)
	assert := assert.New(t)
	s1, cleanupS1 := TestServer(t, nil)
	defer cleanupS1()
	state := s1.fsm.State()
	codec := rpcClient(t, s1)
	testutil.WaitForLeader(t, s1.RPC)

	// Create the namespace
	ns := mock.Namespace()

	// Upsert namespace triggers watches
	time.AfterFunc(100*time.Millisecond, func() {
		assert.Nil(state.UpsertNamespaces(200, []*structs.Namespace{ns}))
	})

	req := &structs.NamespaceListRequest{
		QueryOptions: structs.QueryOptions{
			Region:        "global",
			MinQueryIndex: 150,
		},
	}
	start := time.Now()
	var resp structs.NamespaceListResponse
	assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.ListNamespaces", req, &resp))

	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
		t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
	}
	assert.EqualValues(200, resp.Index)
	assert.Len(resp.Namespaces, 2)

	// Namespace deletion triggers watches
	time.AfterFunc(100*time.Millisecond, func() {
		assert.Nil(state.DeleteNamespaces(300, []string{ns.Name}))
	})

	req.MinQueryIndex = 200
	start = time.Now()
	var resp2 structs.NamespaceListResponse
	assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.ListNamespaces", req, &resp2))

	if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
		t.Fatalf("should block (returned in %s) %#v", elapsed, resp2)
	}
	assert.EqualValues(300, resp2.Index)
	assert.Len(resp2.Namespaces, 1)
}

func TestNamespaceEndpoint_DeleteNamespaces(t *testing.T) {
	ci.Parallel(t)
	assert := assert.New(t)
	s1, cleanupS1 := TestServer(t, nil)
	defer cleanupS1()
	codec := rpcClient(t, s1)
	testutil.WaitForLeader(t, s1.RPC)

	// Create the register request
	ns1 := mock.Namespace()
	ns2 := mock.Namespace()
	s1.fsm.State().UpsertNamespaces(1000, []*structs.Namespace{ns1, ns2})

	// Lookup the namespaces
	req := &structs.NamespaceDeleteRequest{
		Namespaces:   []string{ns1.Name, ns2.Name},
		WriteRequest: structs.WriteRequest{Region: "global"},
	}
	var resp structs.GenericResponse
	assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.DeleteNamespaces", req, &resp))
	assert.NotEqual(uint64(0), resp.Index)
}

func TestNamespaceEndpoint_DeleteNamespaces_NonTerminal_Local(t *testing.T) {
	ci.Parallel(t)
	assert := assert.New(t)
	s1, cleanupS1 := TestServer(t, nil)
	defer cleanupS1()
	codec := rpcClient(t, s1)
	testutil.WaitForLeader(t, s1.RPC)

	// Create the register request
	ns1 := mock.Namespace()
	ns2 := mock.Namespace()
	s1.fsm.State().UpsertNamespaces(1000, []*structs.Namespace{ns1, ns2})

	// Create a job in one
	j := mock.Job()
	j.Namespace = ns1.Name
	assert.Nil(s1.fsm.State().UpsertJob(structs.MsgTypeTestSetup, 1001, nil, j))

	// Lookup the namespaces
	req := &structs.NamespaceDeleteRequest{
		Namespaces:   []string{ns1.Name, ns2.Name},
		WriteRequest: structs.WriteRequest{Region: "global"},
	}
	var resp structs.GenericResponse
	err := msgpackrpc.CallWithCodec(codec, "Namespace.DeleteNamespaces", req, &resp)
	if assert.NotNil(err) {
		assert.Contains(err.Error(), "has non-terminal jobs")
	}
}

func TestNamespaceEndpoint_DeleteNamespaces_NonTerminal_Federated_ACL(t *testing.T) {
	ci.Parallel(t)
	assert := assert.New(t)
	s1, root, cleanupS1 := TestACLServer(t, func(c *Config) {
		c.Region = "region1"
		c.AuthoritativeRegion = "region1"
		c.ACLEnabled = true
	})
	defer cleanupS1()
	s2, _, cleanupS2 := TestACLServer(t, func(c *Config) {
		c.Region = "region2"
		c.AuthoritativeRegion = "region1"
		c.ACLEnabled = true
		c.ReplicationBackoff = 20 * time.Millisecond
		c.ReplicationToken = root.SecretID
	})
	defer cleanupS2()
	TestJoin(t, s1, s2)
	testutil.WaitForLeader(t, s1.RPC)
	testutil.WaitForLeader(t, s2.RPC)
	codec := rpcClient(t, s1)

	// Create the register request
	ns1 := mock.Namespace()
	s1.fsm.State().UpsertNamespaces(1000, []*structs.Namespace{ns1})

	testutil.WaitForResult(func() (bool, error) {
		state := s2.State()
		out, err := state.NamespaceByName(nil, ns1.Name)
		return out != nil, err
	}, func(err error) {
		t.Fatalf("should replicate namespace")
	})

	// Create a job in the namespace on the non-authority
	j := mock.Job()
	j.Namespace = ns1.Name
	assert.Nil(s2.fsm.State().UpsertJob(structs.MsgTypeTestSetup, 1001, nil, j))

	// Delete the namespaces without the correct permissions
	req := &structs.NamespaceDeleteRequest{
		Namespaces: []string{ns1.Name},
		WriteRequest: structs.WriteRequest{
			Region: "global",
		},
	}
	var resp structs.GenericResponse
	err := msgpackrpc.CallWithCodec(codec, "Namespace.DeleteNamespaces", req, &resp)
	if assert.NotNil(err) {
		assert.EqualError(err, structs.ErrPermissionDenied.Error())
	}

	// Try with a auth token
	req.AuthToken = root.SecretID
	var resp2 structs.GenericResponse
	err = msgpackrpc.CallWithCodec(codec, "Namespace.DeleteNamespaces", req, &resp2)
	if assert.NotNil(err) {
		assert.Contains(err.Error(), "has non-terminal jobs")
	}
}

func TestNamespaceEndpoint_DeleteNamespaces_ACL(t *testing.T) {
	ci.Parallel(t)
	assert := assert.New(t)
	s1, root, cleanupS1 := TestACLServer(t, nil)
	defer cleanupS1()
	codec := rpcClient(t, s1)
	testutil.WaitForLeader(t, s1.RPC)

	ns1 := mock.Namespace()
	ns2 := mock.Namespace()
	state := s1.fsm.State()
	s1.fsm.State().UpsertNamespaces(1000, []*structs.Namespace{ns1, ns2})

	// Create the policy and tokens
	invalidToken := mock.CreatePolicyAndToken(t, state, 1003, "test-invalid",
		mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityReadJob}))

	req := &structs.NamespaceDeleteRequest{
		Namespaces:   []string{ns1.Name, ns2.Name},
		WriteRequest: structs.WriteRequest{Region: "global"},
	}

	// Delete namespaces without a token and expect failure
	{
		var resp structs.GenericResponse
		err := msgpackrpc.CallWithCodec(codec, "Namespace.DeleteNamespaces", req, &resp)
		assert.NotNil(err)
		assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())

		// Check we did not delete the namespaces
		out, err := s1.fsm.State().NamespaceByName(nil, ns1.Name)
		assert.Nil(err)
		assert.NotNil(out)

		out, err = s1.fsm.State().NamespaceByName(nil, ns2.Name)
		assert.Nil(err)
		assert.NotNil(out)
	}

	// Try with an invalid token
	req.AuthToken = invalidToken.SecretID
	{
		var resp structs.GenericResponse
		err := msgpackrpc.CallWithCodec(codec, "Namespace.DeleteNamespaces", req, &resp)
		assert.NotNil(err)
		assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())

		// Check we did not delete the namespaces
		out, err := s1.fsm.State().NamespaceByName(nil, ns1.Name)
		assert.Nil(err)
		assert.NotNil(out)

		out, err = s1.fsm.State().NamespaceByName(nil, ns2.Name)
		assert.Nil(err)
		assert.NotNil(out)
	}

	// Try with a root token
	req.AuthToken = root.SecretID
	{
		var resp structs.GenericResponse
		assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.DeleteNamespaces", req, &resp))
		assert.NotEqual(uint64(0), resp.Index)

		// Check we deleted the namespaces
		out, err := s1.fsm.State().NamespaceByName(nil, ns1.Name)
		assert.Nil(err)
		assert.Nil(out)

		out, err = s1.fsm.State().NamespaceByName(nil, ns2.Name)
		assert.Nil(err)
		assert.Nil(out)
	}
}

func TestNamespaceEndpoint_DeleteNamespaces_Default(t *testing.T) {
	ci.Parallel(t)
	assert := assert.New(t)
	s1, cleanupS1 := TestServer(t, nil)
	defer cleanupS1()
	codec := rpcClient(t, s1)
	testutil.WaitForLeader(t, s1.RPC)

	// Delete the default namespace
	req := &structs.NamespaceDeleteRequest{
		Namespaces:   []string{structs.DefaultNamespace},
		WriteRequest: structs.WriteRequest{Region: "global"},
	}
	var resp structs.GenericResponse
	assert.NotNil(msgpackrpc.CallWithCodec(codec, "Namespace.DeleteNamespaces", req, &resp))
}

func TestNamespaceEndpoint_UpsertNamespaces(t *testing.T) {
	ci.Parallel(t)
	assert := assert.New(t)
	s1, cleanupS1 := TestServer(t, nil)
	defer cleanupS1()
	codec := rpcClient(t, s1)
	testutil.WaitForLeader(t, s1.RPC)

	// Create the register request
	ns1 := mock.Namespace()
	ns2 := mock.Namespace()

	// Lookup the namespaces
	req := &structs.NamespaceUpsertRequest{
		Namespaces:   []*structs.Namespace{ns1, ns2},
		WriteRequest: structs.WriteRequest{Region: "global"},
	}
	var resp structs.GenericResponse
	assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.UpsertNamespaces", req, &resp))
	assert.NotEqual(uint64(0), resp.Index)

	// Check we created the namespaces
	out, err := s1.fsm.State().NamespaceByName(nil, ns1.Name)
	assert.Nil(err)
	assert.NotNil(out)

	out, err = s1.fsm.State().NamespaceByName(nil, ns2.Name)
	assert.Nil(err)
	assert.NotNil(out)
}

func TestNamespaceEndpoint_UpsertNamespaces_ACL(t *testing.T) {
	ci.Parallel(t)
	assert := assert.New(t)
	s1, root, cleanupS1 := TestACLServer(t, nil)
	defer cleanupS1()
	codec := rpcClient(t, s1)
	testutil.WaitForLeader(t, s1.RPC)

	ns1 := mock.Namespace()
	ns2 := mock.Namespace()
	state := s1.fsm.State()

	// Create the policy and tokens
	invalidToken := mock.CreatePolicyAndToken(t, state, 1003, "test-invalid",
		mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityReadJob}))

	// Create the register request
	req := &structs.NamespaceUpsertRequest{
		Namespaces:   []*structs.Namespace{ns1, ns2},
		WriteRequest: structs.WriteRequest{Region: "global"},
	}

	// Upsert the namespace without a token and expect failure
	{
		var resp structs.GenericResponse
		err := msgpackrpc.CallWithCodec(codec, "Namespace.UpsertNamespaces", req, &resp)
		assert.NotNil(err)
		assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())

		// Check we did not create the namespaces
		out, err := s1.fsm.State().NamespaceByName(nil, ns1.Name)
		assert.Nil(err)
		assert.Nil(out)

		out, err = s1.fsm.State().NamespaceByName(nil, ns2.Name)
		assert.Nil(err)
		assert.Nil(out)
	}

	// Try with an invalid token
	req.AuthToken = invalidToken.SecretID
	{
		var resp structs.GenericResponse
		err := msgpackrpc.CallWithCodec(codec, "Namespace.UpsertNamespaces", req, &resp)
		assert.NotNil(err)
		assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())

		// Check we did not create the namespaces
		out, err := s1.fsm.State().NamespaceByName(nil, ns1.Name)
		assert.Nil(err)
		assert.Nil(out)

		out, err = s1.fsm.State().NamespaceByName(nil, ns2.Name)
		assert.Nil(err)
		assert.Nil(out)
	}

	// Try with a bogus token
	req.AuthToken = uuid.Generate()
	{
		var resp structs.GenericResponse
		err := msgpackrpc.CallWithCodec(codec, "Namespace.UpsertNamespaces", req, &resp)
		assert.NotNil(err)
		assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())

		// Check we did not create the namespaces
		out, err := s1.fsm.State().NamespaceByName(nil, ns1.Name)
		assert.Nil(err)
		assert.Nil(out)

		out, err = s1.fsm.State().NamespaceByName(nil, ns2.Name)
		assert.Nil(err)
		assert.Nil(out)
	}

	// Try with a root token
	req.AuthToken = root.SecretID
	{
		var resp structs.GenericResponse
		assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.UpsertNamespaces", req, &resp))
		assert.NotEqual(uint64(0), resp.Index)

		// Check we created the namespaces
		out, err := s1.fsm.State().NamespaceByName(nil, ns1.Name)
		assert.Nil(err)
		assert.NotNil(out)

		out, err = s1.fsm.State().NamespaceByName(nil, ns2.Name)
		assert.Nil(err)
		assert.NotNil(out)
	}
}
