1package command
2
3import (
4	"strings"
5	"testing"
6
7	"fmt"
8
9	"github.com/hashicorp/nomad/nomad/mock"
10	"github.com/hashicorp/nomad/nomad/structs"
11	"github.com/hashicorp/nomad/testutil"
12	"github.com/mitchellh/cli"
13	"github.com/posener/complete"
14	"github.com/stretchr/testify/assert"
15	"github.com/stretchr/testify/require"
16)
17
18func TestJobEvalCommand_Implements(t *testing.T) {
19	t.Parallel()
20	var _ cli.Command = &JobEvalCommand{}
21}
22
23func TestJobEvalCommand_Fails(t *testing.T) {
24	t.Parallel()
25	ui := new(cli.MockUi)
26	cmd := &JobEvalCommand{Meta: Meta{Ui: ui}}
27
28	// Fails on misuse
29	if code := cmd.Run([]string{"some", "bad", "args"}); code != 1 {
30		t.Fatalf("expected exit code 1, got: %d", code)
31	}
32	if out := ui.ErrorWriter.String(); !strings.Contains(out, commandErrorText(cmd)) {
33		t.Fatalf("expected help output, got: %s", out)
34	}
35	ui.ErrorWriter.Reset()
36
37	// Fails when job ID is not specified
38	if code := cmd.Run([]string{}); code != 1 {
39		t.Fatalf("expect exit 1, got: %d", code)
40	}
41	if out := ui.ErrorWriter.String(); !strings.Contains(out, "This command takes one argument") {
42		t.Fatalf("unexpected error: %v", out)
43	}
44	ui.ErrorWriter.Reset()
45
46}
47
48func TestJobEvalCommand_Run(t *testing.T) {
49	t.Parallel()
50	srv, client, url := testServer(t, true, nil)
51	defer srv.Shutdown()
52
53	// Wait for a node to be ready
54	testutil.WaitForResult(func() (bool, error) {
55		nodes, _, err := client.Nodes().List(nil)
56		if err != nil {
57			return false, err
58		}
59		for _, node := range nodes {
60			if node.Status == structs.NodeStatusReady {
61				return true, nil
62			}
63		}
64		return false, fmt.Errorf("no ready nodes")
65	}, func(err error) {
66		t.Fatalf("err: %v", err)
67	})
68
69	ui := new(cli.MockUi)
70	cmd := &JobEvalCommand{Meta: Meta{Ui: ui}}
71	require := require.New(t)
72
73	state := srv.Agent.Server().State()
74
75	// Create a job
76	job := mock.Job()
77	err := state.UpsertJob(11, job)
78	require.Nil(err)
79
80	job, err = state.JobByID(nil, structs.DefaultNamespace, job.ID)
81	require.Nil(err)
82
83	// Create a failed alloc for the job
84	alloc := mock.Alloc()
85	alloc.Job = job
86	alloc.JobID = job.ID
87	alloc.TaskGroup = job.TaskGroups[0].Name
88	alloc.Namespace = job.Namespace
89	alloc.ClientStatus = structs.AllocClientStatusFailed
90	err = state.UpsertAllocs(12, []*structs.Allocation{alloc})
91	require.Nil(err)
92
93	if code := cmd.Run([]string{"-address=" + url, "-force-reschedule", "-detach", job.ID}); code != 0 {
94		t.Fatalf("expected exit 0, got: %d", code)
95	}
96
97	// Lookup alloc again
98	alloc, err = state.AllocByID(nil, alloc.ID)
99	require.NotNil(alloc)
100	require.Nil(err)
101	require.True(*alloc.DesiredTransition.ForceReschedule)
102
103}
104
105func TestJobEvalCommand_AutocompleteArgs(t *testing.T) {
106	assert := assert.New(t)
107	t.Parallel()
108
109	srv, _, url := testServer(t, true, nil)
110	defer srv.Shutdown()
111
112	ui := new(cli.MockUi)
113	cmd := &JobEvalCommand{Meta: Meta{Ui: ui, flagAddress: url}}
114
115	// Create a fake job
116	state := srv.Agent.Server().State()
117	j := mock.Job()
118	assert.Nil(state.UpsertJob(1000, j))
119
120	prefix := j.ID[:len(j.ID)-5]
121	args := complete.Args{Last: prefix}
122	predictor := cmd.AutocompleteArgs()
123
124	res := predictor.Predict(args)
125	assert.Equal(1, len(res))
126	assert.Equal(j.ID, res[0])
127}
128