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

package nomad

import (
	"testing"

	"github.com/hashicorp/nomad/ci"
	"github.com/hashicorp/nomad/nomad/mock"
	"github.com/hashicorp/nomad/nomad/structs"
	"github.com/hashicorp/nomad/testutil"
	"github.com/stretchr/testify/require"
)

func TestJobNamespaceConstraintCheckHook_Name(t *testing.T) {
	ci.Parallel(t)

	require.Equal(t, "namespace-constraint-check", new(jobNamespaceConstraintCheckHook).Name())
}

func TestJobNamespaceConstraintCheckHook_taskValidateDriver(t *testing.T) {
	ci.Parallel(t)

	cases := []struct {
		description string
		driver      string
		ns          *structs.Namespace
		result      bool
	}{
		{
			"No drivers enabled/disabled, allow all",
			"docker",
			&structs.Namespace{},
			true,
		},
		{
			"Only exec and docker are allowed 1/2",
			"docker",
			&structs.Namespace{
				Capabilities: &structs.NamespaceCapabilities{
					EnabledTaskDrivers: []string{"docker", "exec"}},
			},
			true,
		},
		{
			"Only exec and docker are allowed 2/2",
			"raw_exec",
			&structs.Namespace{
				Capabilities: &structs.NamespaceCapabilities{
					EnabledTaskDrivers: []string{"docker", "exec"}},
			},
			false,
		},
		{
			"disable takes precedence over enable",
			"docker",
			&structs.Namespace{
				Capabilities: &structs.NamespaceCapabilities{
					EnabledTaskDrivers:  []string{"docker"},
					DisabledTaskDrivers: []string{"docker"}},
			},
			false,
		},
		{
			"All drivers but docker are allowed 1/2",
			"docker",
			&structs.Namespace{
				Capabilities: &structs.NamespaceCapabilities{
					DisabledTaskDrivers: []string{"docker"}},
			},
			false,
		},
		{
			"All drivers but docker are allowed 2/2",
			"raw_exec",
			&structs.Namespace{
				Capabilities: &structs.NamespaceCapabilities{
					DisabledTaskDrivers: []string{"docker"}},
			},
			true,
		},
	}

	for _, c := range cases {
		var task = &structs.Task{Driver: c.driver}
		require.Equal(t, c.result, taskValidateDriver(task, c.ns), c.description)
	}
}

func TestJobNamespaceConstraintCheckHook_validate(t *testing.T) {
	ci.Parallel(t)
	s1, cleanupS1 := TestServer(t, nil)
	defer cleanupS1()
	testutil.WaitForLeader(t, s1.RPC)

	// Create a namespace
	ns := mock.Namespace()
	ns.Name = "default" // fix the name
	ns.Capabilities = &structs.NamespaceCapabilities{
		EnabledTaskDrivers:  []string{"docker", "qemu"},
		DisabledTaskDrivers: []string{"exec", "raw_exec"},
	}
	s1.fsm.State().UpsertNamespaces(1000, []*structs.Namespace{ns})

	hook := jobNamespaceConstraintCheckHook{srv: s1}
	job := mock.LifecycleJob()
	job.TaskGroups[0].Tasks[0].Driver = "docker"
	job.TaskGroups[0].Tasks[1].Driver = "qemu"
	job.TaskGroups[0].Tasks[2].Driver = "docker"
	job.TaskGroups[0].Tasks[3].Driver = "qemu"
	_, err := hook.Validate(job)
	require.Nil(t, err)

	job.TaskGroups[0].Tasks[2].Driver = "raw_exec"
	_, err = hook.Validate(job)
	require.Equal(t, err.Error(), "used task driver \"raw_exec\" is not allowed in namespace \"default\"")

	job.TaskGroups[0].Tasks[1].Driver = "exec"
	_, err = hook.Validate(job)
	require.Equal(t, err.Error(), "used task drivers [\"exec\" \"raw_exec\"] are not allowed in namespace \"default\"")
}
